feat: invoice customer

This commit is contained in:
Reto Aebersold 2023-11-27 09:52:20 +01:00 committed by Christian Cueni
parent 5d21fd0f42
commit 1281be2221
4 changed files with 147 additions and 79 deletions

View File

@ -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.creator import InvoiceCreator, Item
from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository
from vbv_lernwelt.shop.models import CheckoutInformation
class AbacusInvoiceCreator(InvoiceCreator): class AbacusInvoiceCreator(InvoiceCreator):
@ -14,18 +15,25 @@ class AbacusInvoiceCreator(InvoiceCreator):
def create_invoice( def create_invoice(
self, self,
customer_number: str, checkout_information: CheckoutInformation,
purchase_order_date: datetime.date,
delivery_date: datetime.date,
reference_purchase_order: str,
unic_id: str,
items: List[Item],
filename: str = None, 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, customer_number,
purchase_order_date, order_date,
delivery_date,
reference_purchase_order, reference_purchase_order,
unic_id, unic_id,
items, items,
@ -37,10 +45,9 @@ class AbacusInvoiceCreator(InvoiceCreator):
self.repository.upload_invoice(invoice, filename) self.repository.upload_invoice(invoice, filename)
@staticmethod @staticmethod
def render_invoice( def invoice_xml(
customer_number: str, customer_number: str,
purchase_order_date: datetime.date, order_date: datetime.date,
delivery_date: datetime.date,
reference_purchase_order: str, reference_purchase_order: str,
unic_id: str, unic_id: str,
items: List[Item], items: List[Item],
@ -62,10 +69,10 @@ class AbacusInvoiceCreator(InvoiceCreator):
SubElement(sales_order_header_fields, "CustomerNumber").text = customer_number SubElement(sales_order_header_fields, "CustomerNumber").text = customer_number
SubElement( SubElement(
sales_order_header_fields, "PurchaseOrderDate" sales_order_header_fields, "PurchaseOrderDate"
).text = purchase_order_date.isoformat() ).text = order_date.isoformat()
SubElement( SubElement(
sales_order_header_fields, "DeliveryDate" sales_order_header_fields, "DeliveryDate"
).text = delivery_date.isoformat() ).text = order_date.isoformat()
SubElement( SubElement(
sales_order_header_fields, "ReferencePurchaseOrder" sales_order_header_fields, "ReferencePurchaseOrder"
).text = reference_purchase_order ).text = reference_purchase_order
@ -76,7 +83,7 @@ class AbacusInvoiceCreator(InvoiceCreator):
item_fields = SubElement(item_element, "ItemFields", mode="SAVE") item_fields = SubElement(item_element, "ItemFields", mode="SAVE")
SubElement(item_fields, "ItemNumber").text = str(index) SubElement(item_fields, "ItemNumber").text = str(index)
SubElement(item_fields, "ProductNumber").text = item.product_number 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 = SubElement(item_element, "ItemText", mode="SAVE")
item_text_fields = SubElement(item_text, "ItemTextFields", mode="SAVE") item_text_fields = SubElement(item_text, "ItemTextFields", mode="SAVE")
@ -84,6 +91,52 @@ class AbacusInvoiceCreator(InvoiceCreator):
return AbacusInvoiceCreator.create_xml_string(container) 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 @staticmethod
def create_xml_string(container: Element, encoding: str = "UTF-8") -> str: def create_xml_string(container: Element, encoding: str = "UTF-8") -> str:
xml_bytes = tostring(container, encoding) xml_bytes = tostring(container, encoding)

View File

@ -1,13 +1,13 @@
import datetime
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from dataclasses import dataclass from dataclasses import dataclass
from typing import List
from vbv_lernwelt.shop.models import CheckoutInformation
@dataclass @dataclass
class Item: class Item:
product_number: str product_number: str
quantity: str quantity: int
description: str description: str
@ -15,11 +15,7 @@ class InvoiceCreator(ABC):
@abstractmethod @abstractmethod
def create_invoice( def create_invoice(
self, self,
customer_number: str, checkout_information: CheckoutInformation,
purchase_order_date: datetime.date, filename: str = None,
delivery_date: datetime.date,
reference_purchase_order: str,
unic_id: str,
items: List[Item],
): ):
pass pass

View File

@ -1,5 +1,9 @@
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
import structlog
logger = structlog.get_logger(__name__)
class InvoiceRepository(ABC): class InvoiceRepository(ABC):
@abstractmethod @abstractmethod
@ -35,6 +39,6 @@ class SFTPInvoiceRepository(InvoiceRepository):
sftp_client.putfo(invoice_io, f"uploads/{filename}") sftp_client.putfo(invoice_io, f"uploads/{filename}")
except Exception as e: except Exception as e:
print(f"An error occurred: {e}") logger.error("Could not upload invoice", exc_info=e)
finally: finally:
ssh_client.close() ssh_client.close()

View File

@ -1,71 +1,86 @@
from datetime import date from datetime import date
from unittest.mock import create_autospec 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.abacus import AbacusInvoiceCreator
from vbv_lernwelt.shop.invoice.creator import Item from vbv_lernwelt.shop.invoice.creator import Item
from vbv_lernwelt.shop.invoice.repositories import InvoiceRepository 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(): class InvoiceTestCase(TestCase):
# GIVEN def setUp(self) -> None:
creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository)) self.user = User.objects.create_user(
items = [Item(product_number="001", quantity="10", description="Test Item")] username=USER_USERNAME,
customer_number = "12345" email=USER_EMAIL,
purchase_order_date = date(2023, 1, 1) password=USER_PASSWORD,
delivery_date = date(2023, 1, 10) is_active=True,
reference_purchase_order = "PO12345678" )
unic_id = "UNIC001"
# WHEN def test_render_invoice(self):
invoice_xml = creator.render_invoice( # GIVEN
customer_number, creator = AbacusInvoiceCreator(repository=create_autospec(InvoiceRepository))
purchase_order_date, items = [Item(product_number="001", quantity=1, description="Test Item")]
delivery_date, customer_number = "12345"
reference_purchase_order, order_date = date(2023, 1, 1)
unic_id, reference_purchase_order = "PO12345678"
items, unic_id = "UNIC001"
)
# THEN # WHEN
assert "<CustomerNumber>12345</CustomerNumber>" in invoice_xml invoice_xml = creator.invoice_xml(
assert "<ItemNumber>1</ItemNumber>" in invoice_xml customer_number,
assert "<ProductNumber>001</ProductNumber>" in invoice_xml order_date,
assert "<QuantityOrdered>10</QuantityOrdered>" in invoice_xml reference_purchase_order,
assert "<Text>Test Item</Text>" in invoice_xml unic_id,
items,
)
# THEN
assert "<CustomerNumber>12345</CustomerNumber>" in invoice_xml
assert "<ItemNumber>1</ItemNumber>" in invoice_xml
assert "<ProductNumber>001</ProductNumber>" in invoice_xml
assert "<QuantityOrdered>1</QuantityOrdered>" in invoice_xml
assert "<Text>Test Item</Text>" in invoice_xml
def test_create_invoice_calls_upload(): def test_create_invoice_calls_upload(self):
# GIVEN # GIVEN
repository_mock = create_autospec(InvoiceRepository) repository_mock = create_autospec(InvoiceRepository)
creator = AbacusInvoiceCreator(repository=repository_mock) 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"
expected_filename = "test.xml" expected_filename = "test.xml"
# WHEN checkout_information = CheckoutInformation.objects.create(
creator.create_invoice( user=self.user,
customer_number, transaction_id="12345",
purchase_order_date, product_sku="001",
delivery_date, product_name="Test Product",
reference_purchase_order, product_description="Test Product Description",
unic_id, product_price=1000,
items, state="initialized",
filename=expected_filename, )
)
# THEN # WHEN
repository_mock.upload_invoice.assert_called_once() creator.create_invoice(
uploaded_invoice, uploaded_filename = repository_mock.upload_invoice.call_args[0] checkout_information=checkout_information,
filename=expected_filename,
)
assert uploaded_filename == expected_filename # THEN
assert "<CustomerNumber>12345</CustomerNumber>" in uploaded_invoice repository_mock.upload_invoice.assert_called_once()
assert "<ItemNumber>1</ItemNumber>" in uploaded_invoice uploaded_invoice, uploaded_filename = repository_mock.upload_invoice.call_args[
assert "<ProductNumber>001</ProductNumber>" in uploaded_invoice 0
assert "<QuantityOrdered>10</QuantityOrdered>" in uploaded_invoice ]
assert "<Text>Test Item</Text>" in uploaded_invoice
assert uploaded_filename == expected_filename
assert "<CustomerNumber>12345</CustomerNumber>" in uploaded_invoice
assert "<ItemNumber>1</ItemNumber>" in uploaded_invoice
assert "<ProductNumber>001</ProductNumber>" in uploaded_invoice
assert "<QuantityOrdered>1</QuantityOrdered>" in uploaded_invoice
assert "<Text>Test Product Description</Text>" in uploaded_invoice