|
|
|
|
@@ -4,7 +4,9 @@ import io
|
|
|
|
|
import pathlib
|
|
|
|
|
|
|
|
|
|
import jinja2
|
|
|
|
|
from odfdo import Cell, Document, Row, Table
|
|
|
|
|
import odfdo
|
|
|
|
|
# from odfdo import Cell, Document, Row, Style, Table
|
|
|
|
|
from odfdo.element import Element
|
|
|
|
|
from src import models
|
|
|
|
|
from weasyprint import HTML
|
|
|
|
|
|
|
|
|
|
@@ -62,20 +64,181 @@ def generate_html_contract(
|
|
|
|
|
).write_pdf()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def flatten(xss):
|
|
|
|
|
return [x for xs in xss for x in xs]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_column_style_width(size: str) -> odfdo.Style:
|
|
|
|
|
"""Create a table columm style for a given width.
|
|
|
|
|
Paramenters:
|
|
|
|
|
size(str): size of the style (format <number><unit>) unit can be in, cm... see odfdo documentation.
|
|
|
|
|
Returns:
|
|
|
|
|
odfdo.Style with the correct column-width attribute.
|
|
|
|
|
"""
|
|
|
|
|
return odfdo.Element.from_tag(
|
|
|
|
|
'<style:style style:name="product-table.A" style:family="table-column">'
|
|
|
|
|
f'<style:table-column-properties style:column-width="{size}"/>'
|
|
|
|
|
'</style:style>'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_row_style_height(size: str) -> odfdo.Style:
|
|
|
|
|
"""Create a table height style for a given height.
|
|
|
|
|
Paramenters:
|
|
|
|
|
size(str): size of the style (format <number><unit>) unit can be in, cm... see odfdo documentation.
|
|
|
|
|
Returns:
|
|
|
|
|
odfdo.Style with the correct column-height attribute.
|
|
|
|
|
"""
|
|
|
|
|
return odfdo.Element.from_tag(
|
|
|
|
|
'<style:style style:name="product-table.A" style:family="table-row">'
|
|
|
|
|
f'<style:table-row-properties style:row-height="{size}"/>'
|
|
|
|
|
'</style:style>'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_center_cell_style(name: str = "centered-cell") -> odfdo.Style:
|
|
|
|
|
return odfdo.Element.from_tag(
|
|
|
|
|
f'<style:style style:name="{name}" style:family="table-cell">'
|
|
|
|
|
'<style:table-cell-properties style:vertical-align="middle" fo:wrap-option="wrap"/>'
|
|
|
|
|
'<style:paragraph-properties fo:text-align="center"/>'
|
|
|
|
|
'</style:style>'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def create_cell_style_with_font(name: str = "font", font_size="14pt", bold: bool = False) -> odfdo.Style:
|
|
|
|
|
return odfdo.Element.from_tag(
|
|
|
|
|
f'<style:style style:name="{name}" style:family="table-cell" '
|
|
|
|
|
f'xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0">'
|
|
|
|
|
'<style:table-cell-properties style:vertical-align="middle" fo:wrap-option="wrap"/>'
|
|
|
|
|
f'<style:paragraph-properties fo:text-align="center" fo:font-size="{font_size}" '
|
|
|
|
|
f'{"fo:font-weight=\"bold\"" if bold else ""}/>'
|
|
|
|
|
'</style:style>'
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_center_cell_style(document: odfdo.Document, row: odfdo.Row):
|
|
|
|
|
style = document.insert_style(
|
|
|
|
|
create_center_cell_style()
|
|
|
|
|
)
|
|
|
|
|
for cell in row.get_cells():
|
|
|
|
|
cell.style = style
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_column_height_style(document: odfdo.Document, row: odfdo.Row, height: str):
|
|
|
|
|
style = document.insert_style(
|
|
|
|
|
style=create_row_style_height(height), name=height, automatic=True
|
|
|
|
|
)
|
|
|
|
|
row.style = style
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_font_style(document: odfdo.Document, table: odfdo.Table, size: str = "14pt"):
|
|
|
|
|
style_header = document.insert_style(
|
|
|
|
|
style=create_cell_style_with_font(
|
|
|
|
|
'header_font', font_size=size, bold=True
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
style_body = document.insert_style(
|
|
|
|
|
style=create_cell_style_with_font(
|
|
|
|
|
'body_font', font_size=size, bold=False
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
for position in range(table.height):
|
|
|
|
|
row = table.get_row(position)
|
|
|
|
|
for cell in row.get_cells():
|
|
|
|
|
cell.style = style_header if position == 0 or position == 1 else style_body
|
|
|
|
|
for paragraph in cell.get_paragraphs():
|
|
|
|
|
paragraph.style = cell.style
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def apply_column_width_style(document: odfdo.Document, table: odfdo.Table, widths: list[str]):
|
|
|
|
|
"""Apply column width style to a table.
|
|
|
|
|
Parameters:
|
|
|
|
|
document(odfdo.Document): Document where the table is located.
|
|
|
|
|
table(odfdo.Table): Table to apply columns widths.
|
|
|
|
|
widths(list[str]): list of width in format <number><unit> unit ca be in, cm... see odfdo documentation.
|
|
|
|
|
"""
|
|
|
|
|
styles = []
|
|
|
|
|
for w in widths:
|
|
|
|
|
styles.append(document.insert_style(
|
|
|
|
|
style=create_column_style_width(w), name=w, automatic=True))
|
|
|
|
|
|
|
|
|
|
for position in range(table.width):
|
|
|
|
|
col = table.get_column(position)
|
|
|
|
|
col.style = styles[position]
|
|
|
|
|
table.set_column(position, col)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_recap(
|
|
|
|
|
contracts: list[models.Contract],
|
|
|
|
|
form: models.Form,
|
|
|
|
|
):
|
|
|
|
|
recurrents = [pr.name for pr in form.productor.products if pr.type ==
|
|
|
|
|
models.ProductType.RECCURENT]
|
|
|
|
|
recurrents.sort()
|
|
|
|
|
occasionnals = [pr.name for pr in form.productor.products if pr.type ==
|
|
|
|
|
models.ProductType.OCCASIONAL]
|
|
|
|
|
occasionnals.sort()
|
|
|
|
|
shipments = form.shipments
|
|
|
|
|
occasionnals_header = [
|
|
|
|
|
occ for shipment in shipments for occ in occasionnals]
|
|
|
|
|
shipment_header = flatten(
|
|
|
|
|
[[f'{shipment.name} - {shipment.date.strftime('%Y-%m-%d')}'] + ["" * len(occasionnals)] for shipment in shipments])
|
|
|
|
|
product_unit_map = {
|
|
|
|
|
"1": "g",
|
|
|
|
|
"2": "kg",
|
|
|
|
|
"3": "p"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header = (
|
|
|
|
|
["Nom", "Email"] +
|
|
|
|
|
["Tarif panier", "Total Paniers", "Total à payer"] +
|
|
|
|
|
["Cheque 1", "Cheque 2", "Cheque 3"] +
|
|
|
|
|
[f"Total {len(shipments)} livraisons + produits occasionnels"] +
|
|
|
|
|
recurrents +
|
|
|
|
|
occasionnals_header +
|
|
|
|
|
["Remarques", "Nom"]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
data = [
|
|
|
|
|
["nom", "email"],
|
|
|
|
|
[""] * (9 + len(recurrents)) + shipment_header,
|
|
|
|
|
header,
|
|
|
|
|
*[
|
|
|
|
|
[
|
|
|
|
|
f'{contract.firstname} {contract.lastname}',
|
|
|
|
|
f'{contract.email}',
|
|
|
|
|
*[f'{pr.quantity} {product_unit_map[pr.product.unit]}' for pr in sorted(
|
|
|
|
|
contract.products, key=lambda x: x.product.name) if pr.product.type == models.ProductType.RECCURENT],
|
|
|
|
|
*[f'{pr.quantity} {product_unit_map[pr.product.unit]}' for pr in sorted(
|
|
|
|
|
contract.products, key=lambda x: x.product.name) if pr.product.type == models.ProductType.OCCASIONAL],
|
|
|
|
|
"",
|
|
|
|
|
f'{contract.firstname} {contract.lastname}',
|
|
|
|
|
] for contract in contracts
|
|
|
|
|
]
|
|
|
|
|
]
|
|
|
|
|
doc = Document("spreadsheet")
|
|
|
|
|
sheet = Table(name="Recap")
|
|
|
|
|
|
|
|
|
|
doc = odfdo.Document("spreadsheet")
|
|
|
|
|
sheet = doc.body.get_sheet(0)
|
|
|
|
|
sheet.name = 'Recap'
|
|
|
|
|
sheet.set_values(data)
|
|
|
|
|
apply_column_width_style(doc, doc.body.get_table(0), ["4cm"] * len(header))
|
|
|
|
|
apply_column_height_style(
|
|
|
|
|
doc,
|
|
|
|
|
doc.body.get_table(0).get_rows((1, 1))[0],
|
|
|
|
|
"1.20cm"
|
|
|
|
|
)
|
|
|
|
|
apply_center_cell_style(doc, doc.body.get_table(0).get_rows((1, 1))[0])
|
|
|
|
|
apply_font_style(doc, doc.body.get_table(0))
|
|
|
|
|
index = 9 + len(recurrents)
|
|
|
|
|
for _ in enumerate(shipments):
|
|
|
|
|
startcol = index
|
|
|
|
|
endcol = index+len(occasionnals) - 1
|
|
|
|
|
sheet.set_span((startcol, 0, endcol, 0), merge=True)
|
|
|
|
|
index += len(occasionnals)
|
|
|
|
|
|
|
|
|
|
doc.body.append(sheet)
|
|
|
|
|
|
|
|
|
|
buffer = io.BytesIO()
|
|
|
|
|
doc.save(buffer)
|
|
|
|
|
|
|
|
|
|
doc.save('test.ods')
|
|
|
|
|
return buffer.getvalue()
|
|
|
|
|
|