diff --git a/contracts/models.py b/contracts/models.py index a9e1052a208b882c0e4f4e01d88c271de5c184af..10f50361d2e861e670c557a10fe1b51ccd0a398c 100644 --- a/contracts/models.py +++ b/contracts/models.py @@ -106,6 +106,13 @@ class Signee(models.Model): verbose_name = "Jiná smluvní strana" verbose_name_plural = "Ostatní smluvní strany" + @property + def url(self) -> str: + return reverse( + "contracts:view_signee", + args=(self.id,) + ) + @property def entity_has_public_address(self) -> bool: return self.entity_type in ( @@ -181,6 +188,13 @@ class Contractee(models.Model): verbose_name="Role", ) + @property + def url(self) -> str: + return reverse( + "contracts:view_contractee", + args=(self.id,) + ) + class Meta: app_label = "contracts" @@ -202,6 +216,13 @@ class ContractType(NameStrMixin, models.Model): verbose_name="Jméno", ) + @property + def url(self) -> str: + return reverse( + "contracts:view_contract_type", + args=(self.id,) + ) + class Meta: app_label = "contracts" @@ -215,6 +236,13 @@ class ContractIssue(NameStrMixin, models.Model): verbose_name="Jméno", ) + @property + def url(self) -> str: + return reverse( + "contracts:view_contract_issue", + args=(self.id,) + ) + class Meta: app_label = "contracts" @@ -233,6 +261,13 @@ class ContractFilingArea(NameStrMixin, models.Model): verbose_name="Odpovědná osoba", ) + @property + def url(self) -> str: + return reverse( + "contracts:view_contract_filing_area", + args=(self.id,) + ) + class Meta: app_label = "contracts" @@ -276,8 +311,8 @@ class Contract(NameStrMixin, models.Model): is_approved = models.BooleanField( verbose_name="Je schválená", help_text=( - "Může měnit jen schvalovatel. Pokud je smlouva " - "veřejná, schválením se vypustí ven." + "Mohou měnit jen schvalovatelé. Pokud je " + "smlouva veřejná, schválením se vypustí ven." ), ) @@ -297,6 +332,7 @@ class Contract(NameStrMixin, models.Model): types = models.ManyToManyField( ContractType, + related_name="contracts", verbose_name="Typ", ) @@ -341,6 +377,10 @@ class Contract(NameStrMixin, models.Model): is_public = models.BooleanField( verbose_name="Je veřejná", + help_text=( + "Neveřejné smlouvy nejsou vidět bez přihlášení " + "jako min. tajný čtenář." + ) ) paper_form_state = models.CharField( @@ -374,6 +414,7 @@ class Contract(NameStrMixin, models.Model): issues = models.ManyToManyField( ContractIssue, blank=True, + related_name="contracts", verbose_name="Problémy", help_text='Veřejně nazváno "Poznámky".', ) @@ -403,7 +444,7 @@ class Contract(NameStrMixin, models.Model): on_delete=models.SET_NULL, blank=True, null=True, - related_name="filed_contracts", + related_name="contracts", verbose_name="Spisovna", help_text="Obsah není veřejně přístupný.", ) @@ -446,6 +487,13 @@ class Contract(NameStrMixin, models.Model): args=(self.primary_contract.id,), ) + @property + def url(self) -> str: + return reverse( + "contracts:view_contract", + args=(self.id,), + ) + def get_public_files(self): return ContractFile.objects.filter( contract=self, diff --git a/contracts/templates/contracts/includes/contract_list.html b/contracts/templates/contracts/includes/contract_list.html new file mode 100644 index 0000000000000000000000000000000000000000..8198aa203b414ee8b2d68f325f20600dbf100434 --- /dev/null +++ b/contracts/templates/contracts/includes/contract_list.html @@ -0,0 +1,62 @@ +<table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Typy</td> + <td>Platná</td> + <td class="whitespace-nowrap">Účinná od</td> + <td class="whitespace-nowrap">Účinná do</td> + <td class="whitespace-nowrap">Podepsána s</td> + </tr> + </thead> + <tbody> + {% for contract in page %} + <tr> + <td> + {% if user.can_view_confidential and not contract.is_public %} + {% include "contracts/includes/private_info_icon.html" %} + {% endif %} + <a + class="underline" + href="{% url "contracts:view_contract" contract.id %}" + >{{ contract.name }}</a> + </td> + <td> + <ul class="flex flex-wrap gap-1.5"> + {% for type in contract.types.all %} + <li class="flex"> + {% include "contracts/includes/tag.html" with url=type.url icon="ico--folder" content=type.name %} + </li> + {% endfor %} + </ul> + </td> + <td> + <i class="{% if contract.legal_state == contract.LegalStates.VALID %}ico--checkmark{% else %}ico--cross{% endif %}"></i> + </td> + <td class="whitespace-nowrap">{{ contract.valid_start_date }}</td> + <td class="whitespace-nowrap"> + {% if contract.valid_end_date %} + {{ contract.valid_end_date }} + {% else %} + <span class="text-grey-200">Neurčité</span> + {% endif %} + </td> + <td> + <ul class="flex flex-wrap gap-1.5"> + {% for signature in contract.signee_signatures.all %} + <li class="flex"> + {% if signature.signee.entity_type == signature.signee.EntityTypes.LEGAL_ENTITY or signature.signee.entity_type == signature.signee.EntityTypes.OTHER %} + {% include "contracts/includes/tag.html" with url=signature.signee.url icon="ico--office" content=signature.signee.name %} + {% else %} + {% include "contracts/includes/tag.html" with url=signature.signee.url icon="ico--user" content=signature.signee.name %} + {% endif %} + </li> + {% endfor %} + </ul> + </td> + </tr> + {% endfor %} + </tbody> +</table> + +{% include "contracts/includes/pagination.html" with paginator=paginator page=page %} diff --git a/contracts/templates/contracts/includes/pagination.html b/contracts/templates/contracts/includes/pagination.html new file mode 100644 index 0000000000000000000000000000000000000000..b8ab90a24bd60ccb70784968730dda9cb1b54301 --- /dev/null +++ b/contracts/templates/contracts/includes/pagination.html @@ -0,0 +1,84 @@ +{% load subtract %} + +{% if page.has_other_pages %} + <div class="pagination-container"> + <nav class="pagination space-x-1"> + <a + class="btn btn--icon btn--grey-125 btn--hoveractive btn--to-black btn--condensed btn--inverted-icon" + {% if not page.has_previous %}disabled{% endif %} + {% if page.has_previous %}href="{{ request.path }}?page={{ page.previous_page_number }}"{% endif %} + > + <div class="btn__body-wrap"> + <div class="btn__body">Předchozí</div> + <div class="btn__icon"> + <i class="ico--chevron-left"></i> + </div> + </div> + </a> + + {% if page.previous_page_number != 1 %} + <a + class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" + href="{{ request.path }}?page=1" + > + <div class="btn__body">1</div> + </a> + {% endif %} + + {% if page.has_previous and page.previous_page_number != 2 %} + <span class="text-grey-500 hidden md:flex flex-col px-2 justify-center">...</span> + {% endif %} + + {% if page.has_previous %} + <a + class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" + href="{{ request.path }}?page={{ page.previous_page_number }}" + > + <div class="btn__body ">{{ page.previous_page_number }}</div> + </a> + {% endif %} + + <button class="btn btn--grey-500 btn--condensed hidden md:inline-block"> + <div class="btn__body">{{ page.number }}</div> + </button> + + {% if page.has_next %} + <a + class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" + href="{{ request.path }}?page={{ page.next_page_number }}" + > + <div class="btn__body ">{{ page.next_page_number }}</div> + </a> + {% endif %} + + {% if paginator.num_pages != page.next_page_number and paginator.num_pages|subtract:1 != page.next_page_number %} + <span class="text-grey-500 hidden md:flex flex-col px-2 justify-center">...</span> + {% endif %} + + {% comment %}num_pages is equivalent to the last page number{% endcomment %} + + {% if paginator.num_pages != page.next_page_number %} + <a + role="button" + class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" + href="{{ request.path }}?page={{ paginator.num_pages }}" + > + <div class="btn__body">{{ paginator.num_pages }}</div> + </a> + {% endif %} + + <a + class="btn btn--icon btn--grey-125 btn--hoveractive btn--to-black btn--condensed" + {% if not page.has_next %}disabled{% endif %} + {% if page.has_next %}href="{{ request.path }}?page={{ page.next_page_number }}"{% endif %} + > + <div class="btn__body-wrap"> + <div class="btn__body">Další</div> + <div class="btn__icon"> + <i class="ico--chevron-right"></i> + </div> + </div> + </a> + </nav> + </div> +{% endif %} diff --git a/contracts/templates/contracts/includes/private_info_icon.html b/contracts/templates/contracts/includes/private_info_icon.html new file mode 100644 index 0000000000000000000000000000000000000000..e3d0cf9d21a060086f1799fbaf651efd013ff7cf --- /dev/null +++ b/contracts/templates/contracts/includes/private_info_icon.html @@ -0,0 +1,4 @@ +<i + class="ico--eye-off text-red-600 mr-2 __tooltipped" + aria-label="Neveřejný údaj" +></i> diff --git a/contracts/templates/contracts/index.html b/contracts/templates/contracts/index.html index 2e9390683650bc89b2e39924f6db26ea22ee246f..6f105f8b2d89f7afc1d2e289bfc00826b025b1d5 100644 --- a/contracts/templates/contracts/index.html +++ b/contracts/templates/contracts/index.html @@ -1,5 +1,4 @@ {% extends "shared/includes/base.html" %} -{% load subtract %} {% block content %} <div class="prose max-w-none mb-10"> @@ -8,144 +7,48 @@ Temporibus <i>voluptatum commodi delectus est</i>. Commodi et voluptatem dolor odit occaecati dolore aut. Qui dolor nihil excepturi id. Qui deleniti <i>cupiditate voluptatem aut</i> est dolores eum aliquid. Molestiae blanditiis reiciendis odio provident ipsam voluptas enim suscipit. </p> </div> - <table class="table table-auto w-full table--striped table--bordered"> - <thead> - <tr> - <td class="font-bold">Název</td> - <td>Typy</td> - <td>Platná</td> - <td class="whitespace-nowrap">Účinná od</td> - <td class="whitespace-nowrap">Účinná do</td> - <td class="whitespace-nowrap">Podepsána s</td> - </tr> - </thead> - <tbody> - {% for contract in page %} - <tr> - <td> - <a - class="underline" - href="{% url "contracts:view_contract" contract.id %}" - >{{ contract.name }}</a> - </td> - <td> - <ul class="flex flex-wrap gap-1.5"> - {% for type in contract.types.all %} - <li class="flex"> - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--folder" content=type.name %} - </li> - {% endfor %} - </ul> - </td> - <td> - <i class="{% if contract.legal_state == contract.LegalStates.VALID %}ico--checkmark{% else %}ico--cross{% endif %}"></i> - </td> - <td class="whitespace-nowrap">{{ contract.valid_start_date }}</td> - <td class="whitespace-nowrap"> - {% if contract.valid_end_date %} - {{ contract.valid_end_date }} - {% else %} - <span class="text-grey-200">Neurčité</span> - {% endif %} - </td> - <td> - <ul class="flex flex-wrap gap-1.5"> - {% for signature in contract.signee_signatures.all %} - <li class="flex"> - {% if signature.signee.entity_type == signature.signee.EntityTypes.LEGAL_ENTITY or signature.signee.entity_type == signature.signee.EntityTypes.OTHER %} - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--office" content=signature.signee.name %} - {% else %} - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--user" content=signature.signee.name %} - {% endif %} - </li> - {% endfor %} - </ul> - </td> - </tr> - {% endfor %} - </tbody> - </table> - {% if page.has_other_pages %} - <div class="pagination-container"> - <nav class="pagination space-x-1"> - <a - class="btn btn--icon btn--grey-125 btn--hoveractive btn--to-black btn--condensed btn--inverted-icon" - {% if not page.has_previous %}disabled{% endif %} - {% if page.has_previous %}href="{% url "contracts:index" %}?page={{ page.previous_page_number }}"{% endif %} - > - <div class="btn__body-wrap"> - <div class="btn__body">Předchozí</div> - <div class="btn__icon"> - <i class="ico--chevron-left"></i> - </div> - </div> - </a> - - {% if page.previous_page_number != 1 %} - <a - class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" - href="{% url "contracts:index" %}?page=1" - > - <div class="btn__body">1</div> - </a> - {% endif %} - - {% if page.has_previous and page.previous_page_number != 2 %} - <span class="text-grey-500 hidden md:flex flex-col px-2 justify-center">...</span> - {% endif %} - - {% if page.has_previous %} - <a - class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" - href="{% url "contracts:index" %}?page={{ page.previous_page_number }}" - > - <div class="btn__body ">{{ page.previous_page_number }}</div> - </a> - {% endif %} - - <button class="btn btn--grey-500 btn--condensed hidden md:inline-block"> - <div class="btn__body">{{ page.number }}</div> - </button> - - {% if page.has_next %} - <a - class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" - href="{% url "contracts:index" %}?page={{ page.next_page_number }}" - > - <div class="btn__body ">{{ page.next_page_number }}</div> - </a> - {% endif %} - - {% if paginator.num_pages != page.next_page_number and paginator.num_pages|subtract:1 != page.next_page_number %} - <span class="text-grey-500 hidden md:flex flex-col px-2 justify-center">...</span> - {% endif %} - - {% comment %}num_pages is equivalent to the last page number{% endcomment %} - - {% if paginator.num_pages != page.next_page_number %} - <a - role="button" - class="btn btn--grey-125 btn--hoveractive btn--to-black btn--condensed hidden md:inline-block" - href="{% url "contracts:index" %}?page={{ paginator.num_pages }}" - > - <div class="btn__body">{{ paginator.num_pages }}</div> - </a> - {% endif %} + <div class="flex gap-6 mb-5 text-lg"> + <a + class="flex gap-2 items-baseline hover:no-underline" + href="{% url "contracts:view_contract_filing_areas" %}" + > + <i class="ico--drawer"></i> + <span class="underline">Spisovny</span> + </a> + + <a + class="flex gap-2 items-baseline hover:no-underline" + href="{% url "contracts:view_contract_issues" %}" + > + <i class="ico--warning"></i> + <span class="underline">{% if not user.can_view_confidential %}Poznámky{% else %}Problémy{% endif %}</span> + </a> + + <a + class="flex gap-2 items-baseline hover:no-underline" + href="{% url "contracts:view_contract_types" %}" + > + <i class="ico--folder"></i> + <span class="underline">Typy</span> + </a> + + <a + class="flex gap-2 items-baseline hover:no-underline" + href="{% url "contracts:view_contractees" %}" + > + <i class="ico--office"></i> + <span class="underline">Naše smluvní strany</span> + </a> + + <a + class="flex gap-2 items-baseline hover:no-underline" + href="{% url "contracts:view_signees" %}" + > + <i class="ico--office"></i> + <span class="underline">Jiné smluvní strany</span> + </a> + </div> - <a - class="btn btn--icon btn--grey-125 btn--hoveractive btn--to-black btn--condensed" - {% if not page.has_next %}disabled{% endif %} - {% if page.has_next %}href="{% url "contracts:index" %}?page={{ page.next_page_number }}"{% endif %} - > - <div class="btn__body-wrap"> - <div class="btn__body">Další</div> - <div class="btn__icon"> - <i class="ico--chevron-right"></i> - </div> - </div> - </a> - </nav> - </div> - {% endif %} + {% include "contracts/includes/contract_list.html" with page=page paginator=paginator %} {% endblock %} diff --git a/contracts/templates/contracts/view_contract.html b/contracts/templates/contracts/view_contract.html index 8c5cc5d6f030d9968380355eae7175b124d2d942..911499b22d0529db7a0b80f989db18f25b4f0e92 100644 --- a/contracts/templates/contracts/view_contract.html +++ b/contracts/templates/contracts/view_contract.html @@ -2,9 +2,13 @@ {% load subtract markdownify %} {% block content %} - <h1 class="head-alt-lg mb-10"> - <i class="ico--file-blank mr-4"></i>{{ contract.name }} - </h1> + <div class="flex gap-4 mb-10"> + <i class="ico--file-blank text-7xl"></i> + <div> + <h1 class="head-alt-lg">{{ contract.name }}</h1> + <h2 class="head-alt-sm">Smlouva</h2> + </div> + </div> <table class="table table-auto w-full table--striped table--bordered mb-7"> <tbody> @@ -25,25 +29,45 @@ <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> <ul class="flex flex-wrap gap-1.5"> {% for type in contract.types.all %} - <li class="flex"> - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--folder" content=type.name %} + <li> + {% include "contracts/includes/tag.html" with url=type.url icon="ico--folder" content=type.name %} </li> {% endfor %} </ul> </td> </tr> + {% if contract.primary_contract %} <tr> - <td class="w-1/5 !p-2.5">Primární smlouva</td> - <td class="w-4/5 !p-2.5"> + <td class="w-1/5 !p-2.5">Nadřazená smlouva</td> + <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> {% include "contracts/includes/tag.html" with url=contract.primary_contract_url icon="ico--file-blank" content=contract.primary_contract.name %} </td> </tr> {% endif %} + + {% with contract.subcontracts.all as subcontracts %} + {% if subcontracts %} + <tr> + <td class="w-1/5 !p-2.5">Podřazené smlouvy</td> + <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> + <ul class="flex gap-1.5"> + {% for subcontract in subcontracts %} + <li> + {% include "contracts/includes/tag.html" with url=subcontract.url icon="ico--file-blank" content=subcontract.name %} + </li> + {% endfor %} + </ul> + </td> + </tr> + {% endif %} + {% endwith %} + <tr> <td class="w-1/5 !p-2.5">Souhrn</td> <td class="w-4/5 !p-2.5">{{ contract.summary }}</td> </tr> + {% if user.can_view_confidential %} <tr> <td class="w-1/5 !p-2.5">Je veřejná</td> @@ -100,10 +124,7 @@ <tr> <td class="w-1/5 !p-2.5"> <div class="flex items-center"> - <i - class="ico--eye-off text-red-600 mr-2 __tooltipped" - aria-label="Neveřejný údaj" - ></i> + {% include "contracts/includes/private_info_icon.html" %} Neveřejné soubory </div> </td> @@ -144,7 +165,7 @@ <td class="w-1/5 !p-2.5">Spisovna</td> <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> <div class="flex gap-3 items-center"> - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--drawer" content=contract.filing_area.name %} + {% include "contracts/includes/tag.html" with url=contract.filing_area.url icon="ico--drawer" content=contract.filing_area.name %} </div> </td> </tr> @@ -233,7 +254,7 @@ </table> {% with contract.issues.all as issues %} - {% if issues|length != 0 %} + {% if issues or user.can_view_confidential and contract.notes %} <table class="table table-auto w-full table--striped table--bordered mb-10"> <tbody> <tr> @@ -242,31 +263,30 @@ </td> </tr> - <tr> - <td class="w-1/5 !p-2.5">{% if not user.can_view_confidential %}Poznámky{% else %}Problémy{% endif %}</td> - <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> - <ul class="flex gap-2"> - {% for issue in issues %} - <li> - {% include "contracts/includes/tag.html" with url="#TODO" icon="ico--warning" content=issue.name %} - </li> - {% endfor %} - </ul> - </td> - </tr> + {% if issues %} + <tr> + <td class="w-1/5 !p-2.5">{% if not user.can_view_confidential %}Poznámky{% else %}Problémy{% endif %}</td> + <td class="w-4/5 !pl-2.5 !py-1 !pr-1"> + <ul class="flex gap-2"> + {% for issue in issues %} + <li> + {% include "contracts/includes/tag.html" with url=issue.url icon="ico--warning" content=issue.name %} + </li> + {% endfor %} + </ul> + </td> + </tr> + {% endif %} {% if user.can_view_confidential and contract.notes %} <tr> <td class="w-1/5 !p-2.5"> <div class="flex items-center"> - <i - class="ico--eye-off text-red-600 mr-2 __tooltipped" - aria-label="Neveřejný údaj" - ></i> + {% include "contracts/includes/private_info_icon.html" %} Interní poznámky </div> </td> - <td class="w-4/5 !p-2.5"> + <td class="w-4/5 !p-1.5"> <div class="prose max-w-none"> {{ contract.notes|markdownify|safe }} </div> @@ -299,6 +319,7 @@ <div class="mb-1"> <a class="inline-block p-1.5 mb-1 rounded-sm cursor-pointer not-italic hover:no-underline bg-gray-200 duration-100 hover:bg-gray-300" + href="{{ signature.contractee.url }}" > <i class="ico--office mr-2"></i> <strong>{{ signature.contractee.name }}</strong> @@ -357,6 +378,7 @@ <div class="mb-1"> <a class="inline-block p-1.5 mb-1 rounded-sm cursor-pointer not-italic hover:no-underline bg-gray-200 duration-100 hover:bg-gray-300" + href="{{ signature.signee.url }}" > <i class="{% if signature.signee.entity_type == signature.signee.EntityTypes.LEGAL_ENTITY or signature.signee.entity_type == signature.signee.EntityTypes.OTHER %}ico--office{% else %}ico--user{% endif %} mr-2"></i> <strong>{{ signature.signee.name }}</strong> @@ -375,10 +397,7 @@ {% if not signature.signee.entity_has_public_address %} {% if user.can_view_confidential %} <div class="my-2 flex items-center"> - <i - class="ico--eye-off text-red-600 mr-2 __tooltipped" - aria-label="Neveřejný údaj" - ></i><span class="text-gray-500">Máš přístup k celé adrese.</span> + {% include "contracts/includes/private_info_icon.html" %}<span class="text-gray-500">Máš přístup k celé adrese.</span> </div> {% else %} <span class="text-gray-500">(zobrazujeme pouze obec)</span> diff --git a/contracts/templates/contracts/view_contract_filing_area.html b/contracts/templates/contracts/view_contract_filing_area.html new file mode 100644 index 0000000000000000000000000000000000000000..49901dce1897a301c4fc02aa7c8a624b17f59177 --- /dev/null +++ b/contracts/templates/contracts/view_contract_filing_area.html @@ -0,0 +1,17 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--drawer text-7xl"></i> + <div> + <h1 class="head-alt-lg">{{ filing_area.name }}</h1> + <h2 class="head-alt-sm">Fyzická spisovna</h2> + </div> + </div> + + <h2 class="text-lg font-bold mb-10"> + Smlouvy ve spisovně + </h2> + + {% include "contracts/includes/contract_list.html" with page=contracts_page paginator=contracts_paginator %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contract_filing_areas.html b/contracts/templates/contracts/view_contract_filing_areas.html new file mode 100644 index 0000000000000000000000000000000000000000..2271ea32e02627379198b961a94be2c844d474ea --- /dev/null +++ b/contracts/templates/contracts/view_contract_filing_areas.html @@ -0,0 +1,40 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--drawer text-7xl"></i> + <div> + <h1 class="head-alt-lg">Spisovny</h1> + <h2 class="head-alt-sm">Fyzická úložiště smluv</h2> + </div> + </div> + + <table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Odpovědná osoba</td> + <td>Počet smluv</td> + </tr> + </thead> + <tbody> + {% for filing_area in page %} + <tr> + <td> + <a + href="{{ filing_area.url }}" + >{{ filing_area.name }}</a> + </td> + <td> + {{ filing_area.person_responsible }} + </td> + <td> + {{ filing_area.contracts.count }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + + {% include "contracts/includes/pagination.html" with paginator=paginator page=page %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contract_issue.html b/contracts/templates/contracts/view_contract_issue.html new file mode 100644 index 0000000000000000000000000000000000000000..ecd14ca24f499d0f84f43fc4106c493718ca8dc8 --- /dev/null +++ b/contracts/templates/contracts/view_contract_issue.html @@ -0,0 +1,19 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--warning text-7xl"></i> + <div> + <h1 class="head-alt-lg">{{ issue.name }}</h1> + <h2 class="head-alt-sm"> + {% if not user.can_view_confidential %}Poznámka{% else %}Problém{% endif %} + </h2> + </div> + </div> + + <h2 class="text-lg font-bold mb-10"> + Smlouvy s {% if not user.can_view_confidential %}poznámkou{% else %}problémem{% endif %} + </h2> + + {% include "contracts/includes/contract_list.html" with page=contracts_page paginator=contracts_paginator %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contract_issues.html b/contracts/templates/contracts/view_contract_issues.html new file mode 100644 index 0000000000000000000000000000000000000000..07307cfb025b21301c367efb19a5c2e23bdc66df --- /dev/null +++ b/contracts/templates/contracts/view_contract_issues.html @@ -0,0 +1,48 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--warning text-7xl"></i> + <div> + <h1 class="head-alt-lg"> + {% if not user.can_view_confidential %} + Poznámky + {% else %} + Problémy + {% endif %} + </h1> + <h2 class="head-alt-sm"> + {% if not user.can_view_confidential %} + ke smlouvám + {% else %} + se smlouvami + {% endif %} + </h2> + </div> + </div> + + <table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Počet smluv</td> + </tr> + </thead> + <tbody> + {% for issue in page %} + <tr> + <td> + <a + href="{{ issue.url }}" + >{{ issue.name }}</a> + </td> + <td> + {{ issue.contracts.count }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + + {% include "contracts/includes/pagination.html" with paginator=paginator page=page %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contract_type.html b/contracts/templates/contracts/view_contract_type.html new file mode 100644 index 0000000000000000000000000000000000000000..ab7361ea40ff1ca67b6e35945070a221132e8060 --- /dev/null +++ b/contracts/templates/contracts/view_contract_type.html @@ -0,0 +1,17 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--folder text-7xl"></i> + <div> + <h1 class="head-alt-lg">{{ type.name }}</h1> + <h2 class="head-alt-sm">Typ smluv</h2> + </div> + </div> + + <h2 class="text-lg font-bold mb-10"> + Smlouvy zadaného typu + </h2> + + {% include "contracts/includes/contract_list.html" with page=contracts_page paginator=contracts_paginator %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contract_types.html b/contracts/templates/contracts/view_contract_types.html new file mode 100644 index 0000000000000000000000000000000000000000..b5ffa05599fd257879c703564e84257be365e6ad --- /dev/null +++ b/contracts/templates/contracts/view_contract_types.html @@ -0,0 +1,36 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--folder text-7xl"></i> + <div> + <h1 class="head-alt-lg">Typy</h1> + <h2 class="head-alt-sm">smluv</h2> + </div> + </div> + + <table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Počet smluv</td> + </tr> + </thead> + <tbody> + {% for type in page %} + <tr> + <td> + <a + href="{{ type.url }}" + >{{ type.name }}</a> + </td> + <td> + {{ type.contracts.count }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + + {% include "contracts/includes/pagination.html" with paginator=paginator page=page %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contractee.html b/contracts/templates/contracts/view_contractee.html new file mode 100644 index 0000000000000000000000000000000000000000..123be37ea05776af486740274bca5b0b887a3ffd --- /dev/null +++ b/contracts/templates/contracts/view_contractee.html @@ -0,0 +1,39 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--office text-7xl"></i> + <div> + <h1 class="head-alt-lg"> + {{ contractee.name }}{% if contractee.department %}, {{ contractee.department }}{% endif %} + </h1> + <h2 class="head-alt-sm">Naše smluvní strana</h2> + </div> + </div> + + <address class="mb-8"> + <div> + <strong>{{ contractee.name }}</strong> + {% if contractee.department %} + - {{ contractee.department }} + {% endif %} + {% if contractee.role %} + ({{ contractee.role }}) + {% endif %} + </div><br> + + {{ contractee.address_street_with_number }}<br> + {{ contractee.address_zip }} {{ contractee.address_district }}<br> + {{ contractee.get_address_country_display }}<br> + + {% if contractee.ico_number %} + IČO: {{ contractee.ico_number }}<br> + {% endif %} + </address> + + <h2 class="text-lg font-bold mb-10"> + Smlouvy podepsané touto stranou + </h2> + + {% include "contracts/includes/contract_list.html" with page=contracts_page paginator=contracts_paginator %} +{% endblock %} diff --git a/contracts/templates/contracts/view_contractees.html b/contracts/templates/contracts/view_contractees.html new file mode 100644 index 0000000000000000000000000000000000000000..6a9837747bdd3084bcbc4f399903bd5214ae8286 --- /dev/null +++ b/contracts/templates/contracts/view_contractees.html @@ -0,0 +1,44 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--office text-7xl"></i> + <div> + <h1 class="head-alt-lg">Smluvní strany</h1> + <h2 class="head-alt-sm">naše</h2> + </div> + </div> + + <table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Počet podpisů</td> + </tr> + </thead> + <tbody> + {% for contractee in page %} + <tr> + <td> + <a + href="{{ contractee.url }}" + > + <strong>{{ contractee.name }}</strong> + {% if contractee.department %} + - {{ contractee.department }} + {% endif %} + {% if contractee.role %} + ({{ contractee.role }}) + {% endif %} + </a> + </td> + <td> + {{ contractee.signatures.count }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + + {% include "contracts/includes/pagination.html" with paginator=paginator page=page %} +{% endblock %} diff --git a/contracts/templates/contracts/view_signee.html b/contracts/templates/contracts/view_signee.html new file mode 100644 index 0000000000000000000000000000000000000000..e382fdc2b227cd4ad845a50dad3c8e81e067b5fa --- /dev/null +++ b/contracts/templates/contracts/view_signee.html @@ -0,0 +1,58 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="{% if signee.entity_type == signee.EntityTypes.LEGAL_ENTITY or signee.entity_type == signee.EntityTypes.OTHER %}ico--office{% else %}ico--user{% endif %} text-7xl"></i> + <div> + <h1 class="head-alt-lg"> + {{ signee.name }}{% if signee.department %}, {{ signee.department }}{% endif %} + </h1> + <h2 class="head-alt-sm">Jiná smluvní strana</h2> + </div> + </div> + + <address class="mb-8"> + <div class="mb-1"> + <strong>{{ signee.name }}</strong> + {% if signee.department %} + - {{ signee.department }} + {% endif %} + {% if signee.role %} + ({{ signee.role }}) + {% endif %} + </div> + + <div class="mb-1"> + {{ signee.get_entity_type_display }} + + {% if not signee.entity_has_public_address %} + {% if user.can_view_confidential %} + <div class="my-2 flex items-center"> + {% include "contracts/includes/private_info_icon.html" %} + <span class="text-gray-500">Máš přístup k celé adrese.</span> + </div> + {% else %} + <span class="text-gray-500">(zobrazujeme pouze obec)</span> + {% endif %} + {% endif %} + </div> + + {% if user.can_view_confidential or signee.entity_has_public_address %} + {{ signee.address_street_with_number }}<br> + {{ signee.address_zip }} {{ signee.address_district }}<br> + {{ signee.get_address_country_display }}<br> + {% else %} + {{ signee.address_district }}<br> + {% endif %} + + {% if signee.ico_number %} + IČO: {{ signee.ico_number }}<br> + {% endif %} + </address> + + <h2 class="text-lg font-bold mb-10"> + Smlouvy podepsané touto stranou + </h2> + + {% include "contracts/includes/contract_list.html" with page=contracts_page paginator=contracts_paginator %} +{% endblock %} diff --git a/contracts/templates/contracts/view_signees.html b/contracts/templates/contracts/view_signees.html new file mode 100644 index 0000000000000000000000000000000000000000..3d09b0b16b566576321785032914a1b6388faeee --- /dev/null +++ b/contracts/templates/contracts/view_signees.html @@ -0,0 +1,44 @@ +{% extends "shared/includes/base.html" %} + +{% block content %} + <div class="flex gap-4 mb-10"> + <i class="ico--office text-7xl"></i> + <div> + <h1 class="head-alt-lg">Smluvní strany</h1> + <h2 class="head-alt-sm">ostatní</h2> + </div> + </div> + + <table class="table table-auto w-full table--striped table--bordered"> + <thead> + <tr> + <td class="font-bold">Název</td> + <td>Počet podpisů</td> + </tr> + </thead> + <tbody> + {% for signee in page %} + <tr> + <td> + <a + href="{{ signee.url }}" + > + <strong>{{ signee.name }}</strong> + {% if signee.department %} + - {{ signee.department }} + {% endif %} + {% if signee.role %} + ({{ signee.role }}) + {% endif %} + </a> + </td> + <td> + {{ signee.signatures.count }} + </td> + </tr> + {% endfor %} + </tbody> + </table> + + {% include "contracts/includes/pagination.html" with paginator=paginator page=page %} +{% endblock %} diff --git a/contracts/urls.py b/contracts/urls.py index c57bf309a22832c6cd96683822464ca0faae07a5..ad007273f7ea797d39af2fb697e3936a2e3f95ed 100644 --- a/contracts/urls.py +++ b/contracts/urls.py @@ -8,11 +8,67 @@ app_name = "contracts" urlpatterns = [ path("", views.index, name="index"), path("contracts/<int:id>", views.view_contract, name="view_contract"), + + path( + "contracts/filing-areas/<int:id>", + views.view_contract_filing_area, + name="view_contract_filing_area" + ), + path( + "contracts/issues/<int:id>", + views.view_contract_issue, + name="view_contract_issue" + ), + path( + "contracts/types/<int:id>", + views.view_contract_type, + name="view_contract_type" + ), + + path( + "contracts/filing-areas", + views.view_contract_filing_areas, + name="view_contract_filing_areas" + ), + path( + "contracts/issues", + views.view_contract_issues, + name="view_contract_issues" + ), + path( + "contracts/types", + views.view_contract_types, + name="view_contract_types" + ), + path( "contracts/files/<str:pk>", views.ContractFileDownloadView.as_view(), name="download_contract_file", ), + + path( + "contractees", + views.view_contractees, + name="view_contractees" + ), + path( + "signee", + views.view_signees, + name="view_signees" + ), + + path( + "contractees/<int:id>", + views.view_contractee, + name="view_contractee" + ), + path( + "signee/<int:id>", + views.view_signee, + name="view_signee" + ), + path( "contracts/autocomplete", dal.autocomplete.Select2QuerySetView.as_view(model=models.Contract), diff --git a/contracts/views.py b/contracts/views.py index d7bda341d1473662c54e5c0f0753a53d96a47d56..d047b3bc45eeebb4653e2f964ec55308be27223b 100644 --- a/contracts/views.py +++ b/contracts/views.py @@ -26,11 +26,26 @@ class ContractFileDownloadView(ObjectDownloadView): return super().get(request, *args, **kwargs) -def index(request): - filter = { - "is_approved": True +def get_base_context(request) -> dict: + return { + "site_url": settings.SITE_URL, + "user": request.user, } + +def get_pagination(request, objects) -> tuple: + paginator = Paginator(objects, 25) + + page = paginator.get_page( + request.GET.get("page") + ) + + return page, paginator + + +def get_paginated_contracts(request, filter: dict = {}) -> tuple: + filter["is_approved"] = True + if not request.user.has_perm("contracts.view_confidential"): filter["is_public"] = True @@ -40,20 +55,24 @@ def index(request): .order_by("valid_start_date") .all() ) - paginator = Paginator(contracts, 25) - page = paginator.get_page(request.GET.get("page")) + page, paginator = get_pagination(request, contracts) + + return page, paginator + + +def index(request): + page, paginator = get_paginated_contracts(request) return render( request, "contracts/index.html", { - "site_url": settings.SITE_URL, - "user": request.user, + **get_base_context(request), "title": "Seznam smluv", "description": "Description", - "paginator": paginator, "page": page, + "paginator": paginator, }, ) @@ -76,10 +95,276 @@ def view_contract(request, id: int): request, "contracts/view_contract.html", { - "site_url": settings.SITE_URL, - "user": request.user, + **get_base_context(request), "title": contract.name, "description": contract.summary, "contract": contract, }, ) + + +# BEGIN Filtered contract + submodel views + + +def view_contract_filing_area(request, id: int): + filing_area = ( + get_objects_for_user(request.user, "contracts.view_contractfilingarea") + .get(id=id) + ) + + contracts_page, contracts_paginator = get_paginated_contracts( + request, + {"filing_area": filing_area} + ) + + return render( + request, + "contracts/view_contract_filing_area.html", + { + **get_base_context(request), + "title": filing_area.name, + "description": ( + f"Spisovna smluv - {filing_area.name}, " + f"zodpovědná osoba {filing_area.person_responsible}" + ), + "filing_area": filing_area, + "contracts_page": contracts_page, + "contracts_paginator": contracts_paginator, + }, + ) + + +def view_contract_issue(request, id: int): + issue = ( + get_objects_for_user(request.user, "contracts.view_contractissue") + .get(id=id) + ) + + contracts_page, contracts_paginator = get_paginated_contracts( + request, + {"issues": issue} + ) + + return render( + request, + "contracts/view_contract_issue.html", + { + **get_base_context(request), + "title": issue.name, + "description": f"Problém se smlouvami - {issue.name}", + "issue": issue, + "contracts_page": contracts_page, + "contracts_paginator": contracts_paginator, + }, + ) + + +def view_contract_type(request, id: int): + type_ = ( + get_objects_for_user(request.user, "contracts.view_contracttype") + .get(id=id) + ) + + contracts_page, contracts_paginator = get_paginated_contracts( + request, + {"types": type_} + ) + + return render( + request, + "contracts/view_contract_type.html", + { + **get_base_context(request), + "title": type_.name, + "description": f"Typ smluv - {type_.name}", + "type": type_, + "contracts_page": contracts_page, + "contracts_paginator": contracts_paginator, + }, + ) + + +def view_contractee(request, id: int): + contractee = ( + get_objects_for_user(request.user, "contracts.view_contractee") + .get(id=id) + ) + + contracts_page, contracts_paginator = get_paginated_contracts( + request, + {"contractee_signatures__contractee": contractee} + ) + + return render( + request, + "contracts/view_contractee.html", + { + **get_base_context(request), + "title": contractee.name, + "description": f"Naše smluvní strana - {contractee.name}", + "contractee": contractee, + "contracts_page": contracts_page, + "contracts_paginator": contracts_paginator, + }, + ) + + +def view_signee(request, id: int): + signee = ( + get_objects_for_user(request.user, "contracts.view_signee") + .get(id=id) + ) + + contracts_page, contracts_paginator = get_paginated_contracts( + request, + {"signee_signatures__signee": signee} + ) + + return render( + request, + "contracts/view_signee.html", + { + **get_base_context(request), + "title": signee.name, + "description": f"Jiná smluvní strana - {signee.name}", + "signee": signee, + "contracts_page": contracts_page, + "contracts_paginator": contracts_paginator, + }, + ) + + +# END Filtered contract + submodel views + +# BEGIN Submodel listing views + + +def view_contract_filing_areas(request): + filing_areas = ( + get_objects_for_user( + request.user, + "contracts.view_contractfilingarea", + ). + order_by("name") + ) + + page, paginator = get_pagination(request, filing_areas) + + return render( + request, + "contracts/view_contract_filing_areas.html", + { + **get_base_context(request), + "title": "Spisovny", + "description": "Seznam fyzických spisoven, kde jsou ukládány smlouvy.", + "page": page, + "paginator": paginator, + } + ) + + +def view_contract_issues(request): + issues = ( + get_objects_for_user( + request.user, + "contracts.view_contractissue", + ). + order_by("name") + ) + + page, paginator = get_pagination(request, issues) + + return render( + request, + "contracts/view_contract_issues.html", + { + **get_base_context(request), + "title": ( + "Poznámky" + if not request.user.can_view_confidential + else "Problémy" + ), + "description": ( + "Poznámky ke smlouvám." + if not request.user.can_view_confidential + else "Problémy se smlouvami." + ), + "page": page, + "paginator": paginator, + } + ) + + +def view_contract_types(request): + types = ( + get_objects_for_user( + request.user, + "contracts.view_contracttype", + ). + order_by("name") + ) + + page, paginator = get_pagination(request, types) + + return render( + request, + "contracts/view_contract_types.html", + { + **get_base_context(request), + "title": "Typy", + "description": "Typy smluv.", + "page": page, + "paginator": paginator, + } + ) + + +def view_contractees(request): + contractees = ( + get_objects_for_user( + request.user, + "contracts.view_contractee", + ). + order_by("name") + ) + + page, paginator = get_pagination(request, contractees) + + return render( + request, + "contracts/view_contractees.html", + { + **get_base_context(request), + "title": "Naše smluvní strany", + "description": "Naše smluvní strany.", + "page": page, + "paginator": paginator, + } + ) + + +def view_signees(request): + contractees = ( + get_objects_for_user( + request.user, + "contracts.view_signee", + ). + order_by("name") + ) + + page, paginator = get_pagination(request, contractees) + + return render( + request, + "contracts/view_signees.html", + { + **get_base_context(request), + "title": "Jiné smluvní strany", + "description": "Jiné smluvní strany.", + "page": page, + "paginator": paginator, + } + ) + + +# END Submodel listing views diff --git a/registry/settings/base.py b/registry/settings/base.py index e85a7ca7befeee4287f78cbb54865b7cfdf7f3a6..7d07beb7373776086c387b84c115f521622a053a 100644 --- a/registry/settings/base.py +++ b/registry/settings/base.py @@ -220,11 +220,11 @@ ADMIN_INDEX_SHOW_REMAINING_APPS = True ADMIN_ORDERING = { "contracts": [ "Contract", - "Signee", "Contractee", + "Signee", + "ContractFilingArea", "ContractType", "ContractIssue", - "ContractFilingArea", ], } diff --git a/shared/templates/shared/includes/base.html b/shared/templates/shared/includes/base.html index f59fa3791d9234728035f378d21e082a001f0ed1..12f7d15555997299d327b64c26742b5d29f87fa2 100644 --- a/shared/templates/shared/includes/base.html +++ b/shared/templates/shared/includes/base.html @@ -73,7 +73,7 @@ <div v-if="show || isLgScreenSize" class="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto"> <ul class="navbar-menu text-white"> <li class="navbar-menu__item"> - <a href="{% url "contracts:index" %}" data-href="{% url "contracts:index" %}" class="navbar-menu__link">Všechny smlouvy</a> + <a href="{% url "contracts:index" %}" data-href="{% url "contracts:index" %}" class="navbar-menu__link">Seznam</a> </li> <li class="navbar-menu__item"> <a href="#TODO" data-href="#TODO" class="navbar-menu__link">Hledat</a>