Compare commits
2 Commits
a1b44320b6
...
9a66667317
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a66667317 | |||
| 50a315ca18 |
@@ -5,6 +5,7 @@ uvicorn
|
|||||||
uvicorn[standard]
|
uvicorn[standard]
|
||||||
gunicorn
|
gunicorn
|
||||||
python-multipart
|
python-multipart
|
||||||
|
jinja2
|
||||||
|
|
||||||
# Security
|
# Security
|
||||||
python-jose[cryptography]
|
python-jose[cryptography]
|
||||||
@@ -25,4 +26,6 @@ lexorank-py
|
|||||||
celery[redis]
|
celery[redis]
|
||||||
celery
|
celery
|
||||||
# PDF
|
# PDF
|
||||||
reportlab
|
reportlab
|
||||||
|
weasyprint
|
||||||
|
number_to_string
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
|
from io import BytesIO
|
||||||
from typing import Annotated
|
from typing import Annotated
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, Response
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from backend.dependecies import SessionDependency, CurrentUserDependency
|
from backend.dependecies import SessionDependency, CurrentUserDependency
|
||||||
@@ -8,6 +9,7 @@ from backend.session import get_session
|
|||||||
from models import User
|
from models import User
|
||||||
from schemas.deal import *
|
from schemas.deal import *
|
||||||
from services.auth import get_current_user, authorized_user, guest_user
|
from services.auth import get_current_user, authorized_user, guest_user
|
||||||
|
from services.billing import BillingService
|
||||||
from services.deal import DealService
|
from services.deal import DealService
|
||||||
|
|
||||||
deal_router = APIRouter(
|
deal_router = APIRouter(
|
||||||
@@ -175,6 +177,18 @@ async def create_guest_url(
|
|||||||
return DealService(session).create_guest_url(user, request)
|
return DealService(session).create_guest_url(user, request)
|
||||||
|
|
||||||
|
|
||||||
|
@deal_router.get(
|
||||||
|
'/document/{deal_id}',
|
||||||
|
operation_id='get_deal_document',
|
||||||
|
# dependencies=[Depends(authorized_user)],
|
||||||
|
)
|
||||||
|
async def get_deal_document(
|
||||||
|
deal_id: int,
|
||||||
|
session: Annotated[AsyncSession, Depends(get_session)],
|
||||||
|
):
|
||||||
|
pdf_file: BytesIO = await BillingService(session).create_billing_document_pdf(deal_id)
|
||||||
|
return Response(pdf_file.getvalue(), media_type='application/pdf')
|
||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region Deal services
|
# region Deal services
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from typing import Optional
|
|||||||
|
|
||||||
from schemas.base import BaseSchema, OkMessageSchema
|
from schemas.base import BaseSchema, OkMessageSchema
|
||||||
|
|
||||||
|
# region Entities
|
||||||
class DealBillRequestSchema(BaseSchema):
|
class DealBillRequestSchema(BaseSchema):
|
||||||
deal_id: int
|
deal_id: int
|
||||||
created_at: datetime.datetime
|
created_at: datetime.datetime
|
||||||
@@ -11,7 +11,9 @@ class DealBillRequestSchema(BaseSchema):
|
|||||||
pdf_url: Optional[str]
|
pdf_url: Optional[str]
|
||||||
invoice_number: Optional[str]
|
invoice_number: Optional[str]
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region Requests
|
||||||
class CreateDealBillRequest(BaseSchema):
|
class CreateDealBillRequest(BaseSchema):
|
||||||
deal_id: int
|
deal_id: int
|
||||||
|
|
||||||
@@ -19,7 +21,9 @@ class CreateDealBillRequest(BaseSchema):
|
|||||||
class CancelDealBillRequest(BaseSchema):
|
class CancelDealBillRequest(BaseSchema):
|
||||||
deal_id: int
|
deal_id: int
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region Responses
|
||||||
class CreateDealBillResponse(OkMessageSchema):
|
class CreateDealBillResponse(OkMessageSchema):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -30,3 +34,5 @@ class CancelDealBillResponse(OkMessageSchema):
|
|||||||
|
|
||||||
class GetDealBillById(BaseSchema):
|
class GetDealBillById(BaseSchema):
|
||||||
deal_bill: DealBillRequestSchema
|
deal_bill: DealBillRequestSchema
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|||||||
@@ -1,19 +1,30 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
import locale
|
||||||
|
from ast import Bytes
|
||||||
|
from io import BytesIO
|
||||||
|
from pathlib import Path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from fastapi import HTTPException
|
from fastapi import HTTPException
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from number_to_string import get_string_by_number
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
from sqlalchemy.orm import selectinload
|
||||||
from starlette import status
|
from starlette import status
|
||||||
|
from weasyprint import HTML
|
||||||
|
|
||||||
import backend.config
|
import backend.config
|
||||||
from external.billing import BillingClient, CreateBillingRequestValue, CreateBillRequestSchema, CreateBillRequestItems, \
|
from external.billing import BillingClient, CreateBillingRequestValue, CreateBillRequestSchema, CreateBillRequestItems, \
|
||||||
BillStatusUpdateRequest, NotificationChannel, NotifyReceivedBillRequestSchema, BillPaymentInfo, \
|
BillStatusUpdateRequest, NotificationChannel, NotifyReceivedBillRequestSchema, DeleteBillRequestSchema
|
||||||
DeleteBillRequestSchema
|
from models import DealBillRequest, Deal, DealProduct, DealService as DealServiceModel
|
||||||
from models import DealBillRequest, Deal
|
|
||||||
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
|
||||||
|
|
||||||
|
locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8")
|
||||||
|
|
||||||
|
env = Environment(loader=FileSystemLoader(Path("templates") / Path("documents")))
|
||||||
|
|
||||||
|
|
||||||
class BillingService(BaseService):
|
class BillingService(BaseService):
|
||||||
async def _process_update_details(
|
async def _process_update_details(
|
||||||
@@ -132,3 +143,60 @@ class BillingService(BaseService):
|
|||||||
return CancelDealBillResponse(ok=True, message='Заявка успешно отозвана')
|
return CancelDealBillResponse(ok=True, message='Заявка успешно отозвана')
|
||||||
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_services_for_deal(self, deal: Deal) -> List[CreateBillingRequestValue]:
|
||||||
|
services: List[CreateBillingRequestValue] = []
|
||||||
|
|
||||||
|
for product in deal.products:
|
||||||
|
for service in product.services:
|
||||||
|
services.append(
|
||||||
|
CreateBillingRequestValue(
|
||||||
|
name=f'[{product.product.name}] - {service.service.name}',
|
||||||
|
price=service.price,
|
||||||
|
amount=product.quantity
|
||||||
|
)
|
||||||
|
)
|
||||||
|
for service in deal.services:
|
||||||
|
services.append(
|
||||||
|
CreateBillingRequestValue(
|
||||||
|
name=f'{service.service.name}',
|
||||||
|
price=service.price,
|
||||||
|
amount=service.quantity
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
return services
|
||||||
|
|
||||||
|
async def _create_billing_document_html(self, deal_id: int):
|
||||||
|
deal: Deal | None = await self.session.scalar(
|
||||||
|
select(Deal)
|
||||||
|
.where(Deal.id == deal_id)
|
||||||
|
.options(
|
||||||
|
selectinload(Deal.products).selectinload(DealProduct.services),
|
||||||
|
selectinload(Deal.services).selectinload(DealServiceModel.service),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not deal:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
services = await self._get_services_for_deal(deal)
|
||||||
|
|
||||||
|
deal_price = sum((service.price for service in services))
|
||||||
|
deal_price_words = get_string_by_number(deal_price)[0:-10]
|
||||||
|
template = env.get_template("bill-of-payment.html")
|
||||||
|
return template.render({
|
||||||
|
"services": services,
|
||||||
|
"deal_price": deal_price,
|
||||||
|
"deal_price_words": deal_price_words,
|
||||||
|
"deal": deal,
|
||||||
|
"curr_date": datetime.datetime.now().strftime("%d %B %Y г.")
|
||||||
|
})
|
||||||
|
|
||||||
|
async def create_billing_document_pdf(self, deal_id) -> BytesIO:
|
||||||
|
doc = await self._create_billing_document_html(deal_id)
|
||||||
|
|
||||||
|
pdf_file = BytesIO()
|
||||||
|
HTML(string=doc).write_pdf(pdf_file)
|
||||||
|
|
||||||
|
return pdf_file
|
||||||
|
|||||||
BIN
static/icons/denco.png
Normal file
BIN
static/icons/denco.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
147
templates/documents/bill-of-payment.html
Normal file
147
templates/documents/bill-of-payment.html
Normal file
File diff suppressed because one or more lines are too long
61
test.py
61
test.py
@@ -1,54 +1,25 @@
|
|||||||
# import asyncio
|
import asyncio
|
||||||
#
|
|
||||||
# from dict_hash import dict_hash
|
|
||||||
# from sqlalchemy.ext.asyncio import AsyncSession
|
|
||||||
#
|
|
||||||
# from backend.session import session_maker
|
|
||||||
# from marketplaces import MarketplaceControllerFactory
|
|
||||||
# from models import Marketplace
|
|
||||||
# import pickle
|
|
||||||
# pickle.dumps()
|
|
||||||
#
|
|
||||||
# async def main():
|
|
||||||
# a = "example"
|
|
||||||
# b = "example"
|
|
||||||
#
|
|
||||||
# print(hash(a)) # Хэш для строки "example"
|
|
||||||
# print(hash(b)) # Хэш для строки "example", будет таким же как и у a
|
|
||||||
#
|
|
||||||
# return
|
|
||||||
# session: AsyncSession = session_maker()
|
|
||||||
#
|
|
||||||
# try:
|
|
||||||
# mp = await session.get(Marketplace, 2)
|
|
||||||
# if not mp:
|
|
||||||
# return
|
|
||||||
# c = MarketplaceControllerFactory.get_controller(session, mp)
|
|
||||||
# await c.synchronize_products()
|
|
||||||
# finally:
|
|
||||||
# await session.close()
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# if __name__ == '__main__':
|
|
||||||
# loop = asyncio.get_event_loop()
|
|
||||||
# loop.run_until_complete(main())
|
|
||||||
import hashlib
|
|
||||||
import time
|
|
||||||
|
|
||||||
from reportlab.rl_settings import autoGenerateMissingTTFName
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from decorators.locking import lock, redis_client
|
from backend.session import session_maker
|
||||||
|
from services.billing import BillingService
|
||||||
|
|
||||||
|
|
||||||
@lock('synchronize_marketplace', include_args_in_key=True)
|
async def main():
|
||||||
def test(marketplace_id: int):
|
session: AsyncSession = session_maker()
|
||||||
print("test")
|
|
||||||
time.sleep(100)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
service = BillingService(session)
|
||||||
|
|
||||||
def main():
|
pdf_file = await service.create_billing_document_pdf(121)
|
||||||
test(1)
|
|
||||||
|
with open("report.pdf", "wb") as f:
|
||||||
|
f.write(pdf_file.getvalue())
|
||||||
|
finally:
|
||||||
|
await session.close()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
|||||||
Reference in New Issue
Block a user