Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions tapir/coop/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from tapir.core.config import sidebar_link_groups
from tapir.settings import (
PERMISSION_ACCOUNTING_VIEW,
PERMISSION_COOP_ADMIN,
PERMISSION_COOP_MANAGE,
PERMISSION_COOP_VIEW,
)
Expand Down Expand Up @@ -54,6 +55,13 @@ def register_sidebar_link_groups():
ordering=3,
required_permissions=[PERMISSION_ACCOUNTING_VIEW],
)
management_group.add_link(
display_name=_("Coop Management"),
material_icon="admin_panel_settings",
url=reverse_lazy("coop:coop_management"),
ordering=7,
required_permissions=[PERMISSION_COOP_ADMIN],
)

@staticmethod
def register_emails():
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from tapir import settings
from tapir.coop import pdfs
from tapir.coop.models import ShareOwner
from tapir.coop.models import CoopAddress, ShareOwner
from tapir.coop.pdfs import CONTENT_TYPE_PDF
from tapir.coop.services.number_of_shares_service import NumberOfSharesService
from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
Expand Down Expand Up @@ -56,8 +56,9 @@ def get_attachments(self) -> list:
]

def get_extra_context(self) -> dict:
coop_address = CoopAddress.objects.first()
return {
"organization_name": settings.COOP_NAME,
"organization_name": coop_address.coop_name,
"contact_email_address": settings.EMAIL_ADDRESS_MEMBER_OFFICE,
"management_email_address": settings.EMAIL_ADDRESS_MANAGEMENT,
"supervisors_email_address": settings.EMAIL_ADDRESS_SUPERVISORS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
from django.utils import timezone
from django.utils.translation import gettext_lazy as _

from tapir import settings
from tapir.coop import pdfs
from tapir.coop.models import ShareOwner
from tapir.coop.models import CoopAddress, ShareOwner
from tapir.coop.pdfs import CONTENT_TYPE_PDF
from tapir.coop.services.number_of_shares_service import NumberOfSharesService
from tapir.core.tapir_email_builder_base import TapirEmailBuilderBase
Expand Down Expand Up @@ -56,7 +55,8 @@ def get_attachments(self) -> list:
]

def get_extra_context(self) -> dict:
return {"organization_name": settings.COOP_NAME}
coop_address = CoopAddress.objects.first()
return {"organization_name": coop_address.coop_name}

@classmethod
def get_dummy_version(cls) -> TapirEmailBuilderBase | None:
Expand Down
13 changes: 13 additions & 0 deletions tapir/coop/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
COOP_SHARE_PRICE,
)
from tapir.coop.models import (
CoopAddress,
DraftUser,
IncomingPayment,
MembershipPause,
Expand Down Expand Up @@ -466,3 +467,15 @@ def clean_additional_information(self):
_("Additional information must not exceed 500 characters.")
)
return additional_information


class CoopAddressForm(forms.ModelForm):
class Meta:
model = CoopAddress
fields = [
"coop_name",
"coop_full_name",
"street",
"place",
"country",
]
46 changes: 46 additions & 0 deletions tapir/coop/migrations/0056_coopaddress.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Generated by Django 5.2.16 on 2026-07-18 06:04

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
(
"coop",
"0055_membershippauseupdatedlogentry_coop_member_old_val_6e6554_gin_and_more",
),
]

operations = [
migrations.CreateModel(
name="CoopAddress",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"coop_name",
models.CharField(default="SuperCoop Berlin", max_length=255),
),
(
"coop_full_name",
models.CharField(
blank=True, default="SuperCoop Berlin eG", max_length=255
),
),
(
"street",
models.CharField(default="Oudenarder Straße 16", max_length=255),
),
("place", models.CharField(default="13347 Berlin", max_length=255)),
("country", models.CharField(default="Deutschland", max_length=255)),
],
),
]
24 changes: 24 additions & 0 deletions tapir/coop/migrations/0057_create_default_coop_address.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Generated by Django 5.2.16 on 2026-07-18 07:09

from django.db import migrations


def create_default_coop_address(apps, schema_editor):
CoopAddress = apps.get_model("coop", "CoopAddress")
CoopAddress.objects.get_or_create()


def delete_default_coop_address(apps, schema_editor):
CoopAddress = apps.get_model("coop", "CoopAddress")
CoopAddress.objects.all().delete()


class Migration(migrations.Migration):

dependencies = [
("coop", "0056_coopaddress"),
]

operations = [
migrations.RunPython(create_default_coop_address, delete_default_coop_address),
]
21 changes: 20 additions & 1 deletion tapir/coop/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField

from tapir import utils
from tapir import settings, utils
from tapir.accounts.models import TapirUser
from tapir.coop.config import COOP_ENTRY_AMOUNT, COOP_SHARE_PRICE
from tapir.coop.services.investing_status_service import InvestingStatusService
Expand Down Expand Up @@ -866,3 +866,22 @@ def populate(
return super().populate_base(
actor=actor, share_owner=model.share_owner, model=model
)


class CoopAddress(models.Model):
coop_name = models.CharField(
max_length=255, blank=False, null=False, default=settings.COOP_NAME_DEFAULT
)
coop_full_name = models.CharField(
max_length=255, blank=True, default=settings.COOP_FULL_NAME_DEFAULT
)
street = models.CharField(
max_length=255, blank=False, null=False, default=settings.COOP_STREET_DEFAULT
)
place = models.CharField(
max_length=255, blank=False, null=False, default=settings.COOP_PLACE_DEFAULT
)
country = models.CharField(max_length=255, default="Deutschland")

def __str__(self):
return f"coop address is {self.street} {self.place} {self.country}"
33 changes: 19 additions & 14 deletions tapir/coop/pdfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from weasyprint import Document

from tapir.coop import config
from tapir.coop.models import ShareOwner
from tapir.coop.models import CoopAddress, ShareOwner
from tapir.utils.pdfs import render_pdf

CONTENT_TYPE_PDF = "application/pdf"
Expand All @@ -13,6 +13,8 @@
def get_shareowner_membership_confirmation_pdf(
share_owner: ShareOwner, num_shares: int, date: datetime.date
):
coop_address = CoopAddress.objects.first()

templates = [
"coop/pdf/membership_confirmation_pdf.html",
"coop/pdf/membership_confirmation_pdf.default.html",
Expand All @@ -21,11 +23,11 @@ def get_shareowner_membership_confirmation_pdf(
"share_owner": share_owner,
"num_shares": num_shares,
"date": date,
"COOP_NAME": settings.COOP_NAME,
"COOP_NAME": coop_address.coop_name,
"EMAIL_ADDRESS_MEMBER_OFFICE": settings.EMAIL_ADDRESS_MEMBER_OFFICE,
"COOP_FULL_NAME": settings.COOP_FULL_NAME,
"COOP_STREET": settings.COOP_STREET,
"COOP_PLACE": settings.COOP_PLACE,
"COOP_FULL_NAME": coop_address.coop_full_name,
"COOP_STREET": coop_address.street,
"COOP_PLACE": coop_address.place,
}
return render_pdf(
templates=templates,
Expand All @@ -35,15 +37,16 @@ def get_shareowner_membership_confirmation_pdf(


def get_membership_agreement_pdf(share_owner=None, num_shares=1):
coop_address = CoopAddress.objects.first()
templates = [
"coop/pdf/membership_agreement_pdf.html",
"coop/pdf/membership_agreement_pdf.default.html",
]
context = {
"share_owner": share_owner,
"coop_full_name": settings.COOP_FULL_NAME,
"coop_street": settings.COOP_STREET,
"coop_place": settings.COOP_PLACE,
"coop_full_name": coop_address.coop_full_name,
"coop_street": coop_address.street,
"coop_place": coop_address.place,
"share_price": config.COOP_SHARE_PRICE,
"entry_amount": config.COOP_ENTRY_AMOUNT,
"num_shares": num_shares,
Expand All @@ -58,6 +61,7 @@ def get_membership_agreement_pdf(share_owner=None, num_shares=1):
def get_confirmation_extra_shares_pdf(
share_owner: ShareOwner, num_shares: int, date: datetime.date
) -> Document:
coop_address = CoopAddress.objects.first()
templates = [
"coop/pdf/extra_shares_confirmation_pdf.html",
"coop/pdf/extra_shares_confirmation_pdf.default.html",
Expand All @@ -67,9 +71,9 @@ def get_confirmation_extra_shares_pdf(
"num_shares": num_shares,
"date": date,
"member_number": share_owner.id,
"coop_full_name": settings.COOP_FULL_NAME,
"coop_street": settings.COOP_STREET,
"coop_place": settings.COOP_PLACE,
"coop_full_name": coop_address.coop_full_name,
"coop_street": coop_address.street,
"coop_place": coop_address.place,
"contact_email_address": settings.EMAIL_ADDRESS_MEMBER_OFFICE,
}
return render_pdf(
Expand All @@ -85,6 +89,7 @@ def generate_share_request_pdf(
additional_information,
generation_time: datetime.datetime,
):
coop_address = CoopAddress.objects.first()
templates = [
"coop/pdf/membership_agreement_pdf.html",
"coop/pdf/membership_agreement_pdf.default.html",
Expand All @@ -94,9 +99,9 @@ def generate_share_request_pdf(
"num_additional_shares": num_shares,
"additional_to_pay": num_shares * config.COOP_SHARE_PRICE,
"additional_information": additional_information,
"coop_full_name": settings.COOP_FULL_NAME,
"coop_street": settings.COOP_STREET,
"coop_place": settings.COOP_PLACE,
"coop_full_name": coop_address.coop_full_name,
"coop_street": coop_address.street,
"coop_place": coop_address.place,
"contact_email_address": settings.EMAIL_ADDRESS_MEMBER_OFFICE,
"hide_user_signing_field": True,
"generation_time": generation_time,
Expand Down
19 changes: 19 additions & 0 deletions tapir/coop/tests/test_coop_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from django.urls import reverse

from tapir import settings
from tapir.utils.tests_utils import PermissionTestMixin, TapirFactoryTestBase


class TestCoopManagement(PermissionTestMixin, TapirFactoryTestBase):

def get_allowed_groups(self):
return [
settings.GROUP_VORSTAND,
]

def do_request(self):
return self.client.post(
reverse("coop:coop_management"),
data={"street": "221B Baker Street"},
follow=True,
)
3 changes: 3 additions & 0 deletions tapir/coop/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,7 @@
views.MemberSelfRegisterApiView.as_view(),
name="member_self_register",
),
path(
"coop-management/", views.CoopManagementView.as_view(), name="coop_management"
),
]
1 change: 1 addition & 0 deletions tapir/coop/views/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .coop_management import *
from .draftuser import *
from .incoming_payments import *
from .management import *
Expand Down
24 changes: 24 additions & 0 deletions tapir/coop/views/coop_management.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.urls import reverse_lazy
from django.views.generic import UpdateView

from tapir.coop.forms import CoopAddressForm
from tapir.coop.models import CoopAddress
from tapir.core.views import TapirFormMixin
from tapir.settings import PERMISSION_COOP_ADMIN


class CoopManagementView(
LoginRequiredMixin,
PermissionRequiredMixin,
TapirFormMixin,
UpdateView,
):
model = CoopAddress
form_class = CoopAddressForm
success_url = reverse_lazy("accounts:user_me")
permission_required = PERMISSION_COOP_ADMIN

def get_object(self, queryset=None):
obj, created = CoopAddress.objects.get_or_create(pk=1)
return obj
6 changes: 4 additions & 2 deletions tapir/coop/views/shareowner.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
)
from tapir.coop.models import (
MEMBER_STATUS_CHOICES,
CoopAddress,
CreateShareOwnershipsLogEntry,
DeleteShareOwnershipLogEntry,
ExtraSharesForAccountingRecap,
Expand All @@ -83,7 +84,6 @@
from tapir.log.util import freeze_for_log
from tapir.log.views import UpdateViewLogMixin
from tapir.settings import (
DEFAULT_FROM_EMAIL,
PERMISSION_ACCOUNTS_MANAGE,
PERMISSION_COOP_ADMIN,
PERMISSION_COOP_MANAGE,
Expand Down Expand Up @@ -1067,10 +1067,12 @@ def form_valid(self, form):
share_owner, num_shares, additional_information, generation_time
).write_pdf()

coop_address = CoopAddress.objects.first()
from_email = f"{coop_address.coop_name} Mitgliederbüro <{settings.EMAIL_ADDRESS_MEMBER_OFFICE}>"
email = EmailMultiAlternatives(
subject=f"Neue Beteiligungserklärung: {filename}",
body="Eine neue Beteiligungserklärung wurde eingereicht.",
from_email=DEFAULT_FROM_EMAIL,
from_email=from_email,
to=[settings.EMAIL_ADDRESS_MEMBER_OFFICE],
)
email.attach(filename, pdf, "application/pdf")
Expand Down
7 changes: 5 additions & 2 deletions tapir/core/tapir_email_builder_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ def get_full_context(
member_infos,
tapir_user: TapirUser,
) -> dict:
from tapir.coop.models import CoopAddress

coop_address = CoopAddress.objects.get()
if self.context is None:
self.context = {
"share_owner": share_owner,
"tapir_user": tapir_user,
"member_infos": member_infos,
"coop_name": settings.COOP_NAME,
"coop_full_name": settings.COOP_FULL_NAME,
"coop_name": coop_address.coop_name,
"coop_full_name": coop_address.coop_full_name,
"email_unique_id": self.get_unique_id(),
} | self.get_extra_context() # '|' is the union operator for dictionaries.

Expand Down
Loading