import xml.etree.ElementTree as ET from collections.abc import Callable from dataclasses import dataclass from datetime import datetime from enum import StrEnum, auto from xml.dom import minidom ACCOUNT_INFO = [ "Account", "Make", "Site", "Origin", "SalesChannel", "CostCarrier", "CostAccountingString", ] class GchrExportFormat(StrEnum): SKR51 = auto() Volkswagen = auto() Opel = auto() @dataclass class GchrExportConfig: main_site: str current_year: str current_month: str makes_used: dict[str, str] sites_used: dict[str, str] first_month: str period_no: str bookkeep_filter: dict[str, str] extraction_date: datetime export_file: str bookkeep_records = dict[str, list[str]] def export_skr51_xml(export_cfg: GchrExportConfig): record_elements = ( ACCOUNT_INFO + ["Decimals"] + list(export_cfg.bookkeep_filter.values())[: export_cfg.period_no] + ["CumulatedYear"] ) root = ET.Element("HbvData") h = ET.SubElement(root, "Header") for k, v in export_skr51_header(export_cfg).items(): ET.SubElement(h, k).text = str(v) make_list = ET.SubElement(root, "MakeList") for make, make_code in export_cfg.makes_used.items(): e = ET.SubElement(make_list, "MakeListEntry") ET.SubElement(e, "Make").text = make ET.SubElement(e, "MakeCode").text = make_code bm_code_list = ET.SubElement(root, "BmCodeList") for s, bmcode in export_cfg.sites_used.items(): make, site = s.split("-") e = ET.SubElement(bm_code_list, "BmCodeEntry") ET.SubElement(e, "Make").text = make ET.SubElement(e, "Site").text = site ET.SubElement(e, "BmCode").text = bmcode record_list = ET.SubElement(root, "RecordList") for row in export_cfg.bookkeep_records: record = ET.SubElement(record_list, "Record") for e in record_elements: child = ET.SubElement(record, e) field = row.get(e, 0.0) if str(field) == "nan": field = "0" elif type(field) is float: field = "{:.0f}".format(field * 100) child.text = str(field) with open(export_cfg.export_file, "w", encoding="utf-8") as fwh: fwh.write(minidom.parseString(ET.tostring(root)).toprettyxml(indent=" ")) def export_skr51_header(export_cfg: GchrExportConfig) -> dict[str, str]: return { "Country": "DE", "MainBmCode": export_cfg.main_site, "Month": export_cfg.current_month, "Year": export_cfg.current_year, "Currency": "EUR", "NumberOfMakes": len(export_cfg.makes_used), "NumberOfSites": len(export_cfg.sites_used), "ExtractionDate": export_cfg.extraction_date.strftime("%d.%m.%Y"), "ExtractionTime": export_cfg.extraction_date.strftime("%H:%M:%S"), "BeginFiscalYear": export_cfg.first_month, } def export_volkswagen_xml(export_cfg: GchrExportConfig): header = { "PartnerKey": { "Country": "DEU", "Brand": "V", "PartnerNumber": "21996", }, "IsCumulative": "true", "AccountingDate": { "AccountingMonth": "09", "AccountingYear": "2024", }, "Currency": "EUR", "Level": "1", } record_elements = {"ProfitCenter": "00", "AccountKey": "010600002700000007000", "AccountValue": "+11.00"} ET.register_namespace("tns", "http://xmldefs.volkswagenag.com/Retail/AccountBalanceDTS/V1") root = ET.Element("tns:ShowAccountBalance") root = dict_to_xml(root, header) record_list = ET.SubElement(root, "Accounts") for row in export_cfg.bookkeep_records: record = ET.SubElement(record_list, "Account") for e, v in record_elements.items(): child = ET.SubElement(record, e) field = row.get(e, v) if str(field) == "nan": field = "0" elif type(field) is float: field = "{:.2f}".format(field * -1) child.text = str(field) with open(export_cfg.export_file, "w", encoding="latin-1") as fwh: fwh.write( minidom.parseString(ET.tostring(root, encoding="ISO-8859-1")).toprettyxml(indent=" ", encoding="latin-1") ) GchrExportFn = Callable[[GchrExportConfig], None] EXPORT_FN: dict[GchrExportFormat, GchrExportFn] = { GchrExportFormat.SKR51: export_skr51_xml, GchrExportFormat.Volkswagen: export_volkswagen_xml, } def export_dummy(export_cfg: GchrExportConfig) -> None: print("DUMMY") print(export_cfg.main_site) pass def get_export_fn(export_format: GchrExportFormat) -> GchrExportFn: return EXPORT_FN.get(export_format, export_dummy) def dict_to_xml(root: ET.Element, subtree: dict): # if isinstance(subtree, list): # for item in subtree: # dict_to_xml(root, item) # return root for key, value in subtree.items(): e = ET.SubElement(root, key) if isinstance(value, dict): dict_to_xml(e, value) else: e.text = str(value) return root