# Generated by Django 5.2.4 on 2026-07-17 12:13 import uuid from decimal import Decimal import django.core.validators import django.db.models.deletion from django.conf import settings from django.db import migrations, models class Migration(migrations.Migration): initial = True dependencies = [ migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name="PayrollComponent", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "code", models.CharField( max_length=64, unique=True, validators=[ django.core.validators.RegexValidator( message="Код должен начинаться с A-Z и содержать только A-Z, 0-9, _.-", regex="^[A-Z][A-Z0-9_.-]{0,63}$", ) ], verbose_name="Код", ), ), ("name", models.CharField(max_length=160, verbose_name="Название")), ( "kind", models.CharField( choices=[ ("earning", "Начисление"), ("adjustment_credit", "Коррекция +"), ("adjustment_debit", "Коррекция −"), ("deduction", "Удержание"), ("payment", "Выплата"), ], max_length=24, verbose_name="Тип", ), ), ( "requires_reason", models.BooleanField( default=False, verbose_name="Требует основание" ), ), ( "is_active", models.BooleanField(default=True, verbose_name="Активен"), ), ( "display_order", models.PositiveIntegerField(default=100, verbose_name="Порядок"), ), ], options={ "verbose_name": "Компонент зарплаты", "verbose_name_plural": "Компоненты зарплаты", "ordering": ["display_order", "code"], }, ), migrations.CreateModel( name="PayrollPeriod", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "code", models.CharField(max_length=32, unique=True, verbose_name="Код"), ), ( "name", models.CharField( blank=True, max_length=120, verbose_name="Название" ), ), ("date_from", models.DateField(verbose_name="Начало периода")), ("date_to", models.DateField(verbose_name="Конец периода")), ( "pay_date", models.DateField( blank=True, null=True, verbose_name="Дата выплаты" ), ), ( "currency", models.CharField( default="RUB", max_length=3, validators=[ django.core.validators.RegexValidator( message="Валюта должна быть трёхбуквенным кодом в верхнем регистре.", regex="^[A-Z]{3}$", ) ], verbose_name="Валюта", ), ), ( "status", models.CharField( choices=[ ("open", "Открыт"), ("calculated", "Рассчитан"), ("review", "На проверке"), ("approved", "Утверждён"), ("published", "Опубликован"), ("closed", "Закрыт"), ], default="open", max_length=20, verbose_name="Статус", ), ), ( "lock_version", models.PositiveIntegerField( default=0, verbose_name="Версия блокировки" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создан"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлён"), ), ( "created_by", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="created_payroll_periods", to=settings.AUTH_USER_MODEL, verbose_name="Создал", ), ), ], options={ "verbose_name": "Расчётный период", "verbose_name_plural": "Расчётные периоды", "ordering": ["-date_from", "-id"], "permissions": [ ("manage_payroll_inputs", "Может управлять данными для расчёта"), ("approve_payroll_inputs", "Может утверждать данные для расчёта"), ("calculate_payroll", "Может запускать расчёт зарплаты"), ("approve_payroll", "Может утверждать расчёт зарплаты"), ("publish_payroll", "Может публиковать расчётные листки"), ("view_all_payroll", "Может просматривать все расчёты"), ("audit_payroll", "Может просматривать аудит расчётов"), ], }, ), migrations.CreateModel( name="PayrollRun", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("revision", models.PositiveIntegerField(verbose_name="Ревизия")), ( "status", models.CharField( choices=[ ("calculated", "Рассчитан"), ("review", "На проверке"), ("approved", "Утверждён"), ("published", "Опубликован"), ("superseded", "Заменён новой ревизией"), ], default="calculated", max_length=20, verbose_name="Статус", ), ), ( "idempotency_key", models.UUIDField( default=uuid.uuid4, editable=False, unique=True, verbose_name="Ключ идемпотентности", ), ), ( "ruleset_id", models.CharField(max_length=120, verbose_name="Набор правил"), ), ( "ruleset_version", models.CharField(max_length=64, verbose_name="Версия правил"), ), ( "ruleset_hash", models.CharField(max_length=64, verbose_name="Хэш правил"), ), ( "calculator_version", models.CharField(max_length=64, verbose_name="Версия калькулятора"), ), ( "input_hash", models.CharField(max_length=64, verbose_name="Хэш входных данных"), ), ( "result_hash", models.CharField(max_length=64, verbose_name="Хэш результата"), ), ( "employee_count", models.PositiveIntegerField(default=0, verbose_name="Сотрудников"), ), ( "gross_total", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=21, verbose_name="Всего начислено", ), ), ( "deduction_total", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=21, verbose_name="Всего удержано", ), ), ( "payable_total", models.DecimalField( decimal_places=2, default=Decimal("0"), max_digits=21, verbose_name="Всего к выплате", ), ), ( "requested_at", models.DateTimeField(auto_now_add=True, verbose_name="Рассчитан"), ), ( "approved_at", models.DateTimeField( blank=True, null=True, verbose_name="Утверждён" ), ), ( "published_at", models.DateTimeField( blank=True, null=True, verbose_name="Опубликован" ), ), ( "approved_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="approved_payroll_runs", to=settings.AUTH_USER_MODEL, verbose_name="Утвердил", ), ), ( "period", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="runs", to="finance.payrollperiod", verbose_name="Период", ), ), ( "published_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="published_payroll_runs", to=settings.AUTH_USER_MODEL, verbose_name="Опубликовал", ), ), ( "requested_by", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="requested_payroll_runs", to=settings.AUTH_USER_MODEL, verbose_name="Запустил", ), ), ( "supersedes", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="superseded_by_runs", to="finance.payrollrun", verbose_name="Пересчитывает ревизию", ), ), ], options={ "verbose_name": "Запуск расчёта", "verbose_name_plural": "Запуски расчёта", "ordering": ["-period__date_from", "-revision"], }, ), migrations.AddField( model_name="payrollperiod", name="current_run", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="active_for_periods", to="finance.payrollrun", verbose_name="Текущая ревизия расчёта", ), ), migrations.CreateModel( name="PayrollStatement", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "employee_snapshot", models.JSONField(default=dict, verbose_name="Снимок сотрудника"), ), ( "currency", models.CharField( max_length=3, validators=[ django.core.validators.RegexValidator( message="Валюта должна быть трёхбуквенным кодом в верхнем регистре.", regex="^[A-Z]{3}$", ) ], verbose_name="Валюта", ), ), ( "point_delta", models.DecimalField( blank=True, decimal_places=4, max_digits=19, null=True, verbose_name="Отклонение баллов", ), ), ( "gross_before_adjustments", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Начислено до корректировок", ), ), ( "adjustment_total", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Корректировки" ), ), ( "gross_total", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Итого начислено" ), ), ( "deduction_total", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Удержания" ), ), ( "net_pay", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="После удержаний" ), ), ( "payment_total", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Уже выплачено" ), ), ( "payable", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="К выплате" ), ), ( "input_hash", models.CharField(max_length=64, verbose_name="Хэш входных данных"), ), ( "result_hash", models.CharField(max_length=64, verbose_name="Хэш результата"), ), ( "input_snapshot", models.JSONField(default=dict, verbose_name="Входной снимок"), ), ( "result_snapshot", models.JSONField(default=dict, verbose_name="Результат расчёта"), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создан"), ), ( "employee", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="payroll_statements", to=settings.AUTH_USER_MODEL, verbose_name="Сотрудник", ), ), ( "run", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="statements", to="finance.payrollrun", verbose_name="Запуск", ), ), ( "supersedes", models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="superseded_by_statement", to="finance.payrollstatement", verbose_name="Заменяет листок", ), ), ], options={ "verbose_name": "Расчётный листок", "verbose_name_plural": "Расчётные листки", "ordering": ["-run__period__date_from", "employee_id"], }, ), migrations.CreateModel( name="PayrollStatementAcknowledgement", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "content_hash", models.CharField( max_length=64, verbose_name="Хэш подтверждённого листка" ), ), ( "idempotency_key", models.UUIDField( default=uuid.uuid4, editable=False, unique=True, verbose_name="Ключ идемпотентности", ), ), ( "viewed_at", models.DateTimeField( blank=True, null=True, verbose_name="Просмотрен" ), ), ( "acknowledged_at", models.DateTimeField( blank=True, null=True, verbose_name="Получение подтверждено" ), ), ( "disputed_at", models.DateTimeField(blank=True, null=True, verbose_name="Оспорен"), ), ( "dispute_reason", models.TextField(blank=True, verbose_name="Причина оспаривания"), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлено"), ), ( "employee", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="payroll_acknowledgements", to=settings.AUTH_USER_MODEL, verbose_name="Сотрудник", ), ), ( "statement", models.OneToOneField( on_delete=django.db.models.deletion.PROTECT, related_name="acknowledgement", to="finance.payrollstatement", verbose_name="Расчётный листок", ), ), ], options={ "verbose_name": "Подтверждение расчётного листка", "verbose_name_plural": "Подтверждения расчётных листков", }, ), migrations.CreateModel( name="PayrollStatementLine", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("position", models.PositiveIntegerField(verbose_name="Позиция")), ( "line_id", models.CharField( max_length=160, verbose_name="Идентификатор строки" ), ), ("code", models.CharField(max_length=64, verbose_name="Код")), ("label", models.CharField(max_length=160, verbose_name="Название")), ( "kind", models.CharField( choices=[ ("earning", "Начисление"), ("adjustment_credit", "Коррекция +"), ("adjustment_debit", "Коррекция −"), ("deduction", "Удержание"), ("payment", "Выплата"), ], max_length=24, verbose_name="Тип", ), ), ( "amount", models.DecimalField( decimal_places=2, max_digits=21, verbose_name="Сумма" ), ), ( "source_ref", models.CharField(max_length=255, verbose_name="Источник"), ), ("reason", models.TextField(blank=True, verbose_name="Основание")), ( "source_period_from", models.DateField( blank=True, null=True, verbose_name="Период-источник: начало" ), ), ( "source_period_to", models.DateField( blank=True, null=True, verbose_name="Период-источник: конец" ), ), ( "calculated", models.BooleanField(default=False, verbose_name="Рассчитана ядром"), ), ( "statement", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="lines", to="finance.payrollstatement", verbose_name="Расчётный листок", ), ), ], options={ "verbose_name": "Строка расчётного листка", "verbose_name_plural": "Строки расчётных листков", "ordering": ["statement_id", "position"], }, ), migrations.CreateModel( name="PayrollWorkRecord", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "target_points", models.DecimalField( decimal_places=4, max_digits=19, validators=[ django.core.validators.MinValueValidator(Decimal("0.0001")) ], verbose_name="Норма баллов", ), ), ( "actual_points", models.DecimalField( decimal_places=4, max_digits=19, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Фактические баллы", ), ), ( "expected_point_amount", models.DecimalField( blank=True, decimal_places=2, max_digits=19, null=True, verbose_name="Контроль: выработка по баллам", ), ), ( "expected_gross", models.DecimalField( blank=True, decimal_places=2, max_digits=19, null=True, verbose_name="Контроль: итого начислено", ), ), ( "expected_recalculated_gross", models.DecimalField( blank=True, decimal_places=2, max_digits=19, null=True, verbose_name="Контроль: перерасчёт", ), ), ( "expected_payable", models.DecimalField( blank=True, decimal_places=2, max_digits=19, null=True, verbose_name="Контроль: к выплате", ), ), ( "revision", models.PositiveIntegerField(default=1, verbose_name="Ревизия"), ), ( "status", models.CharField( choices=[ ("draft", "Черновик"), ("approved", "Утверждено"), ("voided", "Аннулировано"), ], default="draft", max_length=16, verbose_name="Статус", ), ), ( "source", models.CharField( choices=[ ("manual", "Вручную"), ("excel", "Excel"), ("api", "API"), ], default="manual", max_length=16, verbose_name="Источник", ), ), ( "source_ref", models.CharField( blank=True, max_length=255, verbose_name="Идентификатор в источнике", ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создана"), ), ( "updated_at", models.DateTimeField(auto_now=True, verbose_name="Обновлена"), ), ( "approved_at", models.DateTimeField( blank=True, null=True, verbose_name="Утверждена" ), ), ( "voided_at", models.DateTimeField( blank=True, null=True, verbose_name="Аннулирована" ), ), ( "approved_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="approved_payroll_work_records", to=settings.AUTH_USER_MODEL, verbose_name="Утвердил", ), ), ( "created_by", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="created_payroll_work_records", to=settings.AUTH_USER_MODEL, verbose_name="Создал", ), ), ( "employee", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="payroll_work_records", to=settings.AUTH_USER_MODEL, verbose_name="Сотрудник", ), ), ( "period", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="work_records", to="finance.payrollperiod", verbose_name="Период", ), ), ( "replaces", models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="replaced_by", to="finance.payrollworkrecord", verbose_name="Заменяет запись", ), ), ( "voided_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="voided_payroll_work_records", to=settings.AUTH_USER_MODEL, verbose_name="Аннулировал", ), ), ], options={ "verbose_name": "Выработка за период", "verbose_name_plural": "Выработка за периоды", "ordering": ["period_id", "employee_id", "-revision"], }, ), migrations.CreateModel( name="EmployeePayRate", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "rate_code", models.CharField( default="BASE", max_length=64, validators=[ django.core.validators.RegexValidator( message="Код должен начинаться с A-Z и содержать только A-Z, 0-9, _.-", regex="^[A-Z][A-Z0-9_.-]{0,63}$", ) ], verbose_name="Код ставки", ), ), ( "amount", models.DecimalField( decimal_places=4, max_digits=19, validators=[ django.core.validators.MinValueValidator(Decimal("0.0001")) ], verbose_name="Сумма ставки", ), ), ( "point_rate", models.DecimalField( decimal_places=4, default=Decimal("0"), max_digits=19, validators=[ django.core.validators.MinValueValidator(Decimal("0")) ], verbose_name="Цена балла сверх нормы", ), ), ( "currency", models.CharField( default="RUB", max_length=3, validators=[ django.core.validators.RegexValidator( message="Валюта должна быть трёхбуквенным кодом в верхнем регистре.", regex="^[A-Z]{3}$", ) ], verbose_name="Валюта", ), ), ("effective_from", models.DateField(verbose_name="Действует с")), ( "revision", models.PositiveIntegerField(default=1, verbose_name="Ревизия"), ), ( "status", models.CharField( choices=[ ("draft", "Черновик"), ("approved", "Утверждено"), ("voided", "Аннулировано"), ], default="draft", max_length=16, verbose_name="Статус", ), ), ("reason", models.TextField(blank=True, verbose_name="Основание")), ( "source", models.CharField( choices=[ ("manual", "Вручную"), ("excel", "Excel"), ("api", "API"), ], default="manual", max_length=16, verbose_name="Источник", ), ), ( "source_ref", models.CharField( blank=True, max_length=255, verbose_name="Идентификатор в источнике", ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создана"), ), ( "approved_at", models.DateTimeField( blank=True, null=True, verbose_name="Утверждена" ), ), ( "voided_at", models.DateTimeField( blank=True, null=True, verbose_name="Аннулирована" ), ), ( "void_reason", models.TextField(blank=True, verbose_name="Причина аннулирования"), ), ( "approved_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="approved_pay_rates", to=settings.AUTH_USER_MODEL, verbose_name="Утвердил", ), ), ( "created_by", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="created_pay_rates", to=settings.AUTH_USER_MODEL, verbose_name="Создал", ), ), ( "employee", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="pay_rates", to=settings.AUTH_USER_MODEL, verbose_name="Сотрудник", ), ), ( "replaces", models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="replaced_by", to="finance.employeepayrate", verbose_name="Заменяет ставку", ), ), ( "voided_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="voided_pay_rates", to=settings.AUTH_USER_MODEL, verbose_name="Аннулировал", ), ), ], options={ "verbose_name": "Ставка сотрудника", "verbose_name_plural": "Ставки сотрудников", "ordering": ["employee_id", "-effective_from", "-revision"], "indexes": [ models.Index( fields=["employee", "status", "effective_from"], name="pay_rate_employee_date_idx", ) ], "constraints": [ models.CheckConstraint( condition=models.Q(("amount__gt", 0)), name="pay_rate_amount_positive", ), models.CheckConstraint( condition=models.Q(("point_rate__gte", 0)), name="pay_rate_point_nonnegative", ), models.CheckConstraint( condition=models.Q( ("approved_by__isnull", True), models.Q( ("created_by", models.F("approved_by")), _negated=True ), _connector="OR", ), name="pay_rate_maker_not_approver", ), models.UniqueConstraint( fields=("employee", "rate_code", "effective_from", "revision"), name="pay_rate_revision_unique", ), models.UniqueConstraint( condition=models.Q(("status", "approved")), fields=("employee", "rate_code", "effective_from"), name="pay_rate_one_approved_event", ), models.UniqueConstraint( condition=models.Q(("source_ref", ""), _negated=True), fields=("source", "source_ref"), name="pay_rate_source_ref_unique", ), ], }, ), migrations.CreateModel( name="PayrollInputLine", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "amount", models.DecimalField( decimal_places=2, max_digits=19, validators=[ django.core.validators.MinValueValidator(Decimal("0.01")) ], verbose_name="Сумма", ), ), ("reason", models.TextField(blank=True, verbose_name="Основание")), ( "status", models.CharField( choices=[ ("draft", "Черновик"), ("approved", "Утверждено"), ("voided", "Аннулировано"), ], default="draft", max_length=16, verbose_name="Статус", ), ), ( "source", models.CharField( choices=[ ("manual", "Вручную"), ("excel", "Excel"), ("api", "API"), ], default="manual", max_length=16, verbose_name="Источник", ), ), ( "source_ref", models.CharField( blank=True, max_length=255, verbose_name="Идентификатор в источнике", ), ), ( "idempotency_key", models.UUIDField( default=uuid.uuid4, editable=False, unique=True, verbose_name="Ключ идемпотентности", ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создана"), ), ( "approved_at", models.DateTimeField( blank=True, null=True, verbose_name="Утверждена" ), ), ( "voided_at", models.DateTimeField( blank=True, null=True, verbose_name="Аннулирована" ), ), ( "void_reason", models.TextField(blank=True, verbose_name="Причина аннулирования"), ), ( "approved_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="approved_payroll_input_lines", to=settings.AUTH_USER_MODEL, verbose_name="Утвердил", ), ), ( "component", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="input_lines", to="finance.payrollcomponent", verbose_name="Компонент", ), ), ( "created_by", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="created_payroll_input_lines", to=settings.AUTH_USER_MODEL, verbose_name="Создал", ), ), ( "employee", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="payroll_input_lines", to=settings.AUTH_USER_MODEL, verbose_name="Сотрудник", ), ), ( "reversal_of", models.OneToOneField( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="reversed_by", to="finance.payrollinputline", verbose_name="Сторнирует строку", ), ), ( "voided_by", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="voided_payroll_input_lines", to=settings.AUTH_USER_MODEL, verbose_name="Аннулировал", ), ), ( "period", models.ForeignKey( on_delete=django.db.models.deletion.PROTECT, related_name="input_lines", to="finance.payrollperiod", verbose_name="Период выплаты", ), ), ( "relates_to_period", models.ForeignKey( blank=True, help_text="Заполняется для перерасчёта прошлого периода.", null=True, on_delete=django.db.models.deletion.PROTECT, related_name="retro_input_lines", to="finance.payrollperiod", verbose_name="Относится к периоду", ), ), ], options={ "verbose_name": "Строка начисления/удержания", "verbose_name_plural": "Строки начислений/удержаний", "ordering": [ "period_id", "employee_id", "component__display_order", "id", ], "indexes": [ models.Index( fields=["period", "employee", "status"], name="pay_input_period_employee_idx", ) ], "constraints": [ models.CheckConstraint( condition=models.Q(("amount__gt", 0)), name="pay_input_amount_positive", ), models.CheckConstraint( condition=models.Q( ("approved_by__isnull", True), models.Q( ("created_by", models.F("approved_by")), _negated=True ), _connector="OR", ), name="pay_input_maker_not_approver", ), models.UniqueConstraint( condition=models.Q(("source_ref", ""), _negated=True), fields=("source", "source_ref"), name="pay_input_source_ref_unique", ), ], }, ), migrations.CreateModel( name="PayrollAuditEvent", fields=[ ( "id", models.BigAutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ("action", models.CharField(max_length=80, verbose_name="Действие")), ( "object_type", models.CharField(max_length=80, verbose_name="Тип объекта"), ), ( "object_id", models.CharField( max_length=80, verbose_name="Идентификатор объекта" ), ), ( "before_hash", models.CharField(blank=True, max_length=64, verbose_name="Хэш до"), ), ( "after_hash", models.CharField( blank=True, max_length=64, verbose_name="Хэш после" ), ), ( "metadata", models.JSONField( blank=True, default=dict, verbose_name="Безопасные метаданные" ), ), ( "created_at", models.DateTimeField(auto_now_add=True, verbose_name="Создано"), ), ( "actor", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="payroll_audit_events", to=settings.AUTH_USER_MODEL, verbose_name="Инициатор", ), ), ( "period", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name="audit_events", to="finance.payrollperiod", verbose_name="Период", ), ), ], options={ "verbose_name": "Событие аудита зарплаты", "verbose_name_plural": "События аудита зарплаты", "ordering": ["-created_at", "-id"], "indexes": [ models.Index( fields=["object_type", "object_id", "created_at"], name="pay_audit_object_time_idx", ), models.Index( fields=["period", "created_at"], name="pay_audit_period_time_idx", ), ], }, ), migrations.AddIndex( model_name="payrollrun", index=models.Index( fields=["period", "status", "revision"], name="pay_run_period_status_idx", ), ), migrations.AddConstraint( model_name="payrollrun", constraint=models.UniqueConstraint( fields=("period", "revision"), name="pay_run_revision_unique" ), ), migrations.AddConstraint( model_name="payrollrun", constraint=models.CheckConstraint( condition=models.Q( ("approved_by__isnull", True), models.Q(("requested_by", models.F("approved_by")), _negated=True), _connector="OR", ), name="pay_run_maker_not_approver", ), ), migrations.AddConstraint( model_name="payrollrun", constraint=models.CheckConstraint( condition=models.Q(("gross_total__gte", 0)), name="pay_run_gross_nonnegative", ), ), migrations.AddConstraint( model_name="payrollrun", constraint=models.CheckConstraint( condition=models.Q(("deduction_total__gte", 0)), name="pay_run_deduction_nonnegative", ), ), migrations.AddConstraint( model_name="payrollrun", constraint=models.CheckConstraint( condition=models.Q(("payable_total__gte", 0)), name="pay_run_payable_nonnegative", ), ), migrations.AddIndex( model_name="payrollperiod", index=models.Index( fields=["status", "date_from"], name="pay_period_status_date_idx" ), ), migrations.AddConstraint( model_name="payrollperiod", constraint=models.CheckConstraint( condition=models.Q(("date_from__lte", models.F("date_to"))), name="pay_period_dates_valid", ), ), migrations.AddConstraint( model_name="payrollperiod", constraint=models.UniqueConstraint( fields=("date_from", "date_to"), name="pay_period_dates_unique" ), ), migrations.AddIndex( model_name="payrollstatement", index=models.Index( fields=["employee", "run"], name="pay_statement_employee_idx" ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.UniqueConstraint( fields=("run", "employee"), name="pay_statement_run_employee_unique" ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.CheckConstraint( condition=models.Q(("gross_before_adjustments__gte", 0)), name="pay_statement_pre_gross_nonnegative", ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.CheckConstraint( condition=models.Q(("gross_total__gte", 0)), name="pay_statement_gross_nonnegative", ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.CheckConstraint( condition=models.Q(("deduction_total__gte", 0)), name="pay_statement_deduction_nonnegative", ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.CheckConstraint( condition=models.Q(("payment_total__gte", 0)), name="pay_statement_payment_nonnegative", ), ), migrations.AddConstraint( model_name="payrollstatement", constraint=models.CheckConstraint( condition=models.Q(("payable__gte", 0)), name="pay_statement_payable_nonnegative", ), ), migrations.AddConstraint( model_name="payrollstatementline", constraint=models.UniqueConstraint( fields=("statement", "position"), name="pay_statement_line_position_unique", ), ), migrations.AddConstraint( model_name="payrollstatementline", constraint=models.UniqueConstraint( fields=("statement", "line_id"), name="pay_statement_line_id_unique" ), ), migrations.AddConstraint( model_name="payrollstatementline", constraint=models.CheckConstraint( condition=models.Q(("amount__gte", 0)), name="pay_statement_line_amount_nonnegative", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.CheckConstraint( condition=models.Q(("target_points__gt", 0)), name="pay_work_target_positive", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.CheckConstraint( condition=models.Q(("actual_points__gte", 0)), name="pay_work_actual_nonnegative", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.CheckConstraint( condition=models.Q( ("approved_by__isnull", True), models.Q(("created_by", models.F("approved_by")), _negated=True), _connector="OR", ), name="pay_work_maker_not_approver", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.UniqueConstraint( fields=("period", "employee", "revision"), name="pay_work_revision_unique", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.UniqueConstraint( condition=models.Q(("status", "approved")), fields=("period", "employee"), name="pay_work_one_approved", ), ), migrations.AddConstraint( model_name="payrollworkrecord", constraint=models.UniqueConstraint( condition=models.Q(("source_ref", ""), _negated=True), fields=("source", "source_ref"), name="pay_work_source_ref_unique", ), ), ]