diff --git a/backend/api/v1/attendance/views.py b/backend/api/v1/attendance/views.py index c33c7096..99e5b6b0 100644 --- a/backend/api/v1/attendance/views.py +++ b/backend/api/v1/attendance/views.py @@ -117,6 +117,14 @@ def _has_absence_marker(record: AttendanceRecord) -> bool: ) +def _employee_attendance_aliases(record: AttendanceRecord) -> list[str]: + return [ + str(alias).strip() + for alias in (getattr(record.employee, "attendance_aliases", None) or []) + if str(alias).strip() + ] + + def _comments_count_annotation(): record_ct = ContentType.objects.get_for_model(AttendanceRecord) comments_subquery = ( @@ -639,6 +647,7 @@ class AttendanceRecordDayEventsAPIView(APIView): events = LogStormClient().get_attendance_day_events( employee_id=str(record.employee_id), record_date=record.date, + aliases=_employee_attendance_aliases(record), ) except LogStormClientError as exc: logger.warning("LogStorm attendance day events failed: %s", exc) @@ -685,6 +694,7 @@ class AttendanceRecordDayEventPhotoAPIView(APIView): events = client.get_attendance_day_events( employee_id=str(record.employee_id), record_date=record.date, + aliases=_employee_attendance_aliases(record), ) event = next( ( diff --git a/backend/common/logstorm_client.py b/backend/common/logstorm_client.py index 35f1d1e6..dff4a997 100644 --- a/backend/common/logstorm_client.py +++ b/backend/common/logstorm_client.py @@ -91,13 +91,18 @@ class LogStormClient: *, employee_id: str, record_date: date | str, + aliases: Optional[list[Any]] = None, ) -> list[dict[str, Any]]: + params: dict[str, Any] = { + "employee_id": str(employee_id), + "date": str(record_date), + } + normalized_aliases = _normalize_aliases(aliases) + if normalized_aliases: + params["aliases"] = normalized_aliases response = self.session.get( self._url("/attendance/events/day/"), - params={ - "employee_id": str(employee_id), - "date": str(record_date), - }, + params=params, headers=self._headers(), timeout=self.config.timeout, ) @@ -150,3 +155,12 @@ class LogStormClient: if self.config.token: headers["Authorization"] = f"Bearer {self.config.token}" return headers + + +def _normalize_aliases(aliases: Optional[list[Any]]) -> list[str]: + result = [] + 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 diff --git a/backend/tests/api/v1/attendance/test_attendance_storage.py b/backend/tests/api/v1/attendance/test_attendance_storage.py index 2d50c484..b5256fd5 100644 --- a/backend/tests/api/v1/attendance/test_attendance_storage.py +++ b/backend/tests/api/v1/attendance/test_attendance_storage.py @@ -779,7 +779,7 @@ def test_staff_can_load_attendance_day_events( user_factory, ): staff = user_factory(staff=True) - employee = user_factory() + employee = user_factory(attendance_aliases=["200", "300"]) client = auth_client_factory(staff) with patch( @@ -822,6 +822,7 @@ def test_staff_can_load_attendance_day_events( logstorm_events.assert_called_once_with( employee_id=str(employee.id), record_date=record.date, + aliases=["200", "300"], ) events = response.json() assert [event["caption"] for event in events] == ["Успешный вход", "Успешный выход"] @@ -884,7 +885,7 @@ def test_staff_can_proxy_attendance_day_event_photo( user_factory, ): staff = user_factory(staff=True) - employee = user_factory() + employee = user_factory(attendance_aliases=["200"]) client = auth_client_factory(staff) with patch( @@ -898,7 +899,7 @@ def test_staff_can_proxy_attendance_day_event_photo( patch( "api.v1.attendance.views.LogStormClient.get_attendance_day_events", return_value=[{"event_key": "abc", "has_photo": True}], - ), + ) as logstorm_events, patch( "api.v1.attendance.views.LogStormClient.get_attendance_event_photo", return_value=_PhotoResponse(), @@ -909,6 +910,11 @@ def test_staff_can_proxy_attendance_day_event_photo( assert response.status_code == 200 assert response.content == b"fake-image" assert response["Content-Type"] == "image/jpeg" + logstorm_events.assert_called_once_with( + employee_id=str(employee.id), + record_date=record.date, + aliases=["200"], + ) logstorm_photo.assert_called_once_with("abc") diff --git a/backend/tests/integration/test_logstorm_client.py b/backend/tests/integration/test_logstorm_client.py index 9b6e5bc8..bc5db8b9 100644 --- a/backend/tests/integration/test_logstorm_client.py +++ b/backend/tests/integration/test_logstorm_client.py @@ -140,6 +140,27 @@ def test_get_attendance_day_events_requests_employee_date(): assert kwargs["headers"]["Authorization"] == "Bearer secret" +def test_get_attendance_day_events_includes_aliases(): + session = FakeSession(FakeResponse(payload=[])) + client = LogStormClient( + LogStormConfig(base_url="http://logstorm:8000"), + session=session, + ) + + client.get_attendance_day_events( + employee_id="100", + record_date="2026-04-20", + aliases=[" 200 ", "200", "300"], + ) + + _, _, kwargs = session.requests[0] + assert kwargs["params"] == { + "employee_id": "100", + "date": "2026-04-20", + "aliases": ["200", "300"], + } + + def test_get_attendance_day_events_rejects_invalid_payload(): client = LogStormClient( LogStormConfig(base_url="http://logstorm:8000"),