feat: update le docs logic
This commit is contained in:
@@ -4,7 +4,6 @@ from src.infrastructure.database.models.admin_user import AdminUserModel
|
||||
from src.infrastructure.database.models.admin_session import AdminSessionModel
|
||||
from src.infrastructure.database.models.legal_entity import LegalEntityModel
|
||||
from src.infrastructure.database.models.organization_wallet import OrganizationWalletModel
|
||||
from src.infrastructure.database.models.organization_document import OrganizationDocumentModel
|
||||
from src.infrastructure.database.models.purchase_request import PurchaseRequestModel
|
||||
|
||||
__all__ = [
|
||||
@@ -14,6 +13,5 @@ __all__ = [
|
||||
'AdminSessionModel',
|
||||
'LegalEntityModel',
|
||||
'OrganizationWalletModel',
|
||||
'OrganizationDocumentModel',
|
||||
'PurchaseRequestModel',
|
||||
]
|
||||
|
||||
@@ -35,6 +35,13 @@ class LegalEntityModel(Base, UlidPrimaryKeyMixin, AuditTimestampsMixin):
|
||||
kyc_verified: Mapped[bool] = mapped_column(Boolean, nullable=False, server_default='true', default=True)
|
||||
kyc_verified_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
encrypted_mnemonic: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
charter_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
inn_certificate_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
ogrn_certificate_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
bank_details_document_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
kyc_representative_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
power_of_attorney_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
other_document_s3_key: Mapped[str | None] = mapped_column(String(1024), nullable=True)
|
||||
created_by: Mapped[str | None] = mapped_column(
|
||||
String(26),
|
||||
ForeignKey('admin_users.id'),
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import BigInteger, DateTime, ForeignKey, String, func
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from src.infrastructure.database.models.base import Base
|
||||
from src.infrastructure.database.models.mixins import UlidPrimaryKeyMixin
|
||||
|
||||
|
||||
class OrganizationDocumentModel(Base, UlidPrimaryKeyMixin):
|
||||
__tablename__ = 'organization_documents'
|
||||
|
||||
organization_id: Mapped[str] = mapped_column(
|
||||
String(26),
|
||||
ForeignKey('legal_entities.id', ondelete='RESTRICT'),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
document_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
file_name: Mapped[str] = mapped_column(String(512), nullable=False)
|
||||
s3_key: Mapped[str] = mapped_column(String(1024), nullable=False)
|
||||
content_type: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
file_size_bytes: Mapped[int] = mapped_column(BigInteger, nullable=False)
|
||||
uploaded_by: Mapped[str | None] = mapped_column(
|
||||
String(26),
|
||||
ForeignKey('admin_users.id'),
|
||||
nullable=True,
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True),
|
||||
nullable=False,
|
||||
server_default=func.now(),
|
||||
)
|
||||
@@ -3,7 +3,6 @@ from src.infrastructure.database.repositories.admin_session_repository import Ad
|
||||
from src.infrastructure.database.repositories.user_repository import UserRepository
|
||||
from src.infrastructure.database.repositories.legal_entity_repository import LegalEntityRepository
|
||||
from src.infrastructure.database.repositories.organization_wallet_repository import OrganizationWalletRepository
|
||||
from src.infrastructure.database.repositories.organization_document_repository import OrganizationDocumentRepository
|
||||
from src.infrastructure.database.repositories.purchase_request_repository import PurchaseRequestRepository
|
||||
|
||||
__all__ = [
|
||||
@@ -12,6 +11,5 @@ __all__ = [
|
||||
'UserRepository',
|
||||
'LegalEntityRepository',
|
||||
'OrganizationWalletRepository',
|
||||
'OrganizationDocumentRepository',
|
||||
'PurchaseRequestRepository',
|
||||
]
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from fastapi import status
|
||||
from sqlalchemy import func, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
@@ -11,6 +10,7 @@ from src.application.abstractions.repositories import ILegalEntityRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities.organization import LegalEntityEntity
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.application.domain.organization_documents import DOCUMENT_TYPE_TO_COLUMN, ORGANIZATION_DOCUMENT_TYPES
|
||||
from src.infrastructure.database.models import LegalEntityModel
|
||||
|
||||
|
||||
@@ -19,6 +19,12 @@ class LegalEntityRepository(ILegalEntityRepository):
|
||||
self._session = session
|
||||
self._logger = logger
|
||||
|
||||
def _document_s3_keys_from_model(self, m: LegalEntityModel) -> dict[str, str | None]:
|
||||
return {
|
||||
document_type: getattr(m, DOCUMENT_TYPE_TO_COLUMN[document_type])
|
||||
for document_type in ORGANIZATION_DOCUMENT_TYPES
|
||||
}
|
||||
|
||||
def _to_entity(self, m: LegalEntityModel) -> LegalEntityEntity:
|
||||
return LegalEntityEntity(
|
||||
id=m.id,
|
||||
@@ -37,6 +43,7 @@ class LegalEntityRepository(ILegalEntityRepository):
|
||||
kyc_verified=m.kyc_verified,
|
||||
kyc_verified_at=m.kyc_verified_at,
|
||||
encrypted_mnemonic=m.encrypted_mnemonic,
|
||||
document_s3_keys=self._document_s3_keys_from_model(m),
|
||||
created_by=m.created_by,
|
||||
created_at=m.created_at,
|
||||
updated_at=m.updated_at,
|
||||
@@ -121,3 +128,19 @@ class LegalEntityRepository(ILegalEntityRepository):
|
||||
.values(encrypted_mnemonic=encrypted_mnemonic)
|
||||
)
|
||||
await self._session.flush()
|
||||
|
||||
async def get_document_s3_key(self, organization_id: str, document_type: str) -> str | None:
|
||||
org = await self.get_by_id(organization_id)
|
||||
return org.document_s3_keys.get(document_type)
|
||||
|
||||
async def set_document_s3_key(self, organization_id: str, document_type: str, s3_key: str) -> LegalEntityEntity:
|
||||
column = DOCUMENT_TYPE_TO_COLUMN.get(document_type)
|
||||
if column is None:
|
||||
raise ApplicationException(status_code=400, message='Invalid document type')
|
||||
await self._session.execute(
|
||||
update(LegalEntityModel)
|
||||
.where(LegalEntityModel.id == organization_id)
|
||||
.values(**{column: s3_key})
|
||||
)
|
||||
await self._session.flush()
|
||||
return await self.get_by_id(organization_id)
|
||||
|
||||
@@ -1,66 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from src.application.abstractions.repositories import IOrganizationDocumentRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities.organization import OrganizationDocumentEntity
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.infrastructure.database.models import OrganizationDocumentModel
|
||||
|
||||
|
||||
class OrganizationDocumentRepository(IOrganizationDocumentRepository):
|
||||
def __init__(self, session: AsyncSession, logger: ILogger):
|
||||
self._session = session
|
||||
self._logger = logger
|
||||
|
||||
def _to_entity(self, m: OrganizationDocumentModel) -> OrganizationDocumentEntity:
|
||||
return OrganizationDocumentEntity(
|
||||
id=m.id,
|
||||
organization_id=m.organization_id,
|
||||
document_type=m.document_type,
|
||||
file_name=m.file_name,
|
||||
s3_key=m.s3_key,
|
||||
content_type=m.content_type,
|
||||
file_size_bytes=m.file_size_bytes,
|
||||
uploaded_by=m.uploaded_by,
|
||||
created_at=m.created_at,
|
||||
)
|
||||
|
||||
async def create(self, document: OrganizationDocumentEntity) -> OrganizationDocumentEntity:
|
||||
model = OrganizationDocumentModel(
|
||||
id=document.id,
|
||||
organization_id=document.organization_id,
|
||||
document_type=document.document_type,
|
||||
file_name=document.file_name,
|
||||
s3_key=document.s3_key,
|
||||
content_type=document.content_type,
|
||||
file_size_bytes=document.file_size_bytes,
|
||||
uploaded_by=document.uploaded_by,
|
||||
)
|
||||
self._session.add(model)
|
||||
try:
|
||||
await self._session.flush()
|
||||
return self._to_entity(model)
|
||||
except SQLAlchemyError as exc:
|
||||
self._logger.exception(str(exc))
|
||||
raise ApplicationException(status_code=500, message='Database error')
|
||||
|
||||
async def get_by_id(self, document_id: str) -> OrganizationDocumentEntity:
|
||||
res = await self._session.execute(
|
||||
select(OrganizationDocumentModel).where(OrganizationDocumentModel.id == document_id)
|
||||
)
|
||||
m = res.scalar_one_or_none()
|
||||
if m is None:
|
||||
raise ApplicationException(status_code=404, message='Document not found')
|
||||
return self._to_entity(m)
|
||||
|
||||
async def list_by_organization(self, organization_id: str) -> list[OrganizationDocumentEntity]:
|
||||
res = await self._session.execute(
|
||||
select(OrganizationDocumentModel)
|
||||
.where(OrganizationDocumentModel.organization_id == organization_id)
|
||||
.order_by(OrganizationDocumentModel.created_at.desc())
|
||||
)
|
||||
return [self._to_entity(m) for m in res.scalars().all()]
|
||||
@@ -5,7 +5,6 @@ from src.application.abstractions.repositories import (
|
||||
IAdminSessionRepository,
|
||||
IAdminUserRepository,
|
||||
ILegalEntityRepository,
|
||||
IOrganizationDocumentRepository,
|
||||
IOrganizationWalletRepository,
|
||||
IPurchaseRequestRepository,
|
||||
IUserRepository,
|
||||
@@ -16,7 +15,6 @@ from src.infrastructure.database.repositories import (
|
||||
AdminSessionRepository,
|
||||
AdminUserRepository,
|
||||
LegalEntityRepository,
|
||||
OrganizationDocumentRepository,
|
||||
OrganizationWalletRepository,
|
||||
PurchaseRequestRepository,
|
||||
UserRepository,
|
||||
@@ -32,7 +30,6 @@ class UnitOfWork(IUnitOfWork):
|
||||
self._admin_session_repository: IAdminSessionRepository | None = None
|
||||
self._legal_entity_repository: ILegalEntityRepository | None = None
|
||||
self._organization_wallet_repository: IOrganizationWalletRepository | None = None
|
||||
self._organization_document_repository: IOrganizationDocumentRepository | None = None
|
||||
self._purchase_request_repository: IPurchaseRequestRepository | None = None
|
||||
self._logger: ILogger = logger
|
||||
|
||||
@@ -88,14 +85,6 @@ class UnitOfWork(IUnitOfWork):
|
||||
)
|
||||
return self._organization_wallet_repository
|
||||
|
||||
@property
|
||||
def organization_document_repository(self) -> IOrganizationDocumentRepository:
|
||||
if self._organization_document_repository is None:
|
||||
self._organization_document_repository = OrganizationDocumentRepository(
|
||||
session=self._session, logger=self._logger
|
||||
)
|
||||
return self._organization_document_repository
|
||||
|
||||
@property
|
||||
def purchase_request_repository(self) -> IPurchaseRequestRepository:
|
||||
if self._purchase_request_repository is None:
|
||||
|
||||
@@ -23,9 +23,13 @@ class S3DocumentsService:
|
||||
self._key_prefix = key_prefix.strip('/')
|
||||
self._presigned_ttl_seconds = presigned_ttl_seconds
|
||||
|
||||
def build_object_key(self, organization_id: str, document_id: str, file_name: str) -> str:
|
||||
@staticmethod
|
||||
def file_name_from_key(key: str) -> str:
|
||||
return key.rsplit('/', 1)[-1]
|
||||
|
||||
def build_object_key(self, organization_id: str, document_type: str, file_name: str) -> str:
|
||||
safe_name = file_name.replace('/', '_').replace('\\', '_')
|
||||
return f'{self._key_prefix}/{organization_id}/{document_id}/{safe_name}'
|
||||
return f'{self._key_prefix}/{organization_id}/{document_type}/{safe_name}'
|
||||
|
||||
def _client_kwargs(self) -> dict[str, object]:
|
||||
kw: dict[str, object] = {'region_name': self._region}
|
||||
|
||||
Reference in New Issue
Block a user