15 KiB
HANDEL08 — VAT, wartości i waluty
Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../handel.md.
Rozdział opisuje publiczny kontrakt dokumentu handlowego w zakresie tabeli VAT, podsumowań wartości, ręcznej korekty VAT, sposobu liczenia VAT oraz zmiany waluty dokumentu i cen. Cały kod jest zgodny z C# 10 i operuje wyłącznie na publicznych typach i workerach platformy.
Wartości pieniężne na pozycjach tabeli VAT i podsumowaniach mają dwie reprezentacje:
BruttoNetto— kwoty w walucie systemowej jakodecimal(Netto,VAT,Brutto);BruttoNettoCy— kwoty w walucie dokumentu jakoCurrency(NettoCy,VATCy,BruttoCy). Nie operuj na niezaokrąglonychdecimal— platforma weryfikuje zaokrąglenie (safe-code §10).
HANDEL-W43 — Odczytanie tabeli VAT (SumyVAT)
Cel: odczytać rozbicie wartości dokumentu na stawki VAT (netto / VAT / brutto wg stawki) — np. do wydruku, eksportu lub kontroli sumy podatku.
Warianty:
| Wariant | Źródło | Uwaga |
|---|---|---|
| Tabela VAT dokumentu | dok.SumyVAT (SubTable<SumaVAT>) |
po jednej pozycji na stawkę |
| Kwoty w walucie systemowej | suma.Suma (BruttoNetto) |
Netto/VAT/Brutto jako decimal |
| Kwoty w walucie dokumentu | suma.SumaCy (BruttoNettoCy) |
NettoCy/VATCy/BruttoCy jako Currency |
| Procent / opis stawki | suma.Stawka, suma.DefinicjaStawki |
StawkaVat.Procent: Percent |
| Sumy z dokumentów nadrzędnych | dok.NadrzędneSumyVAT (IList) |
scalone stawki nadrzędnych |
Pola i typy: dok.SumyVAT: SubTable<SumaVAT>. SumaVAT udostępnia: DefinicjaStawki: DefinicjaStawkiVat, Stawka: StawkaVat (Stawka.Procent: Percent), Suma: BruttoNetto
(Netto, VAT, Brutto — decimal), SumaCy: BruttoNettoCy (NettoCy, VATCy, BruttoCy —
Currency), Dokument: DokumentHandlowy.
Snippet:
var dok = session.GetHandel().DokHandlowe.WgDaty[...]; // lub po Guid
// Iteracja po tabeli VAT — jedna pozycja (SumaVAT) na każdą stawkę dokumentu:
foreach (SumaVAT s in dok.SumyVAT)
{
Percent stawka = s.Stawka.Procent; // np. 23%
decimal netto = s.Suma.Netto; // kwota netto w walucie systemowej
decimal vat = s.Suma.VAT; // kwota podatku VAT
decimal brutto = s.Suma.Brutto; // kwota brutto
// Kwoty w walucie dokumentu (Currency = wartość + symbol waluty):
Currency vatCy = s.SumaCy.VATCy;
Console.WriteLine($"{stawka}: netto={netto} VAT={vat} brutto={brutto}");
}
// Łączna kwota VAT dokumentu z tabeli VAT:
decimal vatRazem = dok.SumyVAT.Sum(s => s.Suma.VAT);
Pułapki:
dok.SumyVATtoSubTable<SumaVAT>— kolekcja serwerowa; iteruj po niej, nie materializuj do listy, jeśli wystarczy przebieg jednorazowy. Tabela VAT jest mała (kilka stawek), więc.Sum(...)jest akceptowalne.- Rozróżniaj
Suma(BruttoNetto,decimalw walucie systemowej) odSumaCy(BruttoNettoCy,Currencyw walucie dokumentu). Dla dokumentu walutowego do prezentacji używajSumaCy. StawkatoStawkaVat(typ stawki),ProcentzwracaPercent— nie myl zdecimal.- Tabela VAT jest wyliczana z pozycji dokumentu (chyba że włączono
KorektaVAT— patrz HANDEL-W45). Nie modyfikuj jej, gdy chcesz tylko odczytać wartości.
HANDEL-W44 — Odczyt podsumowań wartości dokumentu
Cel: odczytać zsumowane wartości netto / VAT / brutto całego dokumentu oraz proponowany rabat — bez ręcznego sumowania pozycji.
Warianty:
| Wariant | Pole | Typ | Uwaga |
|---|---|---|---|
| Podsumowanie dokumentu | dok.Suma |
BruttoNetto |
Netto/VAT/Brutto (decimal, waluta systemowa) |
| Wartość brutto w walucie | dok.BruttoCy |
Currency |
brutto w walucie dokumentu |
| Suma wyliczona z pozycji | dok.SumaPozycji |
BruttoNettoPozycji |
Netto/VAT/Brutto (read-only) |
| Suma pozycji tow./prod. | dok.SumaPozycjiTowProd |
BruttoNettoPozycji |
tylko towary i produkty |
| Proponowany rabat | dok.Rabat |
Percent |
przepisywany do pozycji |
Pola i typy: dok.Suma: BruttoNetto (podsumowana wartość dokumentu), dok.BruttoCy: Currency,
dok.SumaPozycji: BruttoNettoPozycji (Netto/VAT/Brutto — decimal, tylko do odczytu,
liczone na bieżąco z pozycji), dok.Rabat: Percent.
Snippet:
var dok = session.GetHandel().DokHandlowe.WgDaty[...];
// Podsumowanie całego dokumentu (waluta systemowa):
decimal netto = dok.Suma.Netto;
decimal vat = dok.Suma.VAT;
decimal brutto = dok.Suma.Brutto;
// Brutto w walucie dokumentu (dla dokumentów walutowych):
Currency bruttoCy = dok.BruttoCy;
// Suma wyliczana z pozycji (przydatne do kontroli spójności z dok.Suma):
var sp = dok.SumaPozycji;
Console.WriteLine($"Pozycje: netto={sp.Netto} VAT={sp.VAT} brutto={sp.Brutto}");
// Proponowany rabat dokumentu (przepisywany do nowych pozycji):
Percent rabat = dok.Rabat;
Pułapki:
dok.Sumato stan zapisany podsumowania, adok.SumaPozycjijest wyliczane na bieżąco z pozycji za każdym odczytem. Dla dokumentu w buforze, przed ponownym przeliczeniem, mogą się chwilowo różnić.SumaPozycji/SumaPozycjiTowProdzwracająBruttoNettoPozycji— typ tylko do odczytu (brak setterów); nie próbuj przez nie modyfikować wartości.dok.RabattoPercent— proponowany rabat dokumentu, przepisywany do nowo dodawanych pozycji; ustawienie nie przelicza wstecznie pozycji już istniejących.- Wartości brutto/netto na poziomie dokumentu zależą od
LiczonaOd(HANDEL-W46) i ewentualnej korekty tabeli VAT (KorektaVAT, HANDEL-W45).
HANDEL-W45 — Ręczna korekta tabeli VAT (KorektaVAT)
Cel: ręcznie skorygować kwoty w tabeli VAT (gdy wyliczenie z pozycji nie odpowiada wartości
docelowej — np. zaokrąglenia faktury źródłowej), włączając flagę KorektaVAT i edytując wiersze
SumyVAT.
Warianty:
| Wariant | Operacja |
|---|---|
| Włączenie trybu korekty | dok.KorektaVAT = true |
| Ręczna zmiana kwoty stawki | edycja suma.Suma.Netto / .VAT / .Brutto na wierszu SumaVAT |
| Dostępność korekty | dok.IsReadOnlyKorektaVAT(), dok.IsReadOnlySumyVAT() (sterowanie UI) |
| Powrót do automatu | dok.KorektaVAT = false (tabela liczona ponownie z pozycji) |
Pola i typy: dok.KorektaVAT: bool (czy sumy VAT zmieniono ręcznie i nie zależą od pozycji),
SumaVAT.Suma: BruttoNetto (Netto/VAT/Brutto — decimal). Wiersze tabeli VAT są edytowalne
tylko gdy KorektaVAT == true (SumaVAT.IsReadOnly() zwraca true przy wyłączonej fladze).
Worker
KorektaTabeliVATWorkerjestinternal— nie da się go zainstancjonować z dodatku zewnętrznego. Publiczny tor korekty prowadzi przez flagędok.KorektaVATi bezpośrednią edycję pól wierszydok.SumyVAT.
Snippet:
var dok = session.GetHandel().DokHandlowe.WgDaty[...];
using (var t = session.Logout(editMode: true)) // CommitUI() w workerze/extenderze
{
// 1. Włącz ręczną korektę — odblokowuje edycję wierszy tabeli VAT:
dok.KorektaVAT = true;
// 2. Skoryguj kwoty na wybranej stawce (np. wyrównanie groszowe na 23%):
foreach (SumaVAT s in dok.SumyVAT)
{
if (s.Stawka.Procent == new Percent(0.23))
{
s.Suma.VAT = 230.01m; // wartości MUSZĄ być zaokrąglone do grosza
s.Suma.Brutto = 1230.01m;
}
}
t.Commit();
}
session.Save();
Pułapki:
- Edycja wierszy
SumyVATbezdok.KorektaVAT = truezostanie zablokowana —SumaVATjest wtedy read-only (sumy zależą od pozycji). - Przypisywane kwoty muszą być zaokrąglone do grosza — w trybie DEBUG ustawienie
niezaokrąglonej wartości
Netto/VAT/BruttorzucaArgumentException. Zaokrąglaj wejście (Soneta.Tools.Math.RoundCy(...)). KorektaVATjest dostępna tylko, gdy definicja dokumentu na to pozwala (Definicja.SumyVATw trybie korekty) — sprawdzajdok.IsReadOnlyKorektaVAT()zanim ustawisz flagę z poziomu UI.- Po włączeniu korekty tabela VAT przestaje śledzić zmiany pozycji. Wyłączenie
(
KorektaVAT = false) przywraca wyliczanie z pozycji i nadpisuje ręczne kwoty. DefinicjaStawkina wierszuSumaVATmożna zmieniać tylko przy włączonej korekcie (IsReadOnlyDefinicjaStawki()zależy odKorektaVAT).
HANDEL-W46 — Sposób liczenia VAT (LiczonaOd) i przeliczenie procedur VAT
Cel: ustawić, czy dokument jest liczony od netto czy od brutto (LiczonaOd), oraz przeliczyć
procedury VAT (JPK) na dokumencie zatwierdzonym/zaksięgowanym przy użyciu publicznego workera.
Warianty:
| Wariant | Mechanizm |
|---|---|
| Liczenie od netto | dok.LiczonaOd = SposobLiczeniaVAT.OdNetto |
| Liczenie od brutto | dok.LiczonaOd = SposobLiczeniaVAT.OdBrutto |
| Od brutto minus netto | dok.LiczonaOd = SposobLiczeniaVAT.OdBruttoMinusNetto |
| Wg ustawień kontrahenta | dok.LiczonaOd = SposobLiczeniaVAT.ZależyOdKontrahenta |
| Przeliczenie procedur VAT | worker PrzeliczProceduryVATWorker (publiczny) |
Pola i typy: dok.LiczonaOd: SposobLiczeniaVAT — enum Soneta.Handel.SposobLiczeniaVAT:
OdNetto=1, OdBrutto=2, OdBruttoMinusNetto=3, ZależyOdKontrahenta=4 (wartość 0 jest
niedozwolona — rzuca RequiredException). Worker PrzeliczProceduryVATWorker ma publiczną klasę
parametrów PrzeliczProceduryVATParams : ContextBase (Zatwierdzone: bool = true,
Zaksiegowane: bool = false) oraz właściwości [Context]: Dokument: DokumentHandlowy,
Params: PrzeliczProceduryVATParams.
Snippet:
var dok = session.GetHandel().DokHandlowe.WgDaty[...];
// 1. Zmiana sposobu liczenia VAT (dokument w buforze):
using (var t = session.Logout(editMode: true))
{
dok.LiczonaOd = SposobLiczeniaVAT.OdBrutto; // 0 jest niedozwolone
t.Commit();
}
session.Save();
// 2. Przeliczenie procedur VAT (JPK) workerem publicznym.
// Worker działa tylko dla dokumentu zatwierdzonego (Params.Zatwierdzone)
// lub zablokowanego/zaksięgowanego (Params.Zaksiegowane):
var p = new PrzeliczProceduryVATWorker.PrzeliczProceduryVATParams(context)
{
Zatwierdzone = true,
Zaksiegowane = false,
};
var worker = new PrzeliczProceduryVATWorker
{
Dokument = dok,
Params = p,
};
worker.PrzeliczProceduryVAT(); // sam otwiera transakcję i Commit
session.Save();
Pułapki:
LiczonaOdnie przyjmuje wartości0(RequiredException). Zawsze ustaw konkretny wariant enuma.- Zmiana
LiczonaOdna dokumencie z pozycjami wpływa na sposób przeliczenia netto↔brutto pozycji i tabeli VAT — rób to przed wprowadzeniem cen lub świadomie po przeliczeniu. PrzeliczProceduryVATWorker.PrzeliczProceduryVAT()nic nie zrobi, jeśli dokument jest w buforze albo stan nie pasuje do flagParams(Zatwierdzone/Zaksiegowane). Worker sam otwiera transakcję (Logout(true)+Commit) — nie owijaj go w dodatkową transakcję edycyjną.- Worker jest widoczny tylko, gdy definicja liczy sumy VAT i ma definicję ewidencji
(
IsVisiblePrzeliczProceduryVAT); z poziomu kodu i tak sprawdź stan dokumentu przed wywołaniem. PrzeliczProceduryVATParamsdziedziczy poContextBase— przy ręcznym tworzeniu przekażContextdo konstruktora.
HANDEL-W47 — Zmiana waluty dokumentu i cen
Cel: zmienić walutę dokumentu handlowego (i opcjonalnie przeliczyć ceny pozycji) — np. wystawić fakturę w EUR zamiast PLN, z kursem z wybranej tabeli kursowej.
Warianty:
| Wariant | Mechanizm |
|---|---|
| Zmiana waluty z przeliczeniem cen | parametry DokumentHandlowyZmianaWalutyWorkerParams + akcja „Zmień walutę dokumentu i cen..." |
| Zmiana waluty bez cen | te same parametry z ZmienCeny = false |
| Ręczne ustawienie waluty/kursu | dok.TabelaKursowa, dok.KursWaluty, dok.DataOgłoszeniaKursu, dok.BruttoCy |
Pola i typy: klasa parametrów (publiczna) DokumentHandlowyZmianaWalutyWorkerParams : PozycjaDokHandlowegoZmianaWalutyCenyWorkerParams (ctor (Context, [Context] DokumentHandlowy))
udostępnia: Waluta: Waluta („na walutę"), WalutaBazowa: Waluta (read-only, „z waluty"),
TabelaKursowa: TabelaKursowa, Data: Date, KursWaluty: double, ZmienCeny: bool. Pola
dokumentu: dok.TabelaKursowa: TabelaKursowa, dok.KursWaluty: double, dok.BruttoCy: Currency.
Moduł walut (jest internal jako extension): Soneta.Waluty.WalutyModule.GetInstance(session) →
.Waluty.WgSymbolu["EUR"], .TabeleKursowe.
Worker
DokumentHandlowyZmianaWalutyWorkerjestinternal— nie da się go zainstancjonować bezpośrednio z dodatku zewnętrznego. Jest jednak zarejestrowany jako akcja menu Czynności („Zmień walutę dokumentu i cen...",Shift+F11) i przyjmuje publiczne parametryDokumentHandlowyZmianaWalutyWorkerParams. Z poziomu kodu dodatku zewnętrznego dostępne tory to: (1) uruchomienie akcji przez mechanizm Czynności z przygotowanymContext, albo (2) bezpośrednie ustawienie pól waluty/kursu na dokumencie i pozycjach.
Snippet:
using Microsoft.Extensions.DependencyInjection; // jeśli korzystasz z serwisów
using Soneta.Waluty;
var dok = session.GetHandel().DokHandlowe.WgDaty[...];
// --- Tor 1: przygotowanie parametrów workera (do uruchomienia przez akcję Czynności) ---
// Worker jest internal — z dodatku przygotowujemy publiczne Params i uruchamiamy akcję
// przez mechanizm menu Czynności (Context z zaznaczonym dokumentem).
var wm = WalutyModule.GetInstance(session);
var p = new DokumentHandlowyZmianaWalutyWorkerParams(context, dok)
{
Waluta = wm.Waluty.WgSymbolu["EUR"], // waluta docelowa
TabelaKursowa = wm.TabeleKursowe.NBP,
Data = Date.Today,
ZmienCeny = true, // przelicz też ceny pozycji
};
// KursWaluty wylicza się automatycznie po ustawieniu Waluta/TabelaKursowa/Data;
// w razie potrzeby można nadpisać: p.KursWaluty = 4.30;
// --- Tor 2: ręczne ustawienie waluty i kursu na dokumencie (bez workera) ---
using (var t = session.Logout(editMode: true))
{
dok.TabelaKursowa = wm.TabeleKursowe.NBP;
dok.KursWaluty = 4.30;
// dok.BruttoCy = new Currency(..., "EUR"); // kwoty w walucie dokumentu
t.Commit();
}
session.Save();
Pułapki:
- Worker
DokumentHandlowyZmianaWalutyWorkerjestinternal— nie wywołasznew ...Worker(...)ani.ZmienWalute()z dodatku zewnętrznego. Używaj publicznychParams+ akcji Czynności lub bezpośredniej edycji pól dokumentu. session.GetWaluty()jest internal — moduł walut pobieraj przezWalutyModule.GetInstance(session)(namespaceSoneta.Waluty).- Jeśli w bazie brak kursu na żądaną datę (np. Demo nie ma kursu EUR „na dziś"), platforma rzuci
KursWalutyNotFoundException.KursWalutyw parametrach wylicza się automatycznie tylko, gdy kurs istnieje; w przeciwnym razie ustawKursWalutyręcznie. - Zmiana waluty ma sens tylko dla dokumentu w buforze (
IsVisibleZmienWalutewymagadok.Bufor); dla dokumentu zatwierdzonego operacja jest niedostępna. WalutaBazowajest read-only — wyznaczana z bieżącej waluty dokumentu (dok.BruttoCy.Symbol). Ustawiasz tylkoWaluta(docelową).- Kwoty pieniężne to
Currency(wartość + symbol), niedecimal/double. SamKursWalutyjestdouble.