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.service_router,
routers.product_router, routers.product_router,
routers.barcode_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: for router in routers_list:
app.include_router(router) 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 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): class User(BaseModel):
__tablename__ = 'users' __tablename__ = 'users'
id = Column(Integer, autoincrement=True, primary_key=True, index=True) id: Mapped[int] = mapped_column(primary_key=True)
telegram_id = Column(BigInteger, nullable=False, index=True)
phone_number = Column(String)
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 .product import product_router
from .barcode import barcode_router from .barcode import barcode_router
from .shipping_warehouse import shipping_warehouse_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 import base64
from io import BytesIO from io import BytesIO
from typing import Annotated, Union from typing import Annotated
from fastapi import APIRouter, Depends, UploadFile from fastapi import APIRouter, Depends, UploadFile
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from starlette.responses import StreamingResponse
from fastapi.responses import FileResponse
import utils.dependecies import utils.dependecies
from backend.session import get_session from backend.session import get_session
from schemas.barcode import GetProductBarcodeResponse, GetProductBarcodeRequest, GetProductBarcodePdfResponse, \ from schemas.barcode import GetProductBarcodeResponse, GetProductBarcodeRequest, GetProductBarcodePdfResponse, \
GetProductBarcodePdfRequest GetProductBarcodePdfRequest
from schemas.base import PaginationSchema from schemas.base import PaginationSchema
from schemas.product import * from schemas.product import *
from services.auth import get_current_user
from services.barcode import BarcodeService from services.barcode import BarcodeService
from services.product import ProductService 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): class AuthLoginRequest(CustomModelSnake):
@@ -9,5 +9,5 @@ class AuthLoginRequest(CustomModelSnake):
photo_url: str photo_url: str
class AuthLoginResponse(CustomModelCamel): class AuthLoginResponse(BaseSchema):
access_token: str access_token: str

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
import datetime 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.client import ClientSchema
from schemas.product import ProductSchema from schemas.product import ProductSchema
from schemas.service import ServiceSchema from schemas.service import ServiceSchema
@@ -12,14 +12,14 @@ from schemas.user import UserSchema
# region Entities # region Entities
class FastDeal(CustomModelCamel): class FastDeal(BaseSchema):
name: str name: str
client: ClientSchema client: ClientSchema
comment: str comment: str
acceptance_date: datetime.datetime acceptance_date: datetime.datetime
class DealSummary(CustomModelCamel): class DealSummary(BaseSchema):
id: int id: int
name: str name: str
client_name: str client_name: str
@@ -30,24 +30,24 @@ class DealSummary(CustomModelCamel):
rank: int rank: int
class DealServiceSchema(CustomModelCamel): class DealServiceSchema(BaseSchema):
service: ServiceSchema service: ServiceSchema
quantity: int quantity: int
price: int price: int
class DealProductServiceSchema(CustomModelCamel): class DealProductServiceSchema(BaseSchema):
service: ServiceSchema service: ServiceSchema
price: int price: int
class DealProductSchema(CustomModelCamel): class DealProductSchema(BaseSchema):
product: ProductSchema product: ProductSchema
services: List[DealProductServiceSchema] services: List[DealProductServiceSchema]
quantity: int quantity: int
class DealStatusHistorySchema(CustomModelCamel): class DealStatusHistorySchema(BaseSchema):
user: UserSchema user: UserSchema
changed_at: datetime.datetime changed_at: datetime.datetime
from_status: int from_status: int
@@ -56,7 +56,7 @@ class DealStatusHistorySchema(CustomModelCamel):
comment: str | None = None comment: str | None = None
class DealSchema(CustomModelCamel): class DealSchema(BaseSchema):
id: int id: int
name: str name: str
client_id: int client_id: int
@@ -69,29 +69,30 @@ class DealSchema(CustomModelCamel):
is_completed: bool is_completed: bool
client: ClientSchema client: ClientSchema
comment: str comment: str
shipping_warehouse: Optional[ShippingWarehouseSchema] = None shipping_warehouse: Optional[Union[ShippingWarehouseSchema, str]] = None
class DealGeneralInfoSchema(CustomModelCamel): class DealGeneralInfoSchema(BaseSchema):
name: str name: str
is_deleted: bool is_deleted: bool
is_completed: bool is_completed: bool
comment: str comment: str
shipping_warehouse: Optional[str] = None
# endregion Entities # endregion Entities
# region Requests # region Requests
class DealChangeStatusRequest(CustomModelCamel): class DealChangeStatusRequest(BaseSchema):
deal_id: int deal_id: int
new_status: int new_status: int
class DealCreateRequest(CustomModelCamel): class DealCreateRequest(BaseSchema):
name: str name: str
class DealQuickCreateRequest(CustomModelCamel): class DealQuickCreateRequest(BaseSchema):
name: constr(strip_whitespace=True) name: constr(strip_whitespace=True)
client_name: constr(strip_whitespace=True) client_name: constr(strip_whitespace=True)
comment: str comment: str
@@ -99,70 +100,70 @@ class DealQuickCreateRequest(CustomModelCamel):
shipping_warehouse: constr(strip_whitespace=True) shipping_warehouse: constr(strip_whitespace=True)
class DealSummaryRequest(CustomModelCamel): class DealSummaryRequest(BaseSchema):
pass pass
class DealAddServicesRequest(CustomModelCamel): class DealAddServicesRequest(BaseSchema):
deal_id: int deal_id: int
services: list[DealServiceSchema] services: list[DealServiceSchema]
class DealUpdateServiceQuantityRequest(CustomModelCamel): class DealUpdateServiceQuantityRequest(BaseSchema):
deal_id: int deal_id: int
service_id: int service_id: int
quantity: int quantity: int
class DealUpdateServiceRequest(CustomModelCamel): class DealUpdateServiceRequest(BaseSchema):
deal_id: int deal_id: int
service: DealServiceSchema service: DealServiceSchema
class DealAddServiceRequest(CustomModelCamel): class DealAddServiceRequest(BaseSchema):
deal_id: int deal_id: int
service_id: int service_id: int
quantity: int quantity: int
price: int price: int
class DealDeleteServiceRequest(CustomModelCamel): class DealDeleteServiceRequest(BaseSchema):
deal_id: int deal_id: int
service_id: int service_id: int
class DealDeleteServicesRequest(CustomModelCamel): class DealDeleteServicesRequest(BaseSchema):
deal_id: int deal_id: int
service_ids: List[int] service_ids: List[int]
class DealUpdateProductQuantityRequest(CustomModelCamel): class DealUpdateProductQuantityRequest(BaseSchema):
deal_id: int deal_id: int
product_id: int product_id: int
quantity: int quantity: int
class DealAddProductRequest(CustomModelCamel): class DealAddProductRequest(BaseSchema):
deal_id: int deal_id: int
product: DealProductSchema product: DealProductSchema
class DealDeleteProductRequest(CustomModelCamel): class DealDeleteProductRequest(BaseSchema):
deal_id: int deal_id: int
product_id: int product_id: int
class DealDeleteProductsRequest(CustomModelCamel): class DealDeleteProductsRequest(BaseSchema):
deal_id: int deal_id: int
product_ids: List[int] product_ids: List[int]
class DealUpdateGeneralInfoRequest(CustomModelCamel): class DealUpdateGeneralInfoRequest(BaseSchema):
deal_id: int deal_id: int
data: DealGeneralInfoSchema data: DealGeneralInfoSchema
class DealSummaryReorderRequest(CustomModelCamel): class DealSummaryReorderRequest(BaseSchema):
deal_id: int deal_id: int
status: int status: int
index: int index: int
@@ -170,11 +171,11 @@ class DealSummaryReorderRequest(CustomModelCamel):
comment: str | None = None comment: str | None = None
class DealDeleteRequest(CustomModelCamel): class DealDeleteRequest(BaseSchema):
deal_id: int deal_id: int
class DealUpdateProductRequest(CustomModelCamel): class DealUpdateProductRequest(BaseSchema):
deal_id: int deal_id: int
product: DealProductSchema product: DealProductSchema
@@ -190,32 +191,32 @@ class DealDeleteServicesResponse(OkMessageSchema):
pass pass
class DealGetAllResponse(CustomModelCamel): class DealGetAllResponse(BaseSchema):
deals: List[DealSchema] deals: List[DealSchema]
class DealChangeStatusResponse(CustomModelCamel): class DealChangeStatusResponse(BaseSchema):
ok: bool ok: bool
class DealCreateResponse(CustomModelCamel): class DealCreateResponse(BaseSchema):
ok: bool ok: bool
class DealQuickCreateResponse(CustomModelCamel): class DealQuickCreateResponse(BaseSchema):
deal_id: int deal_id: int
class DealSummaryResponse(CustomModelCamel): class DealSummaryResponse(BaseSchema):
summaries: List[DealSummary] summaries: List[DealSummary]
class DealAddServicesResponse(CustomModelCamel): class DealAddServicesResponse(BaseSchema):
ok: bool ok: bool
message: str message: str
class DealUpdateServiceQuantityResponse(CustomModelCamel): class DealUpdateServiceQuantityResponse(BaseSchema):
ok: bool ok: bool
message: str 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 typing import List
from schemas.barcode import BarcodeTemplateSchema 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 pydantic import field_validator, model_validator
from models import ProductBarcode from models import ProductBarcode
# region Entities # region Entities
class ProductImageSchema(CustomModelCamel): class ProductImageSchema(BaseSchema):
id: int id: int
product_id: int product_id: int
image_url: str image_url: str
class BaseProductSchema(CustomModelCamel): class BaseProductSchema(BaseSchema):
name: str name: str
article: str | None = '' article: str | None = ''
client_id: int client_id: int
@@ -55,25 +55,25 @@ class ProductCreateRequest(BaseProductSchema):
pass pass
class ProductDeleteRequest(CustomModelCamel): class ProductDeleteRequest(BaseSchema):
product_id: int product_id: int
class ProductUpdateRequest(CustomModelCamel): class ProductUpdateRequest(BaseSchema):
product: ProductSchema product: ProductSchema
class ProductAddBarcodeRequest(CustomModelCamel): class ProductAddBarcodeRequest(BaseSchema):
product_id: int product_id: int
barcode: str barcode: str
class ProductDeleteBarcodeRequest(CustomModelCamel): class ProductDeleteBarcodeRequest(BaseSchema):
product_id: int product_id: int
barcode: str barcode: str
class ProductGenerateBarcodeRequest(CustomModelCamel): class ProductGenerateBarcodeRequest(BaseSchema):
product_id: int product_id: int
@@ -84,7 +84,7 @@ class ProductCreateResponse(OkMessageSchema):
product_id: int | None = None product_id: int | None = None
class ProductGetResponse(CustomModelCamel): class ProductGetResponse(BaseSchema):
products: List[ProductSchema] products: List[ProductSchema]
pagination_info: PaginationInfoSchema pagination_info: PaginationInfoSchema
@@ -109,7 +109,7 @@ class ProductGenerateBarcodeResponse(OkMessageSchema):
barcode: str barcode: str
class ProductExistsBarcodeResponse(CustomModelCamel): class ProductExistsBarcodeResponse(BaseSchema):
exists: bool 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 typing import List, Optional
from schemas.base import CustomModelCamel, OkMessageSchema, BaseEnumSchema from schemas.base import BaseSchema, OkMessageSchema, BaseEnumSchema
# region Entities # region Entities
class ServicePriceRangeSchema(CustomModelCamel): class ServicePriceRangeSchema(BaseSchema):
id: int | None id: int | None
from_quantity: int from_quantity: int
to_quantity: int to_quantity: int
price: float price: float
class ServiceCategorySchema(CustomModelCamel): class ServiceCategorySchema(BaseSchema):
id: int id: int
name: str name: str
class ServiceSchema(CustomModelCamel): class ServiceSchema(BaseSchema):
id: int id: int
name: str name: str
category: ServiceCategorySchema category: ServiceCategorySchema
@@ -30,19 +30,19 @@ class ServiceSchema(CustomModelCamel):
# region Requests # region Requests
class ServiceCreateRequest(CustomModelCamel): class ServiceCreateRequest(BaseSchema):
service: ServiceSchema service: ServiceSchema
class ServiceCreateCategoryRequest(CustomModelCamel): class ServiceCreateCategoryRequest(BaseSchema):
category: ServiceCategorySchema category: ServiceCategorySchema
class ServiceUpdateRequest(CustomModelCamel): class ServiceUpdateRequest(BaseSchema):
data: ServiceSchema data: ServiceSchema
class ServiceDeleteRequest(CustomModelCamel): class ServiceDeleteRequest(BaseSchema):
service_id: int service_id: int
@@ -50,11 +50,11 @@ class ServiceDeleteRequest(CustomModelCamel):
# region Responses # region Responses
class ServiceGetAllResponse(CustomModelCamel): class ServiceGetAllResponse(BaseSchema):
services: List[ServiceSchema] services: List[ServiceSchema]
class ServiceGetAllCategoriesResponse(CustomModelCamel): class ServiceGetAllCategoriesResponse(BaseSchema):
categories: List[ServiceCategorySchema] categories: List[ServiceCategorySchema]

View File

@@ -1,12 +1,12 @@
from typing import List from typing import List
from schemas.base import CustomModelCamel from schemas.base import BaseSchema
class ShippingWarehouseSchema(CustomModelCamel): class ShippingWarehouseSchema(BaseSchema):
id: int id: int
name: str name: str
class GetAllShippingWarehousesResponse(CustomModelCamel): class GetAllShippingWarehousesResponse(BaseSchema):
shipping_warehouses: List[ShippingWarehouseSchema] 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 id: int
telegram_id: int telegram_id: int
phone_number: str | None = None phone_number: str | None = None
first_name: str
second_name: str
comment: str
is_admin: bool 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 backend.config
import constants import constants
from backend.session import get_session from backend.session import get_session
from enums.user import UserRole
from models import User from models import User
from schemas.auth import * from schemas.auth import *
from services.base import BaseService 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)) user: Union[User, None] = await self.session.scalar(select(User).where(User.telegram_id == request.id))
if not user: if not user:
user = User(telegram_id=request.id, user = User(
is_admin=False) telegram_id=request.id,
is_admin=False,
role_key=UserRole.user
)
self.session.add(user) self.session.add(user)
await self.session.commit() await self.session.commit()
access_token = self._generate_jwt_token(user) 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: async def update_general_info(self, request: DealUpdateGeneralInfoRequest) -> DealUpdateGeneralInfoResponse:
try: 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: if not deal:
raise HTTPException(status_code=404, detail="Сделка не найдена") raise HTTPException(status_code=404, detail="Сделка не найдена")
deal.name = request.data.name deal.name = request.data.name
deal.comment = request.data.comment deal.comment = request.data.comment
deal.is_deleted = request.data.is_deleted deal.is_deleted = request.data.is_deleted
deal.is_completed = request.data.is_completed 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() await self.session.commit()
return DealUpdateGeneralInfoResponse(ok=True, message='Данные о сделке успешно обновлены') return DealUpdateGeneralInfoResponse(ok=True, message='Данные о сделке успешно обновлены')
except Exception as e: 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 from typing import Self
import sys
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: class A:
rf.write(data.to_bytes(length=len(str(data)))) @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())