import copy import typing from django.contrib import admin from django.contrib.auth.models import Permission from django.utils.html import format_html from import_export import resources from nested_admin import (NestedModelAdmin, NestedStackedInline, NestedTabularInline) from rangefilter.filters import DateRangeFilter from shared.admin import FieldsetInlineOrder, MarkdownxGuardedModelAdmin from .forms import ContractAdminForm, ContractFileAdminForm, SigneeAdminForm from .models import (Contract, Contractee, ContracteeSignature, ContracteeSignatureRepresentative, ContractFile, ContractFilingArea, ContractIntent, ContractIssue, ContractType, Signee, SigneeSignature, SigneeSignatureRepresentative) class ContractResource(resources.ModelResource): class Meta: model = Contract class IndexHiddenModelAdmin(MarkdownxGuardedModelAdmin): def has_module_permission(self, request): return False def permissions_mixin_factory( change_permission: str, delete_permission: str, obj_conditional: typing.Callable, change_attr_func: typing.Callable = lambda obj: obj, delete_attr_func: typing.Callable = lambda obj: obj, ) -> object: class Mixin: def has_change_permission(self, request, obj=None) -> bool: if ( obj is not None and obj_conditional(request, obj) and not request.user.has_perm(change_permission, change_attr_func(obj)) ): return False return super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None) -> bool: if ( obj is not None and obj_conditional(request, obj) and not request.user.has_perm(delete_permission, delete_attr_func(obj)) ): return False return super().has_change_permission(request, obj) return Mixin get_obj_contract = lambda obj: obj.contract class OwnPermissionsMixin( permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", lambda request, obj: obj.created_by != request.user, ) ): def own_permissions_mixin_save_model(self, request, obj, form, change): if obj.created_by is None: obj.created_by = request.user return super().save_model(request, obj, form, change) ParentContractApprovedPermissionsMixin = permissions_mixin_factory( "contracts.edit_when_approved", "contracts.delete_when_approved", lambda request, obj: get_obj_contract(obj).is_approved, get_obj_contract, get_obj_contract, ) ParentContractOwnPermissionsMixin = permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", lambda request, obj: get_obj_contract(obj).created_by != request.user, get_obj_contract, get_obj_contract, ) # BEGIN Contracts class ContractFileAdmin( IndexHiddenModelAdmin, ParentContractApprovedPermissionsMixin, ParentContractOwnPermissionsMixin, ): form = ContractFileAdminForm class ContracteeSignatureRepresentativeInline(NestedStackedInline): model = ContracteeSignatureRepresentative extra = 0 class ContracteeSignatureInline(NestedStackedInline): model = ContracteeSignature autocomplete_fields = ("contractee",) inlines = (ContracteeSignatureRepresentativeInline,) extra = 0 class SigneeSignatureRepresentativeInline(NestedStackedInline): model = SigneeSignatureRepresentative extra = 0 class SigneeSignatureInline(NestedStackedInline): model = SigneeSignature autocomplete_fields = ("signee",) inlines = (SigneeSignatureRepresentativeInline,) extra = 0 class ContractFileInline(NestedTabularInline): model = ContractFile form = ContractFileAdminForm extra = 0 class ContractIntentInline(NestedTabularInline): model = ContractIntent extra = 0 class ContractAdmin( OwnPermissionsMixin, permissions_mixin_factory( "contracts.edit_when_approved", "contracts.delete_when_approved", lambda request, obj: obj.is_approved, ), MarkdownxGuardedModelAdmin, NestedModelAdmin, ): form = ContractAdminForm ordering = ("name",) search_fields = ("name",) readonly_fields = ("created_by",) autocomplete_fields = ( "primary_contract", "types", "filing_area", "issues", ) inlines = ( ContractFileInline, ContracteeSignatureInline, SigneeSignatureInline, ContractIntentInline, ) def get_form(self, request, *args, **kwargs) -> ContractAdminForm: form = super().get_form(request, *args, **kwargs) form.current_user = request.user return form def get_fieldsets(self, request, obj=None) -> list: fieldsets = [ ( "Základní informace", { "fields": [ "name", "id_number", "types", "summary", "is_public", "legal_state", "primary_contract", ] }, ), ( "Data", { "fields": [ "valid_start_date", "valid_end_date", ] }, ), ( "Náklady", { "fields": [ "cost_amount", "cost_unit", ] }, ), ( "Odkazy", { "fields": [ "tender_url", "agreement_url", ] }, ), ( "Fyzický dokument", { "fields": [ "paper_form_state", "filing_area", ] }, ), ( "Doplňující informace", { "fields": [ "issues", "notes", ] }, ), ] if ( obj is None # Creating confidential data, creator will be request.user or obj.created_by == request.user or request.user.has_perm("view_confidential", obj) ): fieldsets[0][1]["fields"].insert( fieldsets[0][1]["fields"].index("is_public") + 1, "publishing_rejection_comment", ) if request.user.has_perm("approve", self): fieldsets.insert( 5, ("Schválení", {"fields": ["is_approved"]}), ) return fieldsets def get_fieldsets_and_inlines_order(self, context) -> list: order = [ FieldsetInlineOrder.FIELDSET, FieldsetInlineOrder.FIELDSET, FieldsetInlineOrder.FIELDSET, FieldsetInlineOrder.INLINE, FieldsetInlineOrder.INLINE, FieldsetInlineOrder.INLINE, FieldsetInlineOrder.FIELDSET, FieldsetInlineOrder.INLINE, FieldsetInlineOrder.FIELDSET, FieldsetInlineOrder.FIELDSET, ] if context["user"].has_perm("approve", self): order.insert(10, FieldsetInlineOrder.FIELDSET) return order def get_queryset(self, request): queryset = super().get_queryset(request) if not request.user.has_perm("contracts.view_confidential"): queryset = queryset.filter(is_public=True) if not request.user.has_perm("contracts.approve"): queryset = queryset.filter(is_approved=True) return queryset def save_model(self, request, obj, form, change): # Need to generate primary keys first parent_save_response = super().save_model(request, obj, form, change) if obj.valid_start_date is None: last_signature_date = None for signature_set in ( obj.contractee_signatures.all(), obj.signee_signatures.all(), ): for signature in signature_set: if ( last_signature_date is None or last_signature_date < signature.date ): last_signature_date = signature.date obj.valid_start_date = last_signature_date return parent_save_response def has_change_permission(self, request, obj=None): if ( obj is not None and obj.is_approved and not request.user.has_perm("contracts.edit_when_approved", obj) ): return False return super().has_change_permission(request, obj) def has_delete_permission(self, request, obj=None): if ( obj is not None and obj.is_approved and not request.user.has_perm("contracts.delete_when_approved", obj) ): return False return super().has_change_permission(request, obj) list_filter = ( "types", "is_approved", "legal_state", "is_public", "paper_form_state", "issues", ("all_parties_sign_date", DateRangeFilter), ("valid_start_date", DateRangeFilter), ("valid_end_date", DateRangeFilter), ) list_display = ( "name", "is_approved", "is_public", ) class ContractTypeAdmin(MarkdownxGuardedModelAdmin): model = ContractType ordering = ("name",) search_fields = ("name",) class ContractIssueAdmin(MarkdownxGuardedModelAdmin): model = ContractIssue ordering = ("name",) search_fields = ("name",) class ContractFilingAreaAdmin(MarkdownxGuardedModelAdmin): model = ContractFilingArea ordering = ("name",) search_fields = ( "name", "person_responsible", ) # END Contracts # BEGIN Signing parties class ContracteeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): model = Contractee fields = ( "name", "address_street_with_number", "address_district", "address_zip", "address_country", "ico_number", "department", "role", ) search_fields = ( "name", "department", "role", ) ordering = ("name",) class SigneeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): model = Signee fields = ( "name", "entity_type", "address_street_with_number", "address_district", "address_zip", "address_country", "ico_number", "date_of_birth", "department", "role", ) search_fields = ( "name", "department", "role", ) ordering = ("name",) form = SigneeAdminForm readonly_fields = ("load_ares_data_button",) list_filter = ("entity_type",) list_display = ("name", "entity_type") def get_fields(self, request, obj=None): fields = [ "name", "entity_type", "ico_number", "department", "role", ] if ( obj is None # Creating or obj.entity_has_public_address or request.user.has_perm("contracts.view_confidential", obj) ): entity_type_index = fields.index("entity_type") + 1 fields[entity_type_index:entity_type_index] = [ "address_street_with_number", "address_district", "address_zip", "address_country", ] fields.insert( fields.index("department"), "date_of_birth", ) if obj is None or request.user.has_perm( # Allowed to create "contracts.edit_signee", obj ): fields.insert(fields.index("ico_number"), "load_ares_data_button") return fields def load_ares_data_button(self, obj): return format_html( '<button type="button" id="load_ares_data">Načíst data</button>' ) load_ares_data_button.allow_tags = True load_ares_data_button.short_description = "ARES" get_obj_signee_contract = lambda obj: obj.signee.contract get_obj_contractee_contract = lambda obj: obj.contractee.contract class SigneeSignatureRepresentativeAdmin( IndexHiddenModelAdmin, permissions_mixin_factory( "contracts.edit_when_approved", "contracts.delete_when_approved", lambda request, obj: get_obj_signee_contract(obj).is_approved, get_obj_signee_contract, get_obj_signee_contract, ), permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", lambda request, obj: get_obj_contractee_contract(obj).created_by != request.user, get_obj_signee_contract, get_obj_signee_contract, ), ): pass class ContracteeSignatureRepresentativeAdmin( IndexHiddenModelAdmin, permissions_mixin_factory( "contracts.edit_when_approved", "contracts.delete_when_approved", lambda request, obj: get_obj_contractee_contract(obj).is_approved, get_obj_contractee_contract, get_obj_contractee_contract, ), permissions_mixin_factory( "contracts.edit_others", "contracts.delete_others", lambda request, obj: get_obj_contractee_contract(obj).created_by != request.user, get_obj_contractee_contract, get_obj_contractee_contract, ), ): pass # END Signing parties class ContractSubmodelAdmin( IndexHiddenModelAdmin, ParentContractApprovedPermissionsMixin, ParentContractOwnPermissionsMixin, ): pass admin.site.register(Permission) for model in ( SigneeSignature, ContracteeSignature, ContractIntent, ): admin.site.register(model, ContractSubmodelAdmin) admin.site.register(SigneeSignatureRepresentative, SigneeSignatureRepresentativeAdmin) admin.site.register( ContracteeSignatureRepresentative, ContracteeSignatureRepresentativeAdmin ) admin.site.register(ContractType, ContractTypeAdmin) admin.site.register(ContractIssue, ContractIssueAdmin) admin.site.register(ContractFile, ContractFileAdmin) admin.site.register(ContractFilingArea, ContractFilingAreaAdmin) admin.site.register(Contractee, ContracteeAdmin) admin.site.register(Signee, SigneeAdmin) admin.site.register(Contract, ContractAdmin)