Игорь Надеин af26bfc6de
All checks were successful
Deploy production / deploy (push) Successful in 10m8s
Add balance-only payroll carryover adjustments
2026-10-15 16:29:32 +03:00

97 lines
3.5 KiB
Python

"""Balance-only corrections, deliberately unrelated to payroll input lines."""
import uuid
from decimal import Decimal
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from finance.enums import PayrollCarryoverDirection
class PayrollBalanceAdjustment(models.Model):
employee = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name="payroll_balance_adjustments",
)
effective_period = models.ForeignKey(
"finance.PayrollPeriod",
on_delete=models.PROTECT,
related_name="balance_adjustments",
)
source_period = models.ForeignKey(
"finance.PayrollPeriod",
on_delete=models.PROTECT,
null=True,
blank=True,
related_name="referenced_balance_adjustments",
)
direction = models.CharField(
max_length=20, choices=PayrollCarryoverDirection.choices
)
amount = models.DecimalField(
max_digits=19,
decimal_places=2,
validators=[MinValueValidator(Decimal("0.01"))],
)
reason = models.CharField(max_length=1000, blank=True)
request_id = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
created_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
related_name="payroll_balance_adjustments_created",
)
created_at = models.DateTimeField(auto_now_add=True)
voided_at = models.DateTimeField(null=True, blank=True)
voided_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.PROTECT,
null=True,
blank=True,
related_name="payroll_balance_adjustments_voided",
)
void_reason = models.CharField(max_length=1000, blank=True)
class Meta:
ordering = ["-effective_period__date_from", "-created_at", "-pk"]
verbose_name = "Ручная корректировка остатка"
verbose_name_plural = "Ручные корректировки остатков"
constraints = [
models.CheckConstraint(
condition=models.Q(amount__gt=0),
name="balance_adjustment_positive",
),
models.CheckConstraint(
condition=models.Q(direction__in=["company_owes", "employee_owes"]),
name="balance_adjustment_direction",
),
models.CheckConstraint(
condition=(
models.Q(
voided_at__isnull=True, voided_by__isnull=True, void_reason=""
)
| models.Q(voided_at__isnull=False, voided_by__isnull=False)
),
name="balance_adjustment_void_consistent",
),
]
@property
def contribution(self):
return self.amount if self.direction == "company_owes" else -self.amount
def clean(self):
super().clean()
if self.source_period_id and self.effective_period_id:
if self.source_period.currency != self.effective_period.currency:
raise ValidationError(
{"source_period": "Валюты периодов должны совпадать."}
)
if self.source_period.date_to >= self.effective_period.date_from:
raise ValidationError(
{"source_period": "Выберите более ранний период."}
)