From 1f35009e090e6105f37636ce8cb3840976489e48 Mon Sep 17 00:00:00 2001 From: lizerk Date: Mon, 14 Sep 2026 09:42:02 +0300 Subject: [PATCH] fix(finance): ignore closed values when editing columns --- .../v1/finance/admin/period_views/catalog.py | 20 ++++-- .../finance/test_payroll_scheduled_changes.py | 68 +++++++++++++++++++ 2 files changed, 82 insertions(+), 6 deletions(-) diff --git a/backend/api/v1/finance/admin/period_views/catalog.py b/backend/api/v1/finance/admin/period_views/catalog.py index b61c37c1..7c9a6c67 100644 --- a/backend/api/v1/finance/admin/period_views/catalog.py +++ b/backend/api/v1/finance/admin/period_views/catalog.py @@ -205,8 +205,10 @@ class PayrollAdminComponentDetailView(PayrollAdminAPIView): point_base_behavior_changed = current.include_in_point_base != values[ "include_in_point_base" ] or (values["include_in_point_base"] and current.kind != values["kind"]) - existing_lines = PayrollInputLine.objects.filter(component_code=code).exclude( - status=ApprovalStatus.VOIDED + existing_lines = ( + PayrollInputLine.objects.filter(component_code=code) + .exclude(status=ApprovalStatus.VOIDED) + .exclude(period__status=PayrollPeriodStatus.CLOSED) ) if scheduled_route_changed and existing_lines.exists(): _conflict( @@ -274,10 +276,16 @@ class PayrollAdminComponentDetailView(PayrollAdminAPIView): negative_bases = [] if point_base_behavior_changed: affected_by_period = defaultdict(set) - active_assignments = PayrollInputLine.objects.filter( - component_code=code, - status__in=[ApprovalStatus.DRAFT, ApprovalStatus.APPROVED], - ).values_list("period_id", "employee_id") + active_assignments = ( + PayrollInputLine.objects.filter( + component_code=code, + status__in=[ApprovalStatus.DRAFT, ApprovalStatus.APPROVED], + ) + .exclude( + period__status=PayrollPeriodStatus.CLOSED, + ) + .values_list("period_id", "employee_id") + ) for period_id, employee_id in active_assignments: affected_by_period[period_id].add(employee_id) diff --git a/backend/tests/finance/test_payroll_scheduled_changes.py b/backend/tests/finance/test_payroll_scheduled_changes.py index 94682f0b..0dc625b2 100644 --- a/backend/tests/finance/test_payroll_scheduled_changes.py +++ b/backend/tests/finance/test_payroll_scheduled_changes.py @@ -1202,6 +1202,74 @@ def test_marker_routing_cannot_change_after_values_are_entered(scheduled_workspa assert changed.json()["code"] == "COMPONENT_SCHEDULE_HAS_INPUTS" +def test_closed_values_do_not_block_changing_column_to_scheduled_change( + scheduled_workspace, +): + workspace = scheduled_workspace + manager = workspace["manager"] + employee = workspace["employee"] + client = workspace["client"] + closed_period = workspace["periods"][7] + current_period = workspace["periods"][8] + grant(manager, "publish_payroll") + created = client.post( + api_url("admin-component-list"), + { + "name": "Будущий маркер", + "kind": "earning", + "requires_reason": False, + "include_in_point_base": False, + "include_in_carryover": False, + "visible_to_employee": True, + "highlight_color": "", + "period_scope": "all", + }, + format="json", + ) + assert created.status_code == 201, created.content + component = created.json() + saved = set_cell( + client, + period=closed_period, + employee=employee, + component_code=component["code"], + amount="500.00", + ) + assert saved.status_code == 201, saved.content + closed_period.status = "published" + closed_period.save(update_fields=["status", "updated_at"]) + closed = client.post( + api_url("admin-period-close", pk=closed_period.pk), + {}, + format="json", + ) + assert closed.status_code == 200, closed.content + + changed = client.patch( + api_url("admin-component-detail", code=component["code"]), + component_payload( + name=component["name"], + source_period=current_period, + ), + format="json", + ) + + assert changed.status_code == 200, changed.content + assert changed.json()["kind"] == SCHEDULED_CHANGE_KIND + closed_component = next( + column + for column in period_table(client, closed_period)["component_columns"] + if column["code"] == component["code"] + ) + assert closed_component["kind"] == "earning" + current_component = next( + column + for column in period_table(client, current_period)["component_columns"] + if column["code"] == component["code"] + ) + assert current_component["kind"] == SCHEDULED_CHANGE_KIND + + def test_closed_target_prevents_retroactive_marker_change_or_clear( scheduled_workspace, ):