diff --git a/drafthorse/models/accounting.py b/drafthorse/models/accounting.py index 421a6f3..fef9af6 100644 --- a/drafthorse/models/accounting.py +++ b/drafthorse/models/accounting.py @@ -3,6 +3,7 @@ from .elements import Element from .fields import ( CurrencyField, + DateField, DateTimeField, DecimalField, IndicatorField, @@ -78,7 +79,7 @@ class ApplicableTradeTax(Element): profile=BASIC, _d="Grund der Steuerbefreiung (Code)", ) - tax_point_date = DateTimeField( + tax_point_date = DateField( NS_RAM, "TaxPointDate", required=False, profile=COMFORT ) due_date_type_code = StringField( diff --git a/drafthorse/models/elements.py b/drafthorse/models/elements.py index 31a0e3d..1d0cf54 100644 --- a/drafthorse/models/elements.py +++ b/drafthorse/models/elements.py @@ -399,6 +399,62 @@ def __str__(self): return "{}".format(self._value) +class DateElement(StringElement): + def __init__( + self, namespace, tag, value=None, format="102", date_namespace=NS_UDT + ): + super().__init__(namespace, tag) + self._value = value + self._format = format + self._date_namespace = date_namespace + + def to_etree(self): + t = self._etree_node() + node = ET.Element("{%s}%s" % (self._date_namespace, "DateString")) + if self._value: + if self._format == "102": + node.text = self._value.strftime("%Y%m%d") + elif self._format == "616": + if sys.version_info < (3, 6): + node.text = "{}{}".format( + self._value.isocalendar()[0], self._value.isocalendar()[1] + ) + else: + node.text = self._value.strftime("%G%V") + node.attrib["format"] = self._format + t.append(node) + return t + + def from_etree(self, root, strict=True): + if len(root) != 1: + raise TypeError("Date containers should have one child") + if root[0].tag != "{%s}%s" % (self._date_namespace, "DateString"): + if strict: + raise TypeError("Tag %s not recognized" % root[0].tag) + else: + return self + self._format = root[0].attrib["format"] + if self._format == "102": + self._value = datetime.strptime(root[0].text, "%Y%m%d").date() + elif self._format == "616": + if sys.version_info < (3, 6): + from isoweek import Week + + w = Week(int(root[0].text[:4]), int(root[0].text[4:])) + self._value = w.monday() + else: + self._value = datetime.strptime(root[0].text + "1", "%G%V%u").date() + elif strict: + raise TypeError( + "Date format %s cannot be parsed" % root[0].attrib["format"] + ) + self._set_on_input = True + return self + + def __str__(self): + return "{}".format(self._value) + + class IndicatorElement(StringElement): def __init__(self, namespace, tag, value=None): super().__init__(namespace, tag) diff --git a/drafthorse/models/fields.py b/drafthorse/models/fields.py index 6c68c9c..3228ba6 100644 --- a/drafthorse/models/fields.py +++ b/drafthorse/models/fields.py @@ -292,6 +292,35 @@ def initialize(self): return self.cls(self.namespace, self.tag) +class DateField(Field): + def __init__( + self, + namespace, + tag, + default=False, + required=False, + profile=BASIC, + _d=None, + date_namespace=NS_UDT, + ): + from .elements import DateElement + + super().__init__(DateElement, default, required, profile, _d) + self.namespace = namespace + self.tag = tag + self._date_namespace = date_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, date_namespace=self._date_namespace + ) + + class MultiField(Field): def __init__( self, inner_type, default=False, required=False, profile=BASIC, _d=None diff --git a/tests/samples/zugferd_2p1_EN16931_Einfach_TaxPointDate.xml b/tests/samples/zugferd_2p1_EN16931_Einfach_TaxPointDate.xml new file mode 100644 index 0000000..97edd82 --- /dev/null +++ b/tests/samples/zugferd_2p1_EN16931_Einfach_TaxPointDate.xml @@ -0,0 +1,247 @@ + + + + + + + + + + urn:cen.eu:en16931:2017 + + + + 471102 + 380 + + 20180305 + + + Rechnung gemäß Bestellung vom 01.03.2018. + + + Lieferant GmbH +Lieferantenstraße 20 +80333 München +Deutschland +Geschäftsführer: Hans Muster +Handelsregisternummer: H A 123 + + REG + + + + + + 1 + + + 4012345001235 + TB100A4 + Trennblätter A4 + + + + 9.9000 + + + 9.9000 + + + + 20.0000 + + + + VAT + S + 19.00 + + + 198.00 + + + + + + 2 + + + 4000050986428 + ARNR2 + Joghurt Banane + + + + 5.5000 + + + 5.5000 + + + + 50.0000 + + + + VAT + S + 7.00 + + + 275.00 + + + + + + 549910 + 4000001123452 + Lieferant GmbH + + 80333 + Lieferantenstraße 20 + München + DE + + + 201/113/40209 + + + DE123456789 + + + + GE2020211 + Kunden AG Mitte + + 69876 + Kundenstraße 15 + Frankfurt + DE + + + + + + + 20180305 + + + + + EUR + + 19.25 + VAT + 275.00 + S + + 20180305 + + 7.00 + + + 37.62 + VAT + 198.00 + S + 19.00 + + + Zahlbar innerhalb 30 Tagen netto bis 04.04.2018, 3% Skonto innerhalb 10 Tagen bis 15.03.2018 + + + 473.00 + 0.00 + 0.00 + 473.00 + 56.87 + 529.87 + 0.00 + 529.87 + + + + \ No newline at end of file diff --git a/tests/samples/zugferd_2p3_EN16931_Einfach_TaxPointDate.xml b/tests/samples/zugferd_2p3_EN16931_Einfach_TaxPointDate.xml new file mode 100644 index 0000000..7c47a17 --- /dev/null +++ b/tests/samples/zugferd_2p3_EN16931_Einfach_TaxPointDate.xml @@ -0,0 +1,247 @@ + + + + + + + + + + urn:cen.eu:en16931:2017 + + + + 471102 + 380 + + 20241115 + + + Rechnung gemäß Bestellung vom 01.11.2024. + + + Lieferant GmbH +Lieferantenstraße 20 +80333 München +Deutschland +Geschäftsführer: Hans Muster +Handelsregisternummer: H A 123 + + REG + + + + + + 1 + + + 4012345001235 + TB100A4 + Trennblätter A4 + + + + 9.9000 + + + 9.9000 + + + + 20.0000 + + + + VAT + S + 19.00 + + + 198.00 + + + + + + 2 + + + 4000050986428 + ARNR2 + Joghurt Banane + + + + 5.5000 + + + 5.5000 + + + + 50.0000 + + + + VAT + S + 7.00 + + + 275.00 + + + + + + 549910 + 4000001123452 + Lieferant GmbH + + 80333 + Lieferantenstraße 20 + München + DE + + + 201/113/40209 + + + DE123456789 + + + + GE2020211 + Kunden AG Mitte + + 69876 + Kundenstraße 15 + Frankfurt + DE + + + + + + + 20241114 + + + + + EUR + + 19.25 + VAT + 275.00 + S + + 20241114 + + 7.00 + + + 37.62 + VAT + 198.00 + S + 19.00 + + + Zahlbar innerhalb 30 Tagen netto bis 15.12.2024, 3% Skonto innerhalb 10 Tagen bis 25.11.2024 + + + 473.00 + 0.00 + 0.00 + 473.00 + 56.87 + 529.87 + 0.00 + 529.87 + + + + \ No newline at end of file