105 lines
3.4 KiB
Python
105 lines
3.4 KiB
Python
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
import pytest
|
|
from django.conf import settings
|
|
|
|
from finance.enums import PayrollPeriodStatus
|
|
from finance.models import PayrollAuditEvent, PayrollPeriod
|
|
from finance.payroll.carryovers import create_carryover
|
|
from finance.tasks import ensure_monthly_payroll_period, open_monthly_payroll_period
|
|
|
|
pytestmark = pytest.mark.django_db
|
|
|
|
|
|
def test_monthly_period_task_is_scheduled_for_first_day():
|
|
task = settings.CELERY_BEAT_SCHEDULE["open-monthly-payroll-period"]
|
|
|
|
assert task["task"] == "finance.tasks.open_monthly_payroll_period"
|
|
assert task["schedule"].day_of_month == {1}
|
|
assert task["schedule"].hour == {0}
|
|
assert task["schedule"].minute == {5}
|
|
|
|
|
|
def test_ensure_monthly_payroll_period_opens_full_calendar_month():
|
|
period, created = ensure_monthly_payroll_period(date(2026, 8, 1))
|
|
|
|
assert created is True
|
|
assert period.code == "2026-08"
|
|
assert period.name == "Август 2026"
|
|
assert period.date_from == date(2026, 8, 1)
|
|
assert period.date_to == date(2026, 8, 31)
|
|
assert period.pay_date is None
|
|
assert period.currency == "RUB"
|
|
assert period.status == PayrollPeriodStatus.OPEN
|
|
assert period.created_by is None
|
|
|
|
event = PayrollAuditEvent.objects.get(
|
|
action="payroll.period_created",
|
|
period=period,
|
|
)
|
|
assert event.actor is None
|
|
assert event.metadata == {"channel": "automation", "schedule": "monthly"}
|
|
|
|
|
|
def test_ensure_monthly_payroll_period_is_idempotent():
|
|
first, first_created = ensure_monthly_payroll_period(date(2028, 2, 1))
|
|
second, second_created = ensure_monthly_payroll_period(date(2028, 2, 15))
|
|
|
|
assert first_created is True
|
|
assert second_created is False
|
|
assert second.pk == first.pk
|
|
assert first.date_to == date(2028, 2, 29)
|
|
assert PayrollPeriod.objects.count() == 1
|
|
assert (
|
|
PayrollAuditEvent.objects.filter(
|
|
action="payroll.period_created",
|
|
period=first,
|
|
).count()
|
|
== 1
|
|
)
|
|
|
|
|
|
def test_monthly_task_uses_local_current_month(monkeypatch):
|
|
monkeypatch.setattr("finance.tasks.timezone.localdate", lambda: date(2026, 9, 1))
|
|
|
|
result = open_monthly_payroll_period()
|
|
|
|
period = PayrollPeriod.objects.get(pk=result["period_id"])
|
|
assert result == {"period_id": period.pk, "code": "2026-09", "created": True}
|
|
assert period.date_from == date(2026, 9, 1)
|
|
assert period.date_to == date(2026, 9, 30)
|
|
|
|
|
|
def test_monthly_period_opening_does_not_materialize_legacy_manual_carryover(
|
|
user_factory,
|
|
):
|
|
manager = user_factory(email="period.carryover.manager@example.test")
|
|
employee = user_factory(email="period.carryover.employee@example.test")
|
|
source = PayrollPeriod.objects.create(
|
|
code="2026-07",
|
|
name="Июль 2026",
|
|
date_from=date(2026, 7, 1),
|
|
date_to=date(2026, 7, 31),
|
|
currency="RUB",
|
|
status=PayrollPeriodStatus.CLOSED,
|
|
created_by=manager,
|
|
)
|
|
carryover = create_carryover(
|
|
source_period=source,
|
|
employee=employee,
|
|
direction="company_owes",
|
|
amount=Decimal("700.00"),
|
|
reason="Остаток июля",
|
|
actor=manager,
|
|
)
|
|
assert carryover.target_period_id is None
|
|
|
|
target, created = ensure_monthly_payroll_period(date(2026, 8, 1))
|
|
|
|
assert created is True
|
|
carryover.refresh_from_db()
|
|
assert target.status == PayrollPeriodStatus.OPEN
|
|
assert carryover.target_period_id is None
|
|
assert carryover.generated_input_line_id is None
|