feat: add search
This commit is contained in:
@@ -2,7 +2,7 @@ from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import func, select, update
|
||||
from sqlalchemy import func, or_, select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
@@ -11,7 +11,7 @@ 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
|
||||
from src.infrastructure.database.models import LegalEntityModel, UserModel
|
||||
|
||||
|
||||
class LegalEntityRepository(ILegalEntityRepository):
|
||||
@@ -49,6 +49,37 @@ class LegalEntityRepository(ILegalEntityRepository):
|
||||
updated_at=m.updated_at,
|
||||
)
|
||||
|
||||
def _search_filter(self, search: str | None):
|
||||
if not search or not search.strip():
|
||||
return None
|
||||
|
||||
pattern = f'%{search.strip()}%'
|
||||
full_name = func.concat_ws(
|
||||
' ',
|
||||
UserModel.last_name,
|
||||
UserModel.first_name,
|
||||
UserModel.middle_name,
|
||||
)
|
||||
return or_(
|
||||
LegalEntityModel.id.ilike(pattern),
|
||||
LegalEntityModel.name.ilike(pattern),
|
||||
LegalEntityModel.short_name.ilike(pattern),
|
||||
LegalEntityModel.inn.ilike(pattern),
|
||||
LegalEntityModel.ogrn.ilike(pattern),
|
||||
LegalEntityModel.kpp.ilike(pattern),
|
||||
LegalEntityModel.legal_address.ilike(pattern),
|
||||
LegalEntityModel.actual_address.ilike(pattern),
|
||||
LegalEntityModel.contact_person.ilike(pattern),
|
||||
LegalEntityModel.contact_phone.ilike(pattern),
|
||||
UserModel.email.ilike(pattern),
|
||||
UserModel.last_name.ilike(pattern),
|
||||
UserModel.first_name.ilike(pattern),
|
||||
UserModel.middle_name.ilike(pattern),
|
||||
UserModel.phone.ilike(pattern),
|
||||
UserModel.inn.ilike(pattern),
|
||||
full_name.ilike(pattern),
|
||||
)
|
||||
|
||||
async def create(
|
||||
self,
|
||||
*,
|
||||
@@ -102,14 +133,37 @@ class LegalEntityRepository(ILegalEntityRepository):
|
||||
raise ApplicationException(status_code=404, message='Organization not found')
|
||||
return self._to_entity(m)
|
||||
|
||||
async def list_all(self, *, limit: int, offset: int) -> list[LegalEntityEntity]:
|
||||
res = await self._session.execute(
|
||||
select(LegalEntityModel).order_by(LegalEntityModel.created_at.desc()).limit(limit).offset(offset)
|
||||
async def list_all(
|
||||
self,
|
||||
*,
|
||||
limit: int,
|
||||
offset: int,
|
||||
search: str | None = None,
|
||||
) -> list[LegalEntityEntity]:
|
||||
stmt = (
|
||||
select(LegalEntityModel)
|
||||
.join(UserModel, UserModel.id == LegalEntityModel.user_id)
|
||||
.where(UserModel.is_deleted.is_(False))
|
||||
.order_by(LegalEntityModel.created_at.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
)
|
||||
search_filter = self._search_filter(search)
|
||||
if search_filter is not None:
|
||||
stmt = stmt.where(search_filter)
|
||||
res = await self._session.execute(stmt)
|
||||
return [self._to_entity(m) for m in res.scalars().all()]
|
||||
|
||||
async def count_all(self) -> int:
|
||||
res = await self._session.execute(select(func.count()).select_from(LegalEntityModel))
|
||||
async def count_all(self, *, search: str | None = None) -> int:
|
||||
stmt = (
|
||||
select(func.count(LegalEntityModel.id))
|
||||
.join(UserModel, UserModel.id == LegalEntityModel.user_id)
|
||||
.where(UserModel.is_deleted.is_(False))
|
||||
)
|
||||
search_filter = self._search_filter(search)
|
||||
if search_filter is not None:
|
||||
stmt = stmt.where(search_filter)
|
||||
res = await self._session.execute(stmt)
|
||||
return int(res.scalar_one())
|
||||
|
||||
async def update(self, organization_id: str, *, values: dict[str, Any]) -> LegalEntityEntity:
|
||||
|
||||
@@ -3,13 +3,14 @@ from __future__ import annotations
|
||||
from datetime import datetime
|
||||
|
||||
from fastapi import status
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
||||
|
||||
from src.application.abstractions.repositories import IUserRepository
|
||||
from src.application.contracts import ILogger
|
||||
from src.application.domain.entities import UserEntity
|
||||
from src.application.domain.entities.organization import PartySearchEntity
|
||||
from src.application.domain.enums.account_type import AccountType
|
||||
from src.application.domain.exceptions import ApplicationException
|
||||
from src.infrastructure.database.models import UserModel
|
||||
@@ -92,3 +93,69 @@ class UserRepository(IUserRepository):
|
||||
stmt = select(UserModel.id).where(UserModel.email == email, UserModel.is_deleted.is_(False)).limit(1)
|
||||
result = await self._session.execute(stmt)
|
||||
return result.scalar_one_or_none() is not None
|
||||
|
||||
def _individual_search_filter(self, query: str):
|
||||
pattern = f'%{query.strip()}%'
|
||||
full_name = func.concat_ws(
|
||||
' ',
|
||||
UserModel.last_name,
|
||||
UserModel.first_name,
|
||||
UserModel.middle_name,
|
||||
)
|
||||
return or_(
|
||||
UserModel.id.ilike(pattern),
|
||||
UserModel.email.ilike(pattern),
|
||||
UserModel.last_name.ilike(pattern),
|
||||
UserModel.first_name.ilike(pattern),
|
||||
UserModel.middle_name.ilike(pattern),
|
||||
UserModel.phone.ilike(pattern),
|
||||
UserModel.passport_data.ilike(pattern),
|
||||
UserModel.inn.ilike(pattern),
|
||||
UserModel.erc20.ilike(pattern),
|
||||
full_name.ilike(pattern),
|
||||
)
|
||||
|
||||
def _to_search_entity(self, user: UserModel) -> PartySearchEntity:
|
||||
name = ' '.join(
|
||||
part for part in (user.last_name, user.first_name, user.middle_name)
|
||||
if part
|
||||
) or None
|
||||
return PartySearchEntity(
|
||||
id=user.id,
|
||||
account_type=user.account_type,
|
||||
user_id=user.id,
|
||||
email=user.email,
|
||||
name=name,
|
||||
inn=user.inn,
|
||||
phone=user.phone,
|
||||
status=None,
|
||||
kyc_verified=user.kyc_verified,
|
||||
created_at=user.created_at,
|
||||
)
|
||||
|
||||
async def search_individuals(self, *, query: str, limit: int, offset: int) -> list[PartySearchEntity]:
|
||||
stmt = (
|
||||
select(UserModel)
|
||||
.where(
|
||||
UserModel.is_deleted.is_(False),
|
||||
UserModel.account_type == AccountType.INDIVIDUAL,
|
||||
self._individual_search_filter(query),
|
||||
)
|
||||
.order_by(UserModel.created_at.desc())
|
||||
.limit(limit)
|
||||
.offset(offset)
|
||||
)
|
||||
result = await self._session.execute(stmt)
|
||||
return [self._to_search_entity(user) for user in result.scalars().all()]
|
||||
|
||||
async def count_individuals(self, *, query: str) -> int:
|
||||
stmt = (
|
||||
select(func.count(UserModel.id))
|
||||
.where(
|
||||
UserModel.is_deleted.is_(False),
|
||||
UserModel.account_type == AccountType.INDIVIDUAL,
|
||||
self._individual_search_filter(query),
|
||||
)
|
||||
)
|
||||
result = await self._session.execute(stmt)
|
||||
return int(result.scalar_one())
|
||||
|
||||
Reference in New Issue
Block a user