# HANDEL02 — Wystawianie dokumentów > Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../handel.md](../handel.md). Rozdział pokazuje, jak **utworzyć dokument handlowy od zera** w różnych wariantach (faktura sprzedaży, faktura zakupu, dokument magazynowy, zamówienie, dokument walutowy, dokument z usługą) oraz jak **dodawać i parametryzować pozycje**. Wszystkie wzorce operują na publicznym kontrakcie platformy: tabela `DokHandlowe` (`session.GetHandel().DokHandlowe`), definicje `DefDokHandlowych.WgSymbolu[...]`, pozycje `PozycjaDokHandlowego`. > **Kolejność ustawiania pól jest istotna.** Najpierw `AddRow(dok)`, potem `Definicja` (inicjuje > kategorię, kierunek magazynu, sposób liczenia VAT, walutę płatności), następnie `Magazyn`, > `Kontrahent`, daty. Na pozycji najpierw `Towar` (inicjuje jednostkę, stawkę VAT, cenę i rabat), > dopiero potem `Ilosc`, `Cena`, `Rabat`. Cała operacja w jednej transakcji > `session.Logout(editMode: true)` zakończonej `Commit()` (kod biznesowy) / `CommitUI()` > (worker/extender), a po niej `session.Save()` — dopiero `Save()` księguje obroty magazynowe i > wykrywa konflikty. --- ### HANDEL-W4 — Faktura sprzedaży (FV) **Cel:** wystawić fakturę sprzedaży: dokument rozchodowy z kontrahentem-nabywcą, pozycjami towarowymi, automatycznie wyliczoną tabelą VAT i płatnością. **Warianty:** | Wariant | Charakterystyka | Pola krytyczne | |---|---|---| | FV krajowa od netto | standardowa sprzedaż | `Definicja=FV`, `LiczonaOd=Netto`, `Kontrahent` krajowy | | FV liczona od brutto | sprzedaż detaliczna / paragonowa | `LiczonaOd=Brutto` | | FV z rabatem nagłówkowym | rabat przepisywany na pozycje | `Rabat: Percent` na dokumencie | | FV dla odbiorcy unijnego | WDT — stawka 0% | kontrahent `RodzajPodmiotu=Unijny`, stawka z karty/UE (HANDEL-W11) | | FV walutowa | sprzedaż w EUR/USD | patrz **HANDEL-W9** | **Pola i typy:** `Definicja: DefDokHandlowego` (`DefDokHandlowych.WgSymbolu["FV"]`), `Magazyn: Magazyn`, `Kontrahent: Kontrahent`, `Data: Date` (data wystawienia), `DataOperacji: Date` (faktyczna data sprzedaży), `LiczonaOd: SposobLiczeniaVAT` (`Netto`/`Brutto`), `Rabat: Percent`. Wartości wyliczane: `Suma: BruttoNetto`, `SumyVAT: SubTable`, `Platnosci: SubTable` (powstaje automatycznie wg formy/terminu kontrahenta). **Snippet:** ```csharp var handel = session.GetHandel(); var magazyny = session.GetMagazyny(); var crm = session.GetCRM(); using (var t = session.Logout(editMode: true)) { var fv = new DokumentHandlowy(); session.AddRow(fv); // AddRow PRZED ustawianiem pól fv.Definicja = handel.DefDokHandlowych.WgSymbolu["FV"]; // definicja PIERWSZA fv.Magazyn = magazyny.Magazyny.WgSymbol["F"]; fv.Kontrahent = crm.Kontrahenci.WgKodu["Abc"]; // nabywca fv.Data = Date.Today; // data wystawienia fv.DataOperacji = Date.Today; // faktyczna data sprzedaży fv.LiczonaOd = SposobLiczeniaVAT.Netto; // VAT liczony od netto // Pozycja towarowa (szczegóły w HANDEL-W8): var poz = new PozycjaDokHandlowego(fv); session.AddRow(poz); poz.Towar = session.GetTowary().Towary.WgKodu["BIKINI"]; // Towar PIERWSZY poz.Ilosc = new Quantity(2, poz.Ilosc.Symbol); poz.Cena = new DoubleCy(50m, poz.Cena.Symbol); t.Commit(); // CommitUI() w workerze/extenderze } session.Save(); // tu księgują się obroty i VAT // Odczyt wyliczonej tabeli VAT i wartości: foreach (SumaVAT v in fv.SumyVAT) { /* v.Stawka, v.Suma (Netto/VAT/Brutto) */ } BruttoNetto suma = fv.Suma; ``` **Pułapki:** - **Demo blokuje stan ujemny** (`StanUjemnyVerifier`): FV (rozchód) wymaga wcześniejszego **zapisanego** przyjęcia (PW/PZ) tego towaru. Samo `CommitUI` nie księguje obrotów — magazyn aktualizuje się dopiero po `Session.Save()` dokumentu przychodowego. - `SumyVAT`, `Suma`, `Platnosci` są **wyliczane** z pozycji i parametrów dokumentu — nie ustawiaj ich ręcznie (ręczna korekta tabeli VAT to osobny mechanizm: `KorektaVAT=true`). - `LiczonaOd` ustaw przed pozycjami — zmiana po wprowadzeniu pozycji wymusza przeliczenie cen netto↔brutto. - Stawka VAT pozycji jest inicjowana z karty towaru — nie ustawiaj jej „z palca", jeśli nie musisz jej nadpisać. --- ### HANDEL-W5 — Faktura zakupu (FZ) **Cel:** wprowadzić fakturę zakupu otrzymaną od dostawcy: dokument przychodowy z numerem obcym dostawcy oraz datami zakupu i wystawienia dokumentu obcego. **Warianty:** | Wariant | Charakterystyka | Pola krytyczne | |---|---|---| | FZ krajowa | zakup od dostawcy PL | `Definicja=FZ`, `Obcy.Numer`, `DataOperacji` (data zakupu) | | FZ z dostawą magazynową | zakup z przyjęciem na magazyn | `Magazyn`, kierunek przychodowy z definicji | | FZ od dostawcy unijnego (WNT) | nabycie wewnątrzwspólnotowe | kontrahent `RodzajPodmiotu=Unijny` | | FZ walutowa | zakup w walucie obcej | patrz **HANDEL-W9** | **Pola i typy:** `Definicja=DefDokHandlowych.WgSymbolu["FZ"]`, `Kontrahent` = dostawca, `Obcy: DokumentObcy` (subrow): `Obcy.Numer: string` (numer obcy nadany przez dostawcę), `Obcy.DataOtrzymania: Date` (data dokumentu obcego). `Data: Date` (data wystawienia w naszym systemie), `DataOperacji: Date` (faktyczna data zakupu). **Snippet:** ```csharp var handel = session.GetHandel(); using (var t = session.Logout(editMode: true)) { var fz = new DokumentHandlowy(); session.AddRow(fz); fz.Definicja = handel.DefDokHandlowych.WgSymbolu["FZ"]; fz.Magazyn = session.GetMagazyny().Magazyny.WgSymbol["F"]; fz.Kontrahent = session.GetCRM().Kontrahenci.WgKodu["ZEFIR"]; // dostawca fz.Data = Date.Today; // data wystawienia u nas fz.DataOperacji = Date.Today.AddDays(-2); // faktyczna data zakupu // Numer i data dokumentu obcego (od dostawcy): fz.Obcy.Numer = "FV/2026/06/123"; // numer obcy fz.Obcy.DataOtrzymania = Date.Today.AddDays(-2); // data dokumentu obcego var poz = new PozycjaDokHandlowego(fz); session.AddRow(poz); poz.Towar = session.GetTowary().Towary.WgKodu["BIKINI"]; poz.Ilosc = new Quantity(10, poz.Ilosc.Symbol); poz.Cena = new DoubleCy(30m, poz.Cena.Symbol); t.Commit(); } session.Save(); ``` **Pułapki:** - `Obcy` to subrow (pole złożone) — nie da się przypisać `fz.Obcy = …`; ustawiaj jego pola (`fz.Obcy.Numer`, `fz.Obcy.DataOtrzymania`). - Rozróżniaj trzy daty: `Data` (wystawienia u nas), `DataOperacji` (faktyczna data zakupu/sprzedaży, decyduje o okresie magazynowym), `Obcy.DataOtrzymania` (data na dokumencie obcym). To trzy różne pola — nie myl ich. - FZ z przyjęciem na magazyn księguje **przychód** → po `Save()` powstają zasoby (`dok.Zasoby`). - Indeks `WgKontrahentaObcy` (Kontrahent + numer obcy) pozwala wykryć duplikat faktury od tego samego dostawcy — sprawdzaj przed dodaniem. --- ### HANDEL-W6 — Dokument magazynowy (PZ / WZ / RW / PW) **Cel:** wystawić czysto magazynowy dokument wpływający na stan magazynu, bez części handlowej (VAT/płatności) lub z minimalną. **Warianty:** | Wariant | Symbol | Kierunek | Zastosowanie | |---|---|---|---| | Przyjęcie zewnętrzne | `PZ` | przychód | przyjęcie od dostawcy | | Przyjęcie wewnętrzne | `PW` | przychód | przyjęcie z produkcji / bilans otwarcia | | Wydanie zewnętrzne | `WZ` | rozchód | wydanie odbiorcy | | Rozchód wewnętrzny | `RW` | rozchód | zużycie wewnętrzne | **Pola i typy:** `Definicja=DefDokHandlowych.WgSymbolu["PW"]` (itd.), `Magazyn: Magazyn` (wymagany), `Kontrahent` (gdy dotyczy — PZ/WZ tak, RW/PW zwykle nie), `Data`, `DataOperacji`. Kierunek magazynu (`KierunekMagazynu: KierunekPartii` — `Przychód=1`, `Rozchód=-1`) jest ustawiany z definicji (`readonly="set"`). Wynik: `dok.Zasoby` (przy przychodzie), `dok.Obroty`. **Snippet:** ```csharp var handel = session.GetHandel(); // Przyjęcie wewnętrzne PW (przychód — buduje stan magazynu pod późniejsze rozchody): using (var t = session.Logout(editMode: true)) { var pw = new DokumentHandlowy(); session.AddRow(pw); pw.Definicja = handel.DefDokHandlowych.WgSymbolu["PW"]; // kierunek z definicji pw.Magazyn = session.GetMagazyny().Magazyny.WgSymbol["F"]; pw.Data = Date.Today; pw.DataOperacji = Date.Today; var poz = new PozycjaDokHandlowego(pw); session.AddRow(poz); poz.Towar = session.GetTowary().Towary.WgKodu["BIKINI"]; poz.Ilosc = new Quantity(100, poz.Ilosc.Symbol); poz.Cena = new DoubleCy(25m, poz.Cena.Symbol); t.Commit(); } session.Save(); // dopiero teraz powstają zasoby // Stan magazynowy po przyjęciu: foreach (var z in pw.Zasoby) { /* z.* — partia, ilość, magazyn */ } ``` **Pułapki:** - `Magazyn` jest **wymagany** (`required`) dla dokumentów magazynowych — bez niego `Save()` rzuci `RowException`. - `KierunekMagazynu`/`TypPartii` są `readonly="set"` — wynikają z definicji, nie ustawiaj ich ręcznie. - Rozchód (WZ/RW) na bazie Demo wymaga wcześniejszego **zapisanego** przychodu (PW/PZ) — inaczej `StanUjemnyVerifier` zablokuje `Save()`. - Obroty/zasoby księgują się **po `Session.Save()`**, nie po `Commit()`/`CommitUI()`. Aby je odczytać, zapisz dokument i odśwież. --- ### HANDEL-W7 — Zamówienie (ZO / ZD) **Cel:** wystawić zamówienie od odbiorcy (ZO) lub zamówienie do dostawcy (ZD). Zamówienie nie wpływa na stan magazynowy (może tworzyć rezerwacje), jest dokumentem nadrzędnym dla realizacji (FV/WZ — patrz rozdział o relacjach). **Warianty:** | Wariant | Symbol | Strona | Realizacja | |---|---|---|---| | Zamówienie odbiorcy | `ZO` | klient zamawia u nas | → FV / WZ przez `IRelacjeService` | | Zamówienie do dostawcy | `ZD` | my zamawiamy u dostawcy | → FZ / PZ przez `IRelacjeService` | | ZO z rezerwacją | `ZO` | jw. | rezerwacja zasobu (`dok.Rezerwacja`) | | ZO z terminem dostawy | `ZO` | jw. | `Dostawa.Termin` | **Pola i typy:** `Definicja=DefDokHandlowych.WgSymbolu["ZO"]` / `["ZD"]`, `Kontrahent`, `Magazyn`, `Data`, `DataOperacji`, `Dostawa: DokumentDostawa` (subrow): `Dostawa.Termin: Date` (termin realizacji), `Dostawa.Sposob: string`. Powiązanie z realizacją: `dok.Rezerwacja`, generowanie dokumentu podrzędnego przez `IRelacjeService.NowyPodrzednyIndywidualny(...)`. **Snippet:** ```csharp var handel = session.GetHandel(); using (var t = session.Logout(editMode: true)) { var zo = new DokumentHandlowy(); session.AddRow(zo); zo.Definicja = handel.DefDokHandlowych.WgSymbolu["ZO"]; // zamówienie odbiorcy zo.Magazyn = session.GetMagazyny().Magazyny.WgSymbol["F"]; zo.Kontrahent = session.GetCRM().Kontrahenci.WgKodu["Abc"]; // zamawiający odbiorca zo.Data = Date.Today; zo.DataOperacji = Date.Today; zo.Dostawa.Termin = Date.Today.AddDays(7); // oczekiwany termin dostawy var poz = new PozycjaDokHandlowego(zo); session.AddRow(poz); poz.Towar = session.GetTowary().Towary.WgKodu["BIKINI"]; poz.Ilosc = new Quantity(5, poz.Ilosc.Symbol); poz.Cena = new DoubleCy(50m, poz.Cena.Symbol); t.Commit(); } session.Save(); ``` **Pułapki:** - Zamówienie **nie buduje stanu magazynu** — to dokument planistyczny. Realizację (FV/WZ z ZO, FZ/PZ z ZD) tworzysz przez `IRelacjeService` (rozdział o relacjach) — dokument nadrzędny musi być wtedy **zatwierdzony**. - `Dostawa` to subrow — ustawiaj pola (`zo.Dostawa.Termin`), nie przypisuj całego obiektu. - Rezerwacja ilościowa zamówienia jest zarządzana wewnętrznym workerem (`ZmienRezerwacjeIlosciowaWorker` — **internal**, niedostępny z dodatku); z poziomu publicznego odczytuj `zo.Rezerwacja`, a rezerwacje steruj przez definicję dokumentu i relacje. --- ### HANDEL-W8 — Dodawanie pozycji (towar, ilość, cena, rabat, jednostka) **Cel:** dodać pozycję towarową do dokumentu — z automatycznym pobraniem ceny/rabatu z cennika lub z ręcznym nadpisaniem. **Warianty:** | Wariant | Operacja | Pole | |---|---|---| | Pozycja z automatyczną ceną | cena i rabat pobrane z cennika/karty | tylko `Towar` + `Ilosc` | | Ręczna cena | nadpisanie ceny | `Cena: DoubleCy` (ustawia `KorektaCeny=true`) | | Ręczny rabat | nadpisanie rabatu | `Rabat: Percent` (ustawia `KorektaRabatu=true`) | | Inna jednostka | sprzedaż w jednostce zbiorczej | `Ilosc` z symbolem jednostki towaru | | Pozycja bez rabatu | wyłączenie rabatu | `BezRabatu=true` | | Ręczna wartość | korekta wartości pozycji | `WartoscCy: Currency` | **Pola i typy (`PozycjaDokHandlowego`):** `Towar: Towar` (ustaw pierwszy — inicjuje jednostkę, stawkę VAT, cenę i rabat), `Ilosc: Quantity` (ilość + symbol jednostki), `Cena: DoubleCy` (cena + symbol waluty; netto lub brutto wg `Dokument.LiczonaOd`), `Rabat: Percent`, `RabatCeny: DoubleCy` (rabat kwotowy), `WartoscCy: Currency` (wartość pozycji), `DefinicjaStawki: DefinicjaStawkiVat` (stawka VAT). Flagi nadpisań: `KorektaCeny: bool`, `KorektaRabatu: bool`, `BezRabatu: bool`. **Snippet:** ```csharp var towary = session.GetTowary(); using (var t = session.Logout(editMode: true)) { // Wariant A — cena i rabat pobrane automatycznie z cennika/karty towaru: var poz1 = new PozycjaDokHandlowego(dok); session.AddRow(poz1); poz1.Towar = towary.Towary.WgKodu["BIKINI"]; // ustawia jednostkę, cenę, rabat, stawkę VAT poz1.Ilosc = new Quantity(3, poz1.Ilosc.Symbol); // symbol jednostki z towaru // Cena i rabat zostają takie, jakie zaproponował cennik. // Wariant B — ręczne nadpisanie ceny i rabatu: var poz2 = new PozycjaDokHandlowego(dok); session.AddRow(poz2); poz2.Towar = towary.Towary.WgKodu["BIKINI"]; poz2.Ilosc = new Quantity(10, poz2.Ilosc.Symbol); poz2.Cena = new DoubleCy(48m, poz2.Cena.Symbol); // nadpisanie ceny → KorektaCeny=true poz2.Rabat = new Percent(0.1); // 10% rabatu → KorektaRabatu=true t.Commit(); } session.Save(); ``` **Pułapki:** - **`Towar` ustawiaj jako pierwszy.** Dopiero on inicjuje symbol jednostki na `Ilosc`/`Cena`, stawkę VAT i proponowaną cenę/rabat. Ustawienie `Ilosc`/`Cena` przed `Towar` operowałoby na pustych symbolach. - `Ilosc` to `Quantity` (wartość + symbol jednostki), `Cena` to `DoubleCy` (wartość + symbol waluty) — twórz je z symbolem już ustawionym na pozycji: `new Quantity(n, poz.Ilosc.Symbol)`, `new DoubleCy(c, poz.Cena.Symbol)`. Nie wstawiaj „gołego" `decimal`. - Ręczne ustawienie `Cena`/`Rabat` zapala flagi `KorektaCeny`/`KorektaRabatu` — od tej chwili platforma **nie przeliczy** już automatycznie tej wartości (np. po zmianie kontrahenta/ilości). - `Cena` jest netto albo brutto zależnie od `Dokument.LiczonaOd` — interpretuj ją spójnie z dokumentem. - Konstruktor pozycji wymaga dokumentu: `new PozycjaDokHandlowego(dok)`, a po nim `session.AddRow(poz)`. --- ### HANDEL-W9 — Dokument w walucie obcej **Cel:** wystawić dokument rozliczany w walucie obcej (EUR/USD): wskazać walutę płatności, tabelę kursową, datę kursu oraz — w razie potrzeby — wpisać kurs ręcznie. **Warianty:** | Wariant | Mechanizm | Pola | |---|---|---| | Kurs z tabeli na datę | kurs pobierany z `TabelaKursowa` | `TabelaKursowa`, `DataKursu` | | Kurs ręczny | użytkownik podaje kurs | `KursWaluty: double` | | Zmiana waluty istniejącego dokumentu | przeliczenie dokumentu i cen | akcja „Zmień walutę dokumentu i cen..." (worker) | | Waluta na pozycji | cena w walucie | `poz.Cena: DoubleCy` z symbolem waluty | **Pola i typy:** `TabelaKursowa: TabelaKursowa` (wymagana — `WalutyModule.GetInstance(session).TabeleKursowe`), `DataKursu: Date`, `KursWaluty: double`, `BruttoCy: Currency` (kwota płatności w walucie). Waluta płatności wynika z definicji (`DefDokHandlowego.WalutaPlatnosci`). Zmianę waluty istniejącego dokumentu realizuje akcja menu Czynności sterowana klasą parametrów `DokumentHandlowyZmianaWalutyWorkerParams` (publiczna): `Waluta`, `WalutaBazowa` (read-only), `TabelaKursowa`, `Data`, `KursWaluty`, `ZmienCeny: bool`. **Snippet:** ```csharp using Microsoft.Extensions.DependencyInjection; // dla GetRequiredService, jeśli potrzebne using Soneta.Waluty; var wm = WalutyModule.GetInstance(session); // session.GetWaluty() jest internal var eur = wm.Waluty.WgSymbolu["EUR"]; var tabela = wm.TabeleKursowe.NBP; // np. tabela NBP // Zmiana waluty istniejącego (buforowego) dokumentu na EUR z ręcznym kursem. // Worker uruchamiany jest jak akcja menu Czynności — parametry przekazujemy przez Context: var paramy = new DokumentHandlowyZmianaWalutyWorkerParams(context, dok) { Waluta = eur, TabelaKursowa = tabela, KursWaluty = 4.3344, // kurs ręczny; przy zmianie tabeli/daty platforma proponuje kurs sama ZmienCeny = true, // przelicz także ceny pozycji }; context.Set(paramy); // akcja „Zmień walutę dokumentu i cen..." (ZmienWalute) wykonuje przeliczenie w transakcji UI // Dokument walutowy „od zera": ustaw tabelę i datę kursu przed pozycjami: using (var t = session.Logout(editMode: true)) { dok.TabelaKursowa = tabela; dok.DataKursu = Date.Today; // dok.KursWaluty = 4.3344; // tylko gdy chcesz wymusić kurs ręczny t.Commit(); } session.Save(); ``` **Pułapki:** - **Brak kursu na datę = wyjątek.** Jeśli w bazie nie ma kursu danej waluty na `DataKursu`, operacja rzuca `KursWalutyNotFoundException`. Na bazie Demo nie ma kursu EUR „na dziś" — albo dodaj kurs do tabeli kursowej, albo wpisz kurs ręcznie (`KursWaluty`). - `TabelaKursowa` jest **wymagana** dla dokumentu walutowego. - `session.GetWaluty()` jest **internal** — używaj `WalutyModule.GetInstance(session)`. - Worker `DokumentHandlowyZmianaWalutyWorker` jest klasą **internal** — z dodatku nie tworzysz jej instancji bezpośrednio; uruchamiasz akcję przez framework Czynności, przekazując publiczną klasę `DokumentHandlowyZmianaWalutyWorkerParams` przez `Context`. - Zmiana waluty dokumentu jest możliwa tylko w **buforze** (`dok.Bufor == true`). --- ### HANDEL-W10 — Dokument z usługą (pozycja usługowa bez wpływu na magazyn) **Cel:** dodać do dokumentu pozycję usługową (np. „MONTAZ", „TRANSPORT") — towar typu usługa nie ma wpływu na stan magazynu, ale uczestniczy w wartości i tabeli VAT. **Warianty:** | Wariant | Charakterystyka | |---|---| | FV tylko z usługą | faktura za samą usługę (np. montaż) — brak obrotu magazynowego | | FV mieszana | towar magazynowy + pozycja usługowa na jednym dokumencie | | Usługa rozliczana ilościowo | usługa w jednostce (np. „TRANSPORT" w km) | **Pola i typy:** identyczne jak w HANDEL-W8 (`Towar`, `Ilosc`, `Cena`, `Rabat`, `DefinicjaStawki`). Różnica jest w **karcie towaru**: towar usługowy nie generuje obrotu magazynowego — `poz.IloscMagazynu` pozostaje zerowa, `dok.Zasoby`/`dok.Obroty` nie powstają dla tej pozycji. **Snippet:** ```csharp var handel = session.GetHandel(); var towary = session.GetTowary(); using (var t = session.Logout(editMode: true)) { var fv = new DokumentHandlowy(); session.AddRow(fv); fv.Definicja = handel.DefDokHandlowych.WgSymbolu["FV"]; fv.Magazyn = session.GetMagazyny().Magazyny.WgSymbol["F"]; fv.Kontrahent = session.GetCRM().Kontrahenci.WgKodu["Abc"]; fv.Data = Date.Today; fv.DataOperacji = Date.Today; // Pozycja usługowa — towar "MONTAZ" jest usługą (BEZ wpływu na magazyn): var poz = new PozycjaDokHandlowego(fv); session.AddRow(poz); poz.Towar = towary.Towary.WgKodu["MONTAZ"]; // usługa poz.Ilosc = new Quantity(1, poz.Ilosc.Symbol); poz.Cena = new DoubleCy(200m, poz.Cena.Symbol); t.Commit(); } session.Save(); ``` **Pułapki:** - O tym, czy pozycja wpływa na magazyn, decyduje **typ towaru** (usługa vs towar magazynowy), a nie pole na pozycji. Dla usługi `StanUjemnyVerifier` nie blokuje wystawienia rozchodu — usługa nie pobiera ze stanu. - Faktura zawierająca **wyłącznie** usługi nie tworzy obrotów magazynowych, ale nadal liczy tabelę VAT i płatność. - Usługa też ma jednostkę (np. „TRANSPORT" w km) — `Ilosc` używa symbolu jednostki z karty towaru. --- ### HANDEL-W11 — Odbiorca / płatnik inny niż kontrahent + miejsce dostawy **Cel:** wystawić dokument, na którym **nabywca** (`Kontrahent`) różni się od **odbiorcy** towaru (`Odbiorca`), wskazać miejsce dostawy oraz — gdy płatnikiem jest inny podmiot — rozliczyć płatność na płatnika. **Warianty:** | Wariant | Pole / mechanizm | |---|---| | Inny odbiorca towaru | `Odbiorca: Kontrahent` | | Miejsce dostawy odbiorcy | `OdbiorcaMiejsceDostawy: Lokalizacja` | | Osoba odbierająca | `OsobaKontrahenta: KontaktOsoba`, `Osoba: string` (podpisujący) | | Adres / parametry przesyłki | subrow `Dostawa` (`Dostawa.Termin`, `Dostawa.Sposob`, `Dostawa.Odpowiedzialny`) | | Inny płatnik | `dok.InnyPłatnik` (kalkulowane — wynika z relacji podmiotów / płatności) | **Pola i typy:** `Kontrahent: Kontrahent` (nabywca — strona transakcji/VAT), `Odbiorca: Kontrahent` (odbiorca towaru — dane dostawy), `OdbiorcaMiejsceDostawy: Lokalizacja` (miejsce docelowe dostawy), `OsobaKontrahenta: KontaktOsoba`, `Osoba: string`. `InnyPłatnik` jest **kalkulowane (read-only)** — płatnika ustawia się przez relacje podmiotów (płatnik podmiotu) lub przez płatność, nie przez bezpośrednie przypisanie na dokumencie. **Snippet:** ```csharp var crm = session.GetCRM(); using (var t = session.Logout(editMode: true)) { // dok utworzony jak w HANDEL-W4; Kontrahent = nabywca (np. centrala): dok.Kontrahent = crm.Kontrahenci.WgKodu["Abc"]; // nabywca / strona VAT dok.Odbiorca = crm.Kontrahenci.WgKodu["ZEFIR"]; // odbiorca towaru (inny podmiot) // Miejsce dostawy odbiorcy (lokalizacja zdefiniowana u odbiorcy): // dok.OdbiorcaMiejsceDostawy = ... // rekord Lokalizacja powiązany z odbiorcą dok.Osoba = "Jan Kowalski"; // osoba podpisująca po stronie kontrahenta // Parametry dostawy (subrow): dok.Dostawa.Termin = Date.Today.AddDays(3); dok.Dostawa.Sposob = "Kurier"; t.Commit(); } session.Save(); // Odczyt płatnika (kalkulowane): bool jestInnyPlatnik = dok.InnyPłatnik; ``` **Pułapki:** - `Kontrahent` to **nabywca** (strona transakcji i VAT), `Odbiorca` to fizyczny odbiorca towaru — to dwa różne pola, oba typu `Kontrahent`. Faktura wystawiana jest na `Kontrahent`, dostawa idzie do `Odbiorca`. - `InnyPłatnik` jest **kalkulowane** — nie przypisuj go ręcznie. Innego płatnika ustala się przez relacje podmiotów (płatnik nadrzędny) lub przez konfigurację płatności dokumentu. - `OdbiorcaMiejsceDostawy` to referencja do rekordu `Lokalizacja` (zwykle zdefiniowanego u odbiorcy) — pobierz istniejącą lokalizację, nie twórz „w locie". - `Dostawa` to subrow — ustawiaj jego pola, nie przypisuj całego obiektu. - Zmiana płatnika rozkłada się na płatności; do podziału płatności na raty/płatników służy publiczny worker `PodzialPlatnosciWorker`. ---