Select Git revision
templates.js
admin.py 16.47 KiB
import copy
import json
import logging
import typing
import requests
from admin_auto_filters.filters import AutocompleteFilterFactory
from django.conf import settings
from django.contrib import admin
from django.contrib.auth.models import Permission
from django.db import models
from django.utils.html import format_html
from nested_admin import NestedModelAdmin, NestedStackedInline, NestedTabularInline
from rangefilter.filters import DateRangeFilter
from shared.admin import FieldsetInlineOrder, MarkdownxGuardedModelAdmin
from .forms import (
AtLeastOneRequiredInlineFormSet,
ContractAdminForm,
ContractFileAdminForm,
SigneeAdminForm,
)
from .models import (
Contract,
ContractApproval,
Contractee,
ContracteeSignature,
ContracteeSignatureRepresentative,
ContractFile,
ContractFilingArea,
ContractIntent,
ContractIssue,
ContractType,
Signee,
SigneeSignature,
SigneeSignatureRepresentative,
)
logger = logging.getLogger()
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 = lambda request, obj: True,
) -> 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)
):
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(change_permission)
):
return False
return super().has_change_permission(request, obj)
return Mixin
get_obj_contract = lambda request, obj: obj.contract
class OwnPermissionsMixin(
permissions_mixin_factory(
"contracts.edit_others",
"contracts.delete_others",
obj_conditional=lambda request, obj: obj.created_by != request.user,
)
):
def 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",
obj_conditional=lambda request, obj: get_obj_contract(obj).is_approved,
)
ParentContractOwnPermissionsMixin = permissions_mixin_factory(
"contracts.edit_others",
"contracts.delete_others",
obj_conditional=lambda request, obj: get_obj_contract(obj).created_by
!= request.user,
)
# BEGIN Contracts
class ContractFileAdmin(
IndexHiddenModelAdmin,
ParentContractApprovedPermissionsMixin,
ParentContractOwnPermissionsMixin,
):
form = ContractFileAdminForm
class ContracteeSignatureRepresentativeInline(NestedStackedInline):
model = ContracteeSignatureRepresentative
extra = 1
formset = AtLeastOneRequiredInlineFormSet
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 ContractApprovalInline(NestedTabularInline):
model = ContractApproval
extra = 0
class ContractIntentInline(NestedTabularInline):
model = ContractIntent
extra = 0
class ContractAdmin(
OwnPermissionsMixin,
permissions_mixin_factory(
"contracts.edit_when_approved",
"contracts.delete_when_approved",
obj_conditional=lambda request, obj: obj.is_approved,
),
MarkdownxGuardedModelAdmin,
NestedModelAdmin,
):
form = ContractAdminForm
search_fields = ("name",)
readonly_fields = (
"created_by",
"created_on",
"updated_on",
)
autocomplete_fields = (
"primary_contract",
"types",
"filing_area",
"issues",
)
inlines = (
ContractFileInline,
ContracteeSignatureInline,
SigneeSignatureInline,
ContractApprovalInline,
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",
"is_valid",
"primary_contract",
]
},
),
(
"Data",
{
"fields": [
"valid_start_date",
"valid_end_date",
]
},
),
(
"Náklady",
{
"fields": [
"cost_amount",
"cost_unit",
"cost_unit_other",
]
},
),
(
"Odkazy",
{
"fields": [
"tender_url",
]
},
),
(
"Fyzický dokument",
{
"fields": [
"paper_form_state",
"paper_form_person_responsible",
"filing_area",
]
},
),
(
"Doplňující informace",
{
"fields": [
"issues",
"notes",
"created_by",
"created_on",
"updated_on",
]
},
),
]
if (
obj is None # Creating confidential data, creator will be request.user
or obj.created_by == request.user
or request.user.has_perm("contracts.view_confidential")
):
fieldsets[0][1]["fields"].insert(
fieldsets[0][1]["fields"].index("is_public") + 1,
"publishing_rejection_comment",
)
if obj is not None and request.user.has_perm("contracts.approve"):
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.INLINE,
FieldsetInlineOrder.FIELDSET,
FieldsetInlineOrder.FIELDSET,
]
if context["user"].has_perm("contracts.approve"):
order.insert(11, FieldsetInlineOrder.FIELDSET)
return order
def get_queryset(self, request):
queryset = super().get_queryset(request)
if not request.user.has_perm("contracts.view_confidential"):
# Allow user to view their own objects, even if not public
queryset = queryset.filter(
models.Q(is_public=True) | models.Q(created_by=request.user)
)
if not request.user.has_perm("contracts.approve"):
queryset = queryset.filter(
models.Q(is_approved=True) | models.Q(created_by=request.user)
)
return queryset
def save_model(self, request, obj, form, change):
is_new = obj.created_by is None
# 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
from users.models import User
if is_new:
try:
sso_ids = []
for user in User.objects.filter(is_staff=True).all():
if user.is_superuser or user.has_perm("contracts.approve"):
sso_ids.append(user.sso_id)
requests.post(
settings.NASTENKA_API_URL,
data=json.dumps(
{
"name": f"Nová smlouva ke schválení - {obj.name}",
"description": (
obj.summary
if obj.summary is not None
else "Bez popisu."
),
"contract_id": obj.id,
"sso_ids": sso_ids,
}
),
headers={
"Authorization": f"Token {settings.NASTENKA_API_TOKEN}",
"Content-Type": "application/json",
},
)
except Exception as exception:
logger.error(
"Failed to synchronizace Nástěnka notices: %s", str(exception)
)
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")
):
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")
):
return False
return super().has_change_permission(request, obj)
list_filter = (
AutocompleteFilterFactory("Typ", "types"),
AutocompleteFilterFactory("Spisovna", "filing_area"),
AutocompleteFilterFactory("Problém", "issues"),
AutocompleteFilterFactory(
"Naše smluvná strana", "contractee_signatures__contractee"
),
AutocompleteFilterFactory("Jiná smluvní strana", "signee_signatures__signee"),
"is_approved",
"is_valid",
"is_public",
"paper_form_state",
("all_parties_sign_date", DateRangeFilter),
("valid_start_date", DateRangeFilter),
("valid_end_date", DateRangeFilter),
)
list_display = (
"name",
"is_approved",
"is_public",
"created_on",
)
class ContractTypeAdmin(MarkdownxGuardedModelAdmin):
model = ContractType
search_fields = ("name",)
class ContractIssueAdmin(MarkdownxGuardedModelAdmin):
model = ContractIssue
search_fields = ("name",)
class ContractFilingAreaAdmin(MarkdownxGuardedModelAdmin):
model = ContractFilingArea
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",
)
search_fields = (
"name",
"department",
)
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",
)
search_fields = (
"name",
"department",
)
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",
]
if (
obj is None # Creating
or obj.entity_has_public_address
or request.user.has_perm("contracts.view_confidential")
):
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("contracts.edit_signee"):
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 request, obj: obj.signee.contract
get_obj_contractee_contract = lambda request, obj: obj.contractee.contract
class SigneeSignatureRepresentativeAdmin(
IndexHiddenModelAdmin,
permissions_mixin_factory(
"contracts.edit_when_approved",
"contracts.delete_when_approved",
obj_conditional=lambda request, obj: get_obj_signee_contract(obj).is_approved,
),
permissions_mixin_factory(
"contracts.edit_others",
"contracts.delete_others",
obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by
!= request.user,
),
):
pass
class ContracteeSignatureRepresentativeAdmin(
IndexHiddenModelAdmin,
permissions_mixin_factory(
"contracts.edit_when_approved",
"contracts.delete_when_approved",
obj_conditional=lambda request, obj: get_obj_contractee_contract(
obj
).is_approved,
),
permissions_mixin_factory(
"contracts.edit_others",
"contracts.delete_others",
obj_conditional=lambda request, obj: get_obj_contractee_contract(obj).created_by
!= request.user,
),
):
pass
# END Signing parties
class ContractSubmodelAdmin(
IndexHiddenModelAdmin,
ParentContractApprovedPermissionsMixin,
ParentContractOwnPermissionsMixin,
):
pass
admin.site.register(Permission)
for model in (
SigneeSignature,
ContracteeSignature,
ContractApproval,
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)