Source code for pain001.xml.create_xml_v3

# Copyright (C) 2023-2026 Sebastien Rousseau.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This module contains the function `create_xml_v3`, which constructs an XML tree
for the ISO 20022 pain.001.001.03 schema.

The function takes in a root ElementTree element and a list of dictionaries
containing the required data. It then uses Jinja2 templating to dynamically
generate the XML content based on the given data. The function ultimately
returns the root element of the modified XML tree.
"""

# pylint: disable=duplicate-code

import xml.etree.ElementTree as et  # nosec B405
from pathlib import Path
from typing import Any

import defusedxml.ElementTree as defused_et
from jinja2 import Environment, FileSystemLoader

from pain001.security.path_validator import validate_path


[docs] def create_xml_v3(root: et.Element, data: list[dict[str, Any]]) -> et.Element: """ Constructs an XML tree based on the pain.001.001.03 schema and appends it to the provided root element. This function uses the Jinja2 templating engine to generate the XML content. Parameters ---------- root : xml.etree.ElementTree.Element The root element of the XML tree to which the new elements will be appended. data : list of dict A list of dictionaries, where each dictionary contains the data to be added to the XML document. The first dictionary usually contains common attributes that are used throughout the XML, while the subsequent dictionaries contain transaction-specific attributes. Returns ------- xml.etree.ElementTree.Element The updated root element of the XML tree. """ # Create the "CstmrCdtTrfInitn" element and append it to the root cstmr_cdt_trf_initn_element = et.Element("CstmrCdtTrfInitn") root.append(cstmr_cdt_trf_initn_element) # Initialize the Jinja2 environment with package-relative path template_dir = Path(__file__).parent.parent / "templates" validate_path(template_dir, must_exist=True) env = Environment( loader=FileSystemLoader(str(template_dir)), autoescape=True ) # Load the Jinja2 template for the pain.001.001.03 schema template = env.get_template("pain.001.001.03/template.xml") # Prepare the data dictionary for rendering through the Jinja2 template # This dictionary is a reformatted version of the `data` parameter, made to # fit the template's requirements. xml_data_pain001_001_03 = { "id": data[0]["id"], "date": data[0]["date"], "nb_of_txs": data[0]["nb_of_txs"], "initiator_name": data[0]["initiator_name"], "initiator_street_name": data[0]["initiator_street_name"], "initiator_building_number": data[0]["initiator_building_number"], "initiator_postal_code": data[0]["initiator_postal_code"], "initiator_town_name": data[0]["initiator_town_name"], "initiator_country_code": data[0]["initiator_country_code"], "payment_id": data[0]["payment_id"], "payment_method": data[0]["payment_method"], "batch_booking": data[0]["batch_booking"], "requested_execution_date": data[0]["requested_execution_date"], "debtor_name": data[0]["debtor_name"], "debtor_street_name": data[0]["debtor_street_name"], "debtor_building_number": data[0]["debtor_building_number"], "debtor_postal_code": data[0]["debtor_postal_code"], "debtor_town_name": data[0]["debtor_town_name"], "debtor_country_code": data[0]["debtor_country_code"], "debtor_account_IBAN": data[0]["debtor_account_IBAN"], "debtor_agent_BIC": data[0]["debtor_agent_BIC"], "charge_bearer": data[0]["charge_bearer"], "transactions": [ { "payment_id": row["payment_id"], "payment_amount": row.get("payment_amount", ""), "payment_currency": row.get("payment_currency", ""), "charge_bearer": row["charge_bearer"], "creditor_agent_BIC": row["creditor_agent_BIC"], "creditor_name": row["creditor_name"], "creditor_street_name": row["creditor_street_name"], "creditor_building_number": row["creditor_building_number"], "creditor_postal_code": row["creditor_postal_code"], "creditor_town_name": row["creditor_town_name"], "creditor_country_code": row["creditor_country_code"], "creditor_account_IBAN": row["creditor_account_IBAN"], "purpose_code": row["purpose_code"], "reference_number": row["reference_number"], "reference_date": row["reference_date"], } for row in data[1:] ], } # Render the XML content using the Jinja2 template and the prepared data xml_content = template.render(**xml_data_pain001_001_03) # Parse the rendered XML content into an ElementTree object rendered_xml_tree = defused_et.fromstring(xml_content) # Append the rendered XML content as children to the "CstmrCdtTrfInitn" # element for child in rendered_xml_tree: cstmr_cdt_trf_initn_element.append(child) return root