feat: вфыв

This commit is contained in:
2024-07-20 09:32:22 +03:00
parent e7235021f9
commit 6b09251141
27 changed files with 536 additions and 119 deletions

8
backend/dependecies.py Normal file
View File

@@ -0,0 +1,8 @@
from typing import Annotated
from fastapi import Depends
from sqlalchemy.ext.asyncio import AsyncSession
from backend.session import get_session
SessionDependency = Annotated[AsyncSession, Depends(get_session)]

8
enums/user.py Normal file
View File

@@ -0,0 +1,8 @@
from enum import StrEnum
class UserRole(StrEnum):
user = 'user'
employee = 'employee'
manager = 'manager'
admin = 'admin'

View File

@@ -32,7 +32,10 @@ routers_list = [
routers.service_router,
routers.product_router,
routers.barcode_router,
routers.shipping_warehouse_router
routers.shipping_warehouse_router,
routers.position_router,
routers.user_router,
routers.role_router,
]
for router in routers_list:
app.include_router(router)

View File

@@ -1,12 +1,83 @@
from sqlalchemy import Column, Integer, BigInteger, String, Boolean
from sqlalchemy import BigInteger, Table, ForeignKey, Column
from sqlalchemy.orm import Mapped, mapped_column, relationship
from models.base import BaseModel
role_permissions = Table(
'role_permissions',
BaseModel.metadata,
Column('role_key', ForeignKey('roles.key'), primary_key=True),
Column('permission_key', ForeignKey('permissions.key'), primary_key=True)
)
user_position = Table(
'user_position',
BaseModel.metadata,
Column('position_key', ForeignKey('positions.key'), primary_key=True),
Column('user_id', ForeignKey('users.id'), primary_key=True, unique=True)
)
class Permission(BaseModel):
__tablename__ = 'permissions'
key: Mapped[str] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
roles: Mapped[list["Role"]] = relationship('Role',
secondary=role_permissions,
back_populates='permissions')
class Role(BaseModel):
__tablename__ = 'roles'
key: Mapped[str] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
permissions: Mapped[list["Permission"]] = relationship('Permission',
secondary=role_permissions,
back_populates='roles',
lazy='selectin')
# users: Mapped[list["User"]] = relationship("User", back_populates="users")
class User(BaseModel):
__tablename__ = 'users'
id = Column(Integer, autoincrement=True, primary_key=True, index=True)
telegram_id = Column(BigInteger, nullable=False, index=True)
phone_number = Column(String)
id: Mapped[int] = mapped_column(primary_key=True)
is_admin = Column(Boolean, nullable=False, default=False)
first_name: Mapped[str] = mapped_column(nullable=False, server_default='')
second_name: Mapped[str] = mapped_column(nullable=False, server_default='')
comment: Mapped[str] = mapped_column(nullable=False, server_default='')
telegram_id: Mapped[int] = mapped_column(BigInteger,
nullable=False,
index=True)
phone_number: Mapped[str] = mapped_column(nullable=True)
is_admin: Mapped[bool] = mapped_column(nullable=False, default=False)
is_blocked: Mapped[bool] = mapped_column(nullable=False, server_default='0')
is_deleted: Mapped[bool] = mapped_column(nullable=False, server_default='0')
role_key: Mapped[int] = mapped_column(ForeignKey('roles.key'))
role: Mapped["Role"] = relationship(
'Role',
lazy='joined'
)
position: Mapped["Position"] = relationship(
'Position',
secondary=user_position,
uselist=False,
back_populates='users',
lazy="joined"
)
class Position(BaseModel):
__tablename__ = 'positions'
key: Mapped[str] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column()
users: Mapped["User"] = relationship(
'User',
secondary=user_position,
uselist=False,
back_populates='position'
)

View File

@@ -5,3 +5,6 @@ from .service import service_router
from .product import product_router
from .barcode import barcode_router
from .shipping_warehouse import shipping_warehouse_router
from .position import position_router
from .user import user_router
from .role import role_router

33
routers/position.py Normal file
View File

@@ -0,0 +1,33 @@
from fastapi import APIRouter
from backend.dependecies import SessionDependency
from schemas.position import *
from services.position import PositionService
position_router = APIRouter(
prefix="/position",
tags=["position"]
)
@position_router.get(
'/get-all',
operation_id='get_all_positions',
response_model=GetAllPositionsResponse
)
async def get_all(
session: SessionDependency
):
return await PositionService(session).get_all()
@position_router.post(
'/create',
operation_id='create_position',
response_model=CreatePositionResponse
)
async def create(
session: SessionDependency,
request: CreatePositionRequest
):
return await PositionService(session).create(request)

View File

@@ -1,18 +1,16 @@
import base64
from io import BytesIO
from typing import Annotated, Union
from typing import Annotated
from fastapi import APIRouter, Depends, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import StreamingResponse
from fastapi.responses import FileResponse
import utils.dependecies
from backend.session import get_session
from schemas.barcode import GetProductBarcodeResponse, GetProductBarcodeRequest, GetProductBarcodePdfResponse, \
GetProductBarcodePdfRequest
from schemas.base import PaginationSchema
from schemas.product import *
from services.auth import get_current_user
from services.barcode import BarcodeService
from services.product import ProductService

21
routers/role.py Normal file
View File

@@ -0,0 +1,21 @@
from fastapi import APIRouter
from backend.dependecies import SessionDependency
from schemas.role import *
from services.role import RoleService
role_router = APIRouter(
prefix='/role',
tags=['role']
)
@role_router.get(
'/get-all',
response_model=GetAllRolesResponse,
operation_id='get_all_roles'
)
async def get_all(
session: SessionDependency
):
return await RoleService(session).get_all()

33
routers/user.py Normal file
View File

@@ -0,0 +1,33 @@
from fastapi import APIRouter
from backend.dependecies import SessionDependency
from schemas.user import *
from services.user import UserService
user_router = APIRouter(
prefix="/user",
tags=["user"]
)
@user_router.get(
'/get-all',
response_model=GetAllUsersResponse,
operation_id='get_all_users'
)
async def get_all(
session: SessionDependency
):
return await UserService(session).get_all()
@user_router.post(
'/update',
response_model=UpdateUserResponse,
operation_id='update_user'
)
async def update(
session: SessionDependency,
request: UpdateUserRequest
):
return await UserService(session).update(request)

View File

@@ -1,4 +1,4 @@
from schemas.base import CustomModelCamel, CustomModelSnake
from schemas.base import BaseSchema, CustomModelSnake
class AuthLoginRequest(CustomModelSnake):
@@ -9,5 +9,5 @@ class AuthLoginRequest(CustomModelSnake):
photo_url: str
class AuthLoginResponse(CustomModelCamel):
class AuthLoginResponse(BaseSchema):
access_token: str

View File

@@ -1,16 +1,16 @@
from typing import List
from schemas.base import CustomModelCamel, OkMessageSchema
from schemas.base import BaseSchema, OkMessageSchema
# region Entities
class BarcodeTemplateAttributeSchema(CustomModelCamel):
class BarcodeTemplateAttributeSchema(BaseSchema):
id: int
key: str
name: str
class BarcodeTemplateSizeSchema(CustomModelCamel):
class BarcodeTemplateSizeSchema(BaseSchema):
id: int
name: str
key: str
@@ -18,12 +18,12 @@ class BarcodeTemplateSizeSchema(CustomModelCamel):
height: int
class BarcodeTemplateAdditionalAttributeSchema(CustomModelCamel):
class BarcodeTemplateAdditionalAttributeSchema(BaseSchema):
name: str
value: str
class BaseBarcodeTemplateSchema(CustomModelCamel):
class BaseBarcodeTemplateSchema(BaseSchema):
name: str
is_default: bool
size: BarcodeTemplateSizeSchema
@@ -36,12 +36,12 @@ class BarcodeTemplateSchema(BaseBarcodeTemplateSchema):
attributes: list[BarcodeTemplateAttributeSchema]
class BarcodeAttributeSchema(CustomModelCamel):
class BarcodeAttributeSchema(BaseSchema):
name: str
value: str
class BarcodeSchema(CustomModelCamel):
class BarcodeSchema(BaseSchema):
barcode: str
attributes: List[BarcodeAttributeSchema]
additional_field: str | None = None
@@ -50,7 +50,7 @@ class BarcodeSchema(CustomModelCamel):
# endregion
# region Requests
class GetBarcodeTemplateByIdRequest(CustomModelCamel):
class GetBarcodeTemplateByIdRequest(BaseSchema):
id: int
@@ -62,16 +62,16 @@ class BarcodeTemplateUpdateResponse(OkMessageSchema):
pass
class CreateBarcodeTemplateAttributeRequest(CustomModelCamel):
class CreateBarcodeTemplateAttributeRequest(BaseSchema):
name: str
label: str
class BarcodeTemplateDeleteRequest(CustomModelCamel):
class BarcodeTemplateDeleteRequest(BaseSchema):
id: int
class GetProductBarcodeRequest(CustomModelCamel):
class GetProductBarcodeRequest(BaseSchema):
product_id: int
barcode: str
barcode_template_id: int | None = None
@@ -84,7 +84,7 @@ class GetProductBarcodePdfRequest(GetProductBarcodeRequest):
# endregion
# region Responses
class GetBarcodeTemplateByIdResponse(CustomModelCamel):
class GetBarcodeTemplateByIdResponse(BaseSchema):
barcode_template: BarcodeTemplateSchema
@@ -101,11 +101,11 @@ class CreateBarcodeTemplateAttributeResponse(OkMessageSchema):
id: int
class GetAllBarcodeTemplatesResponse(CustomModelCamel):
class GetAllBarcodeTemplatesResponse(BaseSchema):
templates: list[BarcodeTemplateSchema]
class GetAllBarcodeTemplateAttributesResponse(CustomModelCamel):
class GetAllBarcodeTemplateAttributesResponse(BaseSchema):
attributes: list[BarcodeTemplateAttributeSchema]
@@ -113,15 +113,15 @@ class BarcodeTemplateDeleteResponse(OkMessageSchema):
pass
class GetProductBarcodeResponse(CustomModelCamel):
class GetProductBarcodeResponse(BaseSchema):
barcode: BarcodeSchema
class GetAllBarcodeTemplateSizesResponse(CustomModelCamel):
class GetAllBarcodeTemplateSizesResponse(BaseSchema):
sizes: list[BarcodeTemplateSizeSchema]
class GetProductBarcodePdfResponse(CustomModelCamel):
class GetProductBarcodePdfResponse(BaseSchema):
base64_string: str
filename: str
mime_type: str

View File

@@ -1,3 +1,5 @@
from typing import Self
from pydantic import BaseModel
from pydantic.alias_generators import to_camel
@@ -7,44 +9,52 @@ class CustomConfig:
from_attributes = True
class CustomModelCamel(BaseModel):
class BaseSchema(BaseModel):
class Config:
from_attributes = True
alias_generator = to_camel
populate_by_name = True
@classmethod
def from_sql_model(cls, model, fields: dict):
model_dict = {c.name: getattr(model, c.name) for c in model.__table__.columns}
model_dict.update(fields)
return cls(**model_dict)
def model_dump_parent(self):
parent_class: BaseModel = self.__class__.__bases__[0]
parent_fields = set(parent_class.model_fields.keys())
return self.model_dump(include=parent_fields)
@classmethod
def from_orm_list(cls, sql_models) -> list[Self]:
return [cls.model_validate(model) for model in sql_models]
class CustomModelSnake(BaseModel):
class Config:
from_attributes = True
class OkMessageSchema(CustomModelCamel):
class OkMessageSchema(BaseSchema):
ok: bool
message: str
class PaginationSchema(CustomModelCamel):
class PaginationSchema(BaseSchema):
page: int | None = None
items_per_page: int | None = None
class PaginationInfoSchema(CustomModelCamel):
class PaginationInfoSchema(BaseSchema):
total_pages: int
total_items: int
class BaseEnumSchema(CustomModelCamel):
class BaseEnumSchema(BaseSchema):
id: int
name: str
class BaseEnumListSchema(CustomModelCamel):
class BaseEnumListSchema(BaseSchema):
items: list[BaseEnumSchema]

View File

@@ -3,11 +3,11 @@ from typing import List
from pydantic import field_validator
from schemas.barcode import BarcodeTemplateSchema
from schemas.base import CustomModelCamel, OkMessageSchema
from schemas.base import BaseSchema, OkMessageSchema
# region Entities
class ClientDetailsSchema(CustomModelCamel):
class ClientDetailsSchema(BaseSchema):
telegram: str | None = None
phone_number: str | None = None
inn: str | None = None
@@ -18,7 +18,7 @@ class ClientDetailsSchema(CustomModelCamel):
return '' if v is None else v
class ClientSchema(CustomModelCamel):
class ClientSchema(BaseSchema):
id: int
name: str
company_name: str
@@ -29,39 +29,39 @@ class ClientSchema(CustomModelCamel):
# endregion
# region Requests
class ClientSearchRequest(CustomModelCamel):
class ClientSearchRequest(BaseSchema):
name: str
class ClientUpdateDetailsRequest(CustomModelCamel):
class ClientUpdateDetailsRequest(BaseSchema):
client_id: int
details: ClientDetailsSchema
class ClientCreateRequest(CustomModelCamel):
class ClientCreateRequest(BaseSchema):
data: ClientSchema
class ClientUpdateRequest(CustomModelCamel):
class ClientUpdateRequest(BaseSchema):
data: ClientSchema
class ClientDeleteRequest(CustomModelCamel):
class ClientDeleteRequest(BaseSchema):
client_id: int
# endregion
# region Responses
class ClientSearchResponse(CustomModelCamel):
class ClientSearchResponse(BaseSchema):
clients: List[ClientSchema]
class ClientUpdateDetailsResponse(CustomModelCamel):
class ClientUpdateDetailsResponse(BaseSchema):
ok: bool
class ClientGetAllResponse(CustomModelCamel):
class ClientGetAllResponse(BaseSchema):
clients: List[ClientSchema]

View File

@@ -1,9 +1,9 @@
import datetime
from typing import List, Optional
from typing import List, Optional, Union
from pydantic import constr
from pydantic import constr, field_validator
from schemas.base import CustomModelCamel, OkMessageSchema
from schemas.base import BaseSchema, OkMessageSchema
from schemas.client import ClientSchema
from schemas.product import ProductSchema
from schemas.service import ServiceSchema
@@ -12,14 +12,14 @@ from schemas.user import UserSchema
# region Entities
class FastDeal(CustomModelCamel):
class FastDeal(BaseSchema):
name: str
client: ClientSchema
comment: str
acceptance_date: datetime.datetime
class DealSummary(CustomModelCamel):
class DealSummary(BaseSchema):
id: int
name: str
client_name: str
@@ -30,24 +30,24 @@ class DealSummary(CustomModelCamel):
rank: int
class DealServiceSchema(CustomModelCamel):
class DealServiceSchema(BaseSchema):
service: ServiceSchema
quantity: int
price: int
class DealProductServiceSchema(CustomModelCamel):
class DealProductServiceSchema(BaseSchema):
service: ServiceSchema
price: int
class DealProductSchema(CustomModelCamel):
class DealProductSchema(BaseSchema):
product: ProductSchema
services: List[DealProductServiceSchema]
quantity: int
class DealStatusHistorySchema(CustomModelCamel):
class DealStatusHistorySchema(BaseSchema):
user: UserSchema
changed_at: datetime.datetime
from_status: int
@@ -56,7 +56,7 @@ class DealStatusHistorySchema(CustomModelCamel):
comment: str | None = None
class DealSchema(CustomModelCamel):
class DealSchema(BaseSchema):
id: int
name: str
client_id: int
@@ -69,29 +69,30 @@ class DealSchema(CustomModelCamel):
is_completed: bool
client: ClientSchema
comment: str
shipping_warehouse: Optional[ShippingWarehouseSchema] = None
shipping_warehouse: Optional[Union[ShippingWarehouseSchema, str]] = None
class DealGeneralInfoSchema(CustomModelCamel):
class DealGeneralInfoSchema(BaseSchema):
name: str
is_deleted: bool
is_completed: bool
comment: str
shipping_warehouse: Optional[str] = None
# endregion Entities
# region Requests
class DealChangeStatusRequest(CustomModelCamel):
class DealChangeStatusRequest(BaseSchema):
deal_id: int
new_status: int
class DealCreateRequest(CustomModelCamel):
class DealCreateRequest(BaseSchema):
name: str
class DealQuickCreateRequest(CustomModelCamel):
class DealQuickCreateRequest(BaseSchema):
name: constr(strip_whitespace=True)
client_name: constr(strip_whitespace=True)
comment: str
@@ -99,70 +100,70 @@ class DealQuickCreateRequest(CustomModelCamel):
shipping_warehouse: constr(strip_whitespace=True)
class DealSummaryRequest(CustomModelCamel):
class DealSummaryRequest(BaseSchema):
pass
class DealAddServicesRequest(CustomModelCamel):
class DealAddServicesRequest(BaseSchema):
deal_id: int
services: list[DealServiceSchema]
class DealUpdateServiceQuantityRequest(CustomModelCamel):
class DealUpdateServiceQuantityRequest(BaseSchema):
deal_id: int
service_id: int
quantity: int
class DealUpdateServiceRequest(CustomModelCamel):
class DealUpdateServiceRequest(BaseSchema):
deal_id: int
service: DealServiceSchema
class DealAddServiceRequest(CustomModelCamel):
class DealAddServiceRequest(BaseSchema):
deal_id: int
service_id: int
quantity: int
price: int
class DealDeleteServiceRequest(CustomModelCamel):
class DealDeleteServiceRequest(BaseSchema):
deal_id: int
service_id: int
class DealDeleteServicesRequest(CustomModelCamel):
class DealDeleteServicesRequest(BaseSchema):
deal_id: int
service_ids: List[int]
class DealUpdateProductQuantityRequest(CustomModelCamel):
class DealUpdateProductQuantityRequest(BaseSchema):
deal_id: int
product_id: int
quantity: int
class DealAddProductRequest(CustomModelCamel):
class DealAddProductRequest(BaseSchema):
deal_id: int
product: DealProductSchema
class DealDeleteProductRequest(CustomModelCamel):
class DealDeleteProductRequest(BaseSchema):
deal_id: int
product_id: int
class DealDeleteProductsRequest(CustomModelCamel):
class DealDeleteProductsRequest(BaseSchema):
deal_id: int
product_ids: List[int]
class DealUpdateGeneralInfoRequest(CustomModelCamel):
class DealUpdateGeneralInfoRequest(BaseSchema):
deal_id: int
data: DealGeneralInfoSchema
class DealSummaryReorderRequest(CustomModelCamel):
class DealSummaryReorderRequest(BaseSchema):
deal_id: int
status: int
index: int
@@ -170,11 +171,11 @@ class DealSummaryReorderRequest(CustomModelCamel):
comment: str | None = None
class DealDeleteRequest(CustomModelCamel):
class DealDeleteRequest(BaseSchema):
deal_id: int
class DealUpdateProductRequest(CustomModelCamel):
class DealUpdateProductRequest(BaseSchema):
deal_id: int
product: DealProductSchema
@@ -190,32 +191,32 @@ class DealDeleteServicesResponse(OkMessageSchema):
pass
class DealGetAllResponse(CustomModelCamel):
class DealGetAllResponse(BaseSchema):
deals: List[DealSchema]
class DealChangeStatusResponse(CustomModelCamel):
class DealChangeStatusResponse(BaseSchema):
ok: bool
class DealCreateResponse(CustomModelCamel):
class DealCreateResponse(BaseSchema):
ok: bool
class DealQuickCreateResponse(CustomModelCamel):
class DealQuickCreateResponse(BaseSchema):
deal_id: int
class DealSummaryResponse(CustomModelCamel):
class DealSummaryResponse(BaseSchema):
summaries: List[DealSummary]
class DealAddServicesResponse(CustomModelCamel):
class DealAddServicesResponse(BaseSchema):
ok: bool
message: str
class DealUpdateServiceQuantityResponse(CustomModelCamel):
class DealUpdateServiceQuantityResponse(BaseSchema):
ok: bool
message: str

20
schemas/position.py Normal file
View File

@@ -0,0 +1,20 @@
from typing import List
from schemas.base import BaseSchema, OkMessageSchema
class PositionSchema(BaseSchema):
name: str
key: str
class CreatePositionRequest(BaseSchema):
data: PositionSchema
class GetAllPositionsResponse(BaseSchema):
positions: List[PositionSchema]
class CreatePositionResponse(OkMessageSchema):
pass

View File

@@ -1,18 +1,18 @@
from typing import List
from schemas.barcode import BarcodeTemplateSchema
from schemas.base import CustomModelCamel, PaginationInfoSchema, OkMessageSchema
from schemas.base import BaseSchema, PaginationInfoSchema, OkMessageSchema
from pydantic import field_validator, model_validator
from models import ProductBarcode
# region Entities
class ProductImageSchema(CustomModelCamel):
class ProductImageSchema(BaseSchema):
id: int
product_id: int
image_url: str
class BaseProductSchema(CustomModelCamel):
class BaseProductSchema(BaseSchema):
name: str
article: str | None = ''
client_id: int
@@ -55,25 +55,25 @@ class ProductCreateRequest(BaseProductSchema):
pass
class ProductDeleteRequest(CustomModelCamel):
class ProductDeleteRequest(BaseSchema):
product_id: int
class ProductUpdateRequest(CustomModelCamel):
class ProductUpdateRequest(BaseSchema):
product: ProductSchema
class ProductAddBarcodeRequest(CustomModelCamel):
class ProductAddBarcodeRequest(BaseSchema):
product_id: int
barcode: str
class ProductDeleteBarcodeRequest(CustomModelCamel):
class ProductDeleteBarcodeRequest(BaseSchema):
product_id: int
barcode: str
class ProductGenerateBarcodeRequest(CustomModelCamel):
class ProductGenerateBarcodeRequest(BaseSchema):
product_id: int
@@ -84,7 +84,7 @@ class ProductCreateResponse(OkMessageSchema):
product_id: int | None = None
class ProductGetResponse(CustomModelCamel):
class ProductGetResponse(BaseSchema):
products: List[ProductSchema]
pagination_info: PaginationInfoSchema
@@ -109,7 +109,7 @@ class ProductGenerateBarcodeResponse(OkMessageSchema):
barcode: str
class ProductExistsBarcodeResponse(CustomModelCamel):
class ProductExistsBarcodeResponse(BaseSchema):
exists: bool

26
schemas/role.py Normal file
View File

@@ -0,0 +1,26 @@
from typing import List
from schemas.base import BaseSchema
# region Entities
class PermissionSchema(BaseSchema):
key: str
name: str
class RoleSchema(BaseSchema):
key: str
name: str
permissions: List[PermissionSchema]
# endregion
# region Requests
# endregion
# region Responses
class GetAllRolesResponse(BaseSchema):
roles: List[RoleSchema]
# endregion

View File

@@ -1,22 +1,22 @@
from typing import List, Optional
from schemas.base import CustomModelCamel, OkMessageSchema, BaseEnumSchema
from schemas.base import BaseSchema, OkMessageSchema, BaseEnumSchema
# region Entities
class ServicePriceRangeSchema(CustomModelCamel):
class ServicePriceRangeSchema(BaseSchema):
id: int | None
from_quantity: int
to_quantity: int
price: float
class ServiceCategorySchema(CustomModelCamel):
class ServiceCategorySchema(BaseSchema):
id: int
name: str
class ServiceSchema(CustomModelCamel):
class ServiceSchema(BaseSchema):
id: int
name: str
category: ServiceCategorySchema
@@ -30,19 +30,19 @@ class ServiceSchema(CustomModelCamel):
# region Requests
class ServiceCreateRequest(CustomModelCamel):
class ServiceCreateRequest(BaseSchema):
service: ServiceSchema
class ServiceCreateCategoryRequest(CustomModelCamel):
class ServiceCreateCategoryRequest(BaseSchema):
category: ServiceCategorySchema
class ServiceUpdateRequest(CustomModelCamel):
class ServiceUpdateRequest(BaseSchema):
data: ServiceSchema
class ServiceDeleteRequest(CustomModelCamel):
class ServiceDeleteRequest(BaseSchema):
service_id: int
@@ -50,11 +50,11 @@ class ServiceDeleteRequest(CustomModelCamel):
# region Responses
class ServiceGetAllResponse(CustomModelCamel):
class ServiceGetAllResponse(BaseSchema):
services: List[ServiceSchema]
class ServiceGetAllCategoriesResponse(CustomModelCamel):
class ServiceGetAllCategoriesResponse(BaseSchema):
categories: List[ServiceCategorySchema]

View File

@@ -1,12 +1,12 @@
from typing import List
from schemas.base import CustomModelCamel
from schemas.base import BaseSchema
class ShippingWarehouseSchema(CustomModelCamel):
class ShippingWarehouseSchema(BaseSchema):
id: int
name: str
class GetAllShippingWarehousesResponse(CustomModelCamel):
class GetAllShippingWarehousesResponse(BaseSchema):
shipping_warehouses: List[ShippingWarehouseSchema]

View File

@@ -1,8 +1,50 @@
from schemas.base import CustomModelCamel
from typing import List, Optional
from schemas.base import BaseSchema, OkMessageSchema
from schemas.position import PositionSchema
from schemas.role import RoleSchema
class UserSchema(CustomModelCamel):
# region Entities
class BaseUser(BaseSchema):
id: int
telegram_id: int
phone_number: str | None = None
first_name: str
second_name: str
comment: str
is_admin: bool
is_blocked: bool
is_deleted: bool
class UserSchema(BaseUser):
role_key: str
role: RoleSchema
position: Optional[PositionSchema] = None
class UserUpdate(BaseUser):
position_key: Optional[str] = None
# endregion
# region Requests
class UpdateUserRequest(BaseSchema):
data: UserUpdate
# endregion
# region Responses
class GetAllUsersResponse(BaseSchema):
users: List[UserSchema]
class UpdateUserResponse(OkMessageSchema):
pass
# endregion

View File

@@ -10,6 +10,7 @@ from starlette import status
import backend.config
import constants
from backend.session import get_session
from enums.user import UserRole
from models import User
from schemas.auth import *
from services.base import BaseService
@@ -52,8 +53,11 @@ class AuthService(BaseService):
user: Union[User, None] = await self.session.scalar(select(User).where(User.telegram_id == request.id))
if not user:
user = User(telegram_id=request.id,
is_admin=False)
user = User(
telegram_id=request.id,
is_admin=False,
role_key=UserRole.user
)
self.session.add(user)
await self.session.commit()
access_token = self._generate_jwt_token(user)

View File

@@ -240,13 +240,21 @@ class DealService(BaseService):
async def update_general_info(self, request: DealUpdateGeneralInfoRequest) -> DealUpdateGeneralInfoResponse:
try:
deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
deal: Deal = await self.session.scalar(select(Deal).where(Deal.id == request.deal_id))
if not deal:
raise HTTPException(status_code=404, detail="Сделка не найдена")
deal.name = request.data.name
deal.comment = request.data.comment
deal.is_deleted = request.data.is_deleted
deal.is_completed = request.data.is_completed
# Updating shipping warehouse
shipping_warehouse_service = ShippingWarehouseService(self.session)
shipping_warehouse = await shipping_warehouse_service.get_by_name(request.data.shipping_warehouse)
if not shipping_warehouse and request.data.shipping_warehouse:
shipping_warehouse = await shipping_warehouse_service.create_by_name(request.data.shipping_warehouse)
deal.shipping_warehouse = shipping_warehouse
await self.session.commit()
return DealUpdateGeneralInfoResponse(ok=True, message='Данные о сделке успешно обновлены')
except Exception as e:

32
services/position.py Normal file
View File

@@ -0,0 +1,32 @@
from typing import Union
from sqlalchemy import select, insert
from models import Position
from schemas.position import *
from services.base import BaseService
class PositionService(BaseService):
async def get_all(self) -> GetAllPositionsResponse:
stmt = select(Position).order_by(Position.key)
positions = (await self.session.scalars(stmt)).all()
positions_schemas = [PositionSchema.model_validate(position) for position in positions]
response = GetAllPositionsResponse(positions=positions_schemas)
return response
async def get_by_key(self, key: str) -> Union[Position, None]:
stmt = select(Position).where(Position.key == key)
return await self.session.scalar(stmt)
async def create(self, request: CreatePositionRequest) -> CreatePositionResponse:
try:
if await self.get_by_key(request.data.key):
return CreatePositionResponse(ok=False, message='Должность с таким ключом уже существует')
stmt = insert(Position).values(request.data.model_dump())
await self.session.execute(stmt)
await self.session.commit()
return CreatePositionResponse(ok=True, message='Должность успешно создана')
except Exception as e:
return CreatePositionResponse(ok=False, message=str(e))

14
services/role.py Normal file
View File

@@ -0,0 +1,14 @@
from sqlalchemy import select
from models import Role
from schemas.role import *
from services.base import BaseService
class RoleService(BaseService):
async def get_all(self) -> GetAllRolesResponse:
stmt = (select(Role).order_by(Role.key))
roles = (await self.session.scalars(stmt)).all()
return GetAllRolesResponse(
roles=RoleSchema.from_orm_list(roles)
)

47
services/user.py Normal file
View File

@@ -0,0 +1,47 @@
from sqlalchemy import select, update, delete, insert
from models import User, user_position
from services.base import BaseService
from schemas.user import *
class UserService(BaseService):
async def get_all(self) -> GetAllUsersResponse:
stmt = (
select(User)
.order_by(User.id.desc())
# .where(User.is_deleted == False)
)
users = (await self.session.scalars(stmt)).all()
users_schemas = [UserSchema.model_validate(user) for user in users]
return GetAllUsersResponse(users=users_schemas)
async def get_by_id(self, user_id: int) -> Optional[User]:
return await self.session.scalar(select(User).where(User.id == user_id))
async def update(self, request: UpdateUserRequest) -> UpdateUserResponse:
try:
if not self.get_by_id(request.data.id):
return UpdateUserResponse(ok=False, message='Указанный пользователь не найден')
base_fields = request.data.model_dump_parent()
stmt = update(User).values(**base_fields).where(User.id == request.data.id)
await self.session.execute(stmt)
await self.session.flush()
# Updating position
stmt = delete(user_position).where(user_position.c.user_id == request.data.id)
await self.session.execute(stmt)
await self.session.flush()
if not request.data.position_key:
await self.session.commit()
return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен')
stmt = insert(user_position).values(**{
'user_id': request.data.id,
'position_key': request.data.position_key
})
await self.session.execute(stmt)
await self.session.commit()
return UpdateUserResponse(ok=True, message='Пользователь успешно обновлен')
except Exception as e:
pass

19
test.py
View File

@@ -1,9 +1,14 @@
import struct
import sys
from typing import Self
sys.set_int_max_str_digits(-0)
with open('Дизайн.jpg', 'rb') as rf:
data = int.from_bytes(rf.read())
with open('result', 'wb') as rf:
rf.write(data.to_bytes(length=len(str(data))))
class A:
@classmethod
def test(cls) -> Self:
return cls()
class B(A):
pass
a = B.test()

30
utils/init_roles.py Normal file
View File

@@ -0,0 +1,30 @@
import asyncio
from sqlalchemy.ext.asyncio import AsyncSession
from backend.session import session_maker
from enums.user import UserRole
from models import Role
async def main():
role_name_dictionary = {
UserRole.admin: "Админ",
UserRole.user: "Базовый пользователь",
UserRole.manager: "Менеджер",
UserRole.employee: "Сотрудник",
}
session: AsyncSession = session_maker()
for key, name in role_name_dictionary.items():
role = Role(
key=key,
name=name
)
session.add(role)
await session.commit()
await session.close()
if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())