add: contract create serializer, TODO: fix errors with owners create and test
This commit is contained in:
@@ -95,7 +95,7 @@ PATCH /banks/<uuid:pk> # admin # ok
|
|||||||
|
|
||||||
|
|
||||||
GET /contracts # admin # ok
|
GET /contracts # admin # ok
|
||||||
POST /contracts # admin # ok
|
POST /contracts # user # remake
|
||||||
GET /contracts/<uuid:pk> # admin # ok
|
GET /contracts/<uuid:pk> # admin # ok
|
||||||
DELETE /contracts/<uuid:pk> # admin # ok
|
DELETE /contracts/<uuid:pk> # admin # ok
|
||||||
PATCH /contracts/<uuid:pk> # admin # ok
|
PATCH /contracts/<uuid:pk> # admin # ok
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Generated by Django 5.2.4 on 2025-08-05 06:17
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
("companies", "0004_alter_companymodel_logo_and_more"),
|
||||||
|
("contracts", "0001_initial"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name="companyfoldermodel",
|
||||||
|
name="contracts",
|
||||||
|
field=models.ManyToManyField(
|
||||||
|
blank=True, null=True, related_name="folders", to="contracts.contractmodel", verbose_name="Contracts"
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -37,6 +37,8 @@ class CompanyFolderModel(UUIDPrimaryKeyBaseModel):
|
|||||||
ContractModel,
|
ContractModel,
|
||||||
verbose_name=_("Contracts"),
|
verbose_name=_("Contracts"),
|
||||||
related_name="folders",
|
related_name="folders",
|
||||||
|
null=True,
|
||||||
|
blank=True
|
||||||
)
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
|||||||
@@ -1,8 +1,21 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers # type: ignore
|
||||||
|
from rest_framework.generics import ValidationError # type: ignore
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
from django.db import transaction
|
||||||
|
|
||||||
from core.apps.contracts.models import ContractModel
|
from core.apps.contracts.models import ContractModel
|
||||||
|
|
||||||
|
from core.apps.contracts.serializers.attached_files import (
|
||||||
|
CreateContractAttachedFileSerializer
|
||||||
|
)
|
||||||
|
from core.apps.contracts.serializers.owners import (
|
||||||
|
CreateContractOwnerSerializer,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Base Serializers
|
||||||
|
###########################################################
|
||||||
class BaseContractSerializer(serializers.ModelSerializer):
|
class BaseContractSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = ContractModel
|
model = ContractModel
|
||||||
@@ -14,6 +27,9 @@ class BaseContractSerializer(serializers.ModelSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# List Serializers
|
||||||
|
###########################################################
|
||||||
class ListContractSerializer(BaseContractSerializer):
|
class ListContractSerializer(BaseContractSerializer):
|
||||||
class Meta(BaseContractSerializer.Meta):
|
class Meta(BaseContractSerializer.Meta):
|
||||||
fields = (
|
fields = (
|
||||||
@@ -25,18 +41,114 @@ class ListContractSerializer(BaseContractSerializer):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Detail/Retrieve Serializers
|
||||||
|
###########################################################
|
||||||
class RetrieveContractSerializer(BaseContractSerializer):
|
class RetrieveContractSerializer(BaseContractSerializer):
|
||||||
class Meta(BaseContractSerializer.Meta): ...
|
class Meta(BaseContractSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
class CreateContractSerializer(BaseContractSerializer):
|
###########################################################
|
||||||
class Meta(BaseContractSerializer.Meta): ...
|
# Update Serializers
|
||||||
|
###########################################################
|
||||||
|
|
||||||
class UpdateContractSerializer(BaseContractSerializer):
|
class UpdateContractSerializer(BaseContractSerializer):
|
||||||
class Meta(BaseContractSerializer.Meta): ...
|
class Meta(BaseContractSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Delete/Destroy Serializers
|
||||||
|
###########################################################
|
||||||
class DestroyContractSerializer(BaseContractSerializer):
|
class DestroyContractSerializer(BaseContractSerializer):
|
||||||
class Meta(BaseContractSerializer.Meta):
|
class Meta(BaseContractSerializer.Meta):
|
||||||
fields = ["id"]
|
fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Create Serializers
|
||||||
|
###########################################################
|
||||||
|
class CreateOwnersForContractSerializer(CreateContractOwnerSerializer):
|
||||||
|
class Meta(CreateContractOwnerSerializer.Meta):
|
||||||
|
read_only_fields = (
|
||||||
|
*CreateContractOwnerSerializer.Meta.read_only_fields,
|
||||||
|
"contract",
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, validated_data: dict[str, object]) -> Meta.model:
|
||||||
|
contract = self.context.get("contract")
|
||||||
|
contract_id = self.context.get("contract_id")
|
||||||
|
|
||||||
|
context_data: dict[str, object] = {}
|
||||||
|
|
||||||
|
if contract is None and contract_id is None:
|
||||||
|
raise ValidationError(_("Either contract or contract_id should be given in context."))
|
||||||
|
|
||||||
|
if contract is not None:
|
||||||
|
context_data["contract"] = contract
|
||||||
|
else:
|
||||||
|
context_data["contract_id"] = contract_id
|
||||||
|
|
||||||
|
return super().create(
|
||||||
|
validated_data | context_data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateContractAttachedFileForContractSerializer(CreateContractAttachedFileSerializer):
|
||||||
|
class Meta(CreateContractAttachedFileSerializer.Meta): ...
|
||||||
|
|
||||||
|
def create(self, validated_data: dict[str, object]) -> Meta.model:
|
||||||
|
contract = self.context.get("contract")
|
||||||
|
contract_id = self.context.get("contract_id")
|
||||||
|
|
||||||
|
context_data: dict[str, object] = {}
|
||||||
|
|
||||||
|
if contract is None and contract_id is None:
|
||||||
|
raise ValidationError(_("Either contract or contract_id should be given in context."))
|
||||||
|
|
||||||
|
if contract is not None:
|
||||||
|
context_data["contract"] = contract
|
||||||
|
else:
|
||||||
|
context_data["contract_id"] = contract_id
|
||||||
|
|
||||||
|
return super().create( # type: ignore
|
||||||
|
validated_data | context_data
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateContractSerializer(BaseContractSerializer):
|
||||||
|
owners = CreateOwnersForContractSerializer(many=True)
|
||||||
|
attached_files = CreateContractAttachedFileForContractSerializer(
|
||||||
|
many=True, default=list, required=False
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta(BaseContractSerializer.Meta):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def validate_owners(self, owners: list[dict[str, object]]) -> list[dict[str, object]]:
|
||||||
|
if not owners:
|
||||||
|
raise ValidationError("Contract should have minimum one owner.")
|
||||||
|
return owners
|
||||||
|
|
||||||
|
def create(self, validated_data: dict[str, object]) -> Meta.model:
|
||||||
|
attached_files_data = validated_data.pop("attached_files", None)
|
||||||
|
owners_data = validated_data.pop("owners", None)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
contract = self.Meta.model()
|
||||||
|
contract.save()
|
||||||
|
|
||||||
|
owners = CreateOwnersForContractSerializer(
|
||||||
|
data=owners_data, many=True, context=self.context | {"contract": contract}
|
||||||
|
)
|
||||||
|
owners.is_valid(raise_exception=True)
|
||||||
|
owners.save() # type: ignore
|
||||||
|
|
||||||
|
if attached_files_data:
|
||||||
|
attached_files = CreateContractAttachedFileForContractSerializer(
|
||||||
|
data=attached_files_data, many=True, context=self.context | {"contract": contract}
|
||||||
|
)
|
||||||
|
attached_files.is_valid(raise_exception=True)
|
||||||
|
attached_files.save() # type: ignore
|
||||||
|
|
||||||
|
return contract
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,22 +1,19 @@
|
|||||||
from rest_framework import serializers
|
from rest_framework import serializers # type: ignore
|
||||||
|
from rest_framework.exceptions import ValidationError # type: ignore
|
||||||
|
|
||||||
from core.apps.contracts.models import (
|
from core.apps.contracts.models import (
|
||||||
ContractOwnerModel,
|
ContractOwnerModel,
|
||||||
LegalEntityModel,
|
LegalEntityModel,
|
||||||
IndividualModel,
|
IndividualModel,
|
||||||
)
|
)
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
# from core.apps.contracts.serializers.attached_files import (
|
|
||||||
# BaseContractAttachedFileSerializer,
|
|
||||||
# )
|
|
||||||
# from core.apps.contracts.serializers.file_contents import (
|
|
||||||
# BaseContractFileContentSerializer
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
#! TODO fix: BaseContractOwnerSerializer (.create/.update) fix
|
# Base Serializers
|
||||||
|
###########################################################
|
||||||
class BaseIndividualSerializer(serializers.ModelSerializer):
|
class BaseIndividualSerializer(serializers.ModelSerializer):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = IndividualModel
|
model = IndividualModel
|
||||||
@@ -25,26 +22,7 @@ class BaseIndividualSerializer(serializers.ModelSerializer):
|
|||||||
"id",
|
"id",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
)
|
"owner",
|
||||||
extra_kwargs = {
|
|
||||||
"iin_code": {
|
|
||||||
"required": False
|
|
||||||
},
|
|
||||||
"person_code": {
|
|
||||||
"required": False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ListIndividualSerializer(BaseIndividualSerializer):
|
|
||||||
class Meta(BaseIndividualSerializer.Meta):
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"full_name",
|
|
||||||
"phone",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -56,26 +34,7 @@ class BaseLegalEntitySerializer(serializers.ModelSerializer):
|
|||||||
"id",
|
"id",
|
||||||
"created_at",
|
"created_at",
|
||||||
"updated_at",
|
"updated_at",
|
||||||
)
|
"owner",
|
||||||
extra_kwargs = {
|
|
||||||
"bin_code": {
|
|
||||||
"required": False
|
|
||||||
},
|
|
||||||
"identifier": {
|
|
||||||
"required": False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ListLegalEntitySerializer(BaseLegalEntitySerializer):
|
|
||||||
class Meta(BaseLegalEntitySerializer.Meta):
|
|
||||||
fields = (
|
|
||||||
"id",
|
|
||||||
"name",
|
|
||||||
"phone",
|
|
||||||
"created_at",
|
|
||||||
"updated_at",
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -92,61 +51,34 @@ class BaseContractOwnerSerializer(serializers.ModelSerializer):
|
|||||||
"updated_at",
|
"updated_at",
|
||||||
)
|
)
|
||||||
extra_kwargs = {
|
extra_kwargs = {
|
||||||
"legal_entity": {
|
'legal_entity': {'required': False, 'allow_null': True},
|
||||||
"required": False
|
'individual': {'required': False, 'allow_null': True},
|
||||||
},
|
|
||||||
"individual": {
|
|
||||||
"required": False
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def create(self, validated_data):
|
|
||||||
legal_entity_data = validated_data.pop("legal_entity", None)
|
|
||||||
individual_data = validated_data.pop("individual", None)
|
|
||||||
|
|
||||||
with transaction.atomic():
|
###########################################################
|
||||||
if legal_entity_data is not None:
|
# List Serializers
|
||||||
legal_entity_serializer = BaseLegalEntitySerializer(data=legal_entity_data)
|
###########################################################
|
||||||
legal_entity_serializer.is_valid(raise_exception=True)
|
class ListIndividualSerializer(BaseIndividualSerializer):
|
||||||
validated_data["legal_entity"] = legal_entity_serializer.save()
|
class Meta(BaseIndividualSerializer.Meta):
|
||||||
|
fields = (
|
||||||
|
"id",
|
||||||
|
"full_name",
|
||||||
|
"phone",
|
||||||
|
"created_at",
|
||||||
|
"updated_at",
|
||||||
|
)
|
||||||
|
|
||||||
if individual_data is not None:
|
|
||||||
individual_serializer = BaseIndividualSerializer(data=individual_data)
|
|
||||||
individual_serializer.is_valid(raise_exception=True)
|
|
||||||
validated_data["individual"] = individual_serializer.save()
|
|
||||||
|
|
||||||
contract_owner = ContractOwnerModel.objects.create(**validated_data)
|
class ListLegalEntitySerializer(BaseLegalEntitySerializer):
|
||||||
return contract_owner
|
class Meta(BaseLegalEntitySerializer.Meta):
|
||||||
|
fields = (
|
||||||
def update(self, instance, validated_data):
|
"id",
|
||||||
legal_entity_data = validated_data.pop("legal_entity", None)
|
"name",
|
||||||
individual_data = validated_data.pop("individual", None)
|
"phone",
|
||||||
|
"created_at",
|
||||||
with transaction.atomic():
|
"updated_at",
|
||||||
if legal_entity_data is not None:
|
)
|
||||||
if instance.legal_entity:
|
|
||||||
for attr, value in legal_entity_data.items():
|
|
||||||
setattr(instance.legal_entity, attr, value)
|
|
||||||
instance.legal_entity.save()
|
|
||||||
else:
|
|
||||||
legal_entity = LegalEntityModel.objects.create(**legal_entity_data)
|
|
||||||
instance.legal_entity = legal_entity
|
|
||||||
|
|
||||||
if individual_data is not None:
|
|
||||||
if instance.individual:
|
|
||||||
for attr, value in individual_data.items():
|
|
||||||
setattr(instance.individual, attr, value)
|
|
||||||
instance.individual.save()
|
|
||||||
else:
|
|
||||||
individual = IndividualModel.objects.create(**individual_data)
|
|
||||||
instance.individual = individual
|
|
||||||
|
|
||||||
# Update ContractOwnerModel fields
|
|
||||||
for attr, value in validated_data.items():
|
|
||||||
setattr(instance, attr, value)
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
return instance
|
|
||||||
|
|
||||||
|
|
||||||
class ListContractOwnerSerializer(BaseContractOwnerSerializer):
|
class ListContractOwnerSerializer(BaseContractOwnerSerializer):
|
||||||
@@ -156,19 +88,125 @@ class ListContractOwnerSerializer(BaseContractOwnerSerializer):
|
|||||||
class Meta(BaseContractOwnerSerializer.Meta): ...
|
class Meta(BaseContractOwnerSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Detail/Retrieve Serializers
|
||||||
|
###########################################################
|
||||||
class RetrieveContractOwnerSerializer(BaseContractOwnerSerializer):
|
class RetrieveContractOwnerSerializer(BaseContractOwnerSerializer):
|
||||||
class Meta(BaseContractOwnerSerializer.Meta): ...
|
class Meta(BaseContractOwnerSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
class CreateContractOwnerSerializer(BaseContractOwnerSerializer):
|
###########################################################
|
||||||
class Meta(BaseContractOwnerSerializer.Meta): ...
|
# Update Serializers
|
||||||
|
###########################################################
|
||||||
|
class UpdateIndividualSeriailzer(BaseIndividualSerializer):
|
||||||
|
class Meta(BaseIndividualSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateLegalEntitySerializer(BaseLegalEntitySerializer):
|
||||||
|
class Meta(BaseLegalEntitySerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
class UpdateContractOwnerSerializer(BaseContractOwnerSerializer):
|
class UpdateContractOwnerSerializer(BaseContractOwnerSerializer):
|
||||||
|
individual = UpdateIndividualSeriailzer(required=False, partial=True)
|
||||||
|
legal_entity = UpdateLegalEntitySerializer(required=False, partial=True)
|
||||||
|
|
||||||
class Meta(BaseContractOwnerSerializer.Meta): ...
|
class Meta(BaseContractOwnerSerializer.Meta): ...
|
||||||
|
|
||||||
|
def update(self, instance: Meta.model, validated_data: dict[str, object]):
|
||||||
|
individual_data = validated_data.pop("individual", None)
|
||||||
|
legal_entity_data = validated_data.pop("legal_entity", None)
|
||||||
|
|
||||||
|
if instance.individual is not None:
|
||||||
|
if individual_data is not None:
|
||||||
|
individual_serializer = UpdateIndividualSeriailzer(
|
||||||
|
instance=instance.individual,
|
||||||
|
data=individual_data,
|
||||||
|
partial=True,
|
||||||
|
)
|
||||||
|
individual_serializer.is_valid(raise_exception=True)
|
||||||
|
individual_serializer.save() # type: ignore
|
||||||
|
|
||||||
|
elif instance.legal_entity is not None:
|
||||||
|
if legal_entity_data is not None:
|
||||||
|
legal_entity_serializer = UpdateLegalEntitySerializer(
|
||||||
|
instance=instance.legal_entity,
|
||||||
|
data=legal_entity_data,
|
||||||
|
partial=True
|
||||||
|
)
|
||||||
|
legal_entity_serializer.is_valid(raise_exception=True)
|
||||||
|
legal_entity_serializer.save() # type: ignore
|
||||||
|
|
||||||
|
return super().update(instance, validated_data) # type: ignore
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Delete/Destroy Serializers
|
||||||
|
###########################################################
|
||||||
class DestroyContractOwnerSerializer(BaseContractOwnerSerializer):
|
class DestroyContractOwnerSerializer(BaseContractOwnerSerializer):
|
||||||
class Meta(BaseContractOwnerSerializer.Meta):
|
class Meta(BaseContractOwnerSerializer.Meta):
|
||||||
fields = ["id"]
|
fields = ["id"]
|
||||||
|
|
||||||
|
|
||||||
|
###########################################################
|
||||||
|
# Create Serializers
|
||||||
|
###########################################################
|
||||||
|
class CreateIndividualSerializer(BaseIndividualSerializer):
|
||||||
|
class Meta(BaseIndividualSerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class CreateLegalEntitySerializer(BaseLegalEntitySerializer):
|
||||||
|
class Meta(BaseLegalEntitySerializer.Meta): ...
|
||||||
|
|
||||||
|
|
||||||
|
class CreateContractOwnerSerializer(BaseContractOwnerSerializer): #! TODO: problem: individual or legal entity can not be null
|
||||||
|
individual = CreateIndividualSerializer(required=False, allow_null=True, default=None)
|
||||||
|
legal_entity = CreateLegalEntitySerializer(required=False, allow_null=True, default=None)
|
||||||
|
|
||||||
|
class Meta(BaseContractOwnerSerializer.Meta):
|
||||||
|
read_only_fields = (
|
||||||
|
*BaseContractOwnerSerializer.Meta.read_only_fields,
|
||||||
|
"status"
|
||||||
|
)
|
||||||
|
|
||||||
|
def create(self, validated_data: dict[str, object]) -> Meta.model:
|
||||||
|
individual_data = validated_data.pop("individual", None)
|
||||||
|
legal_entity_data = validated_data.pop("legal_entity", None)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
if individual_data is not None:
|
||||||
|
individual_serializer = CreateIndividualSerializer(data=individual_data)
|
||||||
|
individual_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data["individual"] = individual_serializer.save() # type: ignore
|
||||||
|
|
||||||
|
if legal_entity_data is not None:
|
||||||
|
legal_entity_serializer = CreateLegalEntitySerializer(data=legal_entity_data)
|
||||||
|
legal_entity_serializer.is_valid(raise_exception=True)
|
||||||
|
validated_data["legal_entity"] = legal_entity_serializer.save() # type: ignore
|
||||||
|
|
||||||
|
owner = self.Meta.model(**validated_data)
|
||||||
|
owner.save()
|
||||||
|
return owner
|
||||||
|
|
||||||
|
def to_internal_value(self, data: dict[str, object]) -> dict[str, object]:
|
||||||
|
data = data.copy()
|
||||||
|
|
||||||
|
if "legal_entity" in data and data["legal_entity"] in [None, ""]:
|
||||||
|
data["legal_entity"] = None
|
||||||
|
|
||||||
|
if "individual" in data and data["individual"] in [None, ""]:
|
||||||
|
data["individual"] = None
|
||||||
|
|
||||||
|
return super().to_internal_value(data) # type: ignore
|
||||||
|
|
||||||
|
def validate(self, attrs: dict[str, object]):
|
||||||
|
legal = attrs.get("legal_entity")
|
||||||
|
individual = attrs.get("individual")
|
||||||
|
|
||||||
|
# Optional: enforce at least one must be provided
|
||||||
|
if not legal and not individual:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
"Either legal_entity or individual must be provided."
|
||||||
|
)
|
||||||
|
|
||||||
|
return attrs
|
||||||
|
|
||||||
Reference in New Issue
Block a user