refactor(services): общий utc_now вместо локальных _now в сервисах справочников
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012vJkmBGKKqMoEMzvsXG3y2
This commit is contained in:
parent
69d641c14e
commit
e3ea074e4d
8
api/src/common/clock.py
Normal file
8
api/src/common/clock.py
Normal file
@ -0,0 +1,8 @@
|
||||
"""Текущее время сервисов: одно место для «сейчас» (UTC, с часовым поясом) — `valid_from`/`valid_to`, `updated_at`."""
|
||||
|
||||
import datetime
|
||||
|
||||
|
||||
def utc_now() -> datetime.datetime:
|
||||
"""Текущий момент в UTC (aware)."""
|
||||
return datetime.datetime.now(datetime.UTC)
|
||||
@ -4,12 +4,12 @@
|
||||
Аудит `LEGAL_ENTITY_CHANGE` / `object_type = legal_entity_alias` пишется в той же транзакции запроса.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.api.dependencies.audit import RequestAudit
|
||||
from src.common.clock import utc_now
|
||||
from src.common.exceptions import ConflictError, DomainValidationError, NotFoundError
|
||||
from src.domain.aliases import normalize_alias
|
||||
from src.domain.policy import VisibilityScope
|
||||
@ -50,7 +50,7 @@ class AliasService:
|
||||
alias=data.alias,
|
||||
normalized_alias=normalized,
|
||||
created_by=self._audit.user_id,
|
||||
created_at=datetime.datetime.now(datetime.UTC),
|
||||
created_at=utc_now(),
|
||||
)
|
||||
self._session.add(alias)
|
||||
await self._session.flush()
|
||||
|
||||
@ -4,13 +4,13 @@
|
||||
`commit` делает `SessionDep`; `audit_event` пишется в той же транзакции через `RequestAudit`.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import uuid
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.api.dependencies.audit import RequestAudit
|
||||
from src.common.clock import utc_now
|
||||
from src.common.exceptions import ConflictError, DomainValidationError, NotFoundError
|
||||
from src.domain.policy import VisibilityScope, office_for_create
|
||||
from src.models.directories import COMPANY_GROUP_CODE_SEQ, CompanyGroup, CompanyGroupHistory
|
||||
@ -25,10 +25,6 @@ OBJECT_TYPE = "company_group"
|
||||
TRACKED_FIELDS = ("name", "crm_id", "base_regional_office_id") # поля ГК, изменяемые PATCH и попадающие в историю
|
||||
|
||||
|
||||
def _now() -> datetime.datetime:
|
||||
return datetime.datetime.now(datetime.UTC)
|
||||
|
||||
|
||||
class CompanyGroupService:
|
||||
"""Операции над ГК от имени текущего пользователя в его области видимости."""
|
||||
|
||||
@ -43,7 +39,7 @@ class CompanyGroupService:
|
||||
await self._check_office_exists(office_id)
|
||||
if data.crm_id is not None:
|
||||
await self._check_crm_id_free(data.crm_id)
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
group = CompanyGroup(
|
||||
code=await repo.next_code(self._session, COMPANY_GROUP_CODE_SEQ, COMPANY_GROUP_CODE_PREFIX),
|
||||
crm_id=data.crm_id,
|
||||
@ -77,7 +73,7 @@ class CompanyGroupService:
|
||||
if not changes:
|
||||
return group
|
||||
before = {field: getattr(group, field) for field in changes}
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
self._session.add(
|
||||
CompanyGroupHistory(
|
||||
company_group_id=group.id,
|
||||
@ -112,7 +108,7 @@ class CompanyGroupService:
|
||||
if group.is_archived == archived:
|
||||
return group
|
||||
group.is_archived = archived
|
||||
group.updated_at = _now()
|
||||
group.updated_at = utc_now()
|
||||
group.updated_by = self._audit.user_id
|
||||
await self._session.flush()
|
||||
await self._audit.record(
|
||||
|
||||
@ -13,6 +13,7 @@ from typing import Any
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from src.api.dependencies.audit import RequestAudit
|
||||
from src.common.clock import utc_now
|
||||
from src.common.exceptions import ConflictError, DomainValidationError, NotFoundError
|
||||
from src.domain.inn import validate_inn
|
||||
from src.domain.policy import VisibilityScope, office_for_create
|
||||
@ -38,13 +39,9 @@ MEMBER_OBJECT_TYPE = "related_group_member"
|
||||
TRACKED_FIELDS = ("name", "entity_type", "crm_id", "regional_office_id") # поля ЮЛ, изменяемые PATCH
|
||||
|
||||
|
||||
def _now() -> datetime.datetime:
|
||||
return datetime.datetime.now(datetime.UTC)
|
||||
|
||||
|
||||
def _today() -> datetime.date:
|
||||
"""«Сегодня» для проверки `effective_date` — дата UTC (часовой пояс пользователя в Срезе 1 не известен)."""
|
||||
return _now().date()
|
||||
return utc_now().date()
|
||||
|
||||
|
||||
class LegalEntityService:
|
||||
@ -69,7 +66,7 @@ class LegalEntityService:
|
||||
# Филиал не может завести ЮЛ (в т.ч. монозаёмщика) на чужой филиал; без филиала — свой (§10.1)
|
||||
office_id = office_for_create(self._scope, requested_office)
|
||||
await self._check_office_exists(office_id)
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
entity = LegalEntity(
|
||||
inn=inn,
|
||||
crm_id=data.crm_id,
|
||||
@ -107,7 +104,7 @@ class LegalEntityService:
|
||||
if not changes:
|
||||
return entity
|
||||
before = {field: getattr(entity, field) for field in changes}
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
self._snapshot(entity, LeHistoryEvent.UPDATE, now, effective_date=None)
|
||||
for field, value in changes.items():
|
||||
setattr(entity, field, value)
|
||||
@ -133,7 +130,7 @@ class LegalEntityService:
|
||||
if entity.company_group_id == target.id:
|
||||
raise ConflictError("SAME_COMPANY_GROUP", "ЮЛ уже входит в эту ГК")
|
||||
before = {"company_group_id": entity.company_group_id}
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
self._snapshot(entity, LeHistoryEvent.MOVE_TO_GROUP, now, effective_date=data.effective_date)
|
||||
entity.company_group_id = target.id
|
||||
await self._touch(entity, now)
|
||||
@ -164,7 +161,7 @@ class LegalEntityService:
|
||||
changes["close_date"] = data.effective_date
|
||||
changes = {field: value for field, value in changes.items() if getattr(entity, field) != value}
|
||||
before = {field: getattr(entity, field) for field in changes}
|
||||
now = _now()
|
||||
now = utc_now()
|
||||
self._snapshot(entity, event, now, effective_date=data.effective_date)
|
||||
for field, value in changes.items():
|
||||
setattr(entity, field, value)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user