SKILL: Uporządkowanie skills domenowych - podział na mniejsze pliki i wspólna numeracja

This commit is contained in:
Marcin Wojas
2026-06-07 08:31:37 +02:00
parent fb2f2695a3
commit 5605ad2915
42 changed files with 13500 additions and 13526 deletions
@@ -0,0 +1,453 @@
# HANDEL14 — Płatności dokumentu handlowego
> Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../handel.md](../handel.md).
Płatności (należności i zobowiązania) powstają automatycznie z dokumentu handlowego płatnego (np. FV, FZ)
i opisują kwoty do uregulowania: termin, sposób zapłaty, ewidencję środków pieniężnych (ŚP) oraz stan
rozliczenia z zapłatami. Z poziomu dokumentu dostęp do nich daje kolekcja `dok.Platnosci`
(`SubTable<Soneta.Kasa.Platnosc>`). Pojedyncza płatność to obiekt `Soneta.Kasa.Platnosc` — w praktyce jedna
z dwóch klas konkretnych: `Naleznosc` (kierunek `Przychod`, sprzedaż) lub `Zobowiazanie` (kierunek
`Rozchod`, zakup). Wymagana referencja do `Soneta.Kasa`.
> **Pojęcia.** Kwota płatności (`Kwota: Currency`) jest w walucie dokumentu; `KwotaKsiegi: Currency` to jej
> przeliczenie na PLN po `Kurs`. Stan uregulowania to `StanRozliczenia` (+ `KwotaRozliczona`,
> `DoRozliczenia`). Płatności są edytowalne wyłącznie, gdy dokument (i sama płatność) są w **buforze** —
> po zatwierdzeniu pola płatności stają się tylko do odczytu.
---
### HANDEL-W75 — Przeglądanie płatności dokumentu
**Cel:** odczytać płatności wystawione z dokumentu — kwotę, walutę, sposób zapłaty, termin oraz stan
rozliczenia — bez modyfikacji.
**Warianty:**
| Wariant | Źródło / pole |
|---|---|
| Lista płatności dokumentu | `dok.Platnosci` (`SubTable<Platnosc>`) |
| Kwota i waluta | `p.Kwota: Currency` (`.Value`, `.Symbol`) |
| Sposób zapłaty | `p.SposobZaplaty: Soneta.Kasa.SposobZaplaty` (`.Nazwa`, `.Typ`, `.MPP`) |
| Termin płatności | `p.Termin: Date`, `p.TerminDni: int` (dni od daty odniesienia) |
| Stan rozliczenia | `p.StanRozliczenia`, `p.Rozliczono: bool`, `p.KwotaRozliczona`, `p.DoRozliczenia` |
| Kwota nierozliczona po terminie | `p.DoRozliczenia` + warunek `p.Termin < Date.Today` |
| Należność / zobowiązanie | `p.Kierunek`, `p.CzyNaleznosc: bool`, `p.CzyZobowiazanie: bool` |
**Pola i typy:** `Platnosc.Kwota: Soneta.Types.Currency`, `KwotaKsiegi: Currency` (PLN),
`SposobZaplaty: Soneta.Kasa.SposobZaplaty`, `Termin: Soneta.Types.Date`, `TerminDni: int`,
`StanRozliczenia: Soneta.Kasa.StanRozliczenia` (`Nierozliczony=0`, `Czesciowo=1`, `Calkowicie=2`,
`NiePodlega=3`), `Rozliczono: bool`, `KwotaRozliczona: Currency`, `DoRozliczenia: Currency`,
`Kierunek: Soneta.Kasa.KierunekPlatnosci`, `EwidencjaSP: Soneta.Kasa.EwidencjaSP`.
**Snippet:**
```csharp
var hm = session.GetHandel();
var dok = hm.DokHandlowe.WgDaty[...]; // lub inny lookup dokumentu
foreach (Platnosc p in dok.Platnosci)
{
Currency kwota = p.Kwota; // w walucie dokumentu
string waluta = p.Kwota.Symbol; // np. "PLN", "EUR"
string sposob = p.SposobZaplaty.Nazwa; // np. "Przelew", "Gotówka"
Date termin = p.Termin;
StanRozliczenia stan = p.StanRozliczenia;
// Kwota pozostała do zapłaty i to, co już przeterminowane:
Currency doZaplaty = p.DoRozliczenia;
bool poTerminie = !p.Rozliczono && p.Termin < Date.Today && p.DoRozliczenia > Currency.Zero;
}
```
**Pułapki:**
- `dok.Platnosci` to `SubTable` — iteruj serwerowo, nie materializuj do `List` tylko po to, by policzyć
elementy (`IsEmpty`/`Count` są dostępne na kolekcji). Patrz [`rowcondition.md`](references/rowcondition.md).
- `StanRozliczenia.NiePodlega` oznacza płatność **nierozliczaną** (`p.Rozliczana == false`) — nie myl jej
z `Nierozliczony` (rozliczana, ale jeszcze niezapłacona).
- `Kwota` jest w walucie dokumentu; do raportu w PLN użyj `KwotaKsiegi` (HANDEL-W81), nie mnóż „ręcznie".
- „Po terminie" liczysz z `Termin` i `DoRozliczenia` względem `Date.Today` — w samej płatności nie ma
gotowego pola „kwota po terminie".
---
### HANDEL-W76 — Rozbicie płatności na raty
**Cel:** zamienić pojedynczą płatność dokumentu na zestaw rat (cyklicznych miesięcznych) albo na rozbicie
netto + VAT, przy użyciu publicznego workera `PodzialPlatnosciWorker`.
**Warianty:**
| Wariant | Ustawienie `WParams` |
|---|---|
| Raty miesięczne wg liczby rat | `Metoda = WOptions.Raty`, `IlośćRat = n` |
| Raty miesięczne wg kwoty raty | `Metoda = WOptions.Raty`, `Kwota = kwotaRaty` (worker wyliczy liczbę rat) |
| Rozbicie netto + VAT (MPP) | `Metoda = WOptions.NettoPlusVat` |
**Pola i typy:** worker `Soneta.Handel.PodzialPlatnosci.PodzialPlatnosciWorker`, parametry
`Soneta.Handel.PodzialPlatnosci.WParams : ContextBase` (inicjowane z `Context` zawierającego
`DokumentHandlowy`): `Metoda: WOptions` (`NettoPlusVat=0x1`, `Raty=0x2`), `IlośćRat: int`,
`Kwota: Currency` (kwota pojedynczej raty), `TerminPierwszejWpłaty: Date` (read-only — z warunków
płatności), `Cykl: WOptions` (`Miesięczny`). Akcja: `PodzielPlatnosci([Context] DokumentHandlowy)`.
**Snippet:**
```csharp
// Worker działa na dokumencie w BUFORZE z kierunkiem płatności (FV/FZ).
// Parametry tworzymy przez Context (wzorzec worker-z-Params), patrz worker-extender.md.
var context = new Context(session);
context.Set(dok); // DokumentHandlowy w kontekście
var wp = new PodzialPlatnosci.WParams(context)
{
Metoda = PodzialPlatnosci.WOptions.Raty,
IlośćRat = 3, // 3 równe raty miesięczne
};
var worker = new PodzialPlatnosci.PodzialPlatnosciWorker(wp);
worker.PodzielPlatnosci(dok); // sam otwiera transakcję i robi CommitUI
session.Save();
```
**Pułapki:**
- Akcja jest dostępna tylko gdy `dok.Bufor == true` i `dok.Definicja.KierunekPlatnosci != Brak`
(`IsVisiblePodzielPlatnosci`) — na zatwierdzonym dokumencie się nie wykona.
- `PodzielPlatnosci` **sam otwiera transakcję** (`Session.Logout(true)` + `CommitUI`) i **usuwa**
istniejące płatności dokumentu, zastępując je wyliczonymi ratami/podziałem. Nie zawijaj go w drugą
transakcję edycyjną; po nim wywołaj `session.Save()`.
- W trybie `Raty` ustawienie `Kwota` przelicza `IlośćRat` (i odwrotnie) — ustaw jedno z dwóch.
- Ostatnia rata przejmuje resztę z zaokrągleń (kwoty rat sumują się do `BruttoCy` dokumentu) — nie zakładaj
równego podziału co do grosza.
---
### HANDEL-W77 — Ręczne dodanie / edycja pojedynczej płatności
**Cel:** ręcznie ułożyć płatności dokumentu — np. część gotówką, resztę przelewem — ustawiając sposób
zapłaty, ewidencję ŚP, termin i kwotę.
**Warianty:**
| Wariant | Operacja |
|---|---|
| Dodanie należności (sprzedaż) | `new Naleznosc(dok)` + `AddRow` |
| Dodanie zobowiązania (zakup) | `new Zobowiazanie(dok)` + `AddRow` |
| Edycja istniejącej | zmiana pól na elemencie `dok.Platnosci` |
| Częściowo gotówka + przelew | dwie płatności o różnym `SposobZaplaty`, suma `Kwota` = wartość dokumentu |
**Pola i typy:** konstruktory `Naleznosc(IDokumentPlatny)`, `Zobowiazanie(IDokumentPlatny)` (publiczne).
Tabela płatności: `KasaModule.GetInstance(session).Platnosci`. Pola zapisywalne:
`SposobZaplaty: SposobZaplaty`, `EwidencjaSP: EwidencjaSP`, `Termin: Date` (lub `TerminDni: int`),
`Kwota: Currency`, `KwotaMPP: Currency`, `Rachunek: RachunekBankowyPodmiotu`, `Priorytet: int`.
**Snippet:**
```csharp
var kasa = KasaModule.GetInstance(session);
var spZaplaty = kasa.SposobyZaplaty;
using (var t = session.Logout(editMode: true)) // dokument MUSI być w buforze
{
// 1) część gotówką
var gotowka = new Naleznosc(dok); // sprzedaż -> Naleznosc; zakup -> Zobowiazanie
kasa.Platnosci.AddRow(gotowka);
gotowka.SposobZaplaty = spZaplaty.Gotówka;
gotowka.Kwota = new Currency(300m, "PLN");
gotowka.Termin = dok.DataDokumentu; // gotówka -> termin = data dokumentu
// 2) reszta przelewem
var przelew = new Naleznosc(dok);
kasa.Platnosci.AddRow(przelew);
przelew.SposobZaplaty = spZaplaty.WgNazwy["Przelew"];
przelew.Kwota = new Currency(dok.BruttoCy.Value - 300m, "PLN");
przelew.TerminDni = 14; // 14 dni od daty odniesienia
// przelew.Rachunek = ... // dla przelewu wskaż rachunek podmiotu
t.Commit(); // CommitUI() w workerze/extenderze
}
session.Save();
```
**Pułapki:**
- Płatność można dodać **tylko do dokumentu w buforze**`OnAdded` rzuca wyjątek
(„Nie można dodawać płatności do zatwierdzonego dokumentu"). `Platnosc.Bufor`/`IsReadOnly` chronią
edycję po zatwierdzeniu.
- Dobierz klasę do kierunku dokumentu: sprzedaż (`KierunekPlatnosci.Przychod`) → `Naleznosc`, zakup
(`Rozchod`) → `Zobowiazanie`. Zła klasa = niespójny kierunek.
- `Kwota` to `Currency` — twórz `new Currency(wartość, symbolWaluty)`; symbol musi być zgodny z walutą
dokumentu/ewidencji (weryfikator ostrzega o niezgodności).
- Dla sposobu zapłaty typu „przelew" wymagany jest `Rachunek` (weryfikator-ostrzeżenie). Ustaw rachunek
należący do podmiotu płatności (twardy weryfikator `RachunekPodmiotuVerifier`).
- `SposobZaplaty` pobieraj z tabeli (`kasa.SposobyZaplaty.Gotówka`, `...WgNazwy["Przelew"]`) — to rekord
konfiguracyjny, nie ustawiaj „z palca".
---
### HANDEL-W78 — Warunki płatności z kontrahenta i ich przeliczenie na dokumencie
**Cel:** odczytać/ustawić warunki płatności dokumentu (sposób, termin w dniach, ewidencja ŚP) spójnie
z domyślnymi warunkami kontrahenta, przez publiczny `WarunkiPłatnościWorker`.
**Warianty:**
| Wariant | Mechanizm |
|---|---|
| Domyślne warunki z kontrahenta | `Kontrahent.SposobZaplaty`, `Kontrahent.Termin` (HANDEL-W9) — inicjują płatność |
| Odczyt warunków dokumentu | `WarunkiPłatnościWorker`: `Sposób`, `TerminDni`, `Termin`, `EwidencjaSP`, `Kwota`, `Raty` |
| Zmiana terminu (w dniach) | `worker.TerminDni = n` lub `worker.Termin = data` |
| Zmiana sposobu zapłaty | `worker.Sposób = ...` (przelicza też ewidencję ŚP) |
| Bezpośrednio na płatności | `p.TerminDni`, `p.Termin`, `p.SposobZaplaty`, `p.EwidencjaSP` |
**Pola i typy:** worker `Soneta.Kasa.WarunkiPłatnościWorker` (publiczny, zarejestrowany dla
`IDokumentPlatny`): `[Context] Dokument: IDokumentPlatny`, `TerminDni: int`, `Termin: Date`,
`Sposób: SposobZaplaty`, `EwidencjaSP: EwidencjaSP`, `Kwota: Currency` (read-only), `Raty: int`
(liczba płatności). Operuje na **pierwszej** płatności dokumentu. Na kontrahencie:
`Kontrahent.SposobZaplaty: FormaPlatnosci`, `Kontrahent.Termin: int` (patrz kontrahent HANDEL-W9).
**Snippet:**
```csharp
// Warunki płatności kontrahenta są przenoszone na płatność przy jej tworzeniu/zmianie podmiotu.
// Do odczytu/zmiany "zbiorczej" warunków dokumentu służy WarunkiPłatnościWorker:
var context = new Context(session);
context.Set(dok); // dok : IDokumentPlatny (DokumentHandlowy)
var warunki = new WarunkiPłatnościWorker { Dokument = dok };
int dni = warunki.TerminDni; // termin liczony w dniach
SposobZaplaty sp = warunki.Sposób;
int liczbaRat = warunki.Raty;
using (var t = session.Logout(editMode: true)) // dokument w buforze
{
if (!warunki.IsReadOnlyTerminDni())
warunki.TerminDni = 21; // przelicza Termin na pierwszej płatności
if (!warunki.IsReadOnlySposób())
warunki.Sposób = session.GetKasa().SposobyZaplaty.WgNazwy["Przelew"];
t.Commit();
}
session.Save();
```
**Pułapki:**
- `WarunkiPłatnościWorker` działa na **pierwszej** płatności i tylko gdy `Raty <= 1` (jedna płatność);
przy wielu płatnościach (`Raty > 1`) pola są read-only (`IsReadOnly...` zwracają `true`) — wtedy edytuj
poszczególne płatności bezpośrednio (HANDEL-W77) albo użyj podziału (HANDEL-W76).
- `TerminDni` to dni od **daty odniesienia** (`TerminLiczonyOd`/data dokumentu), nie data bezwzględna —
ustawienie `TerminDni` przelicza `Termin`.
- Edycja terminu może być zablokowana polityką (`IEdycjaTerminuPlatnosci`) — zawsze sprawdzaj
`IsReadOnlyTermin()`/`IsReadOnlyTerminDni()` przed zapisem.
- Zmiana `Sposób` przelicza ewidencję ŚP (subewidencję) — nie ustawiaj `EwidencjaSP` „obok", licz na
spójność workera.
---
### HANDEL-W79 — Zmiana płatnika (inny niż kontrahent)
**Cel:** ustawić na płatności podmiot inny niż kontrahent dokumentu (np. płatnik trzeci) i wykryć tę
sytuację z poziomu dokumentu.
**Warianty:**
| Wariant | Mechanizm |
|---|---|
| Zmiana płatnika płatności | `p.Podmiot = innyPodmiot` (`IPodmiotKasowy`) |
| Wykrycie „innego płatnika" | `dok.InnyPłatnik: bool` (read-only — `true`, gdy jakaś płatność ma `Podmiot != Kontrahent`) |
| Płatnik domyślny kontrahenta | `Kontrahent.Platnik: IPodmiotKasowy` (kalkulowane — nadrzędny z relacji) |
**Pola i typy:** `Platnosc.Podmiot: Soneta.Kasa.IPodmiotKasowy` (zapisywalne),
`DokumentHandlowy.InnyPłatnik: bool` (**kalkulowane, read-only**),
`IsReadOnlyPodmiot()`. `Kontrahent` implementuje `IPodmiotKasowy`.
**Snippet:**
```csharp
// "Inny płatnik" ustawiamy na poziomie POJEDYNCZEJ płatności — pole Podmiot:
IPodmiotKasowy platnik = session.GetCRM().Kontrahenci.WgKodu["PLATNIK"];
using (var t = session.Logout(editMode: true)) // dokument w buforze
{
foreach (Platnosc p in dok.Platnosci)
if (!p.IsReadOnlyPodmiot())
p.Podmiot = platnik; // rozrachunek przejdzie na nowy podmiot
t.Commit();
}
session.Save();
// Odczyt: czy dokument ma płatnika innego niż kontrahent:
bool inny = dok.InnyPłatnik; // kalkulowane, tylko do odczytu
```
**Pułapki:**
- `dok.InnyPłatnik` jest **wyłącznie do odczytu** — to flaga wyliczana z porównania `p.Podmiot` z
`dok.Kontrahent`. Aby „zmienić płatnika", ustaw `Platnosc.Podmiot`, nie próbuj przypisać `InnyPłatnik`.
- `Podmiot` jest read-only, gdy płatność jest częściowo rozliczona (`KwotaRozliczona != 0`) — sprawdzaj
`IsReadOnlyPodmiot()`.
- Zmiana podmiotu przenosi rozrachunek na nowy podmiot i może podmienić zablokowany podmiot na jego
zamiennik (wbudowana logika) — odczytaj `p.Podmiot` po zmianie, nie zakładaj wartości wejściowej.
- `Rachunek` musi należeć do nowego `Podmiot` (twardy weryfikator) — po zmianie płatnika zweryfikuj/wyczyść
rachunek.
---
### HANDEL-W80 — Odczyt stanu rozliczenia płatności
**Cel:** ustalić, czy płatność jest rozliczona w całości, częściowo czy nierozliczona, oraz dotrzeć do
powiązanych rozliczeń (zapłat).
**Warianty:**
| Wariant | Pole / kolekcja |
|---|---|
| Stan zbiorczy | `p.StanRozliczenia` (`Nierozliczony`/`Czesciowo`/`Calkowicie`/`NiePodlega`) |
| Rozliczono całkowicie? | `p.Rozliczono: bool`, `p.Zrealizowane: bool` |
| Kwoty | `p.KwotaRozliczona`, `p.DoRozliczenia` |
| Data rozliczenia | `p.DataRozliczenia: Date` (`Date.MaxValue` = nierozliczona) |
| Rozliczono na dzień | `p.RozliczonoDoDnia(Date data)` |
| Powiązane rozliczenia/transakcje | `p.Dokumenty`, `p.Zaplaty` (kolekcje `RozliczenieSP`) |
| Czy podlega rozliczeniu | `p.Rozliczana: bool` |
**Pola i typy:** `StanRozliczenia: Soneta.Kasa.StanRozliczenia`, `Rozliczono: bool`, `Zrealizowane: bool`,
`KwotaRozliczona/DoRozliczenia: Currency`, `DataRozliczenia: Date`, `Rozliczana: bool`,
`Dokumenty`/`Zaplaty` (rozliczenia typu `Soneta.Kasa.RozliczenieSP`),
metoda `RozliczonoDoDnia(Date, bool wgDatyKsi = false): Currency`.
**Snippet:**
```csharp
foreach (Platnosc p in dok.Platnosci)
{
switch (p.StanRozliczenia)
{
case StanRozliczenia.Calkowicie: /* zapłacona w całości */ break;
case StanRozliczenia.Czesciowo: /* część zapłacona: p.DoRozliczenia > 0 */ break;
case StanRozliczenia.Nierozliczony: /* brak zapłat */ break;
case StanRozliczenia.NiePodlega: /* płatność nierozliczana */ break;
}
Currency zaplaconoDoDzis = p.RozliczonoDoDnia(Date.Today);
// Powiązane rozliczenia (transakcje zapłaty):
foreach (RozliczenieSP r in p.Zaplaty) { /* r.Data, r.KwotaDokumentu, ... */ }
foreach (RozliczenieSP r in p.Dokumenty) { /* r.Data, r.KwotaZaplaty, ... */ }
}
```
**Pułapki:**
- `StanRozliczenia` jest kalkulowane z `KwotaRozliczona`/`Kwota` — nie ustawiaj go; rozliczenia powstają
przez operacje kasowe/rozliczeniowe, nie przez bezpośredni zapis na płatności.
- `DataRozliczenia == Date.MaxValue` oznacza „nierozliczona" — nie traktuj `MaxValue` jako realnej daty.
- Rozliczenia są rozdzielone na dwie kolekcje (`Dokumenty` i `Zaplaty`) zależnie od strony powiązania —
do pełnego obrazu przejrzyj obie.
- Dla płatności `Rozliczana == false` (`NiePodlega`) `DoRozliczenia` wynosi zero — nie analizuj jej jak
zaległości.
---
### HANDEL-W81 — Płatności w walucie obcej (kwota w walucie vs PLN, kurs)
**Cel:** poprawnie odczytać/ustawić płatność walutową — kwotę w walucie obcej, jej przeliczenie na PLN
oraz kurs i tabelę kursową.
**Warianty:**
| Wariant | Pole |
|---|---|
| Kwota w walucie dokumentu | `p.Kwota: Currency` (symbol = waluta, np. „EUR") |
| Kwota w PLN (księgowa) | `p.KwotaKsiegi: Currency` |
| Kurs i tabela | `p.Kurs: double`, `p.TabelaKursowa: TabelaKursowa` |
| Interfejs walutowy | `IRowWithKurs`: `KwotaWaluty` (= `Kwota`), `KwotaPLN` (= `KwotaKsiegi`) |
| Słownie | `p.Słownie: string` |
**Pola i typy:** `Kwota: Currency` (waluta dokumentu), `KwotaKsiegi: Currency` (PLN),
`Kurs: double`, `TabelaKursowa: Soneta.Waluty.TabelaKursowa`. `Platnosc` implementuje
`Soneta.Waluty.IRowWithKurs` (`KwotaWaluty`, `KwotaPLN`).
**Snippet:**
```csharp
foreach (Platnosc p in dok.Platnosci)
{
if (p.Kwota.Symbol != Currency.SystemSymbol) // płatność walutowa (np. "EUR")
{
Currency wWalucie = p.Kwota; // np. 1000 EUR
Currency wPln = p.KwotaKsiegi; // przeliczenie na PLN
double kurs = p.Kurs; // kurs zastosowany
TabelaKursowa tab = p.TabelaKursowa; // tabela kursów (lub null)
}
}
// Ustawienie kursu ręcznie (gdy dokument/ewidencja walutowa, w buforze):
using (var t = session.Logout(editMode: true))
{
foreach (Platnosc p in dok.Platnosci)
if (p.Kwota.Symbol != Currency.SystemSymbol && !p.IsReadOnlyTabelaKursowa())
p.TabelaKursowa = session.GetKasa().EwidencjeSP /* ... */ ?.TabelaKursowa;
t.Commit();
}
session.Save();
```
**Pułapki:**
- Dla płatności w PLN `Kurs == 1.0` i `TabelaKursowa == null` — przeliczeniem zajmuj się tylko, gdy
`Kwota.Symbol != Currency.SystemSymbol`.
- `KwotaKsiegi` wylicza się z `Kwota * Kurs`; jeśli ustawisz tabelę bez kursu na datę dokumentu, kurs może
pozostać `0.0` (brak kursu) — wtedy `KwotaKsiegi` będzie zerowa. Upewnij się, że tabela kursowa ma kurs
na `DataDokumentu` (w bazie Demo brak kursów „na dziś" → operacja walutowa rzuca
`KursWalutyNotFoundException`, por. rozdz. o walutach).
- Kwota płatności walutowej musi mieć symbol zgodny z walutą dokumentu/ewidencji ŚP — weryfikator ostrzega
o niezgodności symboli.
- Sumę płatności w PLN czytaj z `KwotaKsiegi` (lub `IRowWithKurs.KwotaPLN`), nie przeliczaj `Kwota` własnym
kursem.
---
### HANDEL-W82 — Powiązanie płatności z terminem i rabatem za wcześniejszą zapłatę
**Cel:** obsłużyć rabat za wcześniejszą zapłatę (skonto) — wskazać termin uprawniający do rabatu i odczytać
jego wpływ na warunki płatności dokumentu.
**Warianty:**
| Wariant | Mechanizm |
|---|---|
| Ustawienie terminu rabatu na dokumencie | `dok.RabatZaTerminPlatnosci.Termin = data` |
| Odczyt naliczonego rabatu | `dok.RabatZaTerminPlatnosci.Rabat: Percent` |
| Rodzaj rabatu | `dok.RabatZaTerminPlatnosci.Rodzaj: RodzajRabatuZaTerminPlatnosci` |
| Termin samej płatności | `p.Termin`, `p.TerminDni` (HANDEL-W77/HANDEL-W78) |
| Parametry rabatu na kontrahencie | `Kontrahent.RodzajRabatuZaTerminPlatnosci`, `TrybRabatu...`, `IloscDniDlaRabatu`, `WartoscRabatuZaKazdyDzien` |
**Pola i typy:** `DokumentHandlowy.RabatZaTerminPlatnosci: Soneta.Handel.RabatZaTerminPlatnosci`
(subrow) z polami `Termin: Date` (zapisywalne — termin uprawniający do rabatu), `Rabat: Percent`
(wyliczane), `Rodzaj: RodzajRabatuZaTerminPlatnosci`. Na płatności: `Termin: Date`,
`TerminDni: int`, `TerminLiczonyOd: Date` (data odniesienia, read-only).
**Snippet:**
```csharp
using (var t = session.Logout(editMode: true)) // dokument w buforze, z kontrahentem
{
// Termin uprawniający do rabatu za wcześniejszą zapłatę (skonto):
if (!dok.RabatZaTerminPlatnosci.IsReadOnlyTermin())
dok.RabatZaTerminPlatnosci.Termin = dok.DataDokumentu.AddDays(7);
t.Commit();
}
session.Save();
// Odczyt naliczonego rabatu (zależny od parametrów rabatu kontrahenta):
Percent rabat = dok.RabatZaTerminPlatnosci.Rabat;
Date terminRabatu = dok.RabatZaTerminPlatnosci.Termin;
```
**Pułapki:**
- `RabatZaTerminPlatnosci.Rabat` jest **wyliczany** z parametrów kontrahenta (tryb: progresywny /
podstawowy / progowy) i różnicy dni między `Termin` rabatu a terminem płatności — nie ustawiaj go wprost.
- Ustawienie `Termin` < `Date.Today` zeruje rabat i czyści termin — przekazuj datę przyszłą.
- Termin rabatu można ustawić tylko, gdy **wszystkie** płatności dokumentu mają ten sam termin
(`Dokument.Platnosci` zgrupowane po `Termin` → jedna grupa); w przeciwnym razie rzuca `RowException`.
- Edycja może być zablokowana polityką `IEdycjaTerminuPlatnosci` — sprawdzaj `IsReadOnlyTermin()`.
- Naliczenie rabatu wymaga skonfigurowanych parametrów na kontrahencie
(`RodzajRabatuZaTerminPlatnosci`, `Tryb...`, progi/wartości) — bez nich `Rabat` pozostanie `Percent.Zero`.
---