From: Raphael Michel Date: Sun, 14 Oct 2018 11:05:29 +0000 (+0200) Subject: Construct schema by sample X-Git-Tag: 1.0.0~26 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=811d3730fef629b98582f1c6cf0a640bb0378e46;p=thirdparty%2Fpython-drafthorse.git Construct schema by sample --- diff --git a/demo.py b/demo.py index f960665..0c69a37 100644 --- a/demo.py +++ b/demo.py @@ -10,5 +10,5 @@ doc.header.name = "RECHNUNG" doc.header.type_code = "380" doc.header.issue_date_time.value = date.today() doc.header.notes.add(IncludedNote(content="Test Node 1")) -doc.header.notes.add(IncludedNote(content="Test Node 2")) +doc.header.notes.add(IncludedNote(content="Test Node 2", subject_code="foo")) print(prettify(doc.serialize())) diff --git a/drafthorse/models/document.py b/drafthorse/models/document.py index 2cb580d..74cca53 100644 --- a/drafthorse/models/document.py +++ b/drafthorse/models/document.py @@ -1,8 +1,10 @@ import xml.etree.cElementTree as ET +from drafthorse.models.note import IncludedNote from . import NS_RAM, NS_UDT, NS_FERD_1p0 from .elements import Element from .fields import DateTimeField, Field, MultiField, StringField +from .trade import TradeTransaction class DocumentContextParameter(Element): @@ -21,27 +23,11 @@ class DocumentContext(Element): tag = "SpecifiedExchangedDocumentContext" -class IncludedNote(Element): - content = StringField(NS_FERD_1p0, "Content") - - class Meta: - namespace = NS_FERD_1p0 - tag = "IncludedNote" - - -class IssueDateTime(Element): - value = DateTimeField() - - class Meta: - namespace = NS_FERD_1p0 - tag = "IssueDateTime" - - class Header(Element): id = StringField(NS_FERD_1p0, "ID") name = StringField(NS_FERD_1p0, "Name") type_code = StringField(NS_FERD_1p0, "TypeCode") - issue_date_time = Field(IssueDateTime) + issue_date_time = DateTimeField(NS_FERD_1p0, "IssueDateTime") notes = MultiField(IncludedNote) class Meta: @@ -50,8 +36,9 @@ class Header(Element): class Document(Element): - context = Field(DocumentContext) + context = Field(DocumentContext, required=True) header = Field(Header) + trade = Field(TradeTransaction) def __init__(self): super().__init__() diff --git a/drafthorse/models/elements.py b/drafthorse/models/elements.py index 9f45924..588a0af 100644 --- a/drafthorse/models/elements.py +++ b/drafthorse/models/elements.py @@ -64,15 +64,69 @@ class StringElement(Element): return node +class DecimalElement(StringElement): + def __init__(self, namespace, tag, value=0): + super().__init__(namespace, tag) + self.value = value + + def to_etree(self): + node = self._etree_node() + node.text = str(self.value) + return node + + +class QuantityElement(StringElement): + def __init__(self, namespace, tag, amount="", unit_code=""): + super().__init__(namespace, tag) + self.amount = amount + self.unit_code = unit_code + + def to_etree(self): + node = self._etree_node() + node.text = str(self.amount) + node.attrib["unitCode"] = self.unit_code + return node + + +class CurrencyElement(StringElement): + def __init__(self, namespace, tag, amount="", currency="EUR"): + super().__init__(namespace, tag) + self.amount = amount + self.currency = currency + + def to_etree(self): + node = self._etree_node() + node.text = str(self.amount) + node.attrib["currencyID"] = self.currency + return node + + +class IDElement(Element): + def __init__(self, text="", scheme_id=""): + super().__init__() + self.text = text + self.scheme_id = scheme_id + + def to_etree(self): + node = self._etree_node() + node.text = self.text + node.attrib['schemeID'] = self.scheme_id + return node + + class DateTimeElement(Element): - def __init__(self, value=None): + def __init__(self, namespace, tag, value=None): super().__init__() self.value = None + self.namespace = namespace + self.tag = tag def to_etree(self): + t = ET.Element("{%s}%s" % (self.namespace, self.tag)) node = self._etree_node() node.text = self.value.strftime("%Y%m%d") - return node + t.append(node) + return t class Meta: namespace = NS_UDT @@ -80,3 +134,22 @@ class DateTimeElement(Element): attributes = { "format": "102" } + + +class IndicatorElement(Element): + def __init__(self, namespace, tag, value=None): + super().__init__() + self.value = None + self.namespace = namespace + self.tag = tag + + def to_etree(self): + t = ET.Element("{%s}%s" % (self.namespace, self.tag)) + node = self._etree_node() + node.text = str(self.value).lower() + t.append(node) + return t + + class Meta: + namespace = NS_UDT + tag = "Indicator" diff --git a/drafthorse/models/fields.py b/drafthorse/models/fields.py index 92258f0..7d974aa 100644 --- a/drafthorse/models/fields.py +++ b/drafthorse/models/fields.py @@ -40,16 +40,103 @@ class StringField(Field): instance._data[self.name].text = value -class DateTimeField(Field): +class IDField(Field): def __init__(self, default=False, required=False, _d=None): + from .elements import IDElement + super().__init__(IDElement, default, required, _d) + + def __set__(self, instance, value): + 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] + + +class DecimalField(Field): + def __init__(self, namespace, tag, default=False, required=False, _d=None): + from .elements import DecimalElement + super().__init__(DecimalElement, default, required, _d) + self.namespace = namespace + self.tag = tag + + def initialize(self): + return self.cls(self.namespace, self.tag) + + +class QuantityField(Field): + def __init__(self, namespace, tag, default=False, required=False, _d=None): + from .elements import QuantityElement + super().__init__(QuantityElement, default, required, _d) + self.namespace = namespace + self.tag = tag + + def __set__(self, instance, value): + 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 amount and unit code.") + instance._data[self.name].value = value[0] + instance._data[self.name].unit_code = value[1] + + def initialize(self): + return self.cls(self.namespace, self.tag) + + +class CurrencyField(Field): + def __init__(self, namespace, tag, default=False, required=False, _d=None): + from .elements import CurrencyElement + super().__init__(CurrencyElement, default, required, _d) + self.namespace = namespace + self.tag = tag + + def __set__(self, instance, value): + 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 amount and currency.") + instance._data[self.name].value = value[0] + instance._data[self.name].currency = value[1] + + def initialize(self): + return self.cls(self.namespace, self.tag) + + +class IndicatorField(Field): + def __init__(self, namespace, tag, default=False, required=False, _d=None): + from .elements import IndicatorElement + super().__init__(IndicatorElement, default, required, _d) + self.namespace = namespace + self.tag = tag + + 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) + + +class DateTimeField(Field): + def __init__(self, namespace, tag, default=False, required=False, _d=None): from .elements import DateTimeElement super().__init__(DateTimeElement, default, required, _d) + self.namespace = namespace + self.tag = tag 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) + class Container(): def __init__(self, child_type): diff --git a/drafthorse/models/note.py b/drafthorse/models/note.py new file mode 100644 index 0000000..811d80f --- /dev/null +++ b/drafthorse/models/note.py @@ -0,0 +1,12 @@ +from drafthorse.models import NS_FERD_1p0 +from drafthorse.models.elements import Element +from drafthorse.models.fields import StringField + + +class IncludedNote(Element): + content = StringField(NS_FERD_1p0, "Content") + subject_code = StringField(NS_FERD_1p0, "SubjectCode") + + class Meta: + namespace = NS_FERD_1p0 + tag = "IncludedNote" diff --git a/drafthorse/models/trade.py b/drafthorse/models/trade.py new file mode 100644 index 0000000..4ccc309 --- /dev/null +++ b/drafthorse/models/trade.py @@ -0,0 +1,243 @@ +from drafthorse.models.note import IncludedNote +from . import NS_RAM, NS_UDT, NS_FERD_1p0 +from .elements import Element +from .fields import DateTimeField, Field, MultiField, StringField, IDField, CurrencyField, DecimalField, IndicatorField, \ + QuantityField + + +class PostalTradeAddress(Element): + postcode = StringField(NS_FERD_1p0, "PostcodeCode") + line_one = StringField(NS_FERD_1p0, "LineOne") + city_name = StringField(NS_FERD_1p0, "CityName") + country_id = StringField(NS_FERD_1p0, "CountryID") + + class Meta: + namespace = NS_FERD_1p0 + tag = "PostalTradeAddress" + + +class TaxRegistration(Element): + id = IDField() + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedTaxRegistration" + + +class BuyerTradeParty(Element): + name = StringField(NS_FERD_1p0, "Name") + address = Field(PostalTradeAddress) + tax_registrations = MultiField(TaxRegistration) + + class Meta: + namespace = NS_FERD_1p0 + tag = "BuyerTradeParty" + + +class SellerTradeParty(Element): + name = StringField(NS_FERD_1p0, "Name") + address = Field(PostalTradeAddress) + tax_registrations = MultiField(TaxRegistration) + + class Meta: + namespace = NS_FERD_1p0 + tag = "SellerTradeParty" + + +class TradeAgreement(Element): + seller = Field(SellerTradeParty) + buyer = Field(BuyerTradeParty) + + class Meta: + namespace = NS_FERD_1p0 + tag = "ApplicableSupplyChainTradeAgreement" + + +class SupplyChainEvent(Element): + occurrence = DateTimeField(NS_FERD_1p0, "OccurenceDateTime") + + class Meta: + namespace = NS_FERD_1p0 + tag = "ActualDeliverySupplyChainEvent" + + +class TradeDelivery(Element): + events = MultiField(SupplyChainEvent) + + class Meta: + namespace = NS_FERD_1p0 + tag = "ApplicableSupplyChainTradeDelivery" + + +class FinancialAccount(Element): + iban = StringField(NS_FERD_1p0, "IBANID") + account_name = StringField(NS_FERD_1p0, "AccountName") + proprietary_id = StringField(NS_FERD_1p0, "ProprietaryID") + + class Meta: + namespace = NS_FERD_1p0 + tag = "PayeePartyCreditorFinancialAccount" + + +class FinancialInstitution(Element): + bic = StringField(NS_FERD_1p0, "BICID") + german_blz = StringField(NS_FERD_1p0, "GermanBankleitzahlID") + name = StringField(NS_FERD_1p0, "Name") + + class Meta: + namespace = NS_FERD_1p0 + tag = "PayeeSpecifiedCreditorFinancialInstitution" + + +class PaymentMeans(Element): + type_code = StringField(NS_FERD_1p0, "TypeCode") + information = StringField(NS_FERD_1p0, "Information") + account = Field(FinancialAccount) + institution = Field(FinancialInstitution) + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedTradeSettlementPaymentMeans" + + +class TradeTax(Element): + calculated_amount = CurrencyField(NS_FERD_1p0, "CalculatedAmount", required=False) + type_code = StringField(NS_FERD_1p0, "TypeCode") + category_code = StringField(NS_FERD_1p0, "CategoryCode", required=False) + basis_amount = CurrencyField(NS_FERD_1p0, "BasisAmount", required=False) + applicable_percent = DecimalField(NS_FERD_1p0, "ApplicablePercent") + + class Meta: + namespace = NS_FERD_1p0 + tag = "ApplicableTradeTax" + + +class PaymentTerms(Element): + description = StringField(NS_FERD_1p0, "Description") + due = DateTimeField(NS_FERD_1p0, "DueDateDateTime") + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedTradePaymentTerms" + + +class MonetarySummation(Element): + line_total = CurrencyField(NS_FERD_1p0, "LineTotalAmount") + charge_total = CurrencyField(NS_FERD_1p0, "ChargeTotalAmount") + allowance_total = CurrencyField(NS_FERD_1p0, "AllowanceTotalAmount") + tax_basis_total = CurrencyField(NS_FERD_1p0, "TaxBasisTotalAmount") + tax_total = CurrencyField(NS_FERD_1p0, "TaxTotalAmount") + grand_total = CurrencyField(NS_FERD_1p0, "GrandTotalAmount") + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedTradeSettlementMonetarySummation" + + +class TradeSettlement(Element): + payment_reference = StringField(NS_FERD_1p0, "PaymentReference") + currency_code = StringField(NS_FERD_1p0, "InvoiceCurrencyCode") + payment_means = Field(PaymentMeans) + trade_tax = MultiField(TradeTax) + terms = Field(PaymentTerms) + + class Meta: + namespace = NS_FERD_1p0 + tag = "ApplicableSupplyChainTradeSettlement" + + +class AllowanceCharge(Element): + indicator = IndicatorField(NS_FERD_1p0, "ChargeIndicator") + actual_amount = CurrencyField(NS_FERD_1p0, "ActualAmount") + + class Meta: + namespace = NS_FERD_1p0 + tag = "AppliedTradeAllowanceCharge" + + +class GrossPrice(Element): + amount = CurrencyField(NS_FERD_1p0, "ChargeAmount") + charge = Field(AllowanceCharge) + + class Meta: + namespace = NS_FERD_1p0 + tag = "GrossPriceProductTradePrice" + + +class NetPrice(Element): + amount = CurrencyField(NS_FERD_1p0, "ChargeAmount") + + class Meta: + namespace = NS_FERD_1p0 + tag = "NetPriceProductTradePrice" + + +class LineDocument(Element): + gross = Field(GrossPrice) + net = Field(NetPrice) + + class Meta: + namespace = NS_FERD_1p0 + tag = "AssociatedDocumentLineDocument" + + +class LineAgreement(Element): + line_id = StringField(NS_FERD_1p0, "LineID") + note = Field(IncludedNote) + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedSupplyChainTradeAgreement" + + +class LineDelivery(Element): + billed_quantity = QuantityField(NS_FERD_1p0, "BilledQuantity") + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedSupplyChainTradeDelivery" + + +class LineSummation(Element): + total_amount = CurrencyField(NS_FERD_1p0, "LineTotalAmount") + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedTradeSettlementMonetarySummation" + + +class LineSettlement(Element): + trade_tax = Field(TradeTax) + monetary_summation = Field(LineSummation) + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedSupplyChainTradeSettlement" + + +class TradeProduct(Element): + seller_assigned_id = StringField(NS_FERD_1p0, "SellerAssignedID") + name = StringField(NS_FERD_1p0, "Name") + + +class LineItem(Element): + document = Field(LineDocument) + agreement = Field(LineAgreement) + delivery = Field(LineDelivery) + settlement = Field(LineSettlement) + product = Field(TradeProduct) + + class Meta: + namespace = NS_FERD_1p0 + tag = "IncludedSupplyChainTradeItem" + + +class TradeTransaction(Element): + agreement = Field(TradeAgreement) + delivery = Field(TradeDelivery) + settlement = Field(TradeSettlement) + items = MultiField(LineItem) + + class Meta: + namespace = NS_FERD_1p0 + tag = "SpecifiedSupplyChainTradeTransaction" diff --git a/sample.xml b/sample.xml new file mode 100644 index 0000000..9705d8d --- /dev/null +++ b/sample.xml @@ -0,0 +1,157 @@ + + + + + urn:ferd:CrossIndustryDocument:invoice:1p0:comfort + + + + RE1337 + RECHNUNG + 380 + + 20130305 + + + Test Node 1 + + + Test Node 2 + + + easybill GmbH + Düsselstr. 21 + 41564 Kaarst + + Geschäftsführer: + Christian Szardenings + Ronny Keyser + + REG + + + + + + Lieferant GmbH + + 80333 + Lieferantenstraße 20 + München + DE + + + 201/113/40209 + + + DE123456789 + + + + Kunden AG Mitte + + 69876 + Hans Muster + Kundenstraße 15 + Frankfurt + DE + + + + + + + 20130305 + + + + + 2013-471102 + EUR + + 31 + Überweisung + + DE08700901001234567890 + + + + + GENODEF1M04 + + + + + + 19.25 + VAT + 275.00 + 7.00 + + + 37.62 + VAT + 198.00 + 19.00 + + + Zahlbar innerhalb von 20 Tagen (bis zum 05.10.2016) unter Abzug von 3% Skonto + (Zahlungsbetrag = 1.766,03 €). Bis zum 29.09.2016 ohne Abzug. + + + 20130404 + + + + 198.00 + 0.00 + 0.00 + 198.00 + 37.62 + 235.62 + + + + + 1 + + Testcontent in einem LineDocument + + + + + 9.9000 + + + false + + 1.8000 + + + + 9.9000 + + + + 20.0000 + + + + VAT + S + 19.00 + + + 198.00 + + + + TB100A4 + Trennblätter A4 + + + + \ No newline at end of file