Files
Fulfillment-Backend/generators/deal_pdf_generator/generator.py

93 lines
3.7 KiB
Python

from io import BytesIO
from typing import List, Dict, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload, joinedload
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 utils.images_fetcher import fetch_images
class DealTechSpecPdfGenerator:
def __init__(self, session: AsyncSession):
self._session = session
@staticmethod
async def _group_deal_products_by_products(deal_products: List[DealProduct]) -> Dict[str, DealTechSpecProductData]:
products: Dict[str, DealTechSpecProductData] = {}
additional_info: Optional[str]
for deal_product in deal_products:
# Для группировки по артикулу и услугам
key = f"{deal_product.product.article} - " + ",".join(
str(service.service_id) for service in deal_product.services
)
if key not in products:
products[key] = {
"deal_products": [deal_product],
"quantity": deal_product.quantity,
"additional_info": deal_product.product.additional_info,
}
else:
products[key]["deal_products"].append(deal_product)
products[key]["quantity"] += deal_product.quantity
if not products[key]["additional_info"]:
products[key]["additional_info"] = deal_product.product.additional_info
return products
async def _get_deal_by_id(self, deal_id: int) -> Optional[Deal]:
deal: Deal | None = await self._session.scalar(
select(Deal)
.where(Deal.id == deal_id)
.options(
selectinload(Deal.products).selectinload(DealProduct.services),
selectinload(Deal.products).selectinload(DealProduct.product).selectinload(Product.barcodes),
selectinload(Deal.services).selectinload(DealServiceModel.service),
selectinload(Deal.status_history),
joinedload(Deal.client),
joinedload(Deal.shipping_warehouse),
)
)
return deal
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 ""
products = await self._group_deal_products_by_products(deal.products)
product_urls: List[Optional[str]] = []
for product in 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,
}
template = ENV.get_template("deal/deal-tech-spec.html")
result = template.render({"data": document_deal_data, "sign_place_text": "_" * 22})
return result
async def create_deal_tech_spec_pdf(self, deal_id) -> BytesIO:
doc = await self._create_deal_tech_spec_document_html(deal_id)
pdf_file = BytesIO()
HTML(string=doc).write_pdf(pdf_file, stylesheets=[CSS(APP_PATH + '/static/css/deal-tech-spec.css')])
return pdf_file