feat: split bills for one deal
This commit is contained in:
@@ -41,8 +41,8 @@ class BillingService(BaseService):
|
||||
prefix = "group-"
|
||||
if not request.listener_transaction_id.startswith(prefix):
|
||||
return
|
||||
group_id = int(request.listener_transaction_id.removeprefix(prefix))
|
||||
group_bill_request = await self._get_group_bill_by_id(group_id)
|
||||
request_id = int(request.listener_transaction_id.removeprefix(prefix))
|
||||
group_bill_request = await self._get_group_bill_by_id(request_id)
|
||||
if not group_bill_request:
|
||||
return
|
||||
group_bill_request.pdf_url = request.info.pdf_url
|
||||
@@ -117,24 +117,43 @@ class BillingService(BaseService):
|
||||
elif request.channel == NotificationChannel.PAYMENT_VERIFICATION:
|
||||
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_id=card.id,
|
||||
created_at=datetime.datetime.now()
|
||||
)
|
||||
self.session.add(card_bill_request)
|
||||
card.is_locked = True
|
||||
await self.session.commit()
|
||||
await self.session.flush()
|
||||
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_id=group.id,
|
||||
created_at=datetime.datetime.now()
|
||||
)
|
||||
self.session.add(group_bill_request)
|
||||
for card in group.cards:
|
||||
card.is_locked = True
|
||||
await self.session.commit()
|
||||
|
||||
await self.session.flush()
|
||||
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:
|
||||
try:
|
||||
@@ -151,56 +170,79 @@ class BillingService(BaseService):
|
||||
services: dict[str, ServiceBillingDocumentPdf]
|
||||
products: dict[str, ProductBillingDocumentPdf]
|
||||
is_size_needed: bool
|
||||
billing_request_values: List[CreateBillingRequestValue] = []
|
||||
|
||||
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
|
||||
)
|
||||
)
|
||||
billing_request_values: List[CreateBillingRequestValue] = self._get_billing_values_from_services(
|
||||
services.values()
|
||||
)
|
||||
|
||||
card = basic_card
|
||||
listener_transaction_id = card.id
|
||||
if card.group:
|
||||
listener_transaction_id = f"group-{basic_card.group.id}"
|
||||
inn: str = card.client.details.inn
|
||||
create_bill_request = CreateBillRequestSchema(
|
||||
listener_transaction_id=listener_transaction_id,
|
||||
payer_name=card.client.name,
|
||||
payer_inn=inn.strip(),
|
||||
payer_phone=card.client.details.phone_number,
|
||||
items=CreateBillRequestItems(
|
||||
values=billing_request_values
|
||||
transaction_prefix = "group-" if card.group else ""
|
||||
inn: str = card.client.details.inn.strip()
|
||||
|
||||
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(
|
||||
listener_transaction_id=listener_transaction_id,
|
||||
payer_name=card.client.name,
|
||||
payer_inn=inn,
|
||||
payer_phone=card.client.details.phone_number,
|
||||
items=CreateBillRequestItems(
|
||||
values=values,
|
||||
)
|
||||
)
|
||||
)
|
||||
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:
|
||||
await self.cancel_card_billing(user, CancelCardBillRequest(card_id=request.card_id, force=True))
|
||||
return await self.create_card_billing(user, request)
|
||||
if not create_bill_response.ok:
|
||||
return CreateCardBillResponse(ok=create_bill_response.ok,
|
||||
message=create_bill_response.message or 'Неизвестная ошибка')
|
||||
create_bill_response = await billing_client.create(create_bill_request)
|
||||
|
||||
if basic_card.group:
|
||||
await self.create_group_bill_request(basic_card.group)
|
||||
if not create_bill_response.ok and 'Request already exists' in create_bill_response.message:
|
||||
await self._cancel_one_bill(bill_request, listener_transaction_id)
|
||||
create_bill_response = await billing_client.create(create_bill_request)
|
||||
|
||||
if not create_bill_response.ok:
|
||||
raise Exception(create_bill_response.message or 'Неизвестная ошибка')
|
||||
|
||||
if card.group:
|
||||
for card in card.group.cards:
|
||||
card.is_locked = True
|
||||
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:
|
||||
await self.session.rollback()
|
||||
return CreateCardBillResponse(ok=False, message=str(e))
|
||||
|
||||
async def _get_card_bill_by_id(self, card_id: int) -> Optional[CardBillRequest]:
|
||||
return await self.session.scalar(select(CardBillRequest).where(CardBillRequest.card_id == card_id))
|
||||
async def _get_card_bill_by_id(self, request_id: int) -> Optional[CardBillRequest]:
|
||||
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]:
|
||||
return await self.session.scalar(select(GroupBillRequest).where(GroupBillRequest.group_id == group_id))
|
||||
async def _get_card_bill_by_card_id(self, card_id: int) -> List[CardBillRequest]:
|
||||
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:
|
||||
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')
|
||||
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:
|
||||
card = await self._get_card_by_id(request.card_id)
|
||||
if not card:
|
||||
return CancelCardBillResponse(ok=False, message='Сделка не найдена')
|
||||
|
||||
if card.group:
|
||||
bill = await self._get_group_bill_by_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))
|
||||
bills = await self._get_group_bill_by_group_id(card.group.id)
|
||||
else:
|
||||
bill = await self._get_card_bill_by_id(request.card_id)
|
||||
if not bill and not request.force:
|
||||
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))
|
||||
bills = await self._get_card_bill_by_card_id(request.card_id)
|
||||
if not bills:
|
||||
return CancelCardBillResponse(ok=False, message='Заявка не найдена')
|
||||
|
||||
if not response.ok:
|
||||
return CancelCardBillResponse(ok=False, message='Ошибка')
|
||||
if bill:
|
||||
await self.session.delete(bill)
|
||||
await self.session.commit()
|
||||
for bill in bills:
|
||||
if card.group:
|
||||
listener_transaction_id = f"group-{bill.id}"
|
||||
else:
|
||||
listener_transaction_id = str(bill.id)
|
||||
|
||||
await self._cancel_one_bill(bill, listener_transaction_id)
|
||||
|
||||
await self.session.commit()
|
||||
return CancelCardBillResponse(ok=True, message='Заявка успешно отозвана')
|
||||
except Exception as e:
|
||||
await self.session.rollback()
|
||||
return CancelCardBillResponse(ok=False, message=str(e))
|
||||
|
||||
def _gen_key_for_service(self, service: ServiceBillingDocumentPdf) -> str:
|
||||
|
||||
Reference in New Issue
Block a user