diff --git a/barcodes/generator/default_generator.py b/barcodes/generator/default_generator.py
index 12fe7b2..8e3a133 100644
--- a/barcodes/generator/default_generator.py
+++ b/barcodes/generator/default_generator.py
@@ -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)
diff --git a/barcodes/pdf/generator.py b/barcodes/pdf/generator.py
index 7aa9f93..b378305 100644
--- a/barcodes/pdf/generator.py
+++ b/barcodes/pdf/generator.py
@@ -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,
- pagesize=(self.page_width, self.page_height),
- rightMargin=1 * mm,
- leftMargin=1 * mm,
- topMargin=1 * mm,
- bottomMargin=1 * mm)
+ doc = SimpleDocTemplate(
+ buffer,
+ pagesize=(self.page_width, self.page_height),
+ rightMargin=1 * mm,
+ leftMargin=1 * mm,
+ topMargin=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,
- barWidth=bar_width,
- barHeight=barcode_height,
- humanReadable=True)
+ barcode = code128.Code128(
+ barcode_value,
+ barWidth=bar_width,
+ barHeight=barcode_height,
+ 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]):
+ # Создание документа с указанным размером страницы
+ 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
+ )
+
+ def generate(self, barcodes_data: List[PdfBarcodeGenData]) -> BytesIO:
buffer = BytesIO()
# Создаем документ с указанным размером страницы
- doc = SimpleDocTemplate(buffer,
- pagesize=(self.page_width, self.page_height),
- rightMargin=1 * mm,
- leftMargin=1 * mm,
- topMargin=1 * mm,
- bottomMargin=1 * mm)
+ 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,
- barWidth=bar_width,
- barHeight=barcode_height,
- humanReadable=True)
+ barcode = code128.Code128(
+ barcode_data.barcode_value,
+ barWidth=bar_width,
+ barHeight=barcode_height,
+ 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
-
diff --git a/services/barcode.py b/services/barcode.py
index c4d641e..ec89bc7 100644
--- a/services/barcode.py
+++ b/services/barcode.py
@@ -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(
diff --git a/services/deal.py b/services/deal.py
index 5ddf4b9..cbacbf6 100644
--- a/services/deal.py
+++ b/services/deal.py
@@ -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:
diff --git a/static/css/deal.css b/static/css/deal.css
index 9ea93a2..687681b 100644
--- a/static/css/deal.css
+++ b/static/css/deal.css
@@ -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;
diff --git a/templates/documents/deal.html b/templates/documents/deal.html
deleted file mode 100644
index 5747fc5..0000000
--- a/templates/documents/deal.html
+++ /dev/null
@@ -1,199 +0,0 @@
-
-
-
-
-
-
-
- Сделка
-
-
-
-
-
-
-
-
-
- Название сделки: {{ deal.name }}
-
-
-
-
- Дата создания: {{ now().strftime("%d.%m.%Y, %H:%M") }}
-
-
- Текущий статус: {{ current_status_str }}
-
-
- {% if deal.comment %}
- Комментарий к сделке: {{ deal.comment }}
- {% endif %}
-
-
-
-
-
- Клиент: {{ deal.client.name }}
-
-
- Дата отгрузки: {{ last_status.next_status_deadline.strftime("%d.%m.%Y") }}
-
- {% if deal.base_marketplace.name %}
-
Маркетплейс: {{ deal.base_marketplace.name }}
- {% endif %}
- {% if deal.shipping_warehouse.name %}
-
Склад отгрузки: {{ deal.shipping_warehouse.name }}
- {% endif %}
- {% if deal.services|length > 0 %}
-
-
-
- | Общие услуги |
- Количество |
- Сумма |
-
-
-
-
-
- {% for service in deal.services %}
-
- | {{ service.service.name }} |
- {{ '{:,}'.format(service.quantity) }} шт. |
- {{ '{:,}'.format(service.price * service.quantity).replace(',', ' ') }} Р |
-
- {% endfor %}
-
-
-
- Итого: {{ general_services_total }} Р
-
- {% else %}
-
-
- {% endif %}
-
-
- {% for product in deal.products %}
-
-
-
-
- {% if product.product.images|length > 0 %}
-

- {% else %}
-

- {% endif %}
-
-
-
- {{ loop.index }}. {{ product.product.name }}
-
-
-
Артикул: {{ product.product.article }}
- {% if product.product.brand %}
-
Бренд: {{ product.product.brand }}
- {% endif %}
- {% if product.product.color %}
-
Цвет: {{ product.product.color }}
- {% endif %}
- {% if product.product.size %}
-
Размер: {{ product.product.size }}
- {% endif %}
- {% if product.quantity %}
-
Количество: {{ product.quantity }}
- {% endif %}
- {% if product.product.additional_info %}
-
- Доп. информация: {{ product.product.additional_info }}
-
- {% endif %}
-
-
-
-
-
-
- {% if product.product.barcodes|length > 0 %}
-
- {{ encode128(product.product.barcodes[0].barcode, "A") }}
-
-
- {{ product.product.barcodes[0].barcode }}
-
- {% else %}
-
- {% endif %}
-
-
-
- {% if product.services|length > 0 %}
-
-
-
- | Наименование услуги |
- Цена |
- Сумма |
-
-
-
-
-
- {% for service in product.services %}
-
- | {{ service.service.name }} |
- {{ '{:,}'.format(product.quantity) }} Р |
- {{ '{:,}'.format(service.price * product.quantity).replace(',', ' ') }} Р |
-
- {% endfor %}
-
-
-
- Итого: {{ product_services_totals[loop.index0].total }} Р, за
- единицу: {{ product_services_totals[loop.index0].total_one_product }} Р
-
- {% else %}
-
- {% endif %}
- {% endfor %}
-
-
-
-
-
-
\ No newline at end of file
diff --git a/templates/documents/deal/deal-product-services.html b/templates/documents/deal/deal-product-services.html
new file mode 100644
index 0000000..3b47dd8
--- /dev/null
+++ b/templates/documents/deal/deal-product-services.html
@@ -0,0 +1,86 @@
+
+
+
+
+
+ {% if product.product.images|length > 0 %}
+

+ {% else %}
+

+ {% endif %}
+
+
+
+ {{ index }}. {{ product.product.name }}
+
+
+
Артикул: {{ product.product.article }}
+ {% if product.product.brand %}
+
Бренд: {{ product.product.brand }}
+ {% endif %}
+ {% if product.product.color %}
+
Цвет: {{ product.product.color }}
+ {% endif %}
+ {% if product.product.size %}
+
Размер: {{ product.product.size }}
+ {% endif %}
+ {% if product.quantity %}
+
Количество: {{ product.quantity }}
+ {% endif %}
+ {% if product.product.additional_info %}
+
+ Доп. информация: {{ product.product.additional_info }}
+
+ {% endif %}
+
+
+
+
+
+
+ {% if product.product.barcodes|length > 0 %}
+
+ {{ encode128(product.product.barcodes[0].barcode, "A") }}
+
+
+ {{ product.product.barcodes[0].barcode }}
+
+ {% else %}
+
+ {% endif %}
+
+
+
+ {% if product.services|length > 0 %}
+
+
+
+ | Наименование услуги |
+ Цена |
+ Сумма |
+
+
+
+
+
+ {% for service in product.services %}
+
+ | {{ service.service.name }} |
+ {{ '{:,}'.format(product.quantity) }} Р |
+ {{ '{:,}'.format(service.price * product.quantity).replace(',', ' ') }} Р |
+
+ {% endfor %}
+
+
+
+ Итого: {{ product_services_totals[loop.index0].total }} Р, за
+ единицу: {{ product_services_totals[loop.index0].total_one_product }} Р
+
+ {% else %}
+
+ {% endif %}
+
diff --git a/templates/documents/deal/deal-services.html b/templates/documents/deal/deal-services.html
new file mode 100644
index 0000000..78c3ab8
--- /dev/null
+++ b/templates/documents/deal/deal-services.html
@@ -0,0 +1,62 @@
+
+
+
+ Название сделки: {{ deal.name }}
+
+
+
+
+ Дата создания: {{ now().strftime("%d.%m.%Y, %H:%M") }}
+
+
+ Текущий статус: {{ current_status_str }}
+
+
+ {% if deal.comment %}
+ Комментарий к сделке: {{ deal.comment }}
+ {% endif %}
+
+
+
+
+
+ Клиент: {{ deal.client.name }}
+
+
+ Дата отгрузки: {{ last_status.next_status_deadline.strftime("%d.%m.%Y") }}
+
+{% if deal.base_marketplace.name %}
+ Маркетплейс: {{ deal.base_marketplace.name }}
+{% endif %}
+{% if deal.shipping_warehouse.name %}
+ Склад отгрузки: {{ deal.shipping_warehouse.name }}
+{% endif %}
+{% if deal.services|length > 0 %}
+
+
+
+ | Общие услуги |
+ Количество |
+ Сумма |
+
+
+
+
+
+ {% for service in deal.services %}
+
+ | {{ service.service.name }} |
+ {{ '{:,}'.format(service.quantity) }} шт. |
+ {{ '{:,}'.format(service.price * service.quantity).replace(',', ' ') }} Р |
+
+ {% endfor %}
+
+
+
+ Итого: {{ general_services_total }} Р
+
+{% else %}
+
+
+{% endif %}
+
\ No newline at end of file
diff --git a/templates/documents/deal/deal.html b/templates/documents/deal/deal.html
new file mode 100644
index 0000000..ed57687
--- /dev/null
+++ b/templates/documents/deal/deal.html
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+ Сделка
+
+
+
+
+
+
+ {% include "deal/deal-services.html" %}
+ {% for product in deal.products %}
+ {% with index = loop.index %}
+ {% include "deal/deal-product-services.html" %}
+ {% endwith %}
+ {% endfor %}
+
+
+
+
+
\ No newline at end of file