From bd5c7b79b0a0008f2b5d0ff14c3f39275642c57a Mon Sep 17 00:00:00 2001 From: lizerk Date: Fri, 1 May 2026 12:28:00 +0300 Subject: [PATCH] feat: forward LogStorm attendance aliases --- backend/api/v1/attendance/serializers.py | 4 ++++ backend/api/v1/attendance/views.py | 3 +++ backend/common/logstorm_attendance.py | 16 ++++++++++++++ .../integration/test_logstorm_attendance.py | 13 +++++++++++ .../test_logstorm_attendance_api.py | 22 +++++++++++++++++++ frontend/src/app/attendance/page.tsx | 1 + 6 files changed, 59 insertions(+) diff --git a/backend/api/v1/attendance/serializers.py b/backend/api/v1/attendance/serializers.py index 27897806..0b095add 100644 --- a/backend/api/v1/attendance/serializers.py +++ b/backend/api/v1/attendance/serializers.py @@ -37,6 +37,10 @@ class LogStormAttendanceAnalyzeSerializer(serializers.Serializer): period_start = serializers.DateField() period_end = serializers.DateField() schedule = LogStormScheduleSerializer(required=False) + aliases = serializers.ListField( + child=serializers.CharField(), + required=False, + ) def validate(self, attrs): if attrs["period_start"] > attrs["period_end"]: diff --git a/backend/api/v1/attendance/views.py b/backend/api/v1/attendance/views.py index e0254e7a..171e674d 100644 --- a/backend/api/v1/attendance/views.py +++ b/backend/api/v1/attendance/views.py @@ -174,6 +174,7 @@ class LogStormAttendanceAnalyzeAPIView(APIView): get_employee_work_schedule_payload(employee) or get_standard_work_schedule_payload() ) + aliases = data.get("aliases") try: result = analyze_employee_attendance( @@ -181,6 +182,7 @@ class LogStormAttendanceAnalyzeAPIView(APIView): period_start=data["period_start"], period_end=data["period_end"], schedule=schedule_payload, + aliases=aliases, client=None, ) except LogStormClientError as exc: @@ -195,6 +197,7 @@ class LogStormAttendanceAnalyzeAPIView(APIView): period_start=data["period_start"], period_end=data["period_end"], schedule=schedule_payload, + aliases=aliases, ) save_logstorm_attendance_result( employee=employee, diff --git a/backend/common/logstorm_attendance.py b/backend/common/logstorm_attendance.py index da8167a8..7abf1d72 100644 --- a/backend/common/logstorm_attendance.py +++ b/backend/common/logstorm_attendance.py @@ -24,6 +24,7 @@ def build_logstorm_attendance_payload( period_end: date, schedule: Optional[dict[str, Any]] = None, date_overrides: Optional[list[dict[str, Any]]] = None, + aliases: Optional[list[Any]] = None, ) -> dict[str, Any]: """ Build a LogStorm attendance analysis payload. @@ -44,6 +45,10 @@ def build_logstorm_attendance_payload( date_overrides=date_overrides, ) + normalized_aliases = _normalize_aliases(aliases) + if normalized_aliases: + payload["aliases"] = normalized_aliases + return payload @@ -75,6 +80,7 @@ def analyze_employee_attendance( period_end: date, schedule: Optional[dict[str, Any]] = None, date_overrides: Optional[list[dict[str, Any]]] = None, + aliases: Optional[list[Any]] = None, client: Optional[LogStormClient] = None, ) -> dict[str, Any]: payload = build_logstorm_attendance_payload( @@ -83,6 +89,7 @@ def analyze_employee_attendance( period_end=period_end, schedule=schedule, date_overrides=date_overrides, + aliases=aliases, ) return (client or LogStormClient()).analyze_attendance(payload) @@ -104,6 +111,15 @@ def _format_date(value: Any) -> str: return str(value) +def _normalize_aliases(aliases: Optional[list[Any]]) -> list[str]: + result: list[str] = [] + for alias in aliases or []: + alias_value = str(alias).strip() + if alias_value and alias_value not in result: + result.append(alias_value) + return result + + def _employee_display_name(employee: Employee) -> str: full_name = employee.get_full_name().strip() return full_name or str(employee) diff --git a/backend/tests/integration/test_logstorm_attendance.py b/backend/tests/integration/test_logstorm_attendance.py index c063e6bf..02868c6d 100644 --- a/backend/tests/integration/test_logstorm_attendance.py +++ b/backend/tests/integration/test_logstorm_attendance.py @@ -79,6 +79,19 @@ def test_payload_includes_schedule_and_date_overrides(user_factory): } +def test_payload_includes_aliases(user_factory): + employee = user_factory(first_name="Ivan", last_name="Petrov") + + payload = build_logstorm_attendance_payload( + employee=employee, + period_start=date(2026, 4, 20), + period_end=date(2026, 4, 20), + aliases=["200", "200", " 300 "], + ) + + assert payload["aliases"] == ["200", "300"] + + def test_schedule_defaults_to_regular_workweek(): schedule = normalize_logstorm_schedule({ "start_time": "09:00", diff --git a/backend/tests/integration/test_logstorm_attendance_api.py b/backend/tests/integration/test_logstorm_attendance_api.py index 8ba17bf4..2b27be0c 100644 --- a/backend/tests/integration/test_logstorm_attendance_api.py +++ b/backend/tests/integration/test_logstorm_attendance_api.py @@ -112,6 +112,28 @@ def test_logstorm_attendance_api_passes_schedule( ) +def test_logstorm_attendance_api_passes_aliases( + auth_client_factory, + user_factory, +): + staff = user_factory(staff=True) + employee = user_factory() + client = auth_client_factory(staff) + + with patch( + "api.v1.attendance.views.analyze_employee_attendance", + return_value={"records": []}, + ) as analyze: + response = client.post( + _url(), + _payload(employee.id, aliases=["200", "300"]), + format="json", + ) + + assert response.status_code == 200 + assert analyze.call_args.kwargs["aliases"] == ["200", "300"] + + def test_logstorm_attendance_api_rejects_invalid_period( auth_client_factory, user_factory, diff --git a/frontend/src/app/attendance/page.tsx b/frontend/src/app/attendance/page.tsx index 23c73fc2..79ca950d 100644 --- a/frontend/src/app/attendance/page.tsx +++ b/frontend/src/app/attendance/page.tsx @@ -84,6 +84,7 @@ type AttendanceAnalyzeRequest = { period_start: string; period_end: string; schedule?: AttendanceSchedulePayload; + aliases?: string[]; }; type MonthlyMatrixRequest = { employee_ids: string; month: string };