Model kanoniczny: Invoice i InvoicePosition
Dokument zawiera analizę porównawczą (KSeF, systemy ERP, UBL/CDM) oraz propozycję dedykowanych zasobów kanonicznych Invoice i InvoicePosition – niezależnie od uniwersalnego Document i rozdziału Faktura w Document a KSeF / prawo (weryfikacja, że dane faktury mieszczą się w modelu Document). Niniejszy model definiuje osobne obiekty z jawnymi polami faktury i wiersza.
Profil maszynowy (snapshot z kodu): zestawienie i pliki JSON — Profile kanoniczne; bezpośrednio: Invoice.profile.json, InvoicePosition.profile.json.
1. Cel: Invoice i InvoicePosition jako zasoby dedykowane
- Document – dokument uniwersalny (FK, WHS, CRM, EOD, HR) z
position[](DocumentPosition) iattribute[]; typ przeztype(np. invoice). - Faktura w Document – mapowanie na Document i weryfikacja zgodności z art. 106e oraz strukturą FA przy KSeF; zob. Faktura i KSeF (Document).
- Invoice (niniejszy model) – dedykowany zasób tylko dla faktur: nagłówek z polami wprost (seller, buyer, issueDate, dueDate, paymentAccount, currency, totalNet, totalVat, totalGross, position[] jako InvoicePosition). Ułatwia walidację, API i mapowanie 1:1 na KSeF/ERP bez przechodzenia przez warstwę Document z konwencją FK.
InvoicePosition – jedna pozycja faktury (wiersz): positionNo, description, quantity, unit, unitPriceNet, netAmount, vatRate, vatAmount, productReference, discount. Zagnieżdżony w Invoice (rekomendacja) lub osobny zasób z referencją do Invoice.
2. Analiza porównawcza: nagłówek faktury
| Źródło | Encja / obiekt | Kluczowe pola nagłówka |
|---|---|---|
| KSeF FA(2)/FA(3) | Naglowek, Podmiot1/2/3, Fa | P_1 (numer), P_2_1 (data wystawienia), Podmiot1 (wystawca), Podmiot2 (nabywca), Fa (wiersze, płatność), DataSprzedazy, TerminPlatnosci, WarunkiPlatnosci, NrKontaBankowego, NumerKSeF |
| UBL 2.3 | Invoice | ID, IssueDate, AccountingSupplierParty, AccountingCustomerParty, PaymentMeans, LegalMonetaryTotal (LineExtensionAmount, TaxExclusiveAmount, TaxInclusiveAmount, PayableAmount), InvoiceLine[] |
| SAP SD | VBRK (Billing Doc) | VBELN, FKART, FKDAT (data), KUNAG (nabywca), VKORG, WAERK (waluta), NETWR, MWSKZ (VAT) |
| SAP FI | BKPF + BSEG / RSEG | Dokument księgowy: nagłówek (daty, waluta) + pozycje (konto, kwota Wn/Ma) |
| Oracle AR | RA_CUSTOMER_TRX | trx_number, trx_date, bill_to_customer_id, currency_code, term_id (termin płatności), status; lines w RA_CUSTOMER_TRX_LINES |
| D365 F&O | CustInvoiceTable (FreeText) | InvoiceId, InvoiceDate, CustomerAccount, DueDate, CurrencyCode, Payment; CustInvoiceTrans (wiersze) |
| Microsoft CDM | Invoice (CRM), FreeTextInvoiceHeaderEntity | InvoiceNumber, InvoiceDate, DueDate, CustomerId, TotalAmount, Currency; CDM CustInvoiceTrans: Amount, Quantity, LineNumber |
| Nasz Document + profil | Document + attribute/profile | identifier (w tym system KSeF, value = numer KSeF), issueDate, participant (seller/buyer), attribute (total-net, total-vat, total-gross), profil: saleDate, dueDate, paymentMethod, paymentAccount, splitPayment |
Wnioski dla kanonicznego Invoice (nagłówek):
- Identyfikacja: identifier[] (numer faktury, KSeF, origin) – jak Document.
- Daty: issueDate (1..1), saleDate (0..1), dueDate (0..1), ksefAcquisitionDate (0..1).
- Strony: seller (1..1 Reference PartyRole), buyer (1..1 Reference PartyRole) – zamiast participant[]; ewentualnie payer (0..1 Reference PartyRole).
- Kwoty zagregowane: totalNet, totalVat, totalGross (Money) – jawnie, nie tylko w attribute[].
- Płatność: paymentMethod (CodeableConcept), paymentAccount (Reference BankAccount), splitPayment (boolean).
- KSeF: numer KSeF = identifier (element z Identifier.system wskazującym na KSeF, np.
https://ksef.mf.gov.pl/, Identifier.value = numer nadany przez KSeF); ksefAcquisitionDate (data otrzymania w KSeF). - Waluta: currency (string ISO 4217) – wspólna dla nagłówka i pozycji.
- Powiązania: relatedDocument (Reference Invoice/Document) – korekta, zamówienie.
- Pozostałe: status, type (invoice, correction, proforma), attachment[].
3. Analiza porównawcza: wiersz faktury (pozycja)
| Źródło | Encja / obiekt | Kluczowe pola wiersza |
|---|---|---|
| KSeF FA | FaWiersz | P_7 (nazwa), P_8A (ilość), P_8B (jednostka), P_9 (cena jedn. netto), P_11 (wartość netto), P_12 (stawka VAT), P_13_1 (kwota VAT), P_14_1 (GTU), odniesienie do towaru/usługi |
| UBL 2.3 | InvoiceLine | ID, InvoicedQuantity, LineExtensionAmount, Item (Description, SellersItemIdentification), Price (PriceAmount), TaxTotal per line |
| SAP SD | VBRP | POSNR, MATNR (materiał), FKIMG (ilość), NETWR (netto), MWSKZ (VAT), WAERK |
| Oracle AR | RA_CUSTOMER_TRX_LINES | line_number, description, quantity, unit_selling_price, extended_amount, tax_code |
| D365 | CustInvoiceTrans | LineNum, ItemId, Qty, SalesPrice, LineAmount, TaxGroup, TaxAmount |
| CDM | CustInvoiceTrans | LineNumber, Amount, Quantity, Description, ProductId |
| Nasz DocumentPosition | position (invoice-line) | code, positionNo, valueItem (quantity, unit-price, net-amount, vat-amount), valueCodeableConcept (vat-rate), valueReference (Product), valueString (opis) |
Wnioski dla kanonicznego InvoicePosition:
- positionNo (integer, 1..1) – numer wiersza od 1.
- description (string, 0..1) – opis towaru/usługi (P_7, UBL Item/Description).
- quantity (Quantity, 0..1) – ilość + jednostka (P_8A, P_8B).
- unitPriceNet (Money, 0..1) – cena jedn. netto (P_9).
- netAmount (Money, 1..1) – wartość netto pozycji (P_11).
- vatRate (CodeableConcept, 0..1) – stawka VAT (P_12; system vat-rate).
- vatAmount (Money, 0..1) – kwota VAT (P_13_1).
- productReference (Reference ProductDefinition | Product, 0..1) – towar/usługa (nie pełny obiekt).
- discount (Money lub CodeableConcept, 0..1) – rabat/opust.
- attribute (0..* Attribute) – rozszerzenia (np. GTU, kod kraju pochodzenia).
4. Proponowana struktura Invoice (zasób kanoniczny)
Invoice rozszerza DomainResource. Pola:
| Pole | Kard. | Typ | Opis |
|---|---|---|---|
| identifier | 1..* | Identifier | Identyfikatory faktury: numer (system np. company); opcjonalnie element z system wskazującym na KSeF (np. https://ksef.mf.gov.pl/), value = numer nadany przez KSeF |
| issueDate | 1..1 | date | Data wystawienia |
| saleDate | 0..1 | date | Data sprzedaży (gdy inna niż issueDate) |
| dueDate | 0..1 | date | Termin płatności |
| seller | 1..1 | Reference(PartyRole) | Wystawca (PartyRole, role=seller) |
| buyer | 1..1 | Reference(PartyRole) | Nabywca (PartyRole, role=buyer) |
| payer | 0..1 | Reference(PartyRole) | Płatnik (gdy inny niż buyer) |
| totalNet | 0..1 | Money | Suma netto (dokument) |
| totalVat | 0..1 | Money | Suma VAT |
| totalExcise | 0..1 | Money | Suma akcyzy (dokument); gdy na fakturze są wyroby akcyzowe |
| totalGross | 1..1 | Money | Kwota należności ogółem |
| currency | 1..1 | string (ISO 4217) | Waluta faktury |
| paymentMethod | 0..1 | CodeableConcept | Sposób zapłaty |
| paymentAccount | 0..1 | Reference(BankAccount) | Rachunek bankowy do wpłaty |
| splitPayment | 0..1 | boolean | Mechanizm podzielonej płatności (MPP) |
| ksefAcquisitionDate | 0..1 | dateTime | Data otrzymania w KSeF (rejestracji) |
| position | 1..* | InvoicePosition | Wiersze faktury (oraz opcjonalnie linie podsumowania VAT) |
| attribute | 0..* | Attribute | Atrybuty rozszerzające (np. totalNet/totalVat per stawka) |
| relatedDocument | 0..* | Reference(Invoice) lub Reference(Document) | Korekta, faktura korygowana, zamówienie |
| attachment | 0..* | Attachment | Załączniki |
type (z DomainResource): invoice, correction, proforma (system document-type). status: draft, issued, sent, paid, cancelled.
5. Proponowana struktura InvoicePosition (typ zagnieżdżony lub zasób)
Opcja A – zagnieżdżony w Invoice (jak DocumentPosition w Document):
| Pole | Kard. | Typ | Opis |
|---|---|---|---|
| positionNo | 1..1 | integer | Numer wiersza (od 1); do referencji np. LedgerEntry.sourcePositionNo |
| description | 0..1 | string | Opis towaru/usługi |
| quantity | 0..1 | Quantity | Ilość + jednostka |
| unitPriceNet | 0..1 | Money | Cena jedn. netto |
| netAmount | 1..1 | Money | Wartość netto pozycji |
| vatRate | 0..1 | CodeableConcept | Stawka VAT (system vat-rate) |
| vatAmount | 0..1 | Money | Kwota VAT pozycji |
| exciseAmount | 0..1 | Money | Kwota akcyzy pozycji (gdy wyroby akcyzowe) |
| exciseType | 0..1 | CodeableConcept | Rodzaj / stawka akcyzy (system np. excise-type); opcjonalnie |
| productReference | 0..1 | Reference(ProductDefinition | Product) |
| discount | 0..1 | Money lub CodeableConcept | Rabat/opust |
| attribute | 0..* | Attribute | Rozszerzenia (GTU, kraj pochodzenia itd.) |
Opcja B – InvoicePosition jako osobny zasób (DomainResource): wtedy Invoice.position → Reference(InvoicePosition) i InvoicePosition.invoice → Reference(Invoice). Daje to osobne URI i wersjonowanie per wiersz; typowe w ERP dla „invoice line” jako encji. Dla uproszczenia API kanonicznego rekomendacja: Opcja A (zagnieżdżony), z możliwością rozszerzenia do Opcji B w profilu.
Linie podsumowania VAT (odpowiednik vat-summary-line): można modelować jako InvoicePosition z konwencją (np. positionNo powyżej 9000 lub pole lineType = invoice-line | vat-summary-line) i wypełnionymi netAmount, vatRate, vatAmount (suma per stawka).
6. Mapowanie: Document + profil → Invoice / InvoicePosition
| Document + profil | Invoice / InvoicePosition |
|---|---|
| Document.identifier | Invoice.identifier |
| Document.issueDate | Invoice.issueDate |
| Document.participant[0] (seller) | Invoice.seller |
| Document.participant[1] (buyer) | Invoice.buyer |
| Profil: saleDate, dueDate | Invoice.saleDate, dueDate |
| Profil: paymentMethod, paymentAccount, splitPayment | Invoice.paymentMethod, paymentAccount, splitPayment |
| Document + FK: numer KSeF w Document.identifier (system KSeF, value), ksefAcquisitionDate | Invoice.identifier (element z system KSeF, value), Invoice.ksefAcquisitionDate |
| Document.attribute (total-net, total-vat, total-excise, total-gross) | Invoice.totalNet, totalVat, totalExcise, totalGross (+ opcjonalnie attribute) |
| Document.position[] (invoice-line) | Invoice.position[] (InvoicePosition) |
| DocumentPosition.positionNo, valueString | InvoicePosition.positionNo, description |
| DocumentPosition.valueItem (quantity, unit-price, net-amount, vat-amount, excise-amount) | InvoicePosition.quantity, unitPriceNet, netAmount, vatAmount, exciseAmount |
| DocumentPosition.valueCodeableConcept (vat-rate, rodzaj akcyzy) | InvoicePosition.vatRate, exciseType |
| DocumentPosition.valueReference (Product) | InvoicePosition.productReference |
| Document.relatedDocument | Invoice.relatedDocument |
7. Mapowanie: KSeF FA(2)/FA(3) → Invoice / InvoicePosition
| KSeF (Naglowek, Podmiot, Fa) | Invoice |
|---|---|
| P_1 (numer), P_2_1 (data wystawienia) | identifier, issueDate |
| Podmiot1 (wystawca) | seller (Reference PartyRole) |
| Podmiot2 (nabywca) | buyer (Reference PartyRole) |
| Podmiot3 (płatnik) | payer (0..1) |
| DataSprzedazy, TerminPlatnosci | saleDate, dueDate |
| WarunkiPlatnosci, NrKontaBankowego | paymentMethod, paymentAccount |
| NumerKSeF, data rejestracji | identifier (system KSeF, value = numer KSeF), ksefAcquisitionDate |
| Fa (sumy) | totalNet, totalVat, totalExcise, totalGross, currency |
| FaWiersz (pozycja) | InvoicePosition (patrz poniżej) |
| KSeF FaWiersz | InvoicePosition |
|---|---|
| P_7 (nazwa towaru/usługi) | description |
| P_8A (ilość), P_8B (jednostka) | quantity (Quantity) |
| P_9 (cena jedn. netto) | unitPriceNet |
| P_11 (wartość netto) | netAmount |
| P_12 (stawka VAT) | vatRate |
| P_13_1 (kwota VAT) | vatAmount |
| Kwota akcyzy (wiersz) | exciseAmount, exciseType |
| Odniesienie do towaru | productReference |
| P_14_1 (GTU) itd. | attribute (code=GTU) |
8. Zgodność z systemami ERP (podsumowanie)
| System | Nagłówek → Invoice | Wiersz → InvoicePosition |
|---|---|---|
| SAP SD (VBRK/VBRP) | VBELN→identifier, FKDAT→issueDate, KUNAG→buyer, WAERK→currency, NETWR→totalNet | POSNR→positionNo, MATNR→productReference, FKIMG→quantity, NETWR→netAmount, MWSKZ→vatRate |
| Oracle AR | trx_number→identifier, trx_date→issueDate, bill_to_customer_id→buyer, currency_code→currency | line_number→positionNo, description, quantity, unit_selling_price, extended_amount→netAmount |
| D365 CustInvoiceTable/Trans | InvoiceId, InvoiceDate, CustomerAccount→buyer, DueDate, CurrencyCode | LineNum→positionNo, Qty, SalesPrice, LineAmount, TaxGroup→vatRate |
| UBL 2.3 Invoice | ID, IssueDate, AccountingSupplierParty→seller, AccountingCustomerParty→buyer, LegalMonetaryTotal→totalNet/totalVat/totalGross | InvoiceLine: InvoicedQuantity→quantity, LineExtensionAmount→netAmount, Price/PriceAmount→unitPriceNet |
| CDM | InvoiceNumber, InvoiceDate, DueDate, CustomerId→buyer, TotalAmount→totalGross | LineNumber→positionNo, Quantity, Amount→netAmount |
9. Relacja do Document i profilu
- Document + konwencja FK dla faktury – nadal obowiązuje dla unifikacji wszystkich typów dokumentów (faktura, zamówienie, PZ, dekret) w jednym zasobie Document z position[] i attribute[]; Faktura i KSeF (Document) opisuje mapowanie oraz weryfikację, że wymagania prawne i struktura FA przy KSeF mieszczą się w tym zasobie.
- Invoice + InvoicePosition – dedykowany model kanoniczny dla faktur: jawna struktura (seller, buyer, totalNet, totalVat, totalGross, position[].netAmount, vatRate itd.), wygodna walidacja i integracja z KSeF/ERP. W implementacji można:
- wystawiać tylko Invoice (API faktur) i opcjonalnie transformować do Document dla EOD/archiwum, albo
- utrzymywać Document jako źródło prawdy i generować Invoice jako widok/zestawienie dla klientów API faktur.
10. Odniesienia
- Document, DocumentPosition, Faktura i KSeF (Document)
- PartyRole, Party, BankAccount, Attribute
- KSeF: struktura FA(3); art. 106e ustawy o VAT
- UBL 2.3 Invoice, OAGIS, SAP SD Billing, Oracle AR, D365 F&O, Microsoft CDM