add: contract create serializer, TODO: fix errors with owners create and test

This commit is contained in:
2025-08-05 15:49:53 +05:00
parent e77f799802
commit 5630429974
6 changed files with 283 additions and 110 deletions

View File

@@ -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

View File

@@ -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"
),
),
]

View File

@@ -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):

View File

@@ -19,7 +19,7 @@ from core.apps.contracts.validators.owners import (
) )
class LegalEntityModel(UUIDPrimaryKeyBaseModel): class LegalEntityModel(UUIDPrimaryKeyBaseModel):
name = models.CharField( name = models.CharField(
_("Name"), _("Name"),
validators=[ validators=[
@@ -104,7 +104,7 @@ class IndividualModel(UUIDPrimaryKeyBaseModel):
) )
iin_code = models.CharField( iin_code = models.CharField(
_("IIN code"), _("IIN code"),
max_length=14, max_length=14,
validators=[ validators=[
iin_code_validator, iin_code_validator,

View File

@@ -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

View File

@@ -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,62 +51,35 @@ 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",
"updated_at",
)
with transaction.atomic():
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):
legal_entity = ListLegalEntitySerializer(read_only=True) legal_entity = ListLegalEntitySerializer(read_only=True)
@@ -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