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
from typing import List
from uuid import uuid4
from xml.dom import minidom
from xml.etree.ElementTree import Element, SubElement, tostring
@ -44,120 +43,165 @@ class AbacusInvoiceCreator(InvoiceCreator):
self.repository.upload_invoice(invoice, filename)
def create_invoice_xml(self, checkout_information: CheckoutInformation):
pass
@staticmethod
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"
def create_invoice_xml(checkout_information: CheckoutInformation):
# set fill abacus numbers
checkout_information = checkout_information.set_increment_abacus_order_id()
customer = checkout_information.user.set_increment_abacus_debitor_number()
transaction = SubElement(task, "Transaction")
sales_order_header = SubElement(transaction, "SalesOrderHeader", mode="SAVE")
sales_order_header_fields = SubElement(
sales_order_header, "SalesOrderHeaderFields", mode="SAVE"
)
invoice_xml_content = render_invoice_xml(
abacus_debitor_number=customer.abacus_debitor_number,
abacus_order_id=checkout_information.abacus_order_id,
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.
SubElement(sales_order_header_fields, "CustomerNumber").text = str(
abacus_debitor_number
)
# YYYYMMDDhhmmss
filename_datetime = checkout_information.created_at.strftime("%Y%m%d%H%M%S")
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
SubElement(sales_order_header_fields, "ProcessFlowNumber").text = "30"
return invoice_xml_filename, invoice_xml_content
# Skender: ePayment: Zahlungskondition für ePaymente in Abacus 9999 Tage Mahnungsfrist, da schon bezahlt
SubElement(sales_order_header_fields, "PaymentCode").text = "9999"
# Skender: Bestellzeitpunkt
SubElement(
sales_order_header_fields, "PurchaseOrderDate"
).text = order_date.isoformat()
def create_customer_xml(checkout_information: CheckoutInformation):
customer = checkout_information.user.set_increment_abacus_debitor_number()
# Skender: ePayment: TRANSACTION-ID von Datatrans in Bestellreferenz
SubElement(
sales_order_header_fields, "ReferencePurchaseOrder"
).text = datatrans_transaction_id
customer_xml_content = render_customer_xml(
abacus_debitor_number=customer.abacus_debitor_number,
last_name=checkout_information.last_name,
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)
SubElement(sales_order_header_fields, "GroupingNumberAscii1").text = str(
abacus_order_id
)
customer_xml_filename = f"myVBV_debi_{customer.abacus_debitor_number}.xml"
item_element = SubElement(sales_order_header, "Item", mode="SAVE")
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"
return customer_xml_filename, customer_xml_content
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
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")
transaction = SubElement(task, "Transaction")
sales_order_header = SubElement(transaction, "SalesOrderHeader", mode="SAVE")
sales_order_header_fields = SubElement(
sales_order_header, "SalesOrderHeaderFields", mode="SAVE"
)
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"
# 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(sales_order_header_fields, "CustomerNumber").text = str(
abacus_debitor_number
)
transaction = SubElement(task, "Transaction")
customer_element = SubElement(transaction, "Customer", mode="SAVE")
# Skender: ePayment: Ablaufnr. für ePayment Rechnung in Abacus
SubElement(sales_order_header_fields, "ProcessFlowNumber").text = "30"
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"
# Skender: ePayment: Zahlungskondition für ePaymente in Abacus 9999 Tage Mahnungsfrist, da schon bezahlt
SubElement(sales_order_header_fields, "PaymentCode").text = "9999"
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
# Skender: Bestellzeitpunkt
SubElement(
sales_order_header_fields, "PurchaseOrderDate"
).text = order_date.isoformat()
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
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)
# Skender: ePayment: OrderID. max 10 Ziffern, erste Ziffer abhängig von der Plattform (4 = LMS, 6 = myVBV, 7 = EduManager)
SubElement(sales_order_header_fields, "GroupingNumberAscii1").text = str(
abacus_order_id
)
item_element = SubElement(sales_order_header, "Item", mode="SAVE")
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_price = 324_30
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 django.test import TestCase
from vbv_lernwelt.core.admin import User
from vbv_lernwelt.shop.invoice.abacus import AbacusInvoiceCreator
from vbv_lernwelt.shop.invoice.creator import Item
from vbv_lernwelt.core.create_default_users import create_default_users
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.models import CheckoutInformation
from vbv_lernwelt.shop.tests.factories import CheckoutInformationFactory
USER_USERNAME = "testuser"
USER_EMAIL = "test@example.com"
USER_PASSWORD = "testpassword"
class InvoiceTestCase(TestCase):
def setUp(self) -> None:
self.user = User.objects.create_user(
username=USER_USERNAME,
email=USER_EMAIL,
password=USER_PASSWORD,
is_active=True,
class AbacusInvoiceTestCase(TestCase):
def setUp(self):
create_default_users()
def test_create_invoice_xml(self):
# set abacus_number before
_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):
# GIVEN
creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository))
# WHEN
invoice_xml = creator.render_invoice_xml(
invoice_xml = render_invoice_xml(
abacus_debitor_number=60000012,
abacus_order_id=6000000001,
order_date=date(2024, 2, 15),
@ -75,17 +115,56 @@ class InvoiceTestCase(TestCase):
</AbaConnectContainer>
"""
# THEN
self.maxDiff = None
self.assertXMLEqual(expected_xml, invoice_xml)
def test_render_customer_xml(self):
# GIVEN
creator = AbacusInvoiceCreator(
repository=create_autospec(InvoiceRepository))
def test_create_customer_xml_with_company_address(self):
_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
)
# WHEN
customer_xml = creator.render_customer_xml(
feuz = User.objects.get(username="andreas.feuz@eiger-versicherungen.ch")
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,
last_name="Gebhart-Krasniqi",
first_name="Skender",
@ -96,11 +175,9 @@ class InvoiceTestCase(TestCase):
city="Bern",
country="CH",
language="de",
email="skender.krasniqi@vbv-afa.ch"
email="skender.krasniqi@vbv-afa.ch",
)
print(customer_xml)
# example from Skender
expected_xml = """<?xml version="1.0" encoding="utf-8"?>
<AbaConnectContainer>
@ -136,7 +213,6 @@ class InvoiceTestCase(TestCase):
</AbaConnectContainer>
"""
# THEN
self.maxDiff = None
self.assertXMLEqual(expected_xml, customer_xml)