feat: split bills for one deal
This commit is contained in:
		@@ -24,7 +24,9 @@ TELEGRAM_BOT_TOKEN = os.environ.get('TELEGRAM_BOT_TOKEN')
 | 
				
			|||||||
SECRET_KEY = os.environ.get('SECRET_KEY')
 | 
					SECRET_KEY = os.environ.get('SECRET_KEY')
 | 
				
			||||||
S3_API_KEY = os.environ.get('S3_API_KEY')
 | 
					S3_API_KEY = os.environ.get('S3_API_KEY')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Billing
 | 
				
			||||||
BILLING_API_KEY = os.environ.get('BILLING_API_KEY')
 | 
					BILLING_API_KEY = os.environ.get('BILLING_API_KEY')
 | 
				
			||||||
 | 
					BILLING_URL = os.environ.get('BILLING_URL')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CHAT_TELEGRAM_BOT_TOKEN = os.environ.get('CHAT_TELEGRAM_BOT_TOKEN')
 | 
					CHAT_TELEGRAM_BOT_TOKEN = os.environ.get('CHAT_TELEGRAM_BOT_TOKEN')
 | 
				
			||||||
CHAT_CONNECTOR_API_KEY = os.environ.get('CHAT_CONNECTOR_API_KEY')
 | 
					CHAT_CONNECTOR_API_KEY = os.environ.get('CHAT_CONNECTOR_API_KEY')
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								external/billing/billing_client.py
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								external/billing/billing_client.py
									
									
									
									
										vendored
									
									
								
							@@ -1,5 +1,6 @@
 | 
				
			|||||||
import aiohttp
 | 
					import aiohttp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from backend.config import BILLING_URL
 | 
				
			||||||
from .schemas import *
 | 
					from .schemas import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -9,7 +10,7 @@ class BillingClient:
 | 
				
			|||||||
        self.headers = {
 | 
					        self.headers = {
 | 
				
			||||||
            'Authorization': f'Bearer {self.api_key}'
 | 
					            'Authorization': f'Bearer {self.api_key}'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        self.base_url = 'https://billing.denco.store'
 | 
					        self.base_url = BILLING_URL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _method(self, http_method, method, **kwargs):
 | 
					    async def _method(self, http_method, method, **kwargs):
 | 
				
			||||||
        async with aiohttp.ClientSession(headers=self.headers) as session:
 | 
					        async with aiohttp.ClientSession(headers=self.headers) as session:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,13 +13,13 @@ if TYPE_CHECKING:
 | 
				
			|||||||
class CardBillRequest(BaseModel):
 | 
					class CardBillRequest(BaseModel):
 | 
				
			||||||
    __tablename__ = 'card_bill_requests'
 | 
					    __tablename__ = 'card_bill_requests'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id: Mapped[int] = mapped_column(primary_key=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    card_id: Mapped[int] = mapped_column(
 | 
					    card_id: Mapped[int] = mapped_column(
 | 
				
			||||||
        ForeignKey('cards.id'),
 | 
					        ForeignKey('cards.id'),
 | 
				
			||||||
        nullable=False,
 | 
					        nullable=False,
 | 
				
			||||||
        primary_key=True,
 | 
					 | 
				
			||||||
        unique=True,
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    card: Mapped['Card'] = relationship(back_populates='bill_request')
 | 
					    card: Mapped['Card'] = relationship(back_populates='bill_requests')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
 | 
					    created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
 | 
				
			||||||
    paid: Mapped[bool] = mapped_column(nullable=False, default=False)
 | 
					    paid: Mapped[bool] = mapped_column(nullable=False, default=False)
 | 
				
			||||||
@@ -31,13 +31,13 @@ class CardBillRequest(BaseModel):
 | 
				
			|||||||
class GroupBillRequest(BaseModel):
 | 
					class GroupBillRequest(BaseModel):
 | 
				
			||||||
    __tablename__ = 'group_bill_requests'
 | 
					    __tablename__ = 'group_bill_requests'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    id: Mapped[int] = mapped_column(primary_key=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    group_id: Mapped[int] = mapped_column(
 | 
					    group_id: Mapped[int] = mapped_column(
 | 
				
			||||||
        ForeignKey('card_groups.id'),
 | 
					        ForeignKey('card_groups.id'),
 | 
				
			||||||
        nullable=False,
 | 
					        nullable=False,
 | 
				
			||||||
        primary_key=True,
 | 
					 | 
				
			||||||
        unique=True,
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    group: Mapped['CardGroup'] = relationship(back_populates='bill_request')
 | 
					    group: Mapped['CardGroup'] = relationship(back_populates='bill_requests')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
 | 
					    created_at: Mapped[datetime.datetime] = mapped_column(nullable=False)
 | 
				
			||||||
    paid: Mapped[bool] = mapped_column(nullable=False, default=False)
 | 
					    paid: Mapped[bool] = mapped_column(nullable=False, default=False)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,7 +93,11 @@ class Card(BaseModel):
 | 
				
			|||||||
        order_by="desc(CardProduct.product_id)"
 | 
					        order_by="desc(CardProduct.product_id)"
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bill_request: Mapped[Optional['CardBillRequest']] = relationship(back_populates='card', lazy='joined')
 | 
					    bill_requests: Mapped[list['CardBillRequest']] = relationship(
 | 
				
			||||||
 | 
					        back_populates='card',
 | 
				
			||||||
 | 
					        lazy='selectin',
 | 
				
			||||||
 | 
					        uselist=True,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # module client
 | 
					    # module client
 | 
				
			||||||
    client_id: Mapped[Optional[int]] = mapped_column(
 | 
					    client_id: Mapped[Optional[int]] = mapped_column(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,9 +25,10 @@ class CardGroup(BaseModel):
 | 
				
			|||||||
        back_populates='group',
 | 
					        back_populates='group',
 | 
				
			||||||
        secondary='card_relations'
 | 
					        secondary='card_relations'
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    bill_request: Mapped[Optional['GroupBillRequest']] = relationship(
 | 
					    bill_requests: Mapped[list['GroupBillRequest']] = relationship(
 | 
				
			||||||
        back_populates='group',
 | 
					        back_populates='group',
 | 
				
			||||||
        lazy='joined'
 | 
					        lazy='joined',
 | 
				
			||||||
 | 
					        uselist=True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -45,9 +45,8 @@ async def create_deal_bill(
 | 
				
			|||||||
async def cancel_deal_billing(
 | 
					async def cancel_deal_billing(
 | 
				
			||||||
        session: SessionDependency,
 | 
					        session: SessionDependency,
 | 
				
			||||||
        request: CancelCardBillRequest,
 | 
					        request: CancelCardBillRequest,
 | 
				
			||||||
        user: CurrentUserDependency
 | 
					 | 
				
			||||||
):
 | 
					):
 | 
				
			||||||
    return await BillingService(session).cancel_card_billing(user, request)
 | 
					    return await BillingService(session).cancel_card_billing(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@billing_router.get(
 | 
					@billing_router.get(
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,7 +30,6 @@ class CreateCardBillRequest(BaseSchema):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class CancelCardBillRequest(BaseSchema):
 | 
					class CancelCardBillRequest(BaseSchema):
 | 
				
			||||||
    card_id: int
 | 
					    card_id: int
 | 
				
			||||||
    force: Optional[bool] = False
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# endregion
 | 
					# endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,7 +49,7 @@ class CardSummary(BaseSchema):
 | 
				
			|||||||
    shipment_warehouse_id: Optional[int]
 | 
					    shipment_warehouse_id: Optional[int]
 | 
				
			||||||
    shipment_warehouse_name: Optional[str]
 | 
					    shipment_warehouse_name: Optional[str]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bill_request: Optional[CardBillRequestSchema] = None
 | 
					    bill_requests: list[CardBillRequestSchema]
 | 
				
			||||||
    group: Optional[CardGroupSchema] = None
 | 
					    group: Optional[CardGroupSchema] = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -98,7 +98,7 @@ class BaseCardSchema(BaseSchema):
 | 
				
			|||||||
    client_id: Optional[int]
 | 
					    client_id: Optional[int]
 | 
				
			||||||
    client: Optional[ClientSchema]
 | 
					    client: Optional[ClientSchema]
 | 
				
			||||||
    shipping_warehouse: Optional[Union[ShippingWarehouseSchema, str]] = None
 | 
					    shipping_warehouse: Optional[Union[ShippingWarehouseSchema, str]] = None
 | 
				
			||||||
    bill_request: Optional[CardBillRequestSchema] = None
 | 
					    bill_requests: list[CardBillRequestSchema] = []
 | 
				
			||||||
    group: Optional[CardGroupSchema] = None
 | 
					    group: Optional[CardGroupSchema] = None
 | 
				
			||||||
    manager: Optional[UserSchema] = None
 | 
					    manager: Optional[UserSchema] = None
 | 
				
			||||||
    pallets: List[PalletSchema] = []
 | 
					    pallets: List[PalletSchema] = []
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,7 @@ class CardGroupSchema(BaseSchema):
 | 
				
			|||||||
    id: int
 | 
					    id: int
 | 
				
			||||||
    name: Optional[str] = None
 | 
					    name: Optional[str] = None
 | 
				
			||||||
    lexorank: str
 | 
					    lexorank: str
 | 
				
			||||||
    bill_request: Optional[GroupBillRequestSchema] = None
 | 
					    bill_requests: list[GroupBillRequestSchema] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# endregion
 | 
					# endregion
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,8 +41,8 @@ class BillingService(BaseService):
 | 
				
			|||||||
        prefix = "group-"
 | 
					        prefix = "group-"
 | 
				
			||||||
        if not request.listener_transaction_id.startswith(prefix):
 | 
					        if not request.listener_transaction_id.startswith(prefix):
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        group_id = int(request.listener_transaction_id.removeprefix(prefix))
 | 
					        request_id = int(request.listener_transaction_id.removeprefix(prefix))
 | 
				
			||||||
        group_bill_request = await self._get_group_bill_by_id(group_id)
 | 
					        group_bill_request = await self._get_group_bill_by_id(request_id)
 | 
				
			||||||
        if not group_bill_request:
 | 
					        if not group_bill_request:
 | 
				
			||||||
            return
 | 
					            return
 | 
				
			||||||
        group_bill_request.pdf_url = request.info.pdf_url
 | 
					        group_bill_request.pdf_url = request.info.pdf_url
 | 
				
			||||||
@@ -117,24 +117,43 @@ class BillingService(BaseService):
 | 
				
			|||||||
        elif request.channel == NotificationChannel.PAYMENT_VERIFICATION:
 | 
					        elif request.channel == NotificationChannel.PAYMENT_VERIFICATION:
 | 
				
			||||||
            await self._process_update_verification(request)
 | 
					            await self._process_update_verification(request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def create_card_bill_request(self, card: Card):
 | 
					    async def create_card_bill_request(self, card: Card) -> CardBillRequest:
 | 
				
			||||||
        card_bill_request = CardBillRequest(
 | 
					        card_bill_request = CardBillRequest(
 | 
				
			||||||
            card_id=card.id,
 | 
					            card_id=card.id,
 | 
				
			||||||
            created_at=datetime.datetime.now()
 | 
					            created_at=datetime.datetime.now()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.session.add(card_bill_request)
 | 
					        self.session.add(card_bill_request)
 | 
				
			||||||
        card.is_locked = True
 | 
					        await self.session.flush()
 | 
				
			||||||
        await self.session.commit()
 | 
					        return card_bill_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def create_group_bill_request(self, group: CardGroup):
 | 
					    async def create_group_bill_request(self, group: CardGroup) -> GroupBillRequest:
 | 
				
			||||||
        group_bill_request = GroupBillRequest(
 | 
					        group_bill_request = GroupBillRequest(
 | 
				
			||||||
            group_id=group.id,
 | 
					            group_id=group.id,
 | 
				
			||||||
            created_at=datetime.datetime.now()
 | 
					            created_at=datetime.datetime.now()
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        self.session.add(group_bill_request)
 | 
					        self.session.add(group_bill_request)
 | 
				
			||||||
        for card in group.cards:
 | 
					
 | 
				
			||||||
            card.is_locked = True
 | 
					        await self.session.flush()
 | 
				
			||||||
        await self.session.commit()
 | 
					        return group_bill_request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def _get_billing_values_from_services(services) -> List[
 | 
				
			||||||
 | 
					        CreateBillingRequestValue
 | 
				
			||||||
 | 
					    ]:
 | 
				
			||||||
 | 
					        billing_request_values: List[CreateBillingRequestValue] = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for service in services:
 | 
				
			||||||
 | 
					            # Omit services that have no price
 | 
				
			||||||
 | 
					            if not service.price:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
 | 
					            billing_request_values.append(
 | 
				
			||||||
 | 
					                CreateBillingRequestValue(
 | 
				
			||||||
 | 
					                    name=service.name,
 | 
				
			||||||
 | 
					                    price=service.price,
 | 
				
			||||||
 | 
					                    amount=service.quantity
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					        return billing_request_values
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def create_card_billing(self, user, request: CreateCardBillRequest) -> CreateCardBillResponse:
 | 
					    async def create_card_billing(self, user, request: CreateCardBillRequest) -> CreateCardBillResponse:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
@@ -151,56 +170,79 @@ class BillingService(BaseService):
 | 
				
			|||||||
            services: dict[str, ServiceBillingDocumentPdf]
 | 
					            services: dict[str, ServiceBillingDocumentPdf]
 | 
				
			||||||
            products: dict[str, ProductBillingDocumentPdf]
 | 
					            products: dict[str, ProductBillingDocumentPdf]
 | 
				
			||||||
            is_size_needed: bool
 | 
					            is_size_needed: bool
 | 
				
			||||||
            billing_request_values: List[CreateBillingRequestValue] = []
 | 
					            billing_request_values: List[CreateBillingRequestValue] = self._get_billing_values_from_services(
 | 
				
			||||||
 | 
					                services.values()
 | 
				
			||||||
            for service in services.values():
 | 
					 | 
				
			||||||
                # Omit services that have no price
 | 
					 | 
				
			||||||
                if not service.price:
 | 
					 | 
				
			||||||
                    continue
 | 
					 | 
				
			||||||
                billing_request_values.append(
 | 
					 | 
				
			||||||
                    CreateBillingRequestValue(
 | 
					 | 
				
			||||||
                        name=service.name,
 | 
					 | 
				
			||||||
                        price=service.price,
 | 
					 | 
				
			||||||
                        amount=service.quantity
 | 
					 | 
				
			||||||
                    )
 | 
					 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            card = basic_card
 | 
					            card = basic_card
 | 
				
			||||||
            listener_transaction_id = card.id
 | 
					            transaction_prefix = "group-" if card.group else ""
 | 
				
			||||||
            if card.group:
 | 
					            inn: str = card.client.details.inn.strip()
 | 
				
			||||||
                listener_transaction_id = f"group-{basic_card.group.id}"
 | 
					
 | 
				
			||||||
            inn: str = card.client.details.inn
 | 
					            one_request_size = 100
 | 
				
			||||||
 | 
					            for i in range(0, len(billing_request_values), one_request_size):
 | 
				
			||||||
 | 
					                values = billing_request_values[i:i + one_request_size]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if basic_card.group:
 | 
				
			||||||
 | 
					                    bill_request = await self.create_group_bill_request(basic_card.group)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    bill_request = await self.create_card_bill_request(basic_card)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                listener_transaction_id = transaction_prefix + str(bill_request.id)
 | 
				
			||||||
                create_bill_request = CreateBillRequestSchema(
 | 
					                create_bill_request = CreateBillRequestSchema(
 | 
				
			||||||
                    listener_transaction_id=listener_transaction_id,
 | 
					                    listener_transaction_id=listener_transaction_id,
 | 
				
			||||||
                    payer_name=card.client.name,
 | 
					                    payer_name=card.client.name,
 | 
				
			||||||
                payer_inn=inn.strip(),
 | 
					                    payer_inn=inn,
 | 
				
			||||||
                    payer_phone=card.client.details.phone_number,
 | 
					                    payer_phone=card.client.details.phone_number,
 | 
				
			||||||
                    items=CreateBillRequestItems(
 | 
					                    items=CreateBillRequestItems(
 | 
				
			||||||
                    values=billing_request_values
 | 
					                        values=values,
 | 
				
			||||||
                    )
 | 
					                    )
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
                create_bill_response = await billing_client.create(create_bill_request)
 | 
					                create_bill_response = await billing_client.create(create_bill_request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if not create_bill_response.ok and 'Request already exists' in create_bill_response.message:
 | 
					                if not create_bill_response.ok and 'Request already exists' in create_bill_response.message:
 | 
				
			||||||
                await self.cancel_card_billing(user, CancelCardBillRequest(card_id=request.card_id, force=True))
 | 
					                    await self._cancel_one_bill(bill_request, listener_transaction_id)
 | 
				
			||||||
                return await self.create_card_billing(user, request)
 | 
					                    create_bill_response = await billing_client.create(create_bill_request)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if not create_bill_response.ok:
 | 
					                if not create_bill_response.ok:
 | 
				
			||||||
                return CreateCardBillResponse(ok=create_bill_response.ok,
 | 
					                    raise Exception(create_bill_response.message or 'Неизвестная ошибка')
 | 
				
			||||||
                                              message=create_bill_response.message or 'Неизвестная ошибка')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if basic_card.group:
 | 
					            if card.group:
 | 
				
			||||||
                await self.create_group_bill_request(basic_card.group)
 | 
					                for card in card.group.cards:
 | 
				
			||||||
 | 
					                    card.is_locked = True
 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                await self.create_card_bill_request(basic_card)
 | 
					                card.is_locked = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return CreateCardBillResponse(ok=create_bill_response.ok, message='Заявка успешно создана!')
 | 
					            await self.session.commit()
 | 
				
			||||||
 | 
					            return CreateCardBillResponse(ok=True, message='Заявка успешно создана!')
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            await self.session.rollback()
 | 
				
			||||||
            return CreateCardBillResponse(ok=False, message=str(e))
 | 
					            return CreateCardBillResponse(ok=False, message=str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _get_card_bill_by_id(self, card_id: int) -> Optional[CardBillRequest]:
 | 
					    async def _get_card_bill_by_id(self, request_id: int) -> Optional[CardBillRequest]:
 | 
				
			||||||
        return await self.session.scalar(select(CardBillRequest).where(CardBillRequest.card_id == card_id))
 | 
					        return await self.session.scalar(
 | 
				
			||||||
 | 
					            select(CardBillRequest)
 | 
				
			||||||
 | 
					            .where(CardBillRequest.id == request_id)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def _get_group_bill_by_id(self, group_id: int) -> Optional[GroupBillRequest]:
 | 
					    async def _get_card_bill_by_card_id(self, card_id: int) -> List[CardBillRequest]:
 | 
				
			||||||
        return await self.session.scalar(select(GroupBillRequest).where(GroupBillRequest.group_id == group_id))
 | 
					        result = await self.session.scalars(
 | 
				
			||||||
 | 
					            select(CardBillRequest)
 | 
				
			||||||
 | 
					            .where(CardBillRequest.card_id == card_id)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return [*result.all()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _get_group_bill_by_id(self, request_id: int) -> Optional[GroupBillRequest]:
 | 
				
			||||||
 | 
					        return await self.session.scalar(
 | 
				
			||||||
 | 
					            select(GroupBillRequest)
 | 
				
			||||||
 | 
					            .where(GroupBillRequest.id == request_id)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def _get_group_bill_by_group_id(self, group_id: int) -> List[GroupBillRequest]:
 | 
				
			||||||
 | 
					        result = await self.session.scalars(
 | 
				
			||||||
 | 
					            select(GroupBillRequest)
 | 
				
			||||||
 | 
					            .where(GroupBillRequest.group_id == group_id)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        return [*result.all()]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def get_card_bill_by_id(self, card_id: int) -> GetCardBillById:
 | 
					    async def get_card_bill_by_id(self, card_id: int) -> GetCardBillById:
 | 
				
			||||||
        bill = await self._get_card_bill_by_id(card_id)
 | 
					        bill = await self._get_card_bill_by_id(card_id)
 | 
				
			||||||
@@ -208,34 +250,42 @@ class BillingService(BaseService):
 | 
				
			|||||||
            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail='Card bill was not found')
 | 
					            raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail='Card bill was not found')
 | 
				
			||||||
        return GetCardBillById(card_bill=CardBillRequestSchema.model_validate(bill))
 | 
					        return GetCardBillById(card_bill=CardBillRequestSchema.model_validate(bill))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async def cancel_card_billing(self, user, request: CancelCardBillRequest) -> CancelCardBillResponse:
 | 
					    async def _cancel_one_bill(self, bill: CardBillRequest | GroupBillRequest, listener_transaction_id: str) -> None:
 | 
				
			||||||
 | 
					        billing_client = BillingClient(backend.config.BILLING_API_KEY)
 | 
				
			||||||
 | 
					        response = await billing_client.delete(
 | 
				
			||||||
 | 
					            DeleteBillRequestSchema(listener_transaction_id=listener_transaction_id)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if not response.ok:
 | 
				
			||||||
 | 
					            raise Exception('Ошибка удаления счета со стороны Billing сервиса')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await self.session.delete(bill)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async def cancel_card_billing(self, request: CancelCardBillRequest) -> CancelCardBillResponse:
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
            card = await self._get_card_by_id(request.card_id)
 | 
					            card = await self._get_card_by_id(request.card_id)
 | 
				
			||||||
            if not card:
 | 
					            if not card:
 | 
				
			||||||
                return CancelCardBillResponse(ok=False, message='Сделка не найдена')
 | 
					                return CancelCardBillResponse(ok=False, message='Сделка не найдена')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if card.group:
 | 
					            if card.group:
 | 
				
			||||||
                bill = await self._get_group_bill_by_id(card.group.id)
 | 
					                bills = await self._get_group_bill_by_group_id(card.group.id)
 | 
				
			||||||
                if not bill and not request.force:
 | 
					 | 
				
			||||||
                    return CancelCardBillResponse(ok=False, message='Заявка не найдена')
 | 
					 | 
				
			||||||
                billing_client = BillingClient(backend.config.BILLING_API_KEY)
 | 
					 | 
				
			||||||
                listener_transaction_id = f"group-{card.group.id}"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                response = await billing_client.delete(DeleteBillRequestSchema(listener_transaction_id=listener_transaction_id))
 | 
					 | 
				
			||||||
            else:
 | 
					            else:
 | 
				
			||||||
                bill = await self._get_card_bill_by_id(request.card_id)
 | 
					                bills = await self._get_card_bill_by_card_id(request.card_id)
 | 
				
			||||||
                if not bill and not request.force:
 | 
					            if not bills:
 | 
				
			||||||
                return CancelCardBillResponse(ok=False, message='Заявка не найдена')
 | 
					                return CancelCardBillResponse(ok=False, message='Заявка не найдена')
 | 
				
			||||||
                billing_client = BillingClient(backend.config.BILLING_API_KEY)
 | 
					 | 
				
			||||||
                response = await billing_client.delete(DeleteBillRequestSchema(listener_transaction_id=request.card_id))
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if not response.ok:
 | 
					            for bill in bills:
 | 
				
			||||||
                return CancelCardBillResponse(ok=False, message='Ошибка')
 | 
					                if card.group:
 | 
				
			||||||
            if bill:
 | 
					                    listener_transaction_id = f"group-{bill.id}"
 | 
				
			||||||
                await self.session.delete(bill)
 | 
					                else:
 | 
				
			||||||
 | 
					                    listener_transaction_id = str(bill.id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                await self._cancel_one_bill(bill, listener_transaction_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await self.session.commit()
 | 
					            await self.session.commit()
 | 
				
			||||||
            return CancelCardBillResponse(ok=True, message='Заявка успешно отозвана')
 | 
					            return CancelCardBillResponse(ok=True, message='Заявка успешно отозвана')
 | 
				
			||||||
        except Exception as e:
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            await self.session.rollback()
 | 
				
			||||||
            return CancelCardBillResponse(ok=False, message=str(e))
 | 
					            return CancelCardBillResponse(ok=False, message=str(e))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _gen_key_for_service(self, service: ServiceBillingDocumentPdf) -> str:
 | 
					    def _gen_key_for_service(self, service: ServiceBillingDocumentPdf) -> str:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ import lexorank
 | 
				
			|||||||
import math
 | 
					import math
 | 
				
			||||||
from fastapi import HTTPException
 | 
					from fastapi import HTTPException
 | 
				
			||||||
from sqlalchemy import select, func, update, delete, insert, and_, Select
 | 
					from sqlalchemy import select, func, update, delete, insert, and_, Select
 | 
				
			||||||
from sqlalchemy.orm import joinedload, selectinload
 | 
					from sqlalchemy.orm import joinedload, selectinload, noload
 | 
				
			||||||
from starlette import status
 | 
					from starlette import status
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from card_attributes import CardAttributesCommandHandler
 | 
					from card_attributes import CardAttributesCommandHandler
 | 
				
			||||||
@@ -252,9 +252,10 @@ class CardsService(BaseService):
 | 
				
			|||||||
                selectinload(Card.status_history),
 | 
					                selectinload(Card.status_history),
 | 
				
			||||||
                joinedload(Card.client),
 | 
					                joinedload(Card.client),
 | 
				
			||||||
                joinedload(Card.shipping_warehouse),
 | 
					                joinedload(Card.shipping_warehouse),
 | 
				
			||||||
                joinedload(Card.bill_request),
 | 
					                selectinload(Card.bill_requests),
 | 
				
			||||||
                joinedload(Card.status),
 | 
					                joinedload(Card.status),
 | 
				
			||||||
                joinedload(Card.board),
 | 
					                joinedload(Card.board),
 | 
				
			||||||
 | 
					                joinedload(Card.group).selectinload(CardGroup.bill_requests)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .outerjoin(
 | 
					            .outerjoin(
 | 
				
			||||||
                price_subquery, Card.id == price_subquery.c.card_id,
 | 
					                price_subquery, Card.id == price_subquery.c.card_id,
 | 
				
			||||||
@@ -299,7 +300,7 @@ class CardsService(BaseService):
 | 
				
			|||||||
                    shipment_warehouse_id=card.shipping_warehouse_id,
 | 
					                    shipment_warehouse_id=card.shipping_warehouse_id,
 | 
				
			||||||
                    shipment_warehouse_name=shipment_warehouse_name,
 | 
					                    shipment_warehouse_name=shipment_warehouse_name,
 | 
				
			||||||
                    total_products=products_count,
 | 
					                    total_products=products_count,
 | 
				
			||||||
                    bill_request=card.bill_request,
 | 
					                    bill_requests=card.bill_requests,
 | 
				
			||||||
                    tags=card.tags,
 | 
					                    tags=card.tags,
 | 
				
			||||||
                    attributes=attributes,
 | 
					                    attributes=attributes,
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
@@ -335,7 +336,7 @@ class CardsService(BaseService):
 | 
				
			|||||||
                    .joinedload(CardStatusHistory.user),
 | 
					                    .joinedload(CardStatusHistory.user),
 | 
				
			||||||
                    selectinload(Card.status_history)
 | 
					                    selectinload(Card.status_history)
 | 
				
			||||||
                    .noload(CardStatusHistory.card),
 | 
					                    .noload(CardStatusHistory.card),
 | 
				
			||||||
 | 
					                    selectinload(Card.bill_requests),
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
@@ -1229,14 +1230,16 @@ class CardsService(BaseService):
 | 
				
			|||||||
    async def _recalculate_price_group(self, group: CardGroup):
 | 
					    async def _recalculate_price_group(self, group: CardGroup):
 | 
				
			||||||
        cards = await self.session.scalars(
 | 
					        cards = await self.session.scalars(
 | 
				
			||||||
            select(Card)
 | 
					            select(Card)
 | 
				
			||||||
 | 
					            .join(card_relations, Card.id == card_relations.c.card_id)
 | 
				
			||||||
            .options(
 | 
					            .options(
 | 
				
			||||||
                selectinload(Card.services)
 | 
					                selectinload(Card.services)
 | 
				
			||||||
                .joinedload(CardService.service),
 | 
					                .joinedload(CardService.service),
 | 
				
			||||||
                selectinload(Card.products)
 | 
					                selectinload(Card.products)
 | 
				
			||||||
                .selectinload(CardProduct.services)
 | 
					                .selectinload(CardProduct.services)
 | 
				
			||||||
                .joinedload(CardProductService.service),
 | 
					                .joinedload(CardProductService.service),
 | 
				
			||||||
 | 
					                noload(Card.group)
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .where(Card.group == group)
 | 
					            .where(card_relations.c.group_id == group.id)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        cards = list(cards.all())
 | 
					        cards = list(cards.all())
 | 
				
			||||||
        services_quantity = await self.get_quantity_dict(cards)
 | 
					        services_quantity = await self.get_quantity_dict(cards)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,8 +52,13 @@ class ClientService(BaseService):
 | 
				
			|||||||
    async def get_all(self) -> ClientGetAllResponse:
 | 
					    async def get_all(self) -> ClientGetAllResponse:
 | 
				
			||||||
        clients_query = await self.session.scalars(
 | 
					        clients_query = await self.session.scalars(
 | 
				
			||||||
            select(Client)
 | 
					            select(Client)
 | 
				
			||||||
            .options(joinedload(Client.details))
 | 
					            .options(
 | 
				
			||||||
            .order_by(Client.id.desc())
 | 
					                joinedload(Client.details),
 | 
				
			||||||
 | 
					                noload(Client.products),
 | 
				
			||||||
 | 
					                noload(Client.pallets),
 | 
				
			||||||
 | 
					                noload(Client.boxes),
 | 
				
			||||||
 | 
					                noload(Client.chat)
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        clients = clients_query.all()
 | 
					        clients = clients_query.all()
 | 
				
			||||||
        result = []
 | 
					        result = []
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user