270 lines
8.0 KiB
Python
270 lines
8.0 KiB
Python
from decimal import Decimal
|
|
|
|
from drf_spectacular.utils import extend_schema_field
|
|
from rest_framework import serializers
|
|
|
|
from finance.models import (
|
|
PayrollStatement,
|
|
PayrollStatementAcknowledgement,
|
|
PayrollStatementPaymentConfirmation,
|
|
PayrollStatementLine,
|
|
PayrollDailyWorkEntry,
|
|
PayrollWorkRecord,
|
|
)
|
|
|
|
|
|
class PayrollStatementLineSerializer(serializers.ModelSerializer):
|
|
is_automatic_carryover = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = PayrollStatementLine
|
|
fields = [
|
|
"code",
|
|
"label",
|
|
"kind",
|
|
"amount",
|
|
"source_period_from",
|
|
"source_period_to",
|
|
"is_retro",
|
|
"is_automatic_carryover",
|
|
"calculated",
|
|
]
|
|
|
|
def get_is_automatic_carryover(self, obj) -> bool:
|
|
return obj.source_ref.startswith("api:payroll-carryover:") or obj.source_ref.startswith(
|
|
"payroll-carryover:"
|
|
)
|
|
|
|
|
|
class PayrollStatementAcknowledgementSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = PayrollStatementAcknowledgement
|
|
fields = ["viewed_at", "acknowledged_at", "disputed_at"]
|
|
|
|
|
|
class PayrollConfirmedBySerializer(serializers.Serializer):
|
|
id = serializers.IntegerField()
|
|
display_name = serializers.CharField()
|
|
|
|
|
|
class PayrollStatementPaymentConfirmationSerializer(serializers.ModelSerializer):
|
|
confirmed_by = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = PayrollStatementPaymentConfirmation
|
|
fields = ["confirmed_at", "confirmed_by"]
|
|
read_only_fields = fields
|
|
|
|
@extend_schema_field(PayrollConfirmedBySerializer)
|
|
def get_confirmed_by(self, obj) -> dict[str, object]:
|
|
user = obj.confirmed_by
|
|
return {
|
|
"id": user.pk,
|
|
"display_name": user.get_full_name().strip() or f"Сотрудник #{user.pk}",
|
|
}
|
|
|
|
|
|
class PayrollPeriodPayloadSerializer(serializers.Serializer):
|
|
code = serializers.CharField()
|
|
name = serializers.CharField()
|
|
date_from = serializers.DateField()
|
|
date_to = serializers.DateField()
|
|
pay_date = serializers.DateField(allow_null=True)
|
|
|
|
|
|
class OwnPayrollPaymentConfirmationWriteSerializer(serializers.Serializer):
|
|
confirmed = serializers.BooleanField()
|
|
|
|
|
|
def period_payload(statement):
|
|
period = statement.run.period
|
|
return {
|
|
"code": period.code,
|
|
"name": period.name,
|
|
"date_from": period.date_from,
|
|
"date_to": period.date_to,
|
|
"pay_date": period.pay_date,
|
|
}
|
|
|
|
|
|
def acknowledgement_payload(statement):
|
|
try:
|
|
acknowledgement = statement.acknowledgement
|
|
except PayrollStatementAcknowledgement.DoesNotExist:
|
|
return None
|
|
return PayrollStatementAcknowledgementSerializer(acknowledgement).data
|
|
|
|
|
|
def payment_confirmation_payload(statement):
|
|
try:
|
|
confirmation = statement.payment_confirmation
|
|
except PayrollStatementPaymentConfirmation.DoesNotExist:
|
|
return None
|
|
return PayrollStatementPaymentConfirmationSerializer(confirmation).data
|
|
|
|
|
|
class OwnPayrollStatementSummarySerializer(serializers.ModelSerializer):
|
|
"""List metadata only; detailed amounts/lines require the audited endpoint."""
|
|
|
|
period = serializers.SerializerMethodField()
|
|
revision = serializers.IntegerField(source="run.revision", read_only=True)
|
|
published_at = serializers.DateTimeField(
|
|
source="run.published_at",
|
|
read_only=True,
|
|
)
|
|
acknowledgement = serializers.SerializerMethodField()
|
|
payment_confirmation = serializers.SerializerMethodField()
|
|
can_confirm_payment = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = PayrollStatement
|
|
fields = [
|
|
"public_id",
|
|
"period",
|
|
"revision",
|
|
"published_at",
|
|
"currency",
|
|
"payable",
|
|
"acknowledgement",
|
|
"payment_confirmation",
|
|
"can_confirm_payment",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
@extend_schema_field(PayrollPeriodPayloadSerializer)
|
|
def get_period(self, obj) -> dict[str, object]:
|
|
return period_payload(obj)
|
|
|
|
@extend_schema_field(
|
|
PayrollStatementAcknowledgementSerializer(allow_null=True)
|
|
)
|
|
def get_acknowledgement(self, obj) -> dict[str, object] | None:
|
|
return acknowledgement_payload(obj)
|
|
|
|
@extend_schema_field(
|
|
PayrollStatementPaymentConfirmationSerializer(allow_null=True)
|
|
)
|
|
def get_payment_confirmation(self, obj) -> dict[str, object] | None:
|
|
return payment_confirmation_payload(obj)
|
|
|
|
def get_can_confirm_payment(self, obj) -> bool:
|
|
return obj.run.period.status == "closed"
|
|
|
|
|
|
class OwnPayrollStatementSerializer(serializers.ModelSerializer):
|
|
period = serializers.SerializerMethodField()
|
|
revision = serializers.IntegerField(source="run.revision", read_only=True)
|
|
published_at = serializers.DateTimeField(
|
|
source="run.published_at",
|
|
read_only=True,
|
|
)
|
|
lines = PayrollStatementLineSerializer(many=True, read_only=True)
|
|
acknowledgement = serializers.SerializerMethodField()
|
|
payment_confirmation = serializers.SerializerMethodField()
|
|
can_confirm_payment = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = PayrollStatement
|
|
fields = [
|
|
"public_id",
|
|
"period",
|
|
"revision",
|
|
"published_at",
|
|
"currency",
|
|
"point_delta",
|
|
"gross_before_adjustments",
|
|
"adjustment_total",
|
|
"gross_total",
|
|
"deduction_total",
|
|
"net_pay",
|
|
"payment_total",
|
|
"opening_balance",
|
|
"closing_balance",
|
|
"payable",
|
|
"lines",
|
|
"acknowledgement",
|
|
"payment_confirmation",
|
|
"can_confirm_payment",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
@extend_schema_field(PayrollPeriodPayloadSerializer)
|
|
def get_period(self, obj) -> dict[str, object]:
|
|
return period_payload(obj)
|
|
|
|
@extend_schema_field(
|
|
PayrollStatementAcknowledgementSerializer(allow_null=True)
|
|
)
|
|
def get_acknowledgement(self, obj) -> dict[str, object] | None:
|
|
return acknowledgement_payload(obj)
|
|
|
|
@extend_schema_field(
|
|
PayrollStatementPaymentConfirmationSerializer(allow_null=True)
|
|
)
|
|
def get_payment_confirmation(self, obj) -> dict[str, object] | None:
|
|
return payment_confirmation_payload(obj)
|
|
|
|
def get_can_confirm_payment(self, obj) -> bool:
|
|
return obj.run.period.status == "closed"
|
|
|
|
|
|
class OwnPayrollWorkRecordSerializer(serializers.ModelSerializer):
|
|
status_label = serializers.CharField(source="get_status_display", read_only=True)
|
|
|
|
class Meta:
|
|
model = PayrollWorkRecord
|
|
fields = [
|
|
"id",
|
|
"period_id",
|
|
"target_points",
|
|
"target_points_overridden",
|
|
"actual_points",
|
|
"revision",
|
|
"status",
|
|
"status_label",
|
|
"lock_version",
|
|
"replaces_id",
|
|
"reason",
|
|
"source",
|
|
"created_at",
|
|
"updated_at",
|
|
"approved_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
class OwnPayrollDailyWorkEntrySerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = PayrollDailyWorkEntry
|
|
fields = [
|
|
"id",
|
|
"period_id",
|
|
"work_date",
|
|
"target_points",
|
|
"actual_points",
|
|
"note",
|
|
"lock_version",
|
|
"created_at",
|
|
"updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
class OwnPayrollDailyWorkEntryWriteSerializer(serializers.Serializer):
|
|
period_id = serializers.IntegerField(min_value=1)
|
|
work_date = serializers.DateField()
|
|
actual_points = serializers.DecimalField(
|
|
max_digits=19,
|
|
decimal_places=4,
|
|
min_value=Decimal("0"),
|
|
)
|
|
note = serializers.CharField(
|
|
allow_blank=True,
|
|
required=False,
|
|
trim_whitespace=True,
|
|
)
|
|
expected_lock_version = serializers.IntegerField(
|
|
min_value=0,
|
|
required=False,
|
|
)
|