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 io import BytesIO
from typing import List
from uuid import uuid4
from fastapi import HTTPException
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, \
BillStatusUpdateRequest, NotificationChannel, NotifyReceivedBillRequestSchema, DeleteBillRequestSchema, \
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 services.base import BaseService
from services.deal import DealService
@@ -170,43 +171,60 @@ class BillingService(BaseService):
except Exception as e:
return CancelDealBillResponse(ok=False, message=str(e))
async def _get_products_for_deal(self, deal: Deal) -> tuple[list, list, bool]:
services: list[ServiceBillingDocumentPdf] = []
products: list[ProductBillingDocumentPdf] = []
def _gen_key_for_service(self, service: ServiceBillingDocumentPdf) -> str:
return f"{service.name}-{service.price}"
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
for product in deal.products:
product_price = 0
for service in product.services:
services.append(
ServiceBillingDocumentPdf(
for deal in deals:
for product in deal.products:
product_price = 0
for service in product.services:
service_data = ServiceBillingDocumentPdf(
name=f'[{product.product.name}] - {service.service.name}',
price=service.price,
quantity=product.quantity
)
)
product_price += service.price
is_size_needed = is_size_needed | bool(product.product.size)
products.append(
ProductBillingDocumentPdf(
key = self._gen_key_for_service(service_data)
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)
product_data = ProductBillingDocumentPdf(
article=product.product.article,
size=product.product.size if product.product.size else "",
price=product_price,
quantity=product.quantity,
)
)
for service in deal.services:
services.append(
ServiceBillingDocumentPdf(
name=f'{service.service.name}',
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:
service_data = ServiceBillingDocumentPdf(
name=service.service.name,
price=service.price,
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
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(
select(Deal)
.where(Deal.id == deal_id)
@@ -215,15 +233,38 @@ class BillingService(BaseService):
selectinload(Deal.services).selectinload(DealServiceModel.service),
joinedload(Deal.shipping_warehouse),
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:
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 = to_locale_number(deal_price)
template = ENV.get_template("bill-of-payment.html")

View File

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