Refactor abacus XML code

This commit is contained in:
Daniel Egger 2024-05-30 11:20:32 +02:00
parent 590b6a17bc
commit 8ce7f9935e
3 changed files with 248 additions and 126 deletions

View File

@ -1,5 +1,4 @@
import datetime import datetime
from typing import List
from uuid import uuid4 from uuid import uuid4
from xml.dom import minidom from xml.dom import minidom
from xml.etree.ElementTree import Element, SubElement, tostring from xml.etree.ElementTree import Element, SubElement, tostring
@ -44,120 +43,165 @@ class AbacusInvoiceCreator(InvoiceCreator):
self.repository.upload_invoice(invoice, filename) self.repository.upload_invoice(invoice, filename)
def create_invoice_xml(self, checkout_information: CheckoutInformation):
pass
@staticmethod def create_invoice_xml(checkout_information: CheckoutInformation):
def render_invoice_xml( # set fill abacus numbers
abacus_debitor_number: int, checkout_information = checkout_information.set_increment_abacus_order_id()
abacus_order_id: int, customer = checkout_information.user.set_increment_abacus_debitor_number()
datatrans_transaction_id: str,
order_date: datetime.date,
item_description: str,
) -> str:
container = Element("AbaConnectContainer")
task = SubElement(container, "Task")
parameter = SubElement(task, "Parameter")
SubElement(parameter, "Application").text = "ORDE"
SubElement(parameter, "Id").text = "Verkaufsauftrag"
SubElement(parameter, "MapId").text = "AbaDefault"
SubElement(parameter, "Version").text = "2022.00"
transaction = SubElement(task, "Transaction") invoice_xml_content = render_invoice_xml(
sales_order_header = SubElement(transaction, "SalesOrderHeader", mode="SAVE") abacus_debitor_number=customer.abacus_debitor_number,
sales_order_header_fields = SubElement( abacus_order_id=checkout_information.abacus_order_id,
sales_order_header, "SalesOrderHeaderFields", mode="SAVE" datatrans_transaction_id=checkout_information.transaction_id,
) order_date=checkout_information.created_at.date(),
# TODO was ist der korrekte text für die item_description?
item_description=f"{checkout_information.product_name} - {checkout_information.created_at.date().isoformat()} - {checkout_information.user.last_name} {checkout_information.user.first_name}",
)
# Skender: Kundennummer, erste Ziffer abhängig von der Plattform (4 = LMS, 6 = myVBV, 7 = EduManager), Plattform führt ein eigenständiges hochzählendes Mapping. # YYYYMMDDhhmmss
SubElement(sales_order_header_fields, "CustomerNumber").text = str( filename_datetime = checkout_information.created_at.strftime("%Y%m%d%H%M%S")
abacus_debitor_number invoice_xml_filename = f"myVBV_orde_{customer.abacus_debitor_number}_{filename_datetime}_{checkout_information.abacus_order_id}.xml"
)
# Skender: ePayment: Ablaufnr. für ePayment Rechnung in Abacus return invoice_xml_filename, invoice_xml_content
SubElement(sales_order_header_fields, "ProcessFlowNumber").text = "30"
# Skender: ePayment: Zahlungskondition für ePaymente in Abacus 9999 Tage Mahnungsfrist, da schon bezahlt
SubElement(sales_order_header_fields, "PaymentCode").text = "9999"
# Skender: Bestellzeitpunkt def create_customer_xml(checkout_information: CheckoutInformation):
SubElement( customer = checkout_information.user.set_increment_abacus_debitor_number()
sales_order_header_fields, "PurchaseOrderDate"
).text = order_date.isoformat()
# Skender: ePayment: TRANSACTION-ID von Datatrans in Bestellreferenz customer_xml_content = render_customer_xml(
SubElement( abacus_debitor_number=customer.abacus_debitor_number,
sales_order_header_fields, "ReferencePurchaseOrder" last_name=checkout_information.last_name,
).text = datatrans_transaction_id first_name=checkout_information.first_name,
company_name=checkout_information.company_name,
street=(checkout_information.company_street or checkout_information.street),
house_number=(
checkout_information.company_street_number
or checkout_information.street_number
),
zip_code=(
checkout_information.company_postal_code or checkout_information.postal_code
),
city=(checkout_information.company_city or checkout_information.city),
country=(checkout_information.company_country or checkout_information.country),
language=customer.language,
email=customer.email,
)
# Skender: ePayment: OrderID. max 10 Ziffern, erste Ziffer abhängig von der Plattform (4 = LMS, 6 = myVBV, 7 = EduManager) customer_xml_filename = f"myVBV_debi_{customer.abacus_debitor_number}.xml"
SubElement(sales_order_header_fields, "GroupingNumberAscii1").text = str(
abacus_order_id
)
item_element = SubElement(sales_order_header, "Item", mode="SAVE") return customer_xml_filename, customer_xml_content
item_fields = SubElement(item_element, "ItemFields", mode="SAVE")
SubElement(item_fields, "DeliveryDate").text = order_date.isoformat()
SubElement(item_fields, "ItemNumber").text = "1"
SubElement(item_fields, "ProductNumber").text = "30202"
SubElement(item_fields, "QuantityOrdered").text = "1"
item_text = SubElement(item_element, "ItemText", mode="SAVE")
item_text_fields = SubElement(item_text, "ItemTextFields", mode="SAVE")
SubElement(item_text_fields, "Text").text = item_description
return AbacusInvoiceCreator.create_xml_string(container) def render_invoice_xml(
abacus_debitor_number: int,
abacus_order_id: int,
datatrans_transaction_id: str,
order_date: datetime.date,
item_description: str,
) -> str:
container = Element("AbaConnectContainer")
task = SubElement(container, "Task")
parameter = SubElement(task, "Parameter")
SubElement(parameter, "Application").text = "ORDE"
SubElement(parameter, "Id").text = "Verkaufsauftrag"
SubElement(parameter, "MapId").text = "AbaDefault"
SubElement(parameter, "Version").text = "2022.00"
@staticmethod transaction = SubElement(task, "Transaction")
def render_customer_xml( sales_order_header = SubElement(transaction, "SalesOrderHeader", mode="SAVE")
abacus_debitor_number: int, sales_order_header_fields = SubElement(
last_name: str, sales_order_header, "SalesOrderHeaderFields", mode="SAVE"
first_name: str, )
company_name: str,
street: str,
house_number: str,
zip_code: str,
city: str,
country: str,
language: str,
email: str,
):
container = Element("AbaConnectContainer")
task = SubElement(container, "Task")
parameter = SubElement(task, "Parameter") # Skender: Kundennummer, erste Ziffer abhängig von der Plattform (4 = LMS, 6 = myVBV, 7 = EduManager), Plattform führt ein eigenständiges hochzählendes Mapping.
SubElement(parameter, "Application").text = "DEBI" SubElement(sales_order_header_fields, "CustomerNumber").text = str(
SubElement(parameter, "ID").text = "Kunden" abacus_debitor_number
SubElement(parameter, "MapID").text = "AbaDefault" )
SubElement(parameter, "Version").text = "2022.00"
transaction = SubElement(task, "Transaction") # Skender: ePayment: Ablaufnr. für ePayment Rechnung in Abacus
customer_element = SubElement(transaction, "Customer", mode="SAVE") SubElement(sales_order_header_fields, "ProcessFlowNumber").text = "30"
SubElement(customer_element, "CustomerNumber").text = str(abacus_debitor_number) # Skender: ePayment: Zahlungskondition für ePaymente in Abacus 9999 Tage Mahnungsfrist, da schon bezahlt
SubElement(customer_element, "DefaultCurrency").text = "CHF" SubElement(sales_order_header_fields, "PaymentCode").text = "9999"
SubElement(customer_element, "PaymentTermNumber").text = "1"
SubElement(customer_element, "ReminderProcedure").text = "NORM"
address_data = SubElement(customer_element, "AddressData", mode="SAVE") # Skender: Bestellzeitpunkt
SubElement(address_data, "AddressNumber").text = str(abacus_debitor_number) SubElement(
SubElement(address_data, "Name").text = last_name sales_order_header_fields, "PurchaseOrderDate"
SubElement(address_data, "FirstName").text = first_name ).text = order_date.isoformat()
SubElement(address_data, "Text").text = company_name
SubElement(address_data, "Street").text = street
SubElement(address_data, "HouseNumber").text = house_number
SubElement(address_data, "ZIP").text = zip_code
SubElement(address_data, "City").text = city
SubElement(address_data, "Country").text = country
SubElement(address_data, "Language").text = language
SubElement(address_data, "Email").text = email
return AbacusInvoiceCreator.create_xml_string(container) # Skender: ePayment: TRANSACTION-ID von Datatrans in Bestellreferenz
SubElement(
sales_order_header_fields, "ReferencePurchaseOrder"
).text = datatrans_transaction_id
@staticmethod # Skender: ePayment: OrderID. max 10 Ziffern, erste Ziffer abhängig von der Plattform (4 = LMS, 6 = myVBV, 7 = EduManager)
def create_xml_string(container: Element, encoding: str = "utf-8") -> str: SubElement(sales_order_header_fields, "GroupingNumberAscii1").text = str(
xml_bytes = tostring(container, encoding) abacus_order_id
xml_pretty_str = minidom.parseString(xml_bytes).toprettyxml( )
indent=" ", encoding=encoding
) item_element = SubElement(sales_order_header, "Item", mode="SAVE")
return xml_pretty_str.decode(encoding) item_fields = SubElement(item_element, "ItemFields", mode="SAVE")
SubElement(item_fields, "DeliveryDate").text = order_date.isoformat()
SubElement(item_fields, "ItemNumber").text = "1"
SubElement(item_fields, "ProductNumber").text = "30202"
SubElement(item_fields, "QuantityOrdered").text = "1"
item_text = SubElement(item_element, "ItemText", mode="SAVE")
item_text_fields = SubElement(item_text, "ItemTextFields", mode="SAVE")
SubElement(item_text_fields, "Text").text = item_description
return create_xml_string(container)
def render_customer_xml(
abacus_debitor_number: int,
last_name: str,
first_name: str,
company_name: str,
street: str,
house_number: str,
zip_code: str,
city: str,
country: str,
language: str,
email: str,
):
container = Element("AbaConnectContainer")
task = SubElement(container, "Task")
parameter = SubElement(task, "Parameter")
SubElement(parameter, "Application").text = "DEBI"
SubElement(parameter, "ID").text = "Kunden"
SubElement(parameter, "MapID").text = "AbaDefault"
SubElement(parameter, "Version").text = "2022.00"
transaction = SubElement(task, "Transaction")
customer_element = SubElement(transaction, "Customer", mode="SAVE")
SubElement(customer_element, "CustomerNumber").text = str(abacus_debitor_number)
SubElement(customer_element, "DefaultCurrency").text = "CHF"
SubElement(customer_element, "PaymentTermNumber").text = "1"
SubElement(customer_element, "ReminderProcedure").text = "NORM"
address_data = SubElement(customer_element, "AddressData", mode="SAVE")
SubElement(address_data, "AddressNumber").text = str(abacus_debitor_number)
SubElement(address_data, "Name").text = last_name
SubElement(address_data, "FirstName").text = first_name
SubElement(address_data, "Text").text = company_name
SubElement(address_data, "Street").text = street
SubElement(address_data, "HouseNumber").text = house_number
SubElement(address_data, "ZIP").text = zip_code
SubElement(address_data, "City").text = city
SubElement(address_data, "Country").text = country
SubElement(address_data, "Language").text = language
SubElement(address_data, "Email").text = email
return create_xml_string(container)
def create_xml_string(container: Element, encoding: str = "utf-8") -> str:
xml_bytes = tostring(container, encoding)
xml_pretty_str = minidom.parseString(xml_bytes).toprettyxml(
indent=" ", encoding=encoding
)
return xml_pretty_str.decode(encoding)

View File

@ -11,3 +11,5 @@ class CheckoutInformationFactory(DjangoModelFactory):
product_sku = VV_DE_PRODUCT_SKU product_sku = VV_DE_PRODUCT_SKU
product_price = 324_30 product_price = 324_30
state = CheckoutState.PAID state = CheckoutState.PAID
product_name = "Versicherungsvermittler/-in VBV"
product_description = "Versicherungsvermittler/-in VBV DE"

View File

@ -1,34 +1,74 @@
from datetime import date from datetime import date, datetime
from unittest.mock import create_autospec from unittest.mock import create_autospec
from django.test import TestCase from django.test import TestCase
from vbv_lernwelt.core.admin import User from vbv_lernwelt.core.admin import User
from vbv_lernwelt.shop.invoice.abacus import AbacusInvoiceCreator from vbv_lernwelt.core.create_default_users import create_default_users
from vbv_lernwelt.shop.invoice.creator import Item from vbv_lernwelt.shop.invoice.abacus import (
AbacusInvoiceCreator,
render_invoice_xml,
render_customer_xml,
create_invoice_xml,
create_customer_xml,
)
from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository
from vbv_lernwelt.shop.models import CheckoutInformation from vbv_lernwelt.shop.models import CheckoutInformation
from vbv_lernwelt.shop.tests.factories import CheckoutInformationFactory
USER_USERNAME = "testuser" USER_USERNAME = "testuser"
USER_EMAIL = "test@example.com" USER_EMAIL = "test@example.com"
USER_PASSWORD = "testpassword" USER_PASSWORD = "testpassword"
class InvoiceTestCase(TestCase): class AbacusInvoiceTestCase(TestCase):
def setUp(self) -> None: def setUp(self):
self.user = User.objects.create_user( create_default_users()
username=USER_USERNAME,
email=USER_EMAIL, def test_create_invoice_xml(self):
password=USER_PASSWORD, # set abacus_number before
is_active=True, _pat = User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch")
_pat.abacus_debitor_number = 60000011
_pat.save()
_ignore_checkout_information = CheckoutInformationFactory(
user=_pat, abacus_order_id=6_000_000_123
)
feuz = User.objects.get(username="andreas.feuz@eiger-versicherungen.ch")
feuz_checkout_info = CheckoutInformationFactory(
user=feuz,
transaction_id="24021508331287484",
)
feuz_checkout_info.created_at = datetime(2024, 2, 15, 8, 33, 12, 0)
invoice_xml_filename, invoice_xml_content = create_invoice_xml(
feuz_checkout_info
)
self.assertEqual(
invoice_xml_filename, "myVBV_orde_60000012_20240215083312_6000000124.xml"
)
# print(invoice_xml_content)
assert "<CustomerNumber>60000012</CustomerNumber>" in invoice_xml_content
assert (
"<PurchaseOrderDate>2024-02-15</PurchaseOrderDate>" in invoice_xml_content
)
assert (
"<GroupingNumberAscii1>6000000124</GroupingNumberAscii1>"
in invoice_xml_content
)
assert (
"<ReferencePurchaseOrder>24021508331287484</ReferencePurchaseOrder>"
in invoice_xml_content
)
assert "<DeliveryDate>2024-02-15</DeliveryDate>" in invoice_xml_content
assert (
"<Text>Versicherungsvermittler/-in VBV - 2024-02-15 - Feuz Andreas</Text>"
in invoice_xml_content
) )
def test_render_invoice_xml(self): def test_render_invoice_xml(self):
# GIVEN invoice_xml = render_invoice_xml(
creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository))
# WHEN
invoice_xml = creator.render_invoice_xml(
abacus_debitor_number=60000012, abacus_debitor_number=60000012,
abacus_order_id=6000000001, abacus_order_id=6000000001,
order_date=date(2024, 2, 15), order_date=date(2024, 2, 15),
@ -75,17 +115,56 @@ class InvoiceTestCase(TestCase):
</AbaConnectContainer> </AbaConnectContainer>
""" """
# THEN
self.maxDiff = None self.maxDiff = None
self.assertXMLEqual(expected_xml, invoice_xml) self.assertXMLEqual(expected_xml, invoice_xml)
def test_render_customer_xml(self): def test_create_customer_xml_with_company_address(self):
# GIVEN _pat = User.objects.get(username="patrizia.huggel@eiger-versicherungen.ch")
creator = AbacusInvoiceCreator( _pat.abacus_debitor_number = 60000011
repository=create_autospec(InvoiceRepository)) _pat.save()
_ignore_checkout_information = CheckoutInformationFactory(
user=_pat, abacus_order_id=6_000_000_123
)
# WHEN feuz = User.objects.get(username="andreas.feuz@eiger-versicherungen.ch")
customer_xml = creator.render_customer_xml( feuz_checkout_info = CheckoutInformationFactory(
user=feuz,
transaction_id="24021508331287484",
first_name="Andreas",
last_name="Feuz",
street="Eggersmatt",
street_number="32",
postal_code="1719",
city="Zumholz",
country="209",
company_name="VBV",
company_street="Laupenstrasse",
company_street_number="10",
company_postal_code="3000",
company_city="Bern",
company_country="209",
)
feuz_checkout_info.created_at = datetime(2024, 2, 15, 8, 33, 12, 0)
customer_xml_filename, customer_xml_content = create_customer_xml(
checkout_information=feuz_checkout_info
)
print(customer_xml_content)
print(customer_xml_filename)
self.assertEqual("myVBV_debi_60000012.xml", customer_xml_filename)
assert "<CustomerNumber>60000012</CustomerNumber>" in customer_xml_content
assert (
"<Email>andreas.feuz@eiger-versicherungen.ch</Email>"
in customer_xml_content
)
assert "<Text>VBV</Text>" in customer_xml_content
# FIXME refactor country
assert "<Country>209</Country>" in customer_xml_content
def test_render_customer_xml(self):
customer_xml = render_customer_xml(
abacus_debitor_number=60000012, abacus_debitor_number=60000012,
last_name="Gebhart-Krasniqi", last_name="Gebhart-Krasniqi",
first_name="Skender", first_name="Skender",
@ -96,11 +175,9 @@ class InvoiceTestCase(TestCase):
city="Bern", city="Bern",
country="CH", country="CH",
language="de", language="de",
email="skender.krasniqi@vbv-afa.ch" email="skender.krasniqi@vbv-afa.ch",
) )
print(customer_xml)
# example from Skender # example from Skender
expected_xml = """<?xml version="1.0" encoding="utf-8"?> expected_xml = """<?xml version="1.0" encoding="utf-8"?>
<AbaConnectContainer> <AbaConnectContainer>
@ -136,7 +213,6 @@ class InvoiceTestCase(TestCase):
</AbaConnectContainer> </AbaConnectContainer>
""" """
# THEN
self.maxDiff = None self.maxDiff = None
self.assertXMLEqual(expected_xml, customer_xml) self.assertXMLEqual(expected_xml, customer_xml)