Convert an Invoice into KSeF FA(2) XML
Skill: Convert a source invoice (PDF, CSV, or JSON) into upload-ready KSeF FA(2) XML
Region: Poland (Polska)
Category: KSeF — Krajowy System e-Faktur
Does: Reads a source invoice — a PDF, a CSV/JSON export, or pasted text — extracts the invoice data, and emits a valid FA(2) XML document ready to send to KSeF.
Schema version: FA(2), wersjaSchemy="1-0E"
This is a conversion task: input = an existing invoice, output = FA(2) XML. The schema tables further down are the mapping target. Always validate the emitted XML against the official FA(2) XSD before sending; field codes and the VAT-summary suffix mapping change between revisions — confirm against the current Ministerstwo Finansów publication.
Conversion procedure
Follow these steps in order. Steps 1–2 get the data out of the source; steps 3–6 build and check the XML.
- Read the source.
- PDF → extract the text layer (e.g.
pdftotext -layout invoice.pdf -). If the PDF is a scan with no text layer, OCR it first, then treat the result as text. Read tables column-by-column so line items stay aligned. - CSV / JSON → parse directly.
- Pasted text → work from it as-is.
- PDF → extract the text layer (e.g.
- Extract these fields from the source (see the Source → FA(2) map below for where each one lands): seller NIP/name/address, buyer NIP/name/address, invoice number, issue date, sale date (if shown), currency, every line item (description, unit, quantity, net unit price, VAT rate), payment method, due date. If a required field is missing or ambiguous in the source, stop and ask rather than guessing.
- Normalize the extracted values:
- NIP → digits only (strip spaces, dashes, the
PLprefix). - Dates → ISO
YYYY-MM-DD. - Amounts → number with exactly 2 decimals (parse Polish formatting:
1 234,56→1234.56). - Map each line's VAT rate to a
P_12code (see table); map the payment method to aFormaPlatnoscicode.
- NIP → digits only (strip spaces, dashes, the
- Compute the line nets, per-rate VAT totals, and the gross total (see Calculation rules). Do the maths yourself — never copy a total blindly from the PDF; recompute and flag any mismatch with the source.
- Emit the XML in the structure shown under Document structure, using the worked example as the template. Set
DataWytworzeniaFato the current timestamp. - Validate against the FA(2) XSD and run the checklist at the bottom before handing back / sending.
Source → FA(2) field map
| From the source invoice | → FA(2) element |
|---|---|
| Seller tax ID | Podmiot1/DaneIdentyfikacyjne/NIP |
| Seller name | Podmiot1/DaneIdentyfikacyjne/Nazwa |
| Seller street / postcode+city | Podmiot1/Adres/AdresL1 / AdresL2 |
| Buyer tax ID / name / address | Podmiot2/… (same shape, Rola=Nabywca) |
| Invoice number | Fa/P_2 |
| Issue date | Fa/P_1 |
| Sale/delivery date (if different) | Fa/P_1M |
| Currency | Fa/KodWaluty |
| Line description | FaWiersz/P_7 |
| Line unit / quantity / net unit price | FaWiersz/P_8A / P_8B / P_9A |
| Line VAT rate | FaWiersz/P_12 (code) |
| (computed) line net | FaWiersz/P_11 |
| (computed) per-rate net / VAT | Fa/P_13_x / P_14_x |
| (computed) gross total | Fa/P_15 |
| Payment method / due date | Platnosc/FormaPlatnosci / TerminPlatnosci |
Worked example (end-to-end)
Input — text extracted from a PDF invoice
FAKTURA VAT nr FV/001/2024
Data wystawienia: 15.01.2024
Sprzedawca: Acme Sp. z o.o., NIP 123-456-78-90
ul. Programistyczna 42, 00-001 Warszawa
Nabywca: Client Sp. z o.o., NIP 098-765-43-21
ul. Biznesowa 7, 30-001 Kraków
Lp Nazwa J.m. Ilość Cena netto VAT Wartość netto
1 Usługi programistyczne godz 40 250,00 zł 23% 10 000,00 zł
Razem netto: 10 000,00 zł VAT 23%: 2 300,00 zł Do zapłaty: 12 300,00 zł
Forma płatności: przelew Termin: 29.01.2024
After extraction + normalization (intermediate)
{
"seller": {"nip": "1234567890", "name": "Acme Sp. z o.o.",
"addrL1": "ul. Programistyczna 42", "addrL2": "00-001 Warszawa"},
"buyer": {"nip": "0987654321", "name": "Client Sp. z o.o.",
"addrL1": "ul. Biznesowa 7", "addrL2": "30-001 Kraków"},
"number": "FV/001/2024", "issueDate": "2024-01-15", "currency": "PLN",
"lines": [{"desc": "Usługi programistyczne", "unit": "godz",
"qty": "40.00", "netUnit": "250.00", "vat": "23"}],
"payment": {"method": "6", "due": "2024-01-29"}
}
Output — FA(2) XML to upload
<?xml version='1.0' encoding='UTF-8'?>
<Faktura xmlns="http://crd.gov.pl/wzor/2023/06/29/12648/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://crd.gov.pl/wzor/2023/06/29/12648/ fa_2-1-0e.xsd">
<Naglowek>
<KodFormularza kodSystemowy="FA (2)" wersjaSchemy="1-0E">FA</KodFormularza>
<WariantFormularza>2</WariantFormularza>
<DataWytworzeniaFa>2024-01-15T10:30:00</DataWytworzeniaFa>
<SystemInfo>Example</SystemInfo>
</Naglowek>
<Podmiot1>
<DaneIdentyfikacyjne><NIP>1234567890</NIP><Nazwa>Acme Sp. z o.o.</Nazwa></DaneIdentyfikacyjne>
<Adres><KodKraju>PL</KodKraju><AdresL1>ul. Programistyczna 42</AdresL1><AdresL2>00-001 Warszawa</AdresL2></Adres>
<Rola>Sprzedawca</Rola>
</Podmiot1>
<Podmiot2>
<DaneIdentyfikacyjne><NIP>0987654321</NIP><Nazwa>Client Sp. z o.o.</Nazwa></DaneIdentyfikacyjne>
<Adres><KodKraju>PL</KodKraju><AdresL1>ul. Biznesowa 7</AdresL1><AdresL2>30-001 Kraków</AdresL2></Adres>
<Rola>Nabywca</Rola>
</Podmiot2>
<Fa>
<KodWaluty>PLN</KodWaluty>
<P_1>2024-01-15</P_1>
<P_2>FV/001/2024</P_2>
<RodzajFaktury>VAT</RodzajFaktury>
<FaWiersz>
<NrWierszaFa>1</NrWierszaFa>
<UU_ID>1</UU_ID>
<P_7>Usługi programistyczne</P_7>
<P_8A>godz</P_8A>
<P_8B>40.00</P_8B>
<P_9A>250.00</P_9A>
<P_11>10000.00</P_11>
<P_12>23</P_12>
</FaWiersz>
<P_13_1>10000.00</P_13_1>
<P_14_1>2300.00</P_14_1>
<P_15>12300.00</P_15>
<Adnotacje>
<P_16>2</P_16><P_17>2</P_17><P_18>2</P_18><P_18A>2</P_18A>
<P_19>2</P_19><P_22>2</P_22><P_23>2</P_23><P_PMarzy>2</P_PMarzy>
</Adnotacje>
<Platnosc>
<FormaPlatnosci>6</FormaPlatnosci>
<TerminPlatnosci>2024-01-29</TerminPlatnosci>
</Platnosc>
</Fa>
</Faktura>
Note how the normalization happened: 123-456-78-90 → 1234567890; 15.01.2024 → 2024-01-15; 10 000,00 zł → 10000.00; przelew → 6; 23% → P_12=23. The Do zapłaty line from the PDF (12 300,00) was recomputed as P_15 and only then trusted.
Document structure
Faktura
├── Naglowek (header)
├── Podmiot1 (seller)
├── Podmiot2 (buyer)
└── Fa (invoice body)
├── FaWiersz ... (one per line item)
├── P_13_x / P_14_x (VAT totals per rate)
├── P_15 (gross total payable)
├── Adnotacje (mandatory markers — `1` applies / `2` does not; set all `2` for an ordinary VAT invoice)
└── Platnosc (payment)
Header is fixed: KodFormularza=FA (kodSystemowy="FA (2)", wersjaSchemy="1-0E"), WariantFormularza=2, DataWytworzeniaFa = generation timestamp, SystemInfo = issuing system name. Parties carry digits-only NIP, Nazwa, and Adres (KodKraju, AdresL1, AdresL2). The mandatory Adnotacje markers are P_16, P_17, P_18, P_18A, P_19, P_22, P_23, P_PMarzy.
Code tables
VAT rate (P_12) and VAT-summary suffix (P_13_x net / P_14_x VAT)
P_12 |
Meaning | Summary suffix |
|---|---|---|
23 |
23% standard | _1 |
8 |
8% reduced | _2 |
5 |
5% reduced | _3 |
0 |
0% | _4 |
oo |
reverse charge | _5 |
zw |
exempt | _6 |
np |
not subject to VAT | _7 |
Invoice type (RodzajFaktury): VAT standard · KOR correcting · ZAL advance · ROZ settlement
Payment method (FormaPlatnosci): 1 cash · 3 card · 5 offset · 6 transfer
Calculation rules
- Line net (
P_11) = quantity × net unit price, rounded to 2 decimals. - Line VAT = line net × rate, rounded to 2 decimals. For
zw,np,oo→0.00. - Per-rate summary — sum net and VAT for each rate into the matching
P_13_x/P_14_x. EmitP_14_xonly when that rate's VAT total > 0. - Gross total (
P_15) = net total + VAT total, rounded to 2 decimals. - Recompute every total from the line items; if it disagrees with a total printed on the source, flag it.
Validation checklist
- All required fields successfully extracted from the source (asked the user about anything missing/ambiguous)
- NIPs digits-only; dates ISO; amounts 2-decimal; VAT rate and payment method mapped to codes
- Each
FaWierszhas sequentialNrWierszaFa, unit, quantity, net unit price, computedP_11, validP_12 -
P_13_x/P_14_xreconcile with the per-rate sum of lines;P_15= net + VAT - Recomputed totals match the source (or the discrepancy was raised)
-
Adnotacjepresent with all required markers; root namespace +xsi:schemaLocationset - Validated against the official FA(2) XSD before sending
Last updated: 2026-05-26 — verify all field codes, schema version, and VAT-summary suffix mapping against the current Ministerstwo Finansów FA(2) XSD.