Faktura w zasobie Document: prawo VAT i struktura pod KSeF (FA)
Ten rozdział nie jest profilem KSeF w rozumieniu dokumentacji czy certyfikacji Ministerstwa Finansów. Opisuje weryfikację zgodności: że dane wymagane przez polskie prawo (ustawa o VAT, art. 106e) oraz logiczną strukturę faktury FA(2)/FA(3) używaną przy wymianie z Krajowym Systemem e-Faktur (KSeF) da się zmieścić w kanonicznym zasobie Document — bez tworzenia osobnego „obiektu KSeF”, lecz przez nagłówek, position[], attribute[] oraz konwencje FK (nazewnictwo i lokalizacja pól).
Chodzi więc o to, żeby integrator mógł sprawdzić: czy payload API.ERP ma miejsce na wszystkie istotne elementy faktury (w tym pod KSeF)? — a nie o rejestrowany profil w systemie KSeF.
Ułożenie jest zbliżone do podejścia Common Data Model (Microsoft CDM) dla faktur (Invoice, FreeTextInvoiceHeader, CustInvoiceTrans): nagłówek + wiersze + sumy.
Dla dedykowanego zasobu kanonicznego Invoice / InvoicePosition (osobna struktura API, nie ten dokument oparty na Document) zob. model kanoniczny oraz profile kanoniczne (snapshot z kodu).
1. Document jako faktura
Faktura to Document z:
- type =
invoice(1..1; systemhttps://api-erp.kamsoft.pl/ns/document-type). - identifier (1..) — co najmniej jeden numer faktury (Identifier.system + value, np. system=company); mogą być dodatkowe (KSeF, origin itd.). Rodzaj identyfikatora wynika z Identifier.system*, nie z osobnego code systemu.
- issueDate (1..1) — data wystawienia (obligatoryjna wg art. 106e).
- participant (2..) — referencje do PartyRole: wystawca (Podmiot1) i nabywca (Podmiot2) obowiązkowo; ewentualnie kolejne role (np. płatnik). Document.participant referuje do PartyRole (role=seller/supplier, role=buyer/customer). Strona (nazwa, adres, NIP) wynika z PartyRole.party* → Party.
- position (1..*) — co najmniej jeden wiersz lub linia podsumowania; wiersze faktury (invoice-line) oraz opcjonalnie linie podsumowania VAT (vat-summary-line).
- attribute (1..*) — podsumowanie dokumentu: co najmniej suma brutto (total-gross); zwykle także total-net, total-vat; opcjonalnie rozbicie VAT per stawka.
Pola specyficzne dla faktury (daty sprzedaży, płatności, KSeF, podział płatności) są w konwencji FK — rozszerzenia Document lub atrybuty z ustalonym code (patrz sekcja 3).
Pola referencyjne a wartości wbudowane: W tym mapowaniu część pól zawiera referencje (Reference) do innych zasobów, a nie pełne obiekty. Document.participant to tablica referencji do PartyRole (np. PartyRole/role-seller-01); w każdym PartyRole pole party to referencja do Party (np. Party/supplier-01). Dane wystawcy i nabywcy (nazwa, adres, NIP) znajdują się w zasobie Party — w payloadzie faktury mogą być dostarczone w contained (inline) lub pobrane osobnym wywołaniem. Podobnie paymentAccount (konwencja FK) to referencja do BankAccount; valueReference w wierszu faktury to referencje do ProductDefinition lub Product. Pola niebędące referencjami (issueDate, identifier, valueMoney, valueString, attribute z valueMoney itd.) niosą wartości bezpośrednio.
2. Mapowanie wymogów prawnych (art. 106e ustawy o VAT) na Document
Poniższa tabela łączy obowiązkowe elementy faktury z przepisów z konkretnymi polami w Document i konwencji FK. Dzięki temu reguły prawne są wprost odzwierciedlone w strukturze.
| Wymóg prawny (art. 106e / KSeF) | Pole w modelu | Kard. | Lokalizacja w Document / konwencja FK |
|---|---|---|---|
| Data wystawienia | issueDate | 1..1 | Document.issueDate |
| Kolejny numer identyfikujący fakturę | invoiceNumber | 1..1 | Document.identifier — co najmniej jeden z numerem faktury (Identifier.system + value, np. system=company) |
| Imiona, nazwiska/nazwy i adresy wystawcy | seller | 1..1 | Document.participant[0] = Reference(PartyRole) (role=seller); dane w zasobie Party wskazanym przez PartyRole.party = Reference(Party) |
| Imiona, nazwiska/nazwy i adresy nabywcy | buyer | 1..1 | Document.participant[1] = Reference(PartyRole) (role=buyer); dane w PartyRole.party = Reference(Party) |
| NIP podatnika (wystawcy) | sellerTaxId | 1..1 | W zasobie Party (referencja PartyRole.party z participant[0]); Identifier z type=nip, system=urn:pl:nip |
| NIP nabywcy (z wyjątkami) | buyerTaxId | 0..1 | W zasobie Party (referencja PartyRole.party z participant[1]); Identifier z type=nip, system=urn:pl:nip |
| Opis towaru/usługi | lineDescription | 0..1 | DocumentPosition (invoice-line).valueString |
| Ilość | quantity | 0..1 | DocumentPosition.valueItem (type=quantity) lub valueQuantity |
| Cena jednostkowa netto | unitPriceNet | 0..1 | DocumentPosition.valueItem (type=unit-price) + valueMoney |
| Rabaty i opusty | discount | 0..1 | DocumentPosition.valueItem lub attribute (document/position) z code np. discount |
| Wartość sprzedaży netto (wiersz) | netAmount | 1..1 | DocumentPosition.valueItem (type=net-amount) + valueMoney |
| Stawka VAT (wiersz) | vatRate | 0..1 | DocumentPosition.valueCodeableConcept (system vat-rate: 23, 8, 5, 0) |
| Kwota VAT (wiersz) | vatAmount | 0..1 | DocumentPosition.valueItem (type=vat-amount) + valueMoney |
| Akcyza (wiersz) | exciseAmount | 0..1 | DocumentPosition.valueItem (type=excise-amount) + valueMoney; opcjonalnie valueCodeableConcept (rodzaj/stawka akcyzy) |
| Suma wartości netto z podziałem na stawki | totalNetByRate | 0..* | Document.attribute (np. total-net-23, total-net-8) lub position (vat-summary-line) |
| Kwota VAT z podziałem na stawki | totalVatByRate | 0..* | Document.attribute lub position (vat-summary-line) |
| Suma akcyzy (dokument) | totalExcise | 0..1 | Document.attribute (code=total-excise, valueMoney) |
| Kwota należności ogółem | totalGross | 1..1 | Document.attribute (code=total-gross, valueMoney) |
| Numer identyfikacyjny KSeF | – | 0..1 | Document.identifier – element z Identifier.system wskazującym na KSeF (np. https://ksef.mf.gov.pl/), Identifier.value = numer nadany przez KSeF |
| Data sprzedaży (gdy inna niż wystawienia) | saleDate | 0..1 | Konwencja FK: saleDate (date) |
| Termin płatności / data płatności | dueDate | 0..1 | Konwencja FK: dueDate (date) |
| Sposób zapłaty / podział płatności | paymentMethod, splitPayment | 0..1 | Konwencja FK: paymentMethod (CodeableConcept), splitPayment (boolean) |
| Rachunek bankowy do płatności | paymentAccount | 0..1 | Konwencja FK: paymentAccount (Reference BankAccount) |
Uwaga: wystawca i nabywca są wyrażeni przez referencje: participant to Reference(PartyRole) (nie pełny obiekt PartyRole); w PartyRole pole party to Reference(Party). Dane strony (nazwa, adres, NIP) są w zasobie Party; w payloadzie faktury można je osadzić w contained lub udostępnić osobnym zasobem.
3. Pola rozszerzenia FK (faktura w Document)
Pola poniżej nie są w rdzeniu Document; należą do konwencji FK dla faktury i są realizowane przez atrybuty (Document.attribute z ustalonym code) oraz ustalone rozszerzenia schematu. W API/OpenAPI mogą być opisane jako rozszerzenie schematu Document.
| Pole rozszerzenia FK | Typ | Kard. | Odpowiednik prawny / KSeF | Uwagi |
|---|---|---|---|---|
| saleDate | date | 0..1 | Data sprzedaży (gdy inna niż data wystawienia) | Art. 106e; KSeF Fa.DataSprzedazy |
| dueDate | date | 0..1 | Termin płatności | CDM: DueDate |
| paymentMethod | CodeableConcept | 0..1 | Sposób zapłaty (przelew, gotówka, karta…) | System np. payment-method |
| paymentAccount | Reference(BankAccount) | 0..1 | Rachunek bankowy do wpłaty | Referencja do BankAccount (nie pełny obiekt); numer konta w zasobie docelowym |
| splitPayment | boolean | 0..1 | Mechanizm podzielonej płatności (MPP) | Wymóg oznakowania na fakturze |
| (numer KSeF) | – | 0..1 | Numer nadany przez KSeF | Document.identifier – element z system wskazującym na KSeF (np. https://ksef.mf.gov.pl/), value = numer KSeF; nie osobne pole ksefIdentifier |
| ksefAcquisitionDate | dateTime | 0..1 | Data otrzymania w KSeF (rejestracji) | Oficjalna data otrzymania faktury |
| invoiceNumber | string | 0..1 | Kolejny numer faktury (jeśli nie tylko identifier) | Może być wyciągnięte z identifier |
| currency | string (ISO 4217) | 0..1 | Waluta faktury | Domyślnie z valueMoney w position/attribute |
| totalAmount | Money | 0..1 | Kwota należności ogółem | Redundantne z attribute total-gross; dla wygody API |
Te pola są „wprost” nazwane zgodnie z funkcją prawną/biznesową, co ułatwia walidację i mapowanie na format wymagany przy obsłudze KSeF (nadal w ramach weryfikacji zgodności modelu, a nie profilu urzędowego).
4. Mapowanie struktury logicznej KSeF (FA) na Document
Struktura FA(2)/FA(3) (Naglowek, Podmiot1/2/3, Fa, FaWiersz, Stopka) mapuje się na Document w następujący sposób:
| Element FA (KSeF) | Odpowiednik w Document |
|---|---|
| Naglowek (numer, daty, identyfikatory) | Document.identifier (w tym element z system KSeF, value = numer KSeF), Document.issueDate, Document.type, Document.status; konwencja FK: saleDate, dueDate, ksefAcquisitionDate |
| Podmiot1 (wystawca) | Document.participant[0] = Reference(PartyRole); role=seller; dane strony w zasobie Party (Reference w PartyRole.party) |
| Podmiot2 (nabywca) | Document.participant[1] = Reference(PartyRole); role=buyer; dane w Party (PartyRole.party) |
| Podmiot3 (inne, np. płatnik) | Document.participant[2] = Reference(PartyRole) z role (CodeableConcept) |
| Fa (część faktury: wiersze, rozliczenia, płatności) | Document.position[] (wiersze + opcjonalnie vat-summary-line), Document.attribute[] (sumy, w tym total-excise), konwencja FK: paymentMethod, paymentAccount, splitPayment |
| FaWiersz (pojedynczy wiersz) | DocumentPosition: code=invoice-line, positionNo, valueItem (quantity, unit-price, net-amount, vat-amount, excise-amount), valueCodeableConcept (vat-rate, rodzaj akcyzy), valueReference = referencje do ProductDefinition/Product, valueString (opis) |
| Stopka (podsumowanie) | Document.attribute (total-net, total-vat, total-excise, total-gross) oraz opcjonalnie position z code=vat-summary-line per stawka |
Dzięki temu jeden zasób Document z type=invoice oraz konwencje FK pozwalają zmieścić zarówno wymagania prawne, jak i logiczną strukturę FA używaną przy KSeF — bez duplikowania osobnego modelu wyłącznie pod KSeF.
5. Wiersz faktury (DocumentPosition, code=invoice-line)
Dla position z code = invoice-line (system https://api-erp.kamsoft.pl/ns/document-position-type) konwencja FK dla faktury ustala użycie pól. Wymagane w każdym wierszu: code (1..1), positionNo (1..1), wartość netto pozycji net-amount (1..1); pozostałe pola wg potrzeb.
| Element prawny / biznesowy | Kard. | Pole DocumentPosition |
|---|---|---|
| Rodzaj pozycji | 1..1 | code = invoice-line |
| Numer pozycji | 1..1 | positionNo (integer, od 1) |
| Opis towaru/usługi | 0..1 | valueString |
| Ilość | 0..1 | valueQuantity lub valueItem (type=quantity) |
| Jednostka miary | 0..1 | valueQuantity.unit lub valueCodeableConcept |
| Cena jedn. netto | 0..1 | valueItem (type=unit-price, valueMoney) |
| Wartość netto pozycji | 1..1 | valueItem (type=net-amount, valueMoney) |
| Stawka VAT | 0..1 | valueCodeableConcept (system vat-rate: 23, 8, 5, 0) |
| Kwota VAT pozycji | 0..1 | valueItem (type=vat-amount, valueMoney) |
| Akcyza (wiersz) | 0..1 | valueItem (type=excise-amount, valueMoney); opcjonalnie valueCodeableConcept (rodzaj akcyzy – system np. excise-type) |
| Towar/usługa (katalog/instancja) | 0..* | valueReference = referencja do ProductDefinition lub Product (nie pełny obiekt) |
| Rabat | 0..1 | valueItem (type=discount) lub attribute w pozycji |
Numeracja wierszy: positionNo (1, 2, 3, …) — spójna dla referencji pozycji źródłowych.
6. Zgodność z Common Data Model (CDM)
Model Document dla faktury (z konwencją FK) jest zbieżny z podejściem Microsoft Common Data Model:
- Invoice (CRM): nagłówek (daty, kontrahent, kwoty) + powiązane szczegóły — u nas Document (participant → PartyRole seller/buyer, issueDate, attribute total-gross) + position.
- FreeTextInvoiceHeaderEntity (Finance): InvoiceNumber, InvoiceDate, DueDate, CustomerAccount, CurrencyCode, PaymentTerms — u nas: identifier (system + value — numer faktury), issueDate, konwencja FK: dueDate, participant[1] → PartyRole (buyer), PartyRole.party = nabywca, currency w Money, konwencja FK: paymentMethod/dueDate.
- CustInvoiceTrans (wiersze): ilość, kwoty, VAT — u nas DocumentPosition (valueItem: quantity, net-amount, vat-amount; valueCodeableConcept: vat-rate).
Różnica: w CDM często osobne encje dla nagłówka i wierszy; u nas jeden zasób Document z position[] i attribute[], co upraszcza wymianę i walidację jednym dokumentem.
7. Odniesienia
- Document — rdzeń dokumentu (identifier, issueDate, participant, position, attribute)
- DocumentPosition — wiersz faktury (invoice-line) i vat-summary-line
- Attribute — atrybuty dokumentu (total-net, total-vat, total-gross)
- PartyRole (participant → seller, buyer), Party (PartyRole.party — dane wystawcy i nabywcy)
- BankAccount — rachunek do płatności (konwencja FK: paymentAccount)
- KSeF – struktura FA(3) — struktura logiczna e-faktury
- Art. 106e ustawy z 11.03.2004 r. o podatku od towarów i usług — obowiązkowe elementy faktury
- Przykład JSON faktury: Document-Examples §2 FK – Faktura (Invoice)