feat: bill of payment for a group of deals

This commit is contained in:
2024-11-10 16:55:06 +04:00
parent 915a1d5f28
commit eff71b3570
2 changed files with 66 additions and 25 deletions

View File

@@ -1,6 +1,7 @@
from collections import defaultdict from collections import defaultdict
from io import BytesIO from io import BytesIO
from typing import List from typing import List
from uuid import uuid4
from fastapi import HTTPException from fastapi import HTTPException
from number_to_string import get_string_by_number from number_to_string import get_string_by_number
@@ -16,7 +17,7 @@ from constants import MONTHS, ENV
from external.billing import BillingClient, CreateBillingRequestValue, CreateBillRequestSchema, CreateBillRequestItems, \ from external.billing import BillingClient, CreateBillingRequestValue, CreateBillRequestSchema, CreateBillRequestItems, \
BillStatusUpdateRequest, NotificationChannel, NotifyReceivedBillRequestSchema, DeleteBillRequestSchema, \ BillStatusUpdateRequest, NotificationChannel, NotifyReceivedBillRequestSchema, DeleteBillRequestSchema, \
ProductBillingDocumentPdf, ServiceBillingDocumentPdf ProductBillingDocumentPdf, ServiceBillingDocumentPdf
from models import DealBillRequest, Deal, DealProduct, DealService as DealServiceModel, DealProductService from models import DealBillRequest, Deal, DealProduct, DealService as DealServiceModel, DealProductService, DealGroup
from schemas.billing import * from schemas.billing import *
from services.base import BaseService from services.base import BaseService
from services.deal import DealService from services.deal import DealService
@@ -170,43 +171,60 @@ class BillingService(BaseService):
except Exception as e: except Exception as e:
return CancelDealBillResponse(ok=False, message=str(e)) return CancelDealBillResponse(ok=False, message=str(e))
async def _get_products_for_deal(self, deal: Deal) -> tuple[list, list, bool]: def _gen_key_for_service(self, service: ServiceBillingDocumentPdf) -> str:
services: list[ServiceBillingDocumentPdf] = [] return f"{service.name}-{service.price}"
products: list[ProductBillingDocumentPdf] = []
def _gen_key_for_product(self, product: ProductBillingDocumentPdf) -> str:
article = product.article if product.article else uuid4()
return f"{article}-{product.size}-{product.price}"
async def _get_products_for_deal(self, deals: list[Deal]) -> tuple[dict, dict, bool]:
services: dict[str, ServiceBillingDocumentPdf] = {}
products: dict[str, ProductBillingDocumentPdf] = {}
is_size_needed: bool = False is_size_needed: bool = False
for deal in deals:
for product in deal.products: for product in deal.products:
product_price = 0 product_price = 0
for service in product.services: for service in product.services:
services.append( service_data = ServiceBillingDocumentPdf(
ServiceBillingDocumentPdf(
name=f'[{product.product.name}] - {service.service.name}', name=f'[{product.product.name}] - {service.service.name}',
price=service.price, price=service.price,
quantity=product.quantity quantity=product.quantity
) )
) key = self._gen_key_for_service(service_data)
product_price += service.price if key in services:
services[key].quantity += product.quantity
else:
services[key] = service_data
product_price += service_data.price
is_size_needed = is_size_needed | bool(product.product.size) is_size_needed = is_size_needed | bool(product.product.size)
products.append( product_data = ProductBillingDocumentPdf(
ProductBillingDocumentPdf(
article=product.product.article, article=product.product.article,
size=product.product.size if product.product.size else "", size=product.product.size if product.product.size else "",
price=product_price, price=product_price,
quantity=product.quantity, quantity=product.quantity,
) )
) product_key = self._gen_key_for_product(product_data)
if product_key in products:
products[product_key].quantity += product_data.quantity
else:
products[product_key] = product_data
for service in deal.services: for service in deal.services:
services.append( service_data = ServiceBillingDocumentPdf(
ServiceBillingDocumentPdf( name=service.service.name,
name=f'{service.service.name}',
price=service.price, price=service.price,
quantity=service.quantity quantity=service.quantity
) )
) key = self._gen_key_for_service(service_data)
if key in services:
services[key].quantity += service_data.quantity
else:
services[key] = service_data
return services, products, is_size_needed return services, products, is_size_needed
async def _create_billing_document_html(self, deal_id: int): async def _get_deal_by_id(self, deal_id: int) -> Optional[Deal]:
deal: Deal | None = await self.session.scalar( deal: Deal | None = await self.session.scalar(
select(Deal) select(Deal)
.where(Deal.id == deal_id) .where(Deal.id == deal_id)
@@ -215,15 +233,38 @@ class BillingService(BaseService):
selectinload(Deal.services).selectinload(DealServiceModel.service), selectinload(Deal.services).selectinload(DealServiceModel.service),
joinedload(Deal.shipping_warehouse), joinedload(Deal.shipping_warehouse),
joinedload(Deal.client), joinedload(Deal.client),
selectinload(Deal.group).selectinload(DealGroup.deals),
) )
) )
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.services).selectinload(DealServiceModel.service),
selectinload(DealGroup.deals).joinedload(Deal.shipping_warehouse),
selectinload(DealGroup.deals).joinedload(Deal.client),
selectinload(DealGroup.deals).selectinload(Deal.group).selectinload(DealGroup.deals),
)
)
return group.deals if group else []
async def _create_billing_document_html(self, deal_id: int):
deal = await self._get_deal_by_id(deal_id)
if not deal: if not deal:
return "" return ""
(services, products, is_size_needed) = await self._get_products_for_deal(deal) if deal.group:
deals = await self._get_deals_by_group_id(deal.group.id)
else:
deals = [deal]
deal_price = sum((service.price * service.quantity for service in services)) (services, products, is_size_needed) = await self._get_products_for_deal(deals)
deal_price = sum((service.price * service.quantity for service in services.values()))
deal_price_words = get_string_by_number(deal_price)[0:-10] deal_price_words = get_string_by_number(deal_price)[0:-10]
deal_price = to_locale_number(deal_price) deal_price = to_locale_number(deal_price)
template = ENV.get_template("bill-of-payment.html") template = ENV.get_template("bill-of-payment.html")

View File

@@ -53,7 +53,7 @@
</tfoot> </tfoot>
</thead> </thead>
<tbody> <tbody>
{% for product in products %} {% for product in products.values() %}
<tr> <tr>
<td>{{ product.article }}</td> <td>{{ product.article }}</td>
{% if is_size_needed %} {% if is_size_needed %}
@@ -78,7 +78,7 @@
</tfoot> </tfoot>
</thead> </thead>
<tbody> <tbody>
{% for service in services %} {% for service in services.values() %}
<tr> <tr>
<td>{{ service.name }}</td> <td>{{ service.name }}</td>
<td>{{ '{:,}'.format(service.quantity) }} шт.</td> <td>{{ '{:,}'.format(service.quantity) }} шт.</td>