feat: tech specification for a group of deals
This commit is contained in:
		@@ -8,13 +8,46 @@ from weasyprint import HTML, CSS
 | 
			
		||||
 | 
			
		||||
from constants import DEAL_STATUS_STR, ENV, APP_PATH
 | 
			
		||||
from generators.deal_pdf_generator.deal_data import DealTechSpecProductData, DealTechSpecData
 | 
			
		||||
from models import Deal, DealProduct, DealService as DealServiceModel, Product
 | 
			
		||||
from models import Deal, DealProduct, DealService as DealServiceModel, Product, DealGroup
 | 
			
		||||
from utils.images_fetcher import fetch_images
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Генерация ключа для группировки deal_product по артикулу и услугам
 | 
			
		||||
def _gen_key_for_product(deal_product: DealProduct) -> str:
 | 
			
		||||
    return f"{deal_product.product.article} - " + ",".join(
 | 
			
		||||
        str(service.service_id) for service in deal_product.services
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Генерация ключа для группировки deal_product из группы сделок по артикулу, сервисам, а также товарам
 | 
			
		||||
def _regen_key_for_product(product_data: DealTechSpecProductData) -> str:
 | 
			
		||||
    if len(product_data['deal_products']) == 0:
 | 
			
		||||
        return ""
 | 
			
		||||
 | 
			
		||||
    article = product_data['deal_products'][0].product.article
 | 
			
		||||
    services_ids = ",".join(str(service.service_id) for service in product_data['deal_products'][0].services)
 | 
			
		||||
 | 
			
		||||
    if len(product_data['deal_products']) == 1:
 | 
			
		||||
        products = product_data['deal_products'][0].product_id
 | 
			
		||||
    else:
 | 
			
		||||
        products = ",".join(
 | 
			
		||||
            f"{deal_product.product_id}-{deal_product.quantity}" for deal_product in product_data['deal_products']
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    return f"{article}+{services_ids}+{products}"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class DealTechSpecPdfGenerator:
 | 
			
		||||
    def __init__(self, session: AsyncSession):
 | 
			
		||||
        self._session = session
 | 
			
		||||
        self.deal_doc: DealTechSpecData = {
 | 
			
		||||
            "deals": [],
 | 
			
		||||
            "products": {},
 | 
			
		||||
            "product_images": (),
 | 
			
		||||
            "deal_ids_header": "",
 | 
			
		||||
            "deal_status_str": DEAL_STATUS_STR,
 | 
			
		||||
        }
 | 
			
		||||
        self.deal: Deal
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    async def _group_deal_products_by_products(deal_products: List[DealProduct]) -> Dict[str, DealTechSpecProductData]:
 | 
			
		||||
@@ -23,15 +56,15 @@ class DealTechSpecPdfGenerator:
 | 
			
		||||
 | 
			
		||||
        for deal_product in deal_products:
 | 
			
		||||
            # Для группировки по артикулу и услугам
 | 
			
		||||
            key = f"{deal_product.product.article} - " + ",".join(
 | 
			
		||||
                str(service.service_id) for service in deal_product.services
 | 
			
		||||
            )
 | 
			
		||||
            key = _gen_key_for_product(deal_product)
 | 
			
		||||
 | 
			
		||||
            if key not in products:
 | 
			
		||||
                products[key] = {
 | 
			
		||||
                    "deal": deal_product.deal,
 | 
			
		||||
                    "deal_products": [deal_product],
 | 
			
		||||
                    "quantity": deal_product.quantity,
 | 
			
		||||
                    "additional_info": deal_product.product.additional_info,
 | 
			
		||||
                    "warehouses_and_quantities": [],
 | 
			
		||||
                }
 | 
			
		||||
            else:
 | 
			
		||||
                products[key]["deal_products"].append(deal_product)
 | 
			
		||||
@@ -50,39 +83,72 @@ class DealTechSpecPdfGenerator:
 | 
			
		||||
                selectinload(Deal.products).selectinload(DealProduct.product).selectinload(Product.barcodes),
 | 
			
		||||
                selectinload(Deal.services).selectinload(DealServiceModel.service),
 | 
			
		||||
                selectinload(Deal.status_history),
 | 
			
		||||
                selectinload(Deal.group).selectinload(DealGroup.deals),
 | 
			
		||||
                joinedload(Deal.client),
 | 
			
		||||
                joinedload(Deal.shipping_warehouse),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        return deal
 | 
			
		||||
 | 
			
		||||
    async def _get_deals_by_group_id(self, group_id: int) -> List[Deal]:
 | 
			
		||||
        group: DealGroup | None = await self._session.scalar(
 | 
			
		||||
            select(DealGroup)
 | 
			
		||||
            .where(DealGroup.id == group_id)
 | 
			
		||||
            .options(
 | 
			
		||||
                selectinload(DealGroup.deals).selectinload(Deal.products).selectinload(DealProduct.services),
 | 
			
		||||
                selectinload(DealGroup.deals).selectinload(Deal.products)
 | 
			
		||||
                .selectinload(DealProduct.product).selectinload(Product.barcodes),
 | 
			
		||||
                selectinload(DealGroup.deals).selectinload(Deal.services).selectinload(DealServiceModel.service),
 | 
			
		||||
                selectinload(DealGroup.deals).selectinload(Deal.status_history),
 | 
			
		||||
                selectinload(DealGroup.deals).selectinload(Deal.group).selectinload(DealGroup.deals),
 | 
			
		||||
                selectinload(DealGroup.deals).joinedload(Deal.client),
 | 
			
		||||
                selectinload(DealGroup.deals).joinedload(Deal.shipping_warehouse),
 | 
			
		||||
            )
 | 
			
		||||
        )
 | 
			
		||||
        return group.deals if group else []
 | 
			
		||||
 | 
			
		||||
    def _set_deals_ids_header(self):
 | 
			
		||||
        self.deal_doc["deal_ids_header"] = f"ID: {self.deal.id}"
 | 
			
		||||
        if self.deal.group:
 | 
			
		||||
            self.deal_doc["deal_ids_header"] = "ID: " + ", ".join(str(d.id) for d in self.deal.group.deals)
 | 
			
		||||
 | 
			
		||||
    async def _create_deal_tech_spec_document_html(self, deal_id: int):
 | 
			
		||||
        deal = await self._get_deal_by_id(deal_id)
 | 
			
		||||
 | 
			
		||||
        if not deal:
 | 
			
		||||
            return ""
 | 
			
		||||
        self.deal = deal
 | 
			
		||||
 | 
			
		||||
        products = await self._group_deal_products_by_products(deal.products)
 | 
			
		||||
        self._set_deals_ids_header()
 | 
			
		||||
 | 
			
		||||
        if deal.group:
 | 
			
		||||
            deals = await self._get_deals_by_group_id(deal.group.id)
 | 
			
		||||
            for d in deals:
 | 
			
		||||
                self.deal_doc["deals"].append(d)
 | 
			
		||||
                grouped_products = await self._group_deal_products_by_products(d.products)
 | 
			
		||||
                for product in grouped_products.values():
 | 
			
		||||
                    key = _regen_key_for_product(product)
 | 
			
		||||
                    if key not in self.deal_doc["products"]:
 | 
			
		||||
                        self.deal_doc["products"][key] = product
 | 
			
		||||
                    else:
 | 
			
		||||
                        self.deal_doc["products"][key]["quantity"] += product["quantity"]
 | 
			
		||||
                    self.deal_doc["products"][key]["warehouses_and_quantities"].append((
 | 
			
		||||
                        product["deal"].shipping_warehouse.name, product["quantity"],
 | 
			
		||||
                    ))
 | 
			
		||||
        else:
 | 
			
		||||
            self.deal_doc["deals"] = [deal]
 | 
			
		||||
            self.deal_doc["products"] = await self._group_deal_products_by_products(deal.products)
 | 
			
		||||
 | 
			
		||||
        product_urls: List[Optional[str]] = []
 | 
			
		||||
        for product in products.values():
 | 
			
		||||
        for product in self.deal_doc["products"].values():
 | 
			
		||||
            if len(product["deal_products"][0].product.images) > 0:
 | 
			
		||||
                product_urls.append(product["deal_products"][0].product.images[0].image_url)
 | 
			
		||||
            else:
 | 
			
		||||
                product_urls.append(None)
 | 
			
		||||
        product_images = await fetch_images(product_urls)
 | 
			
		||||
 | 
			
		||||
        document_deal_data: DealTechSpecData = {
 | 
			
		||||
            "deal": deal,
 | 
			
		||||
            "products": products,
 | 
			
		||||
            "current_status_str": DEAL_STATUS_STR[deal.current_status],
 | 
			
		||||
            "last_status": max(deal.status_history, key=lambda status: status.changed_at),
 | 
			
		||||
            "product_images": product_images,
 | 
			
		||||
        }
 | 
			
		||||
        self.deal_doc["product_images"] = await fetch_images(product_urls)
 | 
			
		||||
 | 
			
		||||
        template = ENV.get_template("deal/deal-tech-spec.html")
 | 
			
		||||
 | 
			
		||||
        result = template.render({"data": document_deal_data, "sign_place_text": "_" * 22})
 | 
			
		||||
        result = template.render({"data": self.deal_doc, "sign_place_text": "_" * 22})
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    async def create_deal_tech_spec_pdf(self, deal_id) -> BytesIO:
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user