Motyw
Podatki i VAT (Silnik kalkulacji)
Moduł podatków stanowi centralny, wysoce precyzyjny silnik obliczeniowy i konfiguracyjny w platformie Ammonly. Odpowiada za automatyczną determinację stawek podatkowych, wielopoziomowe przeliczanie kwot netto/brutto, obsługę procedur specjalnych (VAT Marża, WHT, OSS, Reverse Charge) oraz generowanie niezmiennych snapshotów fiskalnych dla dokumentów handlowych.
Architektura Domain-Driven & API-First
Silnik podatkowy został zaprojektowany w oparciu o czystą domenę (App\Modules\Tax\Domain), pozbawioną zależności od frameworka i bazy danych. Wszystkie kalkulacje są deterministyczne, audytowalne i dostępne zarówno przez REST API (/api/v1/tax/*), jak i reaktywny interfejs HTMX w panelu administracyjnym.
Spis treści
- 1. Architektura i kluczowe zasady
- 2. Cztery tryby kalkulacji podatku
- 3. Statusy prawne i integracja fiskalna (JPK_V7 / KSeF)
- 4. Matryca determinacji podatków (Determination Matrix)
- 5. Grupy wielopodatkowe (Tax Groups)
- 6. Strategie zaokrągleń i precyzja finansowa
- 7. Niezmienny zrzut podatkowy dokumentu (Snapshot JSON)
- 8. Symulator podatków (Live Tester) oraz REST API
1. Architektura i kluczowe zasady
W nowoczesnym systemie ERP/CRM zarządzanie podatkami nie może ograniczać się do prostego mnożenia ceny przez 0.23. Różnice w przepisach międzynarodowych, wymagania transakcji transgranicznych, sprzedaż towarów używanych oraz podatki u źródła wymagają zunifikowanego, elastycznego modelu danych:
mermaid
flowchart TD
Doc[Pozycja dokumentu: Oferta / Zamówienie / Faktura] --> Matrix[Matryca Determinacji Stawek]
Matrix -->|Klient: B2B/B2C, Strefa: PL/UE/Export, Typ: Towar/Usługa| Rate[Wybrana Stawka / Grupa Podatkowa]
Rate --> Engine[Silnik Kalkulacji Finansowej TaxEngine]
Engine --> Strategy{Strategia zaokrągleń}
Strategy -->|Line-Level / Header-Level| Output[Wyliczone kwoty Netto, VAT, Brutto, WHT]
Output --> Snapshot[(Niezmienny Snapshot JSON w rekordzie dokumentu)]Kluczowe założenia projektowe:
- Niezmienność historyczna (Immutability): Zmiana stawek VAT lub usunięcie stawki w konfiguracji systemu nie wpływa na wcześniej wystawione oferty, zamówienia czy faktury. Dokument przechowuje kompletny zrzut JSON.
- Precyzja matematyczna: Wszystkie operacje arytmetyczne operują na wysokiej precyzji zmiennoprzecinkowej, a zaokrąglenie do 2 miejsc po przecinku (groszy) odbywa się ściśle według wybranej strategii biznesowej.
- Separacja profilowa (Multi-Tenant): Konfiguracja podatków jest autonomiczna dla każdej instancji klienta (
App Client), przy jednoczesnym dostępie centralnym z poziomu paneluApp Admin.
2. Cztery tryby kalkulacji podatku
Silnik Ammonly natywnie obsługuje 4 odrębne algorytmy wyliczania podatku, definiowane na poziomie stawki (calculation_mode):
mermaid
graph LR
Mode[Tryb kalkulacji] --> Exc[1. Exclusive: Netto + VAT]
Mode --> Inc[2. Inclusive: W cenie brutto]
Mode --> Mar[3. Margin: Od marży handlowej]
Mode --> Wht[4. WHT: Potrącenie podatku u źródła]2.1. Exclusive — Od netto (Standard B2B)
Podstawowy tryb stosowany w transakcjach pomiędzy przedsiębiorstwami. Wprowadzana cena jednostkowa jest ceną netto, a podatek jest doliczany do wartości pozycji:
$$\text{Kwota Podatku} = \text{Kwota Netto} \times \frac{\text{Stawka}%}{100}$$ $$\text{Kwota Brutto} = \text{Kwota Netto} + \text{Kwota Podatku}$$
Przykład:
- Cena netto: $100{,}00\text{ PLN}$, Stawka VAT: $23%$
- Podatek VAT: $23{,}00\text{ PLN}$, Kwota brutto: $123{,}00\text{ PLN}$
2.2. Inclusive — Od brutto (Standard B2C)
Tryb dedykowany dla sprzedaży konsumenckiej (detalicznej), cenników konsumenckich oraz e-commerce, gdzie prezentowana cena zawiera już podatek VAT (kalkulacja metodą "w stu"):
$$\text{Kwota Netto} = \frac{\text{Kwota Brutto}}{1 + \frac{\text{Stawka}%}{100}}$$ $$\text{Kwota Podatku} = \text{Kwota Brutto} - \text{Kwota Netto}$$
Przykład:
- Cena brutto: $123{,}00\text{ PLN}$, Stawka VAT: $23%$
- Kwota netto: $100{,}00\text{ PLN}$, Podatek VAT: $23{,}00\text{ PLN}$
2.3. Procedura Marży — VAT Marża (art. 120)
Zgodnie z art. 120 ustawy o VAT, w przypadku dostawy towarów używanych, dzieł sztuki, przedmiotów kolekcjonerskich oraz usług turystycznych (biura podróży), podstawą opodatkowania jest wyłącznie marża handlowa (różnica między ceną sprzedaży a ceną nabycia), pomniejszona o kwotę podatku:
$$\text{Marża Brutto} = \max(0, \text{Cena Sprzedaży} - \text{Koszt Nabycia})$$ $$\text{Podstawa Netto Marży} = \frac{\text{Marża Brutto}}{1 + \frac{\text{Stawka}%}{100}}$$ $$\text{Kwota Podatku} = \text{Marża Brutto} - \text{Podstawa Netto Marży}$$ $$\text{Łączna Kwota Brutto do zapłaty} = \text{Cena Sprzedaży}$$
Zasada marży ujemnej
Jeżeli cena sprzedaży jest niższa lub równa kosztowi nabycia ($\text{Marża} \le 0$), podstawa opodatkowania oraz kwota podatku VAT wynoszą dokładnie $0{,}00\text{ PLN}$. Nabywca płaci cenę sprzedaży, a na dokumencie nie wykazuje się kwoty podatku dla nabywcy.
Przykład:
- Cena sprzedaży samochodu używanego: $50,000{,}00\text{ PLN}$, Cena nabycia (zakupu): $40,000{,}00\text{ PLN}$
- Marża brutto: $10,000{,}00\text{ PLN}$
- Podstawa netto marży przy stawce $23%$: $8,130{,}08\text{ PLN}$
- Podatek VAT z marży: $1,869{,}92\text{ PLN}$
- Kwota do zapłaty na fakturze: $50,000{,}00\text{ PLN}$ (z adnotacją procedura marży - towary używane, kod fiskalny
P_106E_3).
2.4. Podatek u źródła — WHT (Withholding Tax)
Mechanizm stosowany w transakcjach zakupu usług niematerialnych (np. doradczych, prawnych, marketingowych, licencji oprogramowania, hostingu) od podmiotów zagranicznych (nierezydentów podatkowych). Zgodnie z ustawami o CIT/PIT, płatnik w Polsce potrąca podatek u źródła (np. 20%) i odprowadza go do polskiego urzędu skarbowego, wypłacając kontrahentowi kwotę pomniejszoną o ten podatek:
$$\text{Kwota WHT} = \text{Kwota Netto} \times \frac{\text{Stawka WHT}%}{100}$$ $$\text{Kwota do wypłaty dla dostawcy} = \text{Kwota Brutto} - \text{Kwota WHT}$$
Przykład:
- Zakup licencji IT z USA o wartości: $10,000{,}00\text{ PLN}$ (stawka WHT 20%)
- Kwota podatku WHT potrącona dla US: $2,000{,}00\text{ PLN}$
- Kwota wypłacona dostawcy zagranicznemu: $8,000{,}00\text{ PLN}$
3. Statusy prawne i integracja fiskalna (JPK_V7 / KSeF)
Każda stawka podatkowa w systemie posiada przypisany status prawny (tax_status) oraz kod fiskalny (fiscal_code), które gwarantują bezbłędne mapowanie do struktur logicznych JPK_V7 oraz Krajowego Systemu e-Faktur (KSeF):
Status prawny (tax_status) | Kod fiskalny KSeF/JPK | Stawka % | Zastosowanie biznesowe | Podstawa prawna / Klauzula |
|---|---|---|---|---|
standard | P_13_1 | 23.00% | Podstawowa stawka VAT na towary i usługi | Art. 41 ust. 1 |
standard | P_13_2 | 8.00% | Stawka obniżona (budownictwo, gastronomia) | Art. 41 ust. 2 |
standard | P_13_3 | 5.00% | Stawka obniżona (żywność, książki) | Art. 41 ust. 2a |
zero_rated | P_13_6_1 | 0.00% | Eksport towarów poza UE oraz WDT wewnątrz UE | Art. 41 ust. 4, Art. 42 |
exempt | P_13_6_2 | 0.00% | Zwolnienie przedmiotowe lub podmiotowe (ZW) | Zwolnienie na podst. art. 43 ust. 1 |
reverse_charge | P_13_6_3 | 0.00% | Odwrotne obciążenie (NP / OO) dla usług B2B UE | Odwrotne obciążenie - art. 28b |
out_of_scope | — | 0.00% | Transakcje niepodlegające ustawie o VAT | Poza zakresem ustawy o podatku VAT |
4. Matryca determinacji podatków (Determination Matrix)
Matryca determinacji eliminuje błędy ludzkie operatorów, automatycznie dobierając właściwą stawkę podatku w oparciu o reguły decyzyjne oceniane według priorytetu (priority, gdzie niższa liczba = wyższy priorytet):
mermaid
sequenceDiagram
autonumber
actor User as Operator / System
participant Engine as Silnik Matrycy (TaxDeterminationMatrix)
participant Rules as Rejestr Reguł (a_mod_tax_rules_records)
participant Rate as Wybrana Stawka VAT
User->>Engine: Przekazuje dane: [Klient: UE B2B, Usługa IT, Strefa: eu_vat]
Engine->>Rules: Pobiera aktywne reguły posortowane wg priorytetu rosnąco
loop Ewaluacja reguł
Rules->>Engine: Reguła 40: Export Goods? -> NIE PASUJE
Rules->>Engine: Reguła 50: EU B2B Services? -> PASUJE!
end
Engine->>Rate: Zwraca stawkę VAT_NP (0% Odwrotne obciążenie)
Rate-->>User: Automatycznie aplikuje stawkę do pozycji oferty/fakturyKryteria ewaluacji:
- Typ kontrahenta (
customer_type):all— dotyczy wszystkich kontrahentów,b2b— przedsiębiorcy zarejestrowani z numerem identyfikacji podatkowej (NIP / Tax ID),b2c— konsumenci i osoby fizyczne nieprowadzące działalności gospodarczej.
- Strefa geograficzna (
geo_zone):domestic— transakcja krajowa (Polska),eu_vat— kontrahent z kraju Unii Europejskiej z aktywnym numerem VAT-UE (VIES),eu_consumer— konsument z kraju UE (obsługa procedury One Stop Shop — OSS),export_world— kontrahent z kraju trzeciego poza obszarem celnym UE (eksport).
- Klasyfikacja pozycji (
item_type):all— wszystkie pozycje asortymentowe,product— towary fizyczne i materiały,service— usługi niematerialne, programistyczne, doradcze, licencje,margin_goods— towary używane rozliczane w procedurze marży.
5. Grupy wielopodatkowe (Tax Groups)
Dla złożonych transakcji system pozwala na grupowanie wielu podatków w pojedynczą wybieralną encję (TaxGroup):
- Podatki równoległe (Parallel Taxes):
- Każdy składnik grupy naliczany jest niezależnie od podstawowej kwoty netto.
- Przykład: Podatek podstawowy VAT 23% + Opłata Cukrowa 5%.
- Podatki kaskadowe / złożone (Compound Taxes —
is_compound = 1):- Składnik podatkowy naliczany jest od kwoty netto powiększonej o wartość wcześniej naliczonych podatków w grupie.
- Stosowane m.in. w kanadyjskim systemie podatkowym (GST + PST) oraz przy akcyzach.
6. Strategie zaokrągleń i precyzja finansowa
System Ammonly oferuje pełną kontrolę nad miejscem i metodą zaokrąglania kwot podatków:
mermaid
graph TD
Calc[Obliczenia kwot] --> Strat{Poziom zaokrągleń}
Strat --> Line[Line Level: Pozycja po pozycji]
Strat --> Head[Header Level: Suma baz opodatkowania]
Line --> Math{Algorytm matematyczny}
Head --> Math
Math --> HalfUp[Half-Up: >= 0.005 w górę]
Math --> HalfEven[Half-Even: Zaokrąglenie bankierskie]Poziom strategii zaokrąglania (RoundingStrategy):
Line Level (Row-by-Row)— Standard: Podatek jest obliczany i zaokrąglany do 2 miejsc po przecinku na każdej pojedynczej pozycji dokumentu. Suma podatku na dokumencie stanowi sumę zaokrąglonych pozycji.Header Level (Summary Table): Na poziomie pozycji zachowywana jest pełna precyzja ułamkowa. W podsumowaniu dokumentu sumowane są dokładne bazy netto dla każdej stawki, a zaokrąglenie do groszy odbywa się jednorazowo na zagregowanej sumie.
Algorytmy zaokrąglania:
Half-Up: Standardowa metoda handlowa — jeśli trzecia cyfra po przecinku wynosi $\ge 5$, zaokrąglenie następuje w górę (np. $10{,}005 \to 10{,}01$).Half-Even(Banker's Rounding): Zaokrąglenie do najbliższej cyfry parzystej — eliminuje statystyczne przeszacowanie sum przy setkach tysięcy operacji rozliczeniowych.
7. Niezmienny zrzut podatkowy dokumentu (Snapshot JSON)
Każdy zatwierdzony dokument handlowy (oferta, zamówienie, faktura) utrwala w bazie danych pełen zrzut kalkulacji podatkowej w formacie JSON (tax_snapshot).
json
{
"currency": "PLN",
"defaultPriceMode": "exclusive",
"roundingStrategy": "line_level",
"totalNet": 100.00,
"totalTax": 23.00,
"totalGross": 123.00,
"totalWht": 0.00,
"totalPaymentDue": 123.00,
"items": [
{
"id": "item-1",
"name": "Konsultacje IT / Wdrożenie",
"quantity": 1.00,
"unitPrice": 100.00,
"netAmount": 100.00,
"taxAmount": 23.00,
"grossAmount": 123.00,
"taxRate": {
"taxCode": "VAT_23",
"taxName": "VAT 23% (Standard)",
"ratePercent": 23.00,
"calculationMode": "exclusive",
"taxStatus": "standard",
"fiscalCode": "P_13_1"
}
}
],
"taxBreakdown": [
{
"taxCode": "VAT_23",
"taxName": "VAT 23% (Standard)",
"ratePercent": 23.00,
"netBase": 100.00,
"taxAmount": 23.00,
"grossAmount": 123.00,
"legalClause": null
}
]
}Gwarancja audytowa
Taki format zapisu gwarantuje, że wydruk faktury po 5 latach będzie w 100% zgodny z pierwotnymi wyliczeniami, nawet jeśli stawki VAT w państwie ulegną zmianie.
8. Symulator podatków (Live Tester) oraz REST API
Live Tax Tester w interfejsie
W zakładce Live Tax Tester (/settings/taxes#tab-simulator) administratorzy mogą w czasie rzeczywistym wprowadzać parametry transakcji (cena, ilość, rabat, marża, strefa geo, typ klienta) i diagnozować:
- Która reguła matrycy decyzyjnej została zastosowana,
- Jakie kwoty netto, VAT, brutto i WHT zostały wygenerowane,
- Dokładną tabelę rozbicia stawek oraz podgląd snapshotu JSON.
REST API Endpoints
Wszystkie operacje kalkulacyjne są dostępne programistycznie przez REST API:
POST /api/v1/tax/calculate: Przelicza przekazany koszyk pozycji, zwracając podsumowanie, rozbicie stawek i snapshot JSON.GET /api/v1/tax/rates: Pobiera listę aktywnych stawek podatkowych dla bieżącego kontekstu.GET /api/v1/tax/rules: Pobiera listę zdefiniowanych reguł matrycy determinacji.