feat: bill of payment for a group of deals
This commit is contained in:
@@ -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")
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user