Files

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 jako decimal (Netto, VAT, Brutto); BruttoNettoCy — kwoty w walucie dokumentu jako Currency (NettoCy, VATCy, BruttoCy). Nie operuj na niezaokrąglonych decimal — 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, Bruttodecimal), SumaCy: BruttoNettoCy (NettoCy, VATCy, BruttoCyCurrency), 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.SumyVAT to SubTable<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, decimal w walucie systemowej) od SumaCy (BruttoNettoCy, Currency w walucie dokumentu). Dla dokumentu walutowego do prezentacji używaj SumaCy.
  • Stawka to StawkaVat (typ stawki), Procent zwraca Percent — nie myl z decimal.
  • 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/Bruttodecimal, 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.Suma to stan zapisany podsumowania, a dok.SumaPozycji jest wyliczane na bieżąco z pozycji za każdym odczytem. Dla dokumentu w buforze, przed ponownym przeliczeniem, mogą się chwilowo różnić.
  • SumaPozycji/SumaPozycjiTowProd zwracają BruttoNettoPozycji — typ tylko do odczytu (brak setterów); nie próbuj przez nie modyfikować wartości.
  • dok.Rabat to Percent — 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/Bruttodecimal). Wiersze tabeli VAT są edytowalne tylko gdy KorektaVAT == true (SumaVAT.IsReadOnly() zwraca true przy wyłączonej fladze).

Worker KorektaTabeliVATWorker jest internal — nie da się go zainstancjonować z dodatku zewnętrznego. Publiczny tor korekty prowadzi przez flagę dok.KorektaVAT i bezpośrednią edycję pól wierszy dok.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 SumyVAT bez dok.KorektaVAT = true zostanie zablokowana — SumaVAT jest 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/Brutto rzuca ArgumentException. Zaokrąglaj wejście (Soneta.Tools.Math.RoundCy(...)).
  • KorektaVAT jest dostępna tylko, gdy definicja dokumentu na to pozwala (Definicja.SumyVAT w trybie korekty) — sprawdzaj dok.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.
  • DefinicjaStawki na wierszu SumaVAT można zmieniać tylko przy włączonej korekcie (IsReadOnlyDefinicjaStawki() zależy od KorektaVAT).

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:

  • LiczonaOd nie przyjmuje wartości 0 (RequiredException). Zawsze ustaw konkretny wariant enuma.
  • Zmiana LiczonaOd na 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 flag Params (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.
  • PrzeliczProceduryVATParams dziedziczy po ContextBase — przy ręcznym tworzeniu przekaż Context do 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 DokumentHandlowyZmianaWalutyWorker jest internal — 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 parametry DokumentHandlowyZmianaWalutyWorkerParams. Z poziomu kodu dodatku zewnętrznego dostępne tory to: (1) uruchomienie akcji przez mechanizm Czynności z przygotowanym Context, 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 DokumentHandlowyZmianaWalutyWorker jest internalnie wywołasz new ...Worker(...) ani .ZmienWalute() z dodatku zewnętrznego. Używaj publicznych Params + akcji Czynności lub bezpośredniej edycji pól dokumentu.
  • session.GetWaluty() jest internal — moduł walut pobieraj przez WalutyModule.GetInstance(session) (namespace Soneta.Waluty).
  • Jeśli w bazie brak kursu na żądaną datę (np. Demo nie ma kursu EUR „na dziś"), platforma rzuci KursWalutyNotFoundException. KursWaluty w parametrach wylicza się automatycznie tylko, gdy kurs istnieje; w przeciwnym razie ustaw KursWaluty ręcznie.
  • Zmiana waluty ma sens tylko dla dokumentu w buforze (IsVisibleZmienWalute wymaga dok.Bufor); dla dokumentu zatwierdzonego operacja jest niedostępna.
  • WalutaBazowa jest read-only — wyznaczana z bieżącej waluty dokumentu (dok.BruttoCy.Symbol). Ustawiasz tylko Waluta (docelową).
  • Kwoty pieniężne to Currency (wartość + symbol), nie decimal/double. Sam KursWaluty jest double.