import copy import typing from django.contrib import admin from django.contrib.auth.models import Permission from django.utils.html import format_html from fieldsets_with_inlines import FieldsetsInlineMixin from import_export import resources from nested_admin import (NestedModelAdmin, NestedStackedInline, NestedTabularInline) from rangefilter.filters import DateRangeFilter from shared.admin import 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 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) OwnPermissionsMixin.save_model = own_permissions_mixin_save_model 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, FieldsetsInlineMixin, NestedModelAdmin, ): form = ContractAdminForm readonly_fields = ("created_by",) autocomplete_fields = ( "types", "filing_area", "issues", ) fieldsets_with_inlines = [ ( "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", ] }, ), ContractFileInline, ContracteeSignatureInline, SigneeSignatureInline, ( "Odkazy", { "fields": [ "tender_url", "agreement_url", ] }, ), ContractIntentInline, ( "Fyzický dokument", { "fields": [ "paper_form_state", "filing_area", ] }, ), ( "Doplňující informace", { "fields": [ "issues", "notes", ] }, ), ] 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): fieldsets_with_inlines = copy.deepcopy(self.fieldsets_with_inlines) 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_with_inlines[0][1]["fields"].insert( fieldsets_with_inlines[0][1]["fields"].index("is_public") + 1, "publishing_rejection_comment", ) if request.user.is_superuser or request.user.has_perm("approve", self): fieldsets_with_inlines.insert( 8, ("Schválení", {"fields": ["is_approved"]}), ) return [ self.make_placeholder(index, fieldset) for index, fieldset in enumerate(fieldsets_with_inlines) ] 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): 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 super().save_model(request, obj, form, change) 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 search_fields = ( "name", "department", "role", ) ordering = ("name",) class SigneeAdmin(OwnPermissionsMixin, MarkdownxGuardedModelAdmin): model = Signee 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)