93 lines
3.7 KiB
Python
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
|