From 4182fe1c645897b9f45561e31a70cd8257cc25f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Valenta?= <git@imaniti.org> Date: Thu, 16 Mar 2023 23:17:13 +0100 Subject: [PATCH] wip - added new contract button, moving representatives to contracts --- contracts/admin.py | 51 +++--- ...ter_contractee_address_country_and_more.py | 23 +++ ...ntractcontracteerepresentative_and_more.py | 55 ++++++ contracts/models.py | 168 ++++++++---------- registry/templates/admin/base_site.html | 2 +- registry/templates/admin/index.html | 18 +- static_src/base.css | 6 +- users/models.py | 5 + 8 files changed, 199 insertions(+), 129 deletions(-) create mode 100644 contracts/migrations/0002_alter_contractee_address_country_and_more.py create mode 100644 contracts/migrations/0003_contractcontracteerepresentative_and_more.py diff --git a/contracts/admin.py b/contracts/admin.py index 5ff3a2b..b680464 100644 --- a/contracts/admin.py +++ b/contracts/admin.py @@ -10,15 +10,15 @@ from .forms import ContractAdminForm, ContractFileAdminForm, SigneeAdminForm from .models import ( Contract, Contractee, - ContracteeRepresentative, ContracteeSignature, ContractFile, ContractFilingArea, ContractIntent, ContractIssue, ContractType, + ContractContracteeRepresentative, + ContractSigneeRepresentative, Signee, - SigneeRepresentative, SigneeSignature, ) @@ -73,6 +73,17 @@ class ContractIntentInline(admin.TabularInline): extra = 0 +class ContractContracteeRepresentativeInline(admin.TabularInline): + form = ContractFileAdminForm + model = ContractContracteeRepresentative + extra = 0 + + +class ContractSigneeRepresentativeInline(admin.TabularInline): + model = ContractSigneeRepresentative + extra = 0 + + class ContractAdmin(MarkdownxGuardedModelAdmin): form = ContractAdminForm @@ -83,8 +94,22 @@ class ContractAdmin(MarkdownxGuardedModelAdmin): SigneeSignatureInline, ContractFileInline, ContractIntentInline, + ContractContracteeRepresentativeInline, + ContractSigneeRepresentativeInline, ) + def __init__(self, *args, **kwargs): + from .models import Contract + + super().__init__(*args, **kwargs) + + if hasattr(self, "instance"): + self.fields["contractee_representatives"].queryset = ( + Contract + .objects + .filter(contract=self.instance) + ) + def get_fieldsets(self, request, obj=None): fieldsets = [ ( @@ -205,11 +230,6 @@ class ContractAdmin(MarkdownxGuardedModelAdmin): # BEGIN Signing parties -class SigneeRepresentativeInline(admin.TabularInline): - model = SigneeRepresentative - extra = 0 - - class SigneeAdmin(MarkdownxGuardedModelAdmin): form = SigneeAdminForm @@ -232,8 +252,6 @@ class SigneeAdmin(MarkdownxGuardedModelAdmin): list_filter = ("entity_type",) list_display = ("name", "entity_type") - inlines = (SigneeRepresentativeInline,) - def load_ares_data_button(self, obj): return format_html( "<button type=\"button\" id=\"load_ares_data\">Načíst data</button>" @@ -243,24 +261,14 @@ class SigneeAdmin(MarkdownxGuardedModelAdmin): load_ares_data_button.short_description = "ARES" -class ContracteeRepresentativeInline(admin.TabularInline): - form = ContractFileAdminForm - model = ContracteeRepresentative - extra = 0 - - -class ContracteeAdmin(MarkdownxGuardedModelAdmin): - inlines = (ContracteeRepresentativeInline,) - - # END Signing parties for model in ( SigneeSignature, ContracteeSignature, - SigneeRepresentative, - ContracteeRepresentative, + ContractSigneeRepresentative, + ContractContracteeRepresentative, ContractType, ContractIntent, ): @@ -275,6 +283,5 @@ for model in ( admin.site.register(model, MarkdownxGuardedModelAdmin) admin.site.register(Signee, SigneeAdmin) -admin.site.register(Contractee, ContracteeAdmin) admin.site.register(Contract, ContractAdmin) diff --git a/contracts/migrations/0002_alter_contractee_address_country_and_more.py b/contracts/migrations/0002_alter_contractee_address_country_and_more.py new file mode 100644 index 0000000..026c299 --- /dev/null +++ b/contracts/migrations/0002_alter_contractee_address_country_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.1.4 on 2023-03-16 21:13 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='contractee', + name='address_country', + field=models.CharField(default='Česká Republika', max_length=256, verbose_name='Země'), + ), + migrations.AlterField( + model_name='signee', + name='address_country', + field=models.CharField(default='Česká Republika', max_length=256, verbose_name='Země'), + ), + ] diff --git a/contracts/migrations/0003_contractcontracteerepresentative_and_more.py b/contracts/migrations/0003_contractcontracteerepresentative_and_more.py new file mode 100644 index 0000000..29fdb31 --- /dev/null +++ b/contracts/migrations/0003_contractcontracteerepresentative_and_more.py @@ -0,0 +1,55 @@ +# Generated by Django 4.1.4 on 2023-03-16 21:56 + +import contracts.models +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contracts', '0002_alter_contractee_address_country_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='ContractContracteeRepresentative', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256, verbose_name='Jméno')), + ('function', models.CharField(blank=True, max_length=256, null=True, verbose_name='Funkce')), + ('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contractee_representatives', to='contracts.contract', verbose_name='Smlouva')), + ('contractee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contractee_representatives', to='contracts.contractee', verbose_name='Naše smluvní strana')), + ], + options={ + 'verbose_name': 'Zástupce naší smluvní strany', + 'verbose_name_plural': 'Zástupci naší smluvní strany', + }, + bases=(contracts.models.RepresentativeMixin, models.Model), + ), + migrations.CreateModel( + name='ContractSigneeRepresentative', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=256, verbose_name='Jméno')), + ('function', models.CharField(blank=True, max_length=256, null=True, verbose_name='Funkce')), + ('contract', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='signee_representatives', to='contracts.contract', verbose_name='Smlouva')), + ('signee', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='representatives', to='contracts.signee', verbose_name='Druhá smluvní strana')), + ], + options={ + 'verbose_name': 'Zástupce druhé smluvní strany', + 'verbose_name_plural': 'Zástupci druhé smluvní strany', + }, + bases=(contracts.models.RepresentativeMixin, models.Model), + ), + migrations.RemoveField( + model_name='signeerepresentative', + name='signee', + ), + migrations.DeleteModel( + name='ContracteeRepresentative', + ), + migrations.DeleteModel( + name='SigneeRepresentative', + ), + ] diff --git a/contracts/models.py b/contracts/models.py index 509ed7d..ea19f34 100644 --- a/contracts/models.py +++ b/contracts/models.py @@ -115,44 +115,9 @@ class Signee(models.Model): if self.department is not None: result += f", {self.department}" - representative_names = [] - - for representative in self.representatives.all(): - representative_names.append(representative.name) - - if len(representative_names) != 0: - result += " - zástupci " + ", ".join(representative_names) - return result -class SigneeRepresentative(RepresentativeMixin, models.Model): - signee = models.ForeignKey( - Signee, - on_delete=models.CASCADE, - related_name="representatives", - verbose_name="Smluvní strana", - ) - - name = models.CharField( - max_length=256, - verbose_name="Jméno", - ) - - function = models.CharField( - max_length=256, - blank=True, - null=True, - verbose_name="Funkce", - ) - - class Meta: - app_label = "contracts" - - verbose_name = "Zástupce" - verbose_name_plural = "Zástupci" - - class Contractee(models.Model): name = models.CharField( max_length=256, @@ -218,44 +183,9 @@ class Contractee(models.Model): if self.department is not None: result += f", {self.department}" - representative_names = [] - - for representative in self.representatives.all(): - representative_names.append(representative.name) - - if len(representative_names) != 0: - result += " - zástupci " + ", ".join(representative_names) - return result -class ContracteeRepresentative(RepresentativeMixin, models.Model): - contractee = models.ForeignKey( - Contractee, - on_delete=models.CASCADE, - related_name="representatives", - verbose_name="Smluvní strana", - ) - - name = models.CharField( - max_length=256, - verbose_name="Jméno", - ) - - function = models.CharField( - max_length=256, - blank=True, - null=True, - verbose_name="Funkce", - ) - - class Meta: - app_label = "contracts" - - verbose_name = "Zástupce" - verbose_name_plural = "Zástupci" - - class ContractType(NameStrMixin, models.Model): name = models.CharField( max_length=32, @@ -510,6 +440,74 @@ class Contract(NameStrMixin, models.Model): ).all() +class ContractContracteeRepresentative(RepresentativeMixin, models.Model): + contract = models.ForeignKey( + Contract, + on_delete=models.CASCADE, + related_name="contractee_representatives", + verbose_name="Smlouva" + ) + + contractee = models.ForeignKey( + Contractee, + on_delete=models.CASCADE, + related_name="contractee_representatives", + verbose_name="Naše smluvní strana", + ) + + name = models.CharField( + max_length=256, + verbose_name="Jméno", + ) + + function = models.CharField( + max_length=256, + blank=True, + null=True, + verbose_name="Funkce", + ) + + class Meta: + app_label = "contracts" + + verbose_name = "Zástupce naší smluvní strany" + verbose_name_plural = "Zástupci naší smluvní strany" + + +class ContractSigneeRepresentative(RepresentativeMixin, models.Model): + contract = models.ForeignKey( + Contract, + on_delete=models.CASCADE, + related_name="signee_representatives", + verbose_name="Smlouva" + ) + + signee = models.ForeignKey( + Signee, + on_delete=models.CASCADE, + related_name="representatives", + verbose_name="Druhá smluvní strana", + ) + + name = models.CharField( + max_length=256, + verbose_name="Jméno", + ) + + function = models.CharField( + max_length=256, + blank=True, + null=True, + verbose_name="Funkce", + ) + + class Meta: + app_label = "contracts" + + verbose_name = "Zástupce druhé smluvní strany" + verbose_name_plural = "Zástupci druhé smluvní strany" + + class ContractFile(NameStrMixin, models.Model): name = models.CharField( max_length=128, @@ -567,20 +565,7 @@ class ContracteeSignature(models.Model): verbose_name_plural = "Podpisy našich smluvních stran" def __str__(self) -> str: - representative_names = [] - - for representative in self.contractee.representatives.all(): - representative_names.append(representative.name) - - representatives = ", ".join(representative_names) - result = self.contractee.name - - if len(representatives) != 0: - result += f" - zastoupena {representatives}" - - result += f", {self.date}" - - return result + return f"{self.contractee.name} - {self.date}" class SigneeSignature(models.Model): @@ -609,20 +594,7 @@ class SigneeSignature(models.Model): verbose_name_plural = "Podpisy ostatních smluvních stran" def __str__(self) -> str: - representative_names = [] - - for representative in self.signee.representatives.all(): - representative_names.append(representative.name) - - representatives = ", ".join(representative_names) - result = self.signee.name - - if len(representatives) != 0: - result += f" - zastoupena {representatives}" - - result += f", {self.date}" - - return result + return f"{self.signee.name} - {self.date}" class ContractIntent(NameStrMixin, models.Model): diff --git a/registry/templates/admin/base_site.html b/registry/templates/admin/base_site.html index 20560f9..e4a08e0 100644 --- a/registry/templates/admin/base_site.html +++ b/registry/templates/admin/base_site.html @@ -14,7 +14,7 @@ .index-action-buttons { display: flex; flex-direction: row; - row-gap: 10px; + column-gap: 10px; } .index-action-buttons button, diff --git a/registry/templates/admin/index.html b/registry/templates/admin/index.html index be5f8f5..1a3722a 100644 --- a/registry/templates/admin/index.html +++ b/registry/templates/admin/index.html @@ -2,12 +2,20 @@ {% block content %} -{% if request.user.can_approve_contracts %} +{% if request.user.can_approve_contracts or request.user.can_create_contracts %} <div class="index-action-buttons"> - <a - href="contracts/contract/?approval_state__exact=no" - aria-role="button" - >Smlouvy ke schválení ({{ request.user.contracts_to_approve_count }})</a> + {% if request.user.can_approve_contracts %} + <a + href="contracts/contract/?approval_state__exact=no" + aria-role="button" + >Smlouvy ke schválení ({{ request.user.contracts_to_approve_count }})</a> + {% endif %} + {% if request.user.can_create_contracts %} + <a + href="contracts/contract/add/" + aria-role="button" + >Nahrát smlouvu</a> + {% endif %} </div> {% endif %} diff --git a/static_src/base.css b/static_src/base.css index 44f1cb6..b4c81f5 100644 --- a/static_src/base.css +++ b/static_src/base.css @@ -4,9 +4,9 @@ @layer base { - html { - font-family: "Roboto Condensed", system-ui, sans-serif; - } + html { + font-family: "Roboto Condensed", system-ui, sans-serif; + } } diff --git a/users/models.py b/users/models.py index 52074a4..d08f101 100644 --- a/users/models.py +++ b/users/models.py @@ -19,6 +19,11 @@ class User(pirates_models.AbstractUser): # TODO: Do we need the superuser check? return self.is_superuser or self.has_perm("contracts.approve") + @property + def can_create_contracts(self) -> bool: + # TODO: Do we need the superuser check? + return self.is_superuser or self.has_perm("contracts.add") + @property def contracts_to_approve_count(self) -> int: if not self.can_approve_contracts: -- GitLab