fix contract recap
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
|
||||
import html
|
||||
import io
|
||||
import math
|
||||
import pathlib
|
||||
import string
|
||||
|
||||
import jinja2
|
||||
import odfdo
|
||||
# from odfdo import Cell, Document, Row, Style, Table
|
||||
from odfdo.element import Element
|
||||
from src import models
|
||||
from src.contracts import service
|
||||
from weasyprint import HTML
|
||||
@@ -99,20 +96,34 @@ def create_row_style_height(size: str) -> odfdo.Style:
|
||||
)
|
||||
|
||||
|
||||
def create_currency_style(name:str = 'currency-euro'):
|
||||
return odfdo.Element.from_tag(
|
||||
f"""
|
||||
<number:currency-style style:name="{name}">
|
||||
<number:number number:min-integer-digits="1" number:decimal-places="2"/>
|
||||
<number:text> €</number:text>
|
||||
</number:currency-style>"""
|
||||
)
|
||||
|
||||
def create_cell_style(
|
||||
name: str = "centered-cell",
|
||||
font_size: str = '10pt',
|
||||
bold: bool = False,
|
||||
background_color: str = '#FFFFFF',
|
||||
color: str = '#000000'
|
||||
color: str = '#000000',
|
||||
currency: bool = False,
|
||||
) -> odfdo.Style:
|
||||
bold_attr = """
|
||||
fo:font-weight="bold"
|
||||
style:font-weight-asian="bold"
|
||||
style:font-weight-complex="bold"
|
||||
""" if bold else ''
|
||||
currency_attr = """
|
||||
style:data-style-name="currency-euro">
|
||||
""" if currency else ''
|
||||
return odfdo.Element.from_tag(
|
||||
f"""<style:style style:name="{name}" style:family="table-cell">
|
||||
f"""<style:style style:name="{name}" style:family="table-cell"
|
||||
{currency_attr}>
|
||||
<style:table-cell-properties
|
||||
fo:border="0.75pt solid #000000"
|
||||
style:vertical-align="middle"
|
||||
@@ -127,7 +138,10 @@ def create_cell_style(
|
||||
)
|
||||
|
||||
|
||||
def apply_cell_style(document: odfdo.Document, table: odfdo.Table):
|
||||
def apply_cell_style(document: odfdo.Document, table: odfdo.Table, currency_cols: list[int]):
|
||||
document.insert_style(
|
||||
style=create_currency_style(),
|
||||
)
|
||||
header_style = document.insert_style(
|
||||
create_cell_style(
|
||||
name="header-cells",
|
||||
@@ -143,7 +157,7 @@ def apply_cell_style(document: odfdo.Document, table: odfdo.Table):
|
||||
name="body-style-even",
|
||||
bold=False,
|
||||
background_color="#e8eaed",
|
||||
color="#000000"
|
||||
color="#000000",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -152,7 +166,7 @@ def apply_cell_style(document: odfdo.Document, table: odfdo.Table):
|
||||
name="body-style-odd",
|
||||
bold=False,
|
||||
background_color="#FFFFFF",
|
||||
color="#000000"
|
||||
color="#000000",
|
||||
)
|
||||
)
|
||||
|
||||
@@ -164,18 +178,54 @@ def apply_cell_style(document: odfdo.Document, table: odfdo.Table):
|
||||
)
|
||||
)
|
||||
|
||||
body_style_even_currency = document.insert_style(
|
||||
create_cell_style(
|
||||
name="body-style-even-currency",
|
||||
bold=False,
|
||||
background_color="#e8eaed",
|
||||
color="#000000",
|
||||
currency=True,
|
||||
)
|
||||
)
|
||||
|
||||
body_style_odd_currency = document.insert_style(
|
||||
create_cell_style(
|
||||
name="body-style-odd-currency",
|
||||
bold=False,
|
||||
background_color="#FFFFFF",
|
||||
color="#000000",
|
||||
currency=True,
|
||||
)
|
||||
)
|
||||
|
||||
footer_style_currency = document.insert_style(
|
||||
create_cell_style(
|
||||
name="footer-cells-currency",
|
||||
bold=True,
|
||||
font_size='12pt',
|
||||
currency=True,
|
||||
)
|
||||
)
|
||||
|
||||
for index, row in enumerate(table.get_rows()):
|
||||
style = body_style_even
|
||||
currency_style = body_style_even_currency
|
||||
if index == 0 or index == 1:
|
||||
style = header_style
|
||||
elif index % 2 == 0:
|
||||
style = body_style_even
|
||||
elif index == len(table.get_rows()) - 1:
|
||||
style = footer_style
|
||||
currency_style = footer_style_currency
|
||||
elif index % 2 == 0:
|
||||
style = body_style_even
|
||||
currency_style = body_style_even_currency
|
||||
else:
|
||||
style = body_style_odd
|
||||
for cell in row.get_cells():
|
||||
cell.style = style
|
||||
currency_style = body_style_odd_currency
|
||||
for cell_index, cell in enumerate(row.get_cells()):
|
||||
if cell_index in currency_cols and not (index == 0 or index == 1):
|
||||
cell.style = currency_style
|
||||
else:
|
||||
cell.style = style
|
||||
|
||||
|
||||
def apply_column_height_style(document: odfdo.Document, table: odfdo.Table):
|
||||
@@ -191,6 +241,12 @@ def apply_column_height_style(document: odfdo.Document, table: odfdo.Table):
|
||||
else:
|
||||
row.style = body_style
|
||||
|
||||
def apply_cell_style_by_column(table: odfdo.Table, style: odfdo.Style, col_index: int):
|
||||
for cell in table.get_column_cells(col_index):
|
||||
print(cell.style)
|
||||
cell.style = style
|
||||
print(cell.serialize())
|
||||
|
||||
|
||||
def apply_column_width_style(document: odfdo.Document, table: odfdo.Table, widths: list[str]):
|
||||
"""Apply column width style to a table.
|
||||
@@ -256,6 +312,31 @@ def compute_contract_prices(contract: models.Contract) -> dict:
|
||||
return prices
|
||||
|
||||
|
||||
def transform_formula_cells(sheet: odfdo.Spreadsheet):
|
||||
for row in sheet.get_rows():
|
||||
for cell in row.get_cells():
|
||||
if not cell.value or cell.get_attribute("office:value-type") == "float":
|
||||
continue
|
||||
if '=' in cell.value:
|
||||
formula = cell.value
|
||||
cell.clear()
|
||||
cell.formula = formula
|
||||
|
||||
|
||||
def merge_shipment_cells(
|
||||
sheet: odfdo.Spreadsheet,
|
||||
prefix_header: list[str],
|
||||
recurrents: list[str],
|
||||
occasionnals: list[str],
|
||||
shipments: list[models.Shipment]
|
||||
):
|
||||
index = len(prefix_header) + len(recurrents) + 1
|
||||
for _ in enumerate(shipments):
|
||||
startcol = index
|
||||
endcol = index+len(occasionnals) - 1
|
||||
sheet.set_span((startcol, 0, endcol, 0), merge=True)
|
||||
index += len(occasionnals)
|
||||
|
||||
def generate_recap(
|
||||
contracts: list[models.Contract],
|
||||
form: models.Form,
|
||||
@@ -266,13 +347,13 @@ def generate_recap(
|
||||
'3': 'Piece'
|
||||
}
|
||||
recurrents = [
|
||||
f'{pr.name}({product_unit_map[pr.unit]})'
|
||||
f'{pr.name}{f' - {pr.quantity}{pr.quantity_unit}' if pr.quantity else ''} ({product_unit_map[pr.unit]})'
|
||||
for pr in form.productor.products
|
||||
if pr.type == models.ProductType.RECCURENT
|
||||
]
|
||||
recurrents.sort()
|
||||
occasionnals = [
|
||||
f'{pr.name}({product_unit_map[pr.unit]})'
|
||||
f'{pr.name}{f' - {pr.quantity}{pr.quantity_unit}' if pr.quantity else ''} ({product_unit_map[pr.unit]})'
|
||||
for pr in form.productor.products
|
||||
if pr.type == models.ProductType.OCCASIONAL
|
||||
]
|
||||
@@ -292,7 +373,6 @@ def generate_recap(
|
||||
info_header +
|
||||
payment_header
|
||||
)
|
||||
|
||||
suffix_header: list[str] = [
|
||||
'Total produits occasionnels',
|
||||
'Remarques',
|
||||
@@ -326,9 +406,6 @@ def generate_recap(
|
||||
len(info_header)+len(payment_formula_letters) +
|
||||
len(recurent_formula_letters)+len(occasionnals_header) + 1
|
||||
]
|
||||
print(payment_formula_letters)
|
||||
print(recurent_formula_letters)
|
||||
print(occasionnals_formula_letters)
|
||||
|
||||
footer = (
|
||||
['', 'Total contrats', ''] +
|
||||
@@ -340,29 +417,39 @@ def generate_recap(
|
||||
for letter in occasionnals_formula_letters]
|
||||
)
|
||||
|
||||
main_data = []
|
||||
for index, contract in enumerate(contracts):
|
||||
prices = compute_contract_prices(contract)
|
||||
occasionnal_sorted = sorted(
|
||||
[product for product in contract.products if product.product.type == models.ProductType.OCCASIONAL],
|
||||
key=lambda x: (x.shipment.name, x.product.name)
|
||||
)
|
||||
recurrent_sorted = sorted(
|
||||
[product for product in contract.products if product.product.type == models.ProductType.RECCURENT],
|
||||
key=lambda x: x.product.name
|
||||
)
|
||||
|
||||
main_data.append([
|
||||
f'{index + 1}',
|
||||
f'{contract.firstname} {contract.lastname}',
|
||||
f'{contract.email}',
|
||||
*[float(contract.cheques[i].value)
|
||||
if len(contract.cheques) > i
|
||||
else ''
|
||||
for i in range(3)],
|
||||
prices['total'],
|
||||
*[pr.quantity for pr in recurrent_sorted],
|
||||
prices['recurrent'],
|
||||
*[pr.quantity for pr in occasionnal_sorted],
|
||||
prices['occasionnal'],
|
||||
'',
|
||||
f'{contract.firstname} {contract.lastname}',
|
||||
])
|
||||
|
||||
data = [
|
||||
[''] * (len(prefix_header) + len(recurrents) + 1) + shipment_header,
|
||||
header,
|
||||
*[
|
||||
[
|
||||
f'{index + 1}',
|
||||
f'{contract.firstname} {contract.lastname}',
|
||||
f'{contract.email}',
|
||||
*[float(contract.cheques[i].value) if len(
|
||||
contract.cheques) > i else '' for i in range(3)],
|
||||
compute_contract_prices(contract)['total'],
|
||||
*[pr.quantity for pr in sorted(
|
||||
contract.products, key=lambda x: x.product.name)
|
||||
if pr.product.type == models.ProductType.RECCURENT],
|
||||
compute_contract_prices(contract)['recurrent'],
|
||||
*[pr.quantity for pr in sorted(
|
||||
contract.products, key=lambda x: x.product.name)
|
||||
if pr.product.type == models.ProductType.OCCASIONAL],
|
||||
compute_contract_prices(contract)['occasionnal'],
|
||||
'',
|
||||
f'{contract.firstname} {contract.lastname}',
|
||||
] for index, contract in enumerate(contracts)
|
||||
],
|
||||
*main_data,
|
||||
footer
|
||||
]
|
||||
|
||||
@@ -371,41 +458,45 @@ def generate_recap(
|
||||
sheet.name = 'Recap'
|
||||
sheet.set_values(data)
|
||||
|
||||
index = len(prefix_header) + len(recurrents) + 1
|
||||
for _ in enumerate(shipments):
|
||||
startcol = index
|
||||
endcol = index+len(occasionnals) - 1
|
||||
sheet.set_span((startcol, 0, endcol, 0), merge=True)
|
||||
index += len(occasionnals)
|
||||
if len(occasionnals) > 0:
|
||||
merge_shipment_cells(
|
||||
sheet,
|
||||
prefix_header,
|
||||
recurrents,
|
||||
occasionnals,
|
||||
shipments
|
||||
)
|
||||
|
||||
for row in sheet.get_rows():
|
||||
for cell in row.get_cells():
|
||||
if not cell.value or cell.get_attribute("office:value-type") == "float":
|
||||
continue
|
||||
if '=' in cell.value:
|
||||
formula = cell.value
|
||||
cell.clear()
|
||||
cell.formula = formula
|
||||
transform_formula_cells(sheet)
|
||||
|
||||
apply_column_width_style(
|
||||
doc,
|
||||
doc.body.get_table(0),
|
||||
['2cm'] +
|
||||
['4cm'] * 2 +
|
||||
['6cm'] * 2 +
|
||||
['2.40cm'] * (len(payment_header) - 1) +
|
||||
['4cm'] * len(recurrents) +
|
||||
['4cm'] +
|
||||
['4cm'] * (len(occasionnals_header) + 1) +
|
||||
['4cm', '8cm', '4cm']
|
||||
['4cm', '8cm', '6cm']
|
||||
)
|
||||
apply_column_height_style(
|
||||
doc,
|
||||
doc.body.get_table(0),
|
||||
)
|
||||
apply_cell_style(doc, doc.body.get_table(0))
|
||||
apply_cell_style(
|
||||
doc,
|
||||
doc.body.get_table(0),
|
||||
[
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
len(info_header) + len(payment_header),
|
||||
len(info_header) + len(payment_header) + 1 + len(occasionnals),
|
||||
]
|
||||
)
|
||||
doc.body.append(sheet)
|
||||
|
||||
buffer = io.BytesIO()
|
||||
doc.save(buffer)
|
||||
# doc.save('test.ods')
|
||||
return buffer.getvalue()
|
||||
|
||||
Reference in New Issue
Block a user