From 72114f061232de3253dd9fa04d75ed4471e810b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Szafra=C5=84ski?= Date: Mon, 22 Jan 2024 13:37:55 +0100 Subject: [PATCH] feat: Base types for models --- src/ksef/compat.py | 12 ++++ src/ksef/models/invoice.py | 98 +++++++++++++++++++++++++- src/ksef/models/invoice_annotations.py | 89 +++++++++++++++++++++++ src/ksef/models/invoice_rows.py | 20 ++++++ 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 src/ksef/compat.py create mode 100644 src/ksef/models/invoice_annotations.py create mode 100644 src/ksef/models/invoice_rows.py diff --git a/src/ksef/compat.py b/src/ksef/compat.py new file mode 100644 index 0000000..d653747 --- /dev/null +++ b/src/ksef/compat.py @@ -0,0 +1,12 @@ +"""Compatibility libraries for python 3.x.""" + +try: + from enum import StrEnum # type: ignore[attr-defined] +except ImportError: + from enum import Enum + + class StrEnum(str, Enum): # type: ignore[no-redef] + """Compatibility class for StrEnum for older python versions.""" + + +__all__ = ["StrEnum"] diff --git a/src/ksef/models/invoice.py b/src/ksef/models/invoice.py index fe6d15b..bc85ccd 100644 --- a/src/ksef/models/invoice.py +++ b/src/ksef/models/invoice.py @@ -1,6 +1,102 @@ """Invoice model.""" +from datetime import date +from decimal import Decimal +from typing import Optional + from pydantic import BaseModel +from ksef.compat import StrEnum +from ksef.models.invoice_annotations import InvoiceAnnotations +from ksef.models.invoice_rows import InvoiceRows + + +class IdentificationData(BaseModel): + """ + Subject identification data. + + Corresponds to the field TPodmiot1/TPodmiot2 from the invoice XML schema. + """ + + nip: str + full_name: str + + +class Address(BaseModel): + """Subject address data. + + Corresponds to the type TAdres from the invoice XML schema. + """ + + country_code: str + city: str + street: str + house_number: str + apartment_number: Optional[str] + postal_code: str + + +class Subject(BaseModel): + """ + A subject of the invoice (issuer or recipient). + + Corresponds to the field TPodmiot1/TPodmiot2 from the invoice XML schema. + """ + + identification_data: IdentificationData + email: str + phone: str + + +class InvoiceType(StrEnum): + """ + Type of the invoice. + + Corresponds to the field TRodzajFaktury from the invoice XML schema. + """ + + # Regularna faktura VAT + REGULAR_VAT = "VAT" + + # Faktura korekcyjna + CORRECTION = "KOR" + + # Faktura zaliczkowa + ADVANCE = "ZAL" + + # Faktura rozliczeniowa + SETTLEMENT = "ROZ" + + # Faktura uproszczona + SIMPLIFIED = "UPR" + + # Faktura korygująca fakturę zaliczkową + CORRECTION_ADVANCE = "KOR_ZAL" + + # Faktura korygująca fakturę rozliczeniową + CORRECTION_SETTLEMENT = "KOR_ROZ" + + +class InvoiceData(BaseModel): + """Invoice data. + + Corresponds to the field Fa from the invoice XML schema. + """ + + currency_code: str + issue_date: date + issue_number: str + sell_date: date + total_amount: Decimal + invoice_annotations: InvoiceAnnotations + invoice_type: InvoiceType + invoice_rows: InvoiceRows + class Invoice(BaseModel): - """Single invoice model.""" + """Single invoice model. + + Corresponds to the root field Faktura from the invoice XML schema. + """ + + issuer: Subject + recipient: Subject diff --git a/src/ksef/models/invoice_annotations.py b/src/ksef/models/invoice_annotations.py new file mode 100644 index 0000000..462afce --- /dev/null +++ b/src/ksef/models/invoice_annotations.py @@ -0,0 +1,89 @@ +"""Models for invoice annotations/metadata.""" + +from pydantic import BaseModel + +from ksef.compat import StrEnum + + +class ReverseCharge(StrEnum): + """Possible values for the reverse charge field.""" + + YES = "1" + NO = "2" + + +class TaxSettlementOnPayment(StrEnum): + """Possible values for the tax settlement on payment field.""" + + ON_PAYMENT = "1" + REGULAR = "2" + + +class SelfInvoicing(StrEnum): + """Possible values for the self invoicing field.""" + + YES = "1" + NO = "2" + + +class SplitPayment(StrEnum): + """Possible values for the split payment field.""" + + YES = "1" + NO = "2" + + +class FreeFromVat(StrEnum): + """Possible values for the free from vat tax field.""" + + YES = "1" + NO = "2" + + +class IntraCommunitySupplyOfNewTransportMethods(StrEnum): + """Possible values for the intra community supply of new transport methods field.""" + + YES = "1" + NO = "2" + + +class SimplifiedProcedureBySecondTaxPayer(StrEnum): + """Possible values of the simplified procedure by second tax payer field.""" + + YES = "1" + NO = "2" + + +class MarginProcedure(StrEnum): + """Possible values for the margin procedure field.""" + + YES = "1" + NO = "2" + + +class InvoiceAnnotations(BaseModel): + """Invoice annotations/metadata.""" + + # P_16, Metoda kasowa + tax_settlement_on_payment: TaxSettlementOnPayment + + # P_17, Samofakturowanie + self_invoice: SelfInvoicing + + # P_18, Odwrotne obciążenie + reverse_charge: ReverseCharge + + # P_18A, Mechanizm podzielonej płatności + split_payment: SplitPayment + + # P_19, Zwolnienie z VAT + free_from_vat: FreeFromVat + + # P_22, Wewnątrzwspólnotowa dostawa nowych środków transportu + intra_community_supply_of_new_transport_methods: IntraCommunitySupplyOfNewTransportMethods + + # P_23, faktura wystawiona uproszczoną procedurą przez drugiego w kolejności podatnika + simplified_procedure_by_second_tax_payer: SimplifiedProcedureBySecondTaxPayer + + # P_PMarzy, znacznik wystąpienia procedur marży + margin_procedure: MarginProcedure diff --git a/src/ksef/models/invoice_rows.py b/src/ksef/models/invoice_rows.py new file mode 100644 index 0000000..92a75fa --- /dev/null +++ b/src/ksef/models/invoice_rows.py @@ -0,0 +1,20 @@ +"""Models for individual invoice rows/positions.""" + +from typing import Sequence + +from pydantic import BaseModel + + +class InvoiceRow(BaseModel): + """Single individual invoice position.""" + + row_number: int # NrWierszaFa + name: str # P_7, nazwa (rodzaj) towaru lub usługi + tax: int # P_12, stawka podatku + + +class InvoiceRows(BaseModel): + """Group of invoice positions.""" + + rows_count: int + rows: Sequence[InvoiceRow]