feat: filling deals from excel file
This commit is contained in:
		@@ -1,4 +1,3 @@
 | 
				
			|||||||
from audioop import ratecv
 | 
					 | 
				
			||||||
from typing import Union
 | 
					from typing import Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from sqlalchemy.ext.asyncio import AsyncSession
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								parsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								parsers/__init__.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					from .deal_parser import DealParser
 | 
				
			||||||
							
								
								
									
										143
									
								
								parsers/deal_parser.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								parsers/deal_parser.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					from io import BytesIO
 | 
				
			||||||
 | 
					from typing import Optional
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from openpyxl.reader.excel import load_workbook
 | 
				
			||||||
 | 
					from openpyxl.utils.cell import get_column_letter
 | 
				
			||||||
 | 
					from sqlalchemy import select, exists
 | 
				
			||||||
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
 | 
					from sqlalchemy.orm import selectinload
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from models import ProductBarcode, Product, ShippingWarehouse, BaseMarketplace
 | 
				
			||||||
 | 
					from schemas.deal import ParsedProductRowSchema, ParseDealsExcelResponse, ParsedCityBreakdownSchema, \
 | 
				
			||||||
 | 
					    OptionalShippingWarehouseSchema
 | 
				
			||||||
 | 
					from schemas.marketplace import MarketplaceSchema, BaseMarketplaceSchema
 | 
				
			||||||
 | 
					from schemas.product import ProductSchema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DealParser:
 | 
				
			||||||
 | 
					    def __init__(self, session: AsyncSession):
 | 
				
			||||||
 | 
					        self._session = session
 | 
				
			||||||
 | 
					        self._warehouses: dict[str, OptionalShippingWarehouseSchema] = {}
 | 
				
			||||||
 | 
					        self._marketplaces: dict[str, BaseMarketplaceSchema] = {}
 | 
				
			||||||
 | 
					        self._errors: list[str] = []
 | 
				
			||||||
 | 
					        self._warehouses_and_marketplaces: dict[
 | 
				
			||||||
 | 
					            int, dict[str, Optional[MarketplaceSchema] | OptionalShippingWarehouseSchema]
 | 
				
			||||||
 | 
					        ] = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _set_warehouses(self):
 | 
				
			||||||
 | 
					        stmt = select(ShippingWarehouse)
 | 
				
			||||||
 | 
					        warehouses = (await self._session.execute(stmt)).scalars().all()
 | 
				
			||||||
 | 
					        for warehouse in warehouses:
 | 
				
			||||||
 | 
					            self._warehouses[warehouse.name.lower()] = OptionalShippingWarehouseSchema.model_validate(warehouse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _set_marketplaces(self):
 | 
				
			||||||
 | 
					        stmt = select(BaseMarketplace)
 | 
				
			||||||
 | 
					        marketplaces = (await self._session.execute(stmt)).scalars().all()
 | 
				
			||||||
 | 
					        for marketplace in marketplaces:
 | 
				
			||||||
 | 
					            self._marketplaces[marketplace.key] = BaseMarketplaceSchema.model_validate(marketplace)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _parse_product_barcode(self, barcode_value: str) -> list[ProductSchema]:
 | 
				
			||||||
 | 
					        products_stmt = (
 | 
				
			||||||
 | 
					            select(Product)
 | 
				
			||||||
 | 
					            .join(ProductBarcode)
 | 
				
			||||||
 | 
					            .where(ProductBarcode.barcode == barcode_value)
 | 
				
			||||||
 | 
					            .options(
 | 
				
			||||||
 | 
					                selectinload(Product.barcodes)
 | 
				
			||||||
 | 
					                .noload(ProductBarcode.product),
 | 
				
			||||||
 | 
					                selectinload(Product.barcode_image),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        products = (await self._session.execute(products_stmt)).scalars().all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return [ProductSchema.model_validate(product) for product in products]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _parse_warehouse_and_marketplace_labels(self, value: str) -> dict[
 | 
				
			||||||
 | 
					        str, Optional[MarketplaceSchema] | OptionalShippingWarehouseSchema
 | 
				
			||||||
 | 
					    ]:
 | 
				
			||||||
 | 
					        stripped = value.split()
 | 
				
			||||||
 | 
					        base_mp_value, warehouse_value = stripped[0], " ".join(stripped[1:])
 | 
				
			||||||
 | 
					        base_mp_lower, warehouse_lower = base_mp_value.lower(), warehouse_value.lower()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        base_mp = self._marketplaces.get(base_mp_lower)
 | 
				
			||||||
 | 
					        warehouse = self._warehouses.get(warehouse_lower)
 | 
				
			||||||
 | 
					        if not warehouse:
 | 
				
			||||||
 | 
					            warehouse = OptionalShippingWarehouseSchema(name=warehouse_value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            "marketplace": base_mp,
 | 
				
			||||||
 | 
					            "warehouse": warehouse,
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _barcode_exists(self, barcode: str) -> bool:
 | 
				
			||||||
 | 
					        stmt = select(exists(ProductBarcode).where(ProductBarcode.barcode == barcode))
 | 
				
			||||||
 | 
					        barcode_exists = (await self._session.execute(stmt)).scalar()
 | 
				
			||||||
 | 
					        return barcode_exists
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _parse_warehouses_and_marketplaces(self, ws):
 | 
				
			||||||
 | 
					        mp_wh_row, mp_wh_start_col = 3, 9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self._set_warehouses()
 | 
				
			||||||
 | 
					        await self._set_marketplaces()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._warehouses_and_marketplaces = {}
 | 
				
			||||||
 | 
					        while True:
 | 
				
			||||||
 | 
					            value = ws.cell(row=mp_wh_row, column=mp_wh_start_col).value
 | 
				
			||||||
 | 
					            if not value:
 | 
				
			||||||
 | 
					                break
 | 
				
			||||||
 | 
					            mp_and_wh = await self._parse_warehouse_and_marketplace_labels(value)
 | 
				
			||||||
 | 
					            if mp_and_wh["marketplace"]:
 | 
				
			||||||
 | 
					                self._warehouses_and_marketplaces[mp_wh_start_col] = mp_and_wh
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                self._errors.append(f"В ячейке {get_column_letter(mp_wh_start_col)}3 не распознан маркетплейс")
 | 
				
			||||||
 | 
					            mp_wh_start_col += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _parse_barcodes(self, ws) -> list[ParsedProductRowSchema]:
 | 
				
			||||||
 | 
					        rows: list[ParsedProductRowSchema] = []
 | 
				
			||||||
 | 
					        barcode_row, barcode_col, count_empty_rows = 4, 3, 0
 | 
				
			||||||
 | 
					        barcode_col_name = get_column_letter(barcode_col)
 | 
				
			||||||
 | 
					        max_empty_rows_between_barcodes = 6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while True:
 | 
				
			||||||
 | 
					            barcode: Optional[int] = ws.cell(row=barcode_row, column=barcode_col).value
 | 
				
			||||||
 | 
					            if barcode and type(barcode) == int:
 | 
				
			||||||
 | 
					                count_empty_rows = 0
 | 
				
			||||||
 | 
					                str_barcode = str(barcode)
 | 
				
			||||||
 | 
					                barcode_exists = await self._barcode_exists(str_barcode)
 | 
				
			||||||
 | 
					                if barcode_exists:
 | 
				
			||||||
 | 
					                    products: list[ProductSchema] = await self._parse_product_barcode(str_barcode)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    breakdowns: list[ParsedCityBreakdownSchema] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    for col, data in self._warehouses_and_marketplaces.items():
 | 
				
			||||||
 | 
					                        quantity: Optional[str] = ws.cell(row=barcode_row, column=col).value
 | 
				
			||||||
 | 
					                        if quantity:
 | 
				
			||||||
 | 
					                            warehouse_excel_info = ParsedCityBreakdownSchema(
 | 
				
			||||||
 | 
					                                base_marketplace=data["marketplace"],
 | 
				
			||||||
 | 
					                                shippingWarehouse=data["warehouse"],
 | 
				
			||||||
 | 
					                                quantity=int(quantity),
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                            breakdowns.append(warehouse_excel_info)
 | 
				
			||||||
 | 
					                    if len(products) > 0 and len(breakdowns) > 0:
 | 
				
			||||||
 | 
					                        rows.append(
 | 
				
			||||||
 | 
					                            ParsedProductRowSchema(products=products, breakdowns=breakdowns, barcode=str(barcode))
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    self._errors.append(f"Штрихкод из ячейки {barcode_col_name}{barcode_row} не найден")
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                count_empty_rows += 1
 | 
				
			||||||
 | 
					                if count_empty_rows > max_empty_rows_between_barcodes:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            barcode_row += 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return rows
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def parse(self, file_bytes: bytes) -> ParseDealsExcelResponse:
 | 
				
			||||||
 | 
					        wb = load_workbook(filename=BytesIO(file_bytes))
 | 
				
			||||||
 | 
					        ws = wb.worksheets[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self._parse_warehouses_and_marketplaces(ws)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        rows = await self._parse_barcodes(ws)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return ParseDealsExcelResponse(rows=rows, errors=self._errors)
 | 
				
			||||||
@@ -2,13 +2,14 @@ import base64
 | 
				
			|||||||
from io import BytesIO
 | 
					from io import BytesIO
 | 
				
			||||||
from typing import Annotated
 | 
					from typing import Annotated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from fastapi import APIRouter, Depends, Response
 | 
					from fastapi import APIRouter, Depends, Response, UploadFile
 | 
				
			||||||
from sqlalchemy.ext.asyncio import AsyncSession
 | 
					from sqlalchemy.ext.asyncio import AsyncSession
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from backend.dependecies import SessionDependency, CurrentUserDependency
 | 
					from backend.dependecies import SessionDependency, CurrentUserDependency
 | 
				
			||||||
from backend.session import get_session
 | 
					from backend.session import get_session
 | 
				
			||||||
from generators.deal_pdf_generator.generator import DealTechSpecPdfGenerator
 | 
					from generators.deal_pdf_generator.generator import DealTechSpecPdfGenerator
 | 
				
			||||||
from models import User
 | 
					from models import User
 | 
				
			||||||
 | 
					from parsers import DealParser
 | 
				
			||||||
from schemas.barcode import GetDealProductsBarcodesPdfRequest, GetDealProductsBarcodesPdfResponse
 | 
					from schemas.barcode import GetDealProductsBarcodesPdfRequest, GetDealProductsBarcodesPdfResponse
 | 
				
			||||||
from schemas.deal import *
 | 
					from schemas.deal import *
 | 
				
			||||||
from services.auth import get_current_user, authorized_user, guest_user
 | 
					from services.auth import get_current_user, authorized_user, guest_user
 | 
				
			||||||
@@ -258,6 +259,31 @@ async def get_available_employees_to_assign(
 | 
				
			|||||||
    return await DealService(session).get_available_employees_to_assign(deal_id)
 | 
					    return await DealService(session).get_available_employees_to_assign(deal_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@deal_router.post(
 | 
				
			||||||
 | 
					    '/prefill/excel/parse',
 | 
				
			||||||
 | 
					    response_model=ParseDealsExcelResponse,
 | 
				
			||||||
 | 
					    operation_id='parse_deals_excel',
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def parse_deals_excel(
 | 
				
			||||||
 | 
					        session: Annotated[AsyncSession, Depends(get_session)],
 | 
				
			||||||
 | 
					        upload_file: UploadFile,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    file_bytes = upload_file.file.read()
 | 
				
			||||||
 | 
					    return await DealParser(session).parse(file_bytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@deal_router.post(
 | 
				
			||||||
 | 
					    '/prefill/excel/create',
 | 
				
			||||||
 | 
					    response_model=CreateDealsFromExcelResponse,
 | 
				
			||||||
 | 
					    operation_id='create_deals_excel',
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					async def create_deals_from_excel(
 | 
				
			||||||
 | 
					        session: Annotated[AsyncSession, Depends(get_session)],
 | 
				
			||||||
 | 
					        request: CreateDealsFromExcelRequest,
 | 
				
			||||||
 | 
					        user: CurrentUserDependency,
 | 
				
			||||||
 | 
					):
 | 
				
			||||||
 | 
					    return await DealService(session).create_deals_from_excel(request, user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# endregion
 | 
					# endregion
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# region Deal services
 | 
					# region Deal services
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,7 +12,7 @@ from schemas.marketplace import BaseMarketplaceSchema
 | 
				
			|||||||
from schemas.product import ProductSchema
 | 
					from schemas.product import ProductSchema
 | 
				
			||||||
from schemas.service import ServiceSchema, ServicePriceCategorySchema
 | 
					from schemas.service import ServiceSchema, ServicePriceCategorySchema
 | 
				
			||||||
from schemas.shipping import PalletSchema, BoxSchema
 | 
					from schemas.shipping import PalletSchema, BoxSchema
 | 
				
			||||||
from schemas.shipping_warehouse import ShippingWarehouseSchema
 | 
					from schemas.shipping_warehouse import ShippingWarehouseSchema, BaseShippingWarehouseSchema
 | 
				
			||||||
from schemas.user import UserSchema
 | 
					from schemas.user import UserSchema
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,6 +127,33 @@ class DealGeneralInfoSchema(BaseSchema):
 | 
				
			|||||||
    manager: Optional[UserSchema] = None
 | 
					    manager: Optional[UserSchema] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OptionalShippingWarehouseSchema(BaseShippingWarehouseSchema):
 | 
				
			||||||
 | 
					    id: Optional[int] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParsedCityBreakdownSchema(BaseSchema):
 | 
				
			||||||
 | 
					    base_marketplace: BaseMarketplaceSchema
 | 
				
			||||||
 | 
					    shipping_warehouse: OptionalShippingWarehouseSchema
 | 
				
			||||||
 | 
					    quantity: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParsedProductRowSchema(BaseSchema):
 | 
				
			||||||
 | 
					    barcode: str
 | 
				
			||||||
 | 
					    products: list[ProductSchema]
 | 
				
			||||||
 | 
					    breakdowns: list[ParsedCityBreakdownSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CityBreakdownFromExcelSchema(BaseSchema):
 | 
				
			||||||
 | 
					    base_marketplace: BaseMarketplaceSchema
 | 
				
			||||||
 | 
					    shipping_warehouse: OptionalShippingWarehouseSchema
 | 
				
			||||||
 | 
					    quantity: int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProductFromExcelSchema(BaseSchema):
 | 
				
			||||||
 | 
					    product_id: int
 | 
				
			||||||
 | 
					    cities_breakdown: list[CityBreakdownFromExcelSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# endregion Entities
 | 
					# endregion Entities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# region Requests
 | 
					# region Requests
 | 
				
			||||||
@@ -269,6 +296,11 @@ class ManageEmployeeRequest(BaseSchema):
 | 
				
			|||||||
    is_assign: bool
 | 
					    is_assign: bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CreateDealsFromExcelRequest(BaseSchema):
 | 
				
			||||||
 | 
					    client_id: int
 | 
				
			||||||
 | 
					    products: list[ProductFromExcelSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DealAddToGroupRequest(BaseSchema):
 | 
					class DealAddToGroupRequest(BaseSchema):
 | 
				
			||||||
    deal_id: int
 | 
					    deal_id: int
 | 
				
			||||||
    group_id: int
 | 
					    group_id: int
 | 
				
			||||||
@@ -409,6 +441,15 @@ class GetAvailableEmployeesToAssignResponse(BaseSchema):
 | 
				
			|||||||
    employees: list[UserSchema]
 | 
					    employees: list[UserSchema]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ParseDealsExcelResponse(BaseSchema):
 | 
				
			||||||
 | 
					    rows: list[ParsedProductRowSchema]
 | 
				
			||||||
 | 
					    errors: list[str]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class CreateDealsFromExcelResponse(OkMessageSchema):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class DealAddToGroupResponse(OkMessageSchema):
 | 
					class DealAddToGroupResponse(OkMessageSchema):
 | 
				
			||||||
    pass
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1218,6 +1218,92 @@ class DealService(BaseService):
 | 
				
			|||||||
        free_employees = (await self.session.execute(stmt_free_employees)).scalars().all()
 | 
					        free_employees = (await self.session.execute(stmt_free_employees)).scalars().all()
 | 
				
			||||||
        return GetAvailableEmployeesToAssignResponse(employees=free_employees)
 | 
					        return GetAvailableEmployeesToAssignResponse(employees=free_employees)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _create_deal_from_excel(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            client: Client,
 | 
				
			||||||
 | 
					            breakdown: CityBreakdownFromExcelSchema,
 | 
				
			||||||
 | 
					            user: User,
 | 
				
			||||||
 | 
					    ) -> Deal:
 | 
				
			||||||
 | 
					        rank = await self._get_rank_for_deal(DealStatus.CREATED)
 | 
				
			||||||
 | 
					        deal = Deal(
 | 
				
			||||||
 | 
					            name=f"{client.name} - {breakdown.base_marketplace.key.upper()} - {breakdown.shipping_warehouse.name}",
 | 
				
			||||||
 | 
					            created_at=datetime.datetime.now(),
 | 
				
			||||||
 | 
					            current_status=DealStatus.CREATED,
 | 
				
			||||||
 | 
					            lexorank=rank,
 | 
				
			||||||
 | 
					            client_id=client.id,
 | 
				
			||||||
 | 
					            base_marketplace_key=breakdown.base_marketplace.key,
 | 
				
			||||||
 | 
					            shipping_warehouse_id=breakdown.shipping_warehouse.id,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.session.add(deal)
 | 
				
			||||||
 | 
					        await self.session.flush()
 | 
				
			||||||
 | 
					        await self.change_status(deal, DealStatus.AWAITING_ACCEPTANCE, user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return deal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _create_group(self) -> DealGroup:
 | 
				
			||||||
 | 
					        group = models.DealGroup(
 | 
				
			||||||
 | 
					            name='',
 | 
				
			||||||
 | 
					            lexorank=lexorank.middle(lexorank.Bucket.BUCEKT_0).__str__(),
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        self.session.add(group)
 | 
				
			||||||
 | 
					        await self.session.flush()
 | 
				
			||||||
 | 
					        return group
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _get_or_create_warehouse(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            shipping_warehouse: OptionalShippingWarehouseSchema,
 | 
				
			||||||
 | 
					    ) -> OptionalShippingWarehouseSchema:
 | 
				
			||||||
 | 
					        if not shipping_warehouse.id:
 | 
				
			||||||
 | 
					            stmt = select(ShippingWarehouse).where(ShippingWarehouse.name == shipping_warehouse.name)
 | 
				
			||||||
 | 
					            row = (await self.session.execute(stmt)).first()
 | 
				
			||||||
 | 
					            warehouse_model: Optional[ShippingWarehouse] = row[0] if row else None
 | 
				
			||||||
 | 
					            if warehouse_model:
 | 
				
			||||||
 | 
					                shipping_warehouse.id = warehouse_model.id
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                warehouse = await ShippingWarehouseService(self.session).create_by_name(shipping_warehouse.name)
 | 
				
			||||||
 | 
					                shipping_warehouse.id = warehouse.id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return shipping_warehouse
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def create_deals_from_excel(
 | 
				
			||||||
 | 
					            self,
 | 
				
			||||||
 | 
					            request: CreateDealsFromExcelRequest,
 | 
				
			||||||
 | 
					            user: User,
 | 
				
			||||||
 | 
					    ) -> CreateDealsFromExcelResponse:
 | 
				
			||||||
 | 
					        client: Optional[Client] = await self.session.get(Client, request.client_id)
 | 
				
			||||||
 | 
					        if not client:
 | 
				
			||||||
 | 
					            return CreateDealsFromExcelResponse(ok=False, message=f"Клиент с ID {request.client_id} не найден")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        deals_dict: dict[str, Deal] = {}
 | 
				
			||||||
 | 
					        group = await self._create_group()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for product_data in request.products:
 | 
				
			||||||
 | 
					            for breakdown in product_data.cities_breakdown:
 | 
				
			||||||
 | 
					                breakdown.shipping_warehouse = await self._get_or_create_warehouse(breakdown.shipping_warehouse)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                key = f"{breakdown.shipping_warehouse.id} - {breakdown.base_marketplace.key}"
 | 
				
			||||||
 | 
					                deal = deals_dict.get(key)
 | 
				
			||||||
 | 
					                if not deal:
 | 
				
			||||||
 | 
					                    deal = await self._create_deal_from_excel(client, breakdown, user)
 | 
				
			||||||
 | 
					                    deals_dict[key] = deal
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    insert_stmt = insert(deal_relations).values({
 | 
				
			||||||
 | 
					                        'deal_id': deal.id,
 | 
				
			||||||
 | 
					                        'group_id': group.id
 | 
				
			||||||
 | 
					                    })
 | 
				
			||||||
 | 
					                    await self.session.execute(insert_stmt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                deal_product = DealProduct(
 | 
				
			||||||
 | 
					                    deal_id=deal.id,
 | 
				
			||||||
 | 
					                    product_id=product_data.product_id,
 | 
				
			||||||
 | 
					                    quantity=breakdown.quantity,
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					                self.session.add(deal_product)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.session.commit()
 | 
				
			||||||
 | 
					        return CreateDealsFromExcelResponse(ok=True, message="Сделки успешно созданы")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def add_to_group(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
 | 
					    async def add_to_group(self, user: User, request: DealAddToGroupRequest) -> DealAddToGroupResponse:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
 | 
					            group_bill_request = await self.session.get(GroupBillRequest, request.group_id)
 | 
				
			||||||
@@ -1254,14 +1340,8 @@ class DealService(BaseService):
 | 
				
			|||||||
        try:
 | 
					        try:
 | 
				
			||||||
            # getting lexorank for grop
 | 
					            # getting lexorank for grop
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            group = models.DealGroup(
 | 
					            group = await self._create_group()
 | 
				
			||||||
                name='',
 | 
					 | 
				
			||||||
                lexorank=lexorank.middle(lexorank.Bucket.BUCEKT_0).__str__(),
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            self.session.add(group)
 | 
					 | 
				
			||||||
            await self.session.flush()
 | 
					 | 
				
			||||||
            for deal_id in [request.dragging_deal_id, request.hovered_deal_id]:
 | 
					            for deal_id in [request.dragging_deal_id, request.hovered_deal_id]:
 | 
				
			||||||
                insert_stmt = insert(deal_relations).values({
 | 
					                insert_stmt = insert(deal_relations).values({
 | 
				
			||||||
                    'deal_id': deal_id,
 | 
					                    'deal_id': deal_id,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user