diff --git a/server/vbv_lernwelt/shop/invoice/abacus.py b/server/vbv_lernwelt/shop/invoice/abacus.py index 059c6e3b..a92b1056 100644 --- a/server/vbv_lernwelt/shop/invoice/abacus.py +++ b/server/vbv_lernwelt/shop/invoice/abacus.py @@ -6,6 +6,7 @@ from xml.etree.ElementTree import Element, SubElement, tostring from vbv_lernwelt.shop.invoice.creator import InvoiceCreator, Item from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository +from vbv_lernwelt.shop.models import CheckoutInformation class AbacusInvoiceCreator(InvoiceCreator): @@ -14,18 +15,25 @@ class AbacusInvoiceCreator(InvoiceCreator): def create_invoice( self, - customer_number: str, - purchase_order_date: datetime.date, - delivery_date: datetime.date, - reference_purchase_order: str, - unic_id: str, - items: List[Item], + checkout_information: CheckoutInformation, filename: str = None, ): - invoice = self.render_invoice( + customer_number = checkout_information.transaction_id + order_date = checkout_information.created_at.date() + reference_purchase_order = str(checkout_information.id) + unic_id = checkout_information.transaction_id + + items = [ + Item( + product_number=checkout_information.product_sku, + quantity=1, + description=checkout_information.product_description, + ) + ] + + invoice = self.invoice_xml( customer_number, - purchase_order_date, - delivery_date, + order_date, reference_purchase_order, unic_id, items, @@ -37,10 +45,9 @@ class AbacusInvoiceCreator(InvoiceCreator): self.repository.upload_invoice(invoice, filename) @staticmethod - def render_invoice( + def invoice_xml( customer_number: str, - purchase_order_date: datetime.date, - delivery_date: datetime.date, + order_date: datetime.date, reference_purchase_order: str, unic_id: str, items: List[Item], @@ -62,10 +69,10 @@ class AbacusInvoiceCreator(InvoiceCreator): SubElement(sales_order_header_fields, "CustomerNumber").text = customer_number SubElement( sales_order_header_fields, "PurchaseOrderDate" - ).text = purchase_order_date.isoformat() + ).text = order_date.isoformat() SubElement( sales_order_header_fields, "DeliveryDate" - ).text = delivery_date.isoformat() + ).text = order_date.isoformat() SubElement( sales_order_header_fields, "ReferencePurchaseOrder" ).text = reference_purchase_order @@ -76,7 +83,7 @@ class AbacusInvoiceCreator(InvoiceCreator): item_fields = SubElement(item_element, "ItemFields", mode="SAVE") SubElement(item_fields, "ItemNumber").text = str(index) SubElement(item_fields, "ProductNumber").text = item.product_number - SubElement(item_fields, "QuantityOrdered").text = item.quantity + SubElement(item_fields, "QuantityOrdered").text = str(item.quantity) item_text = SubElement(item_element, "ItemText", mode="SAVE") item_text_fields = SubElement(item_text, "ItemTextFields", mode="SAVE") @@ -84,6 +91,52 @@ class AbacusInvoiceCreator(InvoiceCreator): return AbacusInvoiceCreator.create_xml_string(container) + @staticmethod + def customer_xml( + customer_number: str, + name: str, + first_name: str, + address_text: 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 = customer_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 = customer_number + SubElement(address_data, "Name").text = name + SubElement(address_data, "FirstName").text = first_name + SubElement(address_data, "Text").text = address_text + 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) + @staticmethod def create_xml_string(container: Element, encoding: str = "UTF-8") -> str: xml_bytes = tostring(container, encoding) diff --git a/server/vbv_lernwelt/shop/invoice/creator.py b/server/vbv_lernwelt/shop/invoice/creator.py index c8758e91..85c08475 100644 --- a/server/vbv_lernwelt/shop/invoice/creator.py +++ b/server/vbv_lernwelt/shop/invoice/creator.py @@ -1,13 +1,13 @@ -import datetime from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import List + +from vbv_lernwelt.shop.models import CheckoutInformation @dataclass class Item: product_number: str - quantity: str + quantity: int description: str @@ -15,11 +15,7 @@ class InvoiceCreator(ABC): @abstractmethod def create_invoice( self, - customer_number: str, - purchase_order_date: datetime.date, - delivery_date: datetime.date, - reference_purchase_order: str, - unic_id: str, - items: List[Item], + checkout_information: CheckoutInformation, + filename: str = None, ): pass diff --git a/server/vbv_lernwelt/shop/invoice/repositories.py b/server/vbv_lernwelt/shop/invoice/repositories.py index 5f599b22..df231324 100644 --- a/server/vbv_lernwelt/shop/invoice/repositories.py +++ b/server/vbv_lernwelt/shop/invoice/repositories.py @@ -1,5 +1,9 @@ from abc import ABC, abstractmethod +import structlog + +logger = structlog.get_logger(__name__) + class InvoiceRepository(ABC): @abstractmethod @@ -35,6 +39,6 @@ class SFTPInvoiceRepository(InvoiceRepository): sftp_client.putfo(invoice_io, f"uploads/{filename}") except Exception as e: - print(f"An error occurred: {e}") + logger.error("Could not upload invoice", exc_info=e) finally: ssh_client.close() diff --git a/server/vbv_lernwelt/shop/tests/test_invoice.py b/server/vbv_lernwelt/shop/tests/test_invoice.py index ffc4936f..538aeaf3 100644 --- a/server/vbv_lernwelt/shop/tests/test_invoice.py +++ b/server/vbv_lernwelt/shop/tests/test_invoice.py @@ -1,71 +1,86 @@ from datetime import date 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.shop.invoice.repositories import InvoiceRepository +from vbv_lernwelt.shop.models import CheckoutInformation + +USER_USERNAME = "testuser" +USER_EMAIL = "test@example.com" +USER_PASSWORD = "testpassword" -def test_render_invoice(): - # GIVEN - creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository)) - items = [Item(product_number="001", quantity="10", description="Test Item")] - customer_number = "12345" - purchase_order_date = date(2023, 1, 1) - delivery_date = date(2023, 1, 10) - reference_purchase_order = "PO12345678" - unic_id = "UNIC001" +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, + ) - # WHEN - invoice_xml = creator.render_invoice( - customer_number, - purchase_order_date, - delivery_date, - reference_purchase_order, - unic_id, - items, - ) + def test_render_invoice(self): + # GIVEN + creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository)) + items = [Item(product_number="001", quantity=1, description="Test Item")] + customer_number = "12345" + order_date = date(2023, 1, 1) + reference_purchase_order = "PO12345678" + unic_id = "UNIC001" - # THEN - assert "12345" in invoice_xml - assert "1" in invoice_xml - assert "001" in invoice_xml - assert "10" in invoice_xml - assert "Test Item" in invoice_xml + # WHEN + invoice_xml = creator.invoice_xml( + customer_number, + order_date, + reference_purchase_order, + unic_id, + items, + ) + # THEN + assert "12345" in invoice_xml + assert "1" in invoice_xml + assert "001" in invoice_xml + assert "1" in invoice_xml + assert "Test Item" in invoice_xml -def test_create_invoice_calls_upload(): - # GIVEN - repository_mock = create_autospec(InvoiceRepository) + def test_create_invoice_calls_upload(self): + # GIVEN + repository_mock = create_autospec(InvoiceRepository) - creator = AbacusInvoiceCreator(repository=repository_mock) - items = [Item(product_number="001", quantity="10", description="Test Item")] - customer_number = "12345" - purchase_order_date = date(2023, 1, 1) - delivery_date = date(2023, 1, 10) - reference_purchase_order = "PO12345678" - unic_id = "UNIC001" + creator = AbacusInvoiceCreator(repository=repository_mock) - expected_filename = "test.xml" + expected_filename = "test.xml" - # WHEN - creator.create_invoice( - customer_number, - purchase_order_date, - delivery_date, - reference_purchase_order, - unic_id, - items, - filename=expected_filename, - ) + checkout_information = CheckoutInformation.objects.create( + user=self.user, + transaction_id="12345", + product_sku="001", + product_name="Test Product", + product_description="Test Product Description", + product_price=1000, + state="initialized", + ) - # THEN - repository_mock.upload_invoice.assert_called_once() - uploaded_invoice, uploaded_filename = repository_mock.upload_invoice.call_args[0] + # WHEN + creator.create_invoice( + checkout_information=checkout_information, + filename=expected_filename, + ) - assert uploaded_filename == expected_filename - assert "12345" in uploaded_invoice - assert "1" in uploaded_invoice - assert "001" in uploaded_invoice - assert "10" in uploaded_invoice - assert "Test Item" in uploaded_invoice + # THEN + repository_mock.upload_invoice.assert_called_once() + uploaded_invoice, uploaded_filename = repository_mock.upload_invoice.call_args[ + 0 + ] + + assert uploaded_filename == expected_filename + assert "12345" in uploaded_invoice + assert "1" in uploaded_invoice + assert "001" in uploaded_invoice + assert "1" in uploaded_invoice + assert "Test Product Description" in uploaded_invoice