feat: spacers between barcodes of diff products, avoid product cut in deal document, deal document refactoring

This commit is contained in:
2024-09-30 01:36:31 +04:00
parent 413d8755cc
commit 61d379e7dc
9 changed files with 271 additions and 240 deletions

View File

@@ -1,3 +1,4 @@
from io import BytesIO
from typing import List, Dict
from barcodes.attributes import AttributeWriterFactory
@@ -8,7 +9,7 @@ from schemas.barcode import PdfBarcodeGenData
class DefaultBarcodeGenerator(BaseBarcodeGenerator):
def generate(self, barcodes_data: List[Dict[str, str | Product | BarcodeTemplate | int]]):
def generate(self, barcodes_data: List[Dict[str, str | Product | BarcodeTemplate | int]]) -> BytesIO:
pdf_generator = PDFGenerator()
pdf_barcodes_gen_data: List[PdfBarcodeGenData] = []
@@ -35,6 +36,4 @@ class DefaultBarcodeGenerator(BaseBarcodeGenerator):
)
)
print(f"value = {barcode_data['barcode']}, text = {barcode_text}, num = {barcode_data['quantity']}")
return pdf_generator.generate(pdf_barcodes_gen_data)

View File

@@ -20,6 +20,7 @@ class PDFGenerator:
FONT_FILE_PATH = os.path.join(FONTS_FOLDER, 'DejaVuSans.ttf')
self.page_width = 58 * mm
self.page_height = 40 * mm
self.number_of_spacing_pages = 2
pdfmetrics.registerFont(TTFont('DejaVuSans', FONT_FILE_PATH))
@@ -40,12 +41,14 @@ class PDFGenerator:
buffer = BytesIO()
# Create document with specified page size
doc = SimpleDocTemplate(buffer,
doc = SimpleDocTemplate(
buffer,
pagesize=(self.page_width, self.page_height),
rightMargin=1 * mm,
leftMargin=1 * mm,
topMargin=1 * mm,
bottomMargin=1 * mm)
bottomMargin=1 * mm
)
# Create paragraph with new style
paragraph = Paragraph(text, self.small_style)
@@ -61,10 +64,12 @@ class PDFGenerator:
# Calculate barWidth
bar_width = available_width / num_elements
barcode = code128.Code128(barcode_value,
barcode = code128.Code128(
barcode_value,
barWidth=bar_width,
barHeight=barcode_height,
humanReadable=True)
humanReadable=True
)
# Function to draw barcode on canvas
def add_barcode(canvas, doc):
@@ -85,22 +90,26 @@ class PDFGenerator:
buffer.seek(0)
return buffer
def generate(self, barcodes_data: List[PdfBarcodeGenData]):
buffer = BytesIO()
# Создаем документ с указанным размером страницы
doc = SimpleDocTemplate(buffer,
# Создание документа с указанным размером страницы
def _create_doc(self, buffer):
return SimpleDocTemplate(
buffer,
pagesize=(self.page_width, self.page_height),
rightMargin=1 * mm,
leftMargin=1 * mm,
topMargin=1 * mm,
bottomMargin=1 * mm)
bottomMargin=1 * mm
)
def generate(self, barcodes_data: List[PdfBarcodeGenData]) -> BytesIO:
buffer = BytesIO()
# Создаем документ с указанным размером страницы
doc = self._create_doc(buffer)
# Список элементов для добавления в документ
elements = []
product_barcode_canvases = []
product_counter = 0
product_barcodes_counter = 0
for barcode_data in barcodes_data:
# Создаем абзац с новым стилем
@@ -122,10 +131,12 @@ class PDFGenerator:
# Рассчитываем ширину штриха
bar_width = available_width / num_elements
barcode = code128.Code128(barcode_data.barcode_value,
barcode = code128.Code128(
barcode_data.barcode_value,
barWidth=bar_width,
barHeight=barcode_height,
humanReadable=True)
humanReadable=True
)
product_barcode_canvases.append(barcode)
# Добавление штрихкодов в список элементов документа
@@ -134,24 +145,39 @@ class PDFGenerator:
elements.append(Spacer(1, space_between_text_and_barcode)) # Отступ между текстом и штрихкодом
elements.append(PageBreak())
# Добавление спейсеров
for _ in range(self.number_of_spacing_pages):
elements.append(PageBreak())
# Удалить последние спейсеры
elements = elements[:-2]
product_counter, product_barcodes_counter = 0, 0
# Функция для отрисовки штрихкода на canvas
def add_barcode(canvas, doc):
nonlocal product_barcodes_counter, product_counter
barcode_canvas = product_barcode_canvases[product_counter]
product_barcodes_counter += 1
# Если данная страница это спейсер, то оставить пустой
num_duplicates = barcodes_data[product_counter].num_duplicates
if product_barcodes_counter > num_duplicates:
if product_barcodes_counter >= num_duplicates + self.number_of_spacing_pages:
product_barcodes_counter = 0
product_counter += 1
return
# Отрисовка штрихкода
barcode_width = barcode_canvas.width
barcode_x = (self.page_width - barcode_width) / 2 # Центрируем штрихкод
barcode_y = human_readable_height + 2 * mm # Размещаем штрихкод снизу с учетом отступа
barcode_canvas.drawOn(canvas, barcode_x, barcode_y)
product_barcodes_counter += 1
if product_barcodes_counter >= barcodes_data[product_counter].num_duplicates:
product_barcodes_counter = 0
product_counter += 1
# Создаем документ
doc.build(elements[:-1], onFirstPage=add_barcode, onLaterPages=add_barcode) # Убираем последний PageBreak
buffer.seek(0)
return buffer

View File

@@ -1,4 +1,5 @@
from typing import Dict
from io import BytesIO
from typing import Dict, Tuple
from sqlalchemy import select, update, insert
from sqlalchemy.orm import selectinload, joinedload
@@ -76,8 +77,7 @@ class BarcodeService(BaseService):
)
return GetProductBarcodeResponse(barcode=barcode)
async def get_barcode_pdf(self, request: GetProductBarcodePdfRequest) -> GetProductBarcodeResponse:
# get product by id
async def get_barcode_pdf(self, request: GetProductBarcodePdfRequest) -> Tuple[str, BytesIO]:
stmt = (
select(Product)
.options(
@@ -102,7 +102,7 @@ class BarcodeService(BaseService):
)
return filename, pdf_buffer
async def get_deal_barcodes_pdf(self, request: GetDealProductsBarcodesPdfRequest) -> GetProductBarcodeResponse:
async def get_deal_barcodes_pdf(self, request: GetDealProductsBarcodesPdfRequest) -> Tuple[str, BytesIO]:
stmt = (
select(Deal)
.options(

View File

@@ -1036,7 +1036,7 @@ class DealService(BaseService):
last_status: DealStatusHistory = max(deal.status_history, key=lambda status: status.changed_at)
general_services_total = sum((service.price * service.quantity for service in deal.services))
product_services_totals: List[Dict[str, int]] = await self._get_product_services_totals(deal)
template = ENV.get_template("deal.html")
template = ENV.get_template("deal/deal.html")
product_urls: List[Optional[str]] = []
for product in deal.products:

View File

@@ -60,14 +60,14 @@ hr {
}
.product-data {
height: 160px;
height: 130px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.product-data > * {
width: 170px;
width: 200px;
padding-right: 170px;
}
@@ -143,8 +143,8 @@ table tfoot {
}
.product-img {
height: 300px;
width: 200px;
height: 240px;
width: 160px;
object-fit: cover;
padding: 0;
margin: 0;

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,62 @@
<!--#region Header -->
<div class="deal-name-id-container">
<div class="medium-text bold">
Название сделки: {{ deal.name }}
</div>
</div>
<hr/>
<div class="medium-text">
Дата создания: {{ now().strftime("%d.%m.%Y, %H:%M") }}
</div>
<div class="medium-text">
Текущий статус: {{ current_status_str }}
</div>
<div class="medium-text">
{% if deal.comment %}
Комментарий к сделке: {{ deal.comment }}
{% endif %}
</div>
<hr/>
<!--#endregion -->
<!--#region General services -->
<div class="large-text bold">
Клиент: {{ deal.client.name }}
</div>
<div class="medium-text bold">
Дата отгрузки: {{ last_status.next_status_deadline.strftime("%d.%m.%Y") }}
</div>
{% if deal.base_marketplace.name %}
<div class="medium-text bold">Маркетплейс: {{ deal.base_marketplace.name }}</div>
{% endif %}
{% if deal.shipping_warehouse.name %}
<div class="medium-text bold">Склад отгрузки: {{ deal.shipping_warehouse.name }}</div>
{% endif %}
{% if deal.services|length > 0 %}
<table>
<thead>
<tr>
<th>Общие услуги</th>
<th>Количество</th>
<th>Сумма</th>
</tr>
<tfoot>
</tfoot>
</thead>
<tbody>
{% for service in deal.services %}
<tr>
<td>{{ service.service.name }}</td>
<td>{{ '{:,}'.format(service.quantity) }} шт.</td>
<td>{{ '{:,}'.format(service.price * service.quantity).replace(',', ' ') }} Р</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="medium-text total align-right bold">
Итого: {{ general_services_total }} Р
</div>
{% else %}
<div class="medium-text total align-right bold">
</div>
{% endif %}
<!--#endregion -->

File diff suppressed because one or more lines are too long