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 product in deal.products:
 | 
					        for deal in deals:
 | 
				
			||||||
            product_price = 0
 | 
					            for product in deal.products:
 | 
				
			||||||
            for service in product.services:
 | 
					                product_price = 0
 | 
				
			||||||
                services.append(
 | 
					                for service in product.services:
 | 
				
			||||||
                    ServiceBillingDocumentPdf(
 | 
					                    service_data = 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:
 | 
				
			||||||
            is_size_needed = is_size_needed | bool(product.product.size)
 | 
					                        services[key].quantity += product.quantity
 | 
				
			||||||
            products.append(
 | 
					                    else:
 | 
				
			||||||
                ProductBillingDocumentPdf(
 | 
					                        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,
 | 
					                    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)
 | 
				
			||||||
        for service in deal.services:
 | 
					                if product_key in products:
 | 
				
			||||||
            services.append(
 | 
					                    products[product_key].quantity += product_data.quantity
 | 
				
			||||||
                ServiceBillingDocumentPdf(
 | 
					                else:
 | 
				
			||||||
                    name=f'{service.service.name}',
 | 
					                    products[product_key] = product_data
 | 
				
			||||||
 | 
					            for service in deal.services:
 | 
				
			||||||
 | 
					                service_data = ServiceBillingDocumentPdf(
 | 
				
			||||||
 | 
					                    name=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