Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixes Issue #20, Issue #22, Issue #26 and Issue #29. It replaces PR #21 and PR #23 and contains alignments to the v2.2 specification #27

Merged
merged 4 commits into from
Mar 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,14 @@ Generating::
doc.trade.settlement.currency_code = "EUR"
doc.trade.settlement.payment_means.type_code = "ZZZ"

doc.trade.agreement.seller.address.country_id = "DE"
doc.trade.agreement.seller.address.country_subdivision = "Bayern"

doc.trade.agreement.seller_order.issue_date_time = datetime.now(timezone.utc)
doc.trade.agreement.buyer_order.issue_date_time = datetime.now(timezone.utc)
doc.trade.settlement.advance_payment.received_date = datetime.now(timezone.utc)
doc.trade.agreement.customer_order.issue_date_time = datetime.now(timezone.utc)

li = LineItem()
li.document.line_id = "1"
li.product.name = "Rainbow"
Expand Down
2 changes: 1 addition & 1 deletion drafthorse/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
NS_RAM = (
"urn:un:unece:uncefact:data:standard:ReusableAggregateBusinessInformationEntity:100"
)
NS_QDT = "urn:un:unece:uncefact:data:standard:QualifiedDataType:10"
NS_QDT = "urn:un:unece:uncefact:data:standard:QualifiedDataType:100"
BASIC = "BASIC"
COMFORT = "COMFORT"
EXTENDED = "EXTENDED"
71 changes: 15 additions & 56 deletions drafthorse/models/accounting.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,47 +28,6 @@ class Meta:
tag = "BillingSpecifiedPeriod"


class SellerOrderReferencedDocument(Element):
issuer_ID = StringField(NS_RAM, "IssuerAssignedID", profile=COMFORT)
issue_date_time = DateTimeField(
NS_RAM, "FormattedIssueDateTime", required=True, profile=EXTENDED
)

class Meta:
namespace = NS_RAM
tag = "SellerOrderReferencedDocument"


class LineApplicableTradeTax(Element):
calculated_amount = DecimalField(
NS_RAM, "CalculatedAmount", required=True, profile=BASIC, _d="Steuerbetrag"
)
type_code = StringField(
NS_RAM, "TypeCode", required=True, profile=BASIC, _d="Steuerart (Code)"
)
exemption_reason = StringField(
NS_RAM,
"ExemptionReason",
required=False,
profile=COMFORT,
_d="Grund der Steuerbefreiung (Freitext)",
)
category_code = StringField(
NS_RAM,
"CategoryCode",
required=False,
profile=COMFORT,
_d="Steuerkategorie (Wert)",
)
rate_applicable_percent = DecimalField(
NS_RAM, "RateApplicablePercent", required=True, profile=BASIC
)

class Meta:
namespace = NS_RAM
tag = "ApplicableTradeTax"


class ApplicableTradeTax(Element):
calculated_amount = DecimalField(
NS_RAM, "CalculatedAmount", required=True, profile=BASIC, _d="Steuerbetrag"
Expand All @@ -80,17 +39,8 @@ class ApplicableTradeTax(Element):
NS_RAM,
"ExemptionReason",
required=False,
profile=COMFORT,
_d="Grund der Steuerbefreiung (Freitext)",
)
tax_point_date = DateTimeField(
NS_RAM, "TaxPointDate", required=False, profile=COMFORT
)
due_date_type_code = StringField(
NS_RAM,
"DueDateTypeCode",
required=False,
profile=BASIC,
_d="Grund der Steuerbefreiung (Freitext)",
)
basis_amount = DecimalField(
NS_RAM,
Expand All @@ -117,16 +67,25 @@ class ApplicableTradeTax(Element):
NS_RAM,
"CategoryCode",
required=False,
profile=COMFORT,
profile=BASIC,
_d="Steuerkategorie (Wert)",
)
exemption_reason_code = StringField(
NS_RAM,
"ExemptionReasonCode",
required=False,
profile=EXTENDED,
profile=BASIC,
_d="Grund der Steuerbefreiung (Code)",
)
tax_point_date = DateTimeField(
NS_RAM, "TaxPointDate", required=False, profile=COMFORT
)
due_date_type_code = StringField(
NS_RAM,
"DueDateTypeCode",
required=False,
profile=BASIC,
)
rate_applicable_percent = DecimalField(
NS_RAM, "RateApplicablePercent", required=True, profile=BASIC
)
Expand Down Expand Up @@ -254,14 +213,14 @@ class TradeAllowanceCharge(Element):
NS_RAM,
"CalculationPercent",
required=False,
profile=EXTENDED,
profile=COMFORT,
_d="Rabatt in Prozent",
)
basis_amount = DecimalField( # TODO: Should be deprecated?
NS_RAM,
"BasisAmount",
required=False,
profile=EXTENDED,
profile=COMFORT,
_d="Basisbetrag des Rabatts",
)
basis_quantity = QuantityField(
Expand All @@ -278,7 +237,7 @@ class TradeAllowanceCharge(Element):
profile=COMFORT,
_d="Betrag des Zu-/Abschlags",
)
reason_code = StringField(NS_RAM, "ReasonCode", required=False, profile=EXTENDED)
reason_code = StringField(NS_RAM, "ReasonCode", required=False, profile=COMFORT)
reason = StringField(NS_RAM, "Reason", required=False, profile=COMFORT)
trade_tax = MultiField(CategoryTradeTax, required=False, profile=COMFORT)

Expand Down
14 changes: 9 additions & 5 deletions drafthorse/models/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def __setattr__(self, key, value):
if (
not hasattr(self, key)
and not key.startswith("_")
and not key in ("required",)
and key not in ("required",)
):
raise AttributeError(
f"Element {type(self)} has no attribute '{key}'. If you set it, it would not be included in the output."
Expand Down Expand Up @@ -301,7 +301,8 @@ def __init__(self, namespace, tag, text="", scheme_id=""):
def to_etree(self):
node = self._etree_node()
node.text = self._text
node.attrib["schemeID"] = self._scheme_id
if self._scheme_id != "":
node.attrib["schemeID"] = self._scheme_id
return node

def from_etree(self, root):
Expand All @@ -319,14 +320,17 @@ def __str__(self):


class DateTimeElement(StringElement):
def __init__(self, namespace, tag, value=None, format="102"):
def __init__(
self, namespace, tag, value=None, format="102", date_time_namespace=NS_UDT
):
super().__init__(namespace, tag)
self._value = value
self._format = format
self._date_time_namespace = date_time_namespace

def to_etree(self):
t = self._etree_node()
node = ET.Element("{%s}%s" % (NS_UDT, "DateTimeString"))
node = ET.Element("{%s}%s" % (self._date_time_namespace, "DateTimeString"))
if self._value:
if self._format == "102":
node.text = self._value.strftime("%Y%m%d")
Expand All @@ -344,7 +348,7 @@ def to_etree(self):
def from_etree(self, root):
if len(root) != 1:
raise TypeError("Date containers should have one child")
if root[0].tag != "{%s}%s" % (NS_UDT, "DateTimeString"):
if root[0].tag != "{%s}%s" % (self._date_time_namespace, "DateTimeString"):
raise TypeError("Tag %s not recognized" % root[0].tag)
self._format = root[0].attrib["format"]
if self._format == "102":
Expand Down
34 changes: 26 additions & 8 deletions drafthorse/models/fields.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from decimal import Decimal

from . import BASIC
from . import BASIC, NS_UDT
from .container import (
Container,
CurrencyContainer,
Expand Down Expand Up @@ -119,10 +119,16 @@
if instance._data.get(self.name, None) is None:
instance._data[self.name] = self.initialize()

if not isinstance(value, (tuple, list)):
raise TypeError("Please pass a 2-tuple of including scheme ID and ID.")
instance._data[self.name]._text = value[1]
instance._data[self.name]._scheme_id = value[0]
if isinstance(value, (tuple, list)):
if len(value) == 2:
instance._data[self.name]._text = value[1]
instance._data[self.name]._scheme_id = value[0]

Check warning on line 125 in drafthorse/models/fields.py

View check run for this annotation

Codecov / codecov/patch

drafthorse/models/fields.py#L122-L125

Added lines #L122 - L125 were not covered by tests
else:
raise TypeError(

Check warning on line 127 in drafthorse/models/fields.py

View check run for this annotation

Codecov / codecov/patch

drafthorse/models/fields.py#L127

Added line #L127 was not covered by tests
"Please pass a 2-tuple of including scheme ID and ID, or just an ID."
)
else:
instance._data[self.name]._text = value

Check warning on line 131 in drafthorse/models/fields.py

View check run for this annotation

Codecov / codecov/patch

drafthorse/models/fields.py#L131

Added line #L131 was not covered by tests


class CurrencyField(Field):
Expand Down Expand Up @@ -208,7 +214,9 @@
instance._data[self.name] = self.initialize()

if not isinstance(value, (tuple, list)):
raise TypeError("Please pass a 2-tuple of including amount and unit code.")
raise TypeError(

Check warning on line 217 in drafthorse/models/fields.py

View check run for this annotation

Codecov / codecov/patch

drafthorse/models/fields.py#L217

Added line #L217 was not covered by tests
"Please pass a 3-tuple of mimeCode, filename and base64-encoded binary."
)
instance._data[self.name]._text = value[2]
instance._data[self.name]._mime_code = value[0]
instance._data[self.name]._filename = value[1]
Expand Down Expand Up @@ -238,21 +246,31 @@

class DateTimeField(Field):
def __init__(
self, namespace, tag, default=False, required=False, profile=BASIC, _d=None
self,
namespace,
tag,
default=False,
required=False,
profile=BASIC,
_d=None,
date_time_namespace=NS_UDT,
):
from .elements import DateTimeElement

super().__init__(DateTimeElement, default, required, profile, _d)
self.namespace = namespace
self.tag = tag
self._date_time_namespace = date_time_namespace

def __set__(self, instance, value):
if instance._data.get(self.name, None) is None:
instance._data[self.name] = self.initialize()
instance._data[self.name]._value = value

def initialize(self):
return self.cls(self.namespace, self.tag)
return self.cls(
self.namespace, self.tag, date_time_namespace=self._date_time_namespace
)


class DirectDateTimeField(Field):
Expand Down
6 changes: 6 additions & 0 deletions drafthorse/models/party.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,12 @@ class Meta:
tag = "PayeeTradeParty"


class PayerTradeParty(TradeParty):
class Meta:
namespace = NS_RAM
tag = "PayerTradeParty"


class InvoicerTradeParty(TradeParty):
class Meta:
namespace = NS_RAM
Expand Down
37 changes: 23 additions & 14 deletions drafthorse/models/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ class ProductCharacteristic(Element):
type_code = StringField(
NS_RAM,
"TypeCode",
required=True,
required=False,
profile=EXTENDED,
_d="Art der Produkteigenschaft",
)
description = StringField(NS_RAM, "Description", required=True, profile=EXTENDED)
description = StringField(NS_RAM, "Description", required=True, profile=COMFORT)
value_measure = QuantityField(
NS_RAM,
"ValueMeasure",
required=False,
profile=EXTENDED,
_d="Numerische Messgröße",
)
value = StringField(NS_RAM, "Value", required=False, profile=EXTENDED)
value = StringField(NS_RAM, "Value", required=False, profile=COMFORT)

class Meta:
namespace = NS_RAM
Expand All @@ -34,15 +34,26 @@ class Meta:

class ProductClassification(Element):
class_code = ClassificationField(
NS_RAM, "ClassCode", required=True, profile=EXTENDED
NS_RAM, "ClassCode", required=False, profile=COMFORT
)
value = StringField(NS_RAM, "ClassName", required=True, profile=EXTENDED)
value = StringField(NS_RAM, "ClassName", required=False, profile=EXTENDED)

class Meta:
namespace = NS_RAM
tag = "DesignatedProductClassification"


class ProductInstance(Element):
batch_id = IDField(NS_RAM, "BatchID", required=False, profile=EXTENDED)
serial_id = StringField(
NS_RAM, "SupplierAssignedSerialID", required=False, profile=EXTENDED
)

class Meta:
namespace = NS_RAM
tag = "IndividualTradeProductInstance"


class OriginCountry(Element):
id = StringField(
NS_RAM, "ID", required=True, profile=EXTENDED, _d="Land der Produktherkunft"
Expand Down Expand Up @@ -73,22 +84,20 @@ class Meta:


class TradeProduct(Element):
global_id = IDField(NS_RAM, "GlobalID", required=False, profile=COMFORT)
id = IDField(NS_RAM, "ID", required=False, profile=EXTENDED)
global_id = IDField(NS_RAM, "GlobalID", required=False)
seller_assigned_id = StringField(
NS_RAM, "SellerAssignedID", required=False, profile=COMFORT
)
buyer_assigned_id = StringField(
NS_RAM, "BuyerAssignedID", required=False, profile=COMFORT
)
name = StringField(NS_RAM, "Name", required=False, profile=COMFORT)
name = StringField(NS_RAM, "Name", required=False)
description = StringField(NS_RAM, "Description", required=False, profile=COMFORT)
characteristics = MultiField(
ProductCharacteristic, required=False, profile=EXTENDED
)
classifications = MultiField(
ProductClassification, required=False, profile=EXTENDED
)
origins = MultiField(OriginCountry, required=False, profile=EXTENDED)
characteristics = MultiField(ProductCharacteristic, required=False, profile=COMFORT)
classifications = MultiField(ProductClassification, required=False, profile=COMFORT)
instance = MultiField(ProductInstance, required=False, profile=EXTENDED)
origins = MultiField(OriginCountry, required=False, profile=COMFORT)
included_products = MultiField(ReferencedProduct, required=False, profile=EXTENDED)

class Meta:
Expand Down
Loading
Loading