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 readonly_fields = ("created_by",) autocomplete_fields = ( "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): 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)