28 KiB
HANDEL13 — Tematy specjalistyczne (KSeF, fiskalizacja, kompletacja, Intrastat)
Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../handel.md.
Rozdział obejmuje obszary, które łączą dokument handlowy z systemami zewnętrznymi (KSeF), urządzeniami (drukarka fiskalna) oraz specjalistyczną logiką magazynową (kompletacja) i sprawozdawczą (Intrastat).
Ważne — co jest, a co nie jest testowalne jednostkowo. Część operacji wymaga sieci (komunikacja z bramką KSeF, wysyłka e-mail e-paragonu) albo sprzętu (drukarka fiskalna). Tych fragmentów nie da się odtworzyć w teście jednostkowym — testuj wyłącznie ustawienie pól i parametrów oraz strukturę (np.
XmlValidated, parametry workera, polaKSeFKomunikat). Każdy wzorzec poniżej oznacza, która część jest „offline" (testowalna), a która „online/sprzętowa" (NIE testuj — patrzdh-facts.md, „Reguły testów").Wszystkie workery wymienione w tym rozdziale są publiczne i mogą być wywołane z dodatku zewnętrznego. Operacje modyfikujące dokument wykonuj w transakcji (
session.Logout(true)+Commit/CommitUI), potemsession.Save(). Kod zgodny z C# 10.
HANDEL-W67 — Wysłanie faktury do KSeF (pojedynczo i zbiorczo)
Cel: wysłać zatwierdzony dokument sprzedaży do Krajowego Systemu e-Faktur — pojedynczo
(KSeFWyslijWorker) albo wsadowo dla wielu dokumentów naraz (KSeFWysylkaWsadowaWorker). Sama wysyłka
to operacja online (NIE testuj); offline (testowalne) jest przygotowanie dokumentu: wygenerowanie XML,
walidacja struktury i ustawienie parametrów autoryzacji.
Warianty:
| Wariant | Worker / akcja | Uwaga |
|---|---|---|
| Wysyłka pojedyncza | KSeFWyslijWorker.Wyslij(dok) (akcja „KSeF/Wyślij") |
dla jednego dokumentu |
| Wysyłka zbiorcza | KSeFWysylkaWsadowaWorker.WyslijZbiorczo() (akcja „KSeF/Wyślij zbiorczo") |
Dokumenty[], generuje XML brakującym, pomija zaimportowane/odrzucone |
| Faktura offline (awaria/tryb 24h) | wysyłka z KSeFKomunikat.Offline == true |
używa tokenu i kontekstu zapisanych na komunikacie |
| Data wystawienia ≠ dziś | weryfikator KSeFWyslijWorker.Weryfikator(dok) |
rzuca wyjątek wg konfiguracji i uprawnień (data przyszła/przeszła) |
Pola i typy:
- Parametry:
KSeFWyslijParams : ContextBase—SystemZewn: SystemZewnPlatformaEDI([Required]),Token: SysZewToken([Required], „Sposób autoryzacji"),KontekstAutentykacjiKSeF(„Kontekst autoryzacji"). Listy:GetListSystemZewn(),GetListToken(),GetListKontekstAutentykacjiKSeF(). KSeFWyslijWorker:[Context] Dokument: DokumentHandlowy,[Context] Parametry: KSeFWyslijParams,[Context] Context: Context. Akcjaobject Wyslij(DokumentHandlowy dok).KSeFWysylkaWsadowaWorker:[Context] Parametry: KsefEksportIWyslijParams,[Context(Required=false)] Dokumenty: DokumentHandlowy[],[Context(Required=false)] Dokument: DokumentHandlowy,[Context] Context: Context.- Warunek wstępny (sprawdzany przez
WeryfikatorPolaXmlValidated): każdy dokument musi miećdok.ImportExportKSeF.XmlValidated == ThreeStateBoolean.True(czyli wcześniej wykonaną walidację struktury — HANDEL-W69).
Snippet:
using Microsoft.Extensions.DependencyInjection;
using Soneta.KSeF.Workers;
var hm = session.GetHandel();
var dok = hm.DokHandlowe.WgNumer[...]; // zatwierdzona faktura sprzedaży
// 1) Walidacja daty wystawienia (offline, testowalne) — rzuca wyjątek dla daty != dziś
// wg konfiguracji KSeF i uprawnień operatora:
KSeFWyslijWorker.Weryfikator(dok);
// 2) Przygotowanie parametrów autoryzacji (offline). System i token wybierane z list:
var ctx = session.GetEmptyContext();
ctx.TryAdd(() => dok);
var parametry = new KSeFWyslijParams(ctx); // konstruktor sam wybiera domyślny system/token
// parametry.SystemZewn / parametry.Token można ustawić jawnie z GetListSystemZewn()/GetListToken()
// 3) Wysyłka pojedyncza — OPERACJA ONLINE (NIE testuj jednostkowo):
var worker = new KSeFWyslijWorker { Dokument = dok, Parametry = parametry, Context = ctx };
object wynik = worker.Wyslij(dok); // wewnątrz: SesjaWysylki + WyslijDokument, zapis KSeFKomunikat
// Wysyłka zbiorcza — ONLINE; Dokument musi być pierwszym elementem tablicy Dokumenty:
DokumentHandlowy[] doks = hm.DokHandlowe.WgNumer[...].ToArray();
var workerZb = new KSeFWysylkaWsadowaWorker { Dokument = doks[0], Dokumenty = doks, Context = ctx, Parametry = paramsZb };
workerZb.WyslijZbiorczo();
Pułapki:
- Tylko dokumenty zatwierdzone podlegają wysyłce (
IsVisible*wymagajądok.Zatwierdzony). Bufor i dokument anulowany nie są wysyłane. - Przed wysyłką dokument musi mieć zwalidowany XML (
XmlValidated == True) — inaczejWeryfikatorPolaXmlValidatedrzuci wyjątek „nie posiada zweryfikowanego pliku XML". Najpierw wykonaj HANDEL-W69 (Sprawdź strukturę pliku) lub wygeneruj XML (wysyłka zbiorcza robi to automatycznie dla statusuBrak). - Wysyłka zbiorcza pomija dokumenty: zaimportowane z KSeF (
ImportExportKSeF.Rodzaj == Import), o nieprawidłowym/niezweryfikowanym XML, wygenerowane inną definicją niż w parametrach, w trybie offline z innym tokenem — wszystkie pominięcia trafiają do logu „KSeF". - Cała komunikacja z bramką (
IKSeFAPIv2Service/IKSeFAPIService) wymaga sieci → NIE testuj jednostkowo. W teście weryfikuj jedynie: utworzenieKSeFWyslijParams, dobór systemu/tokenu z list,Weryfikatororaz że XML jest zwalidowany. - Po wysyłce na dokumencie zatwierdzonym ustawiana jest flaga
Session.SaveImmediatelyIfPossible = true(natychmiastowy zapis komunikatu KSeF).
HANDEL-W68 — Sprawdzenie statusu KSeF i odczyt numeru KSeF
Cel: po wysyłce sprawdzić w bramce, czy dokument został przyjęty, i pobrać nadany numer KSeF
(KSeFSprawdzStatusWorker). Sprawdzenie statusu to operacja online (NIE testuj); odczyt już zapisanego
statusu/numeru jest offline (testowalne — pola kalkulowane na dokumencie).
Warianty:
| Wariant | Mechanizm |
|---|---|
| Sprawdzenie statusu po sesji wysyłki | KSeFSprawdzStatusWorker.SprawdzStatus(dok) (akcja „KSeF/Sprawdź status") — ONLINE |
| Odczyt aktualnego statusu | dok.StatusKSeF: KSeFState — offline, kalkulowane |
| Odczyt numeru KSeF / nr referencyjnych | dok.KSeFKomunikat.NumerDokumentuKSeF itd. — offline |
| Czy dokument w ogóle podlega KSeF | dok.PodlegaKSeF, dok.PosiadaKSeF — offline |
Pola i typy:
dok.StatusKSeF: Soneta.Core.KSeF.KSeFState(kalkulowane). Wartości:NieDotyczy=1, Brak=2, DoWyslania=4, Wyslany=8, Przyjety=16, Odrzucony=32(orazRobocze=14,Razem=31zachowane dla kompatybilności). Status wyliczany z zawartościKSeFKomunikati stanu dokumentu (Bufor/Anulowany ⇒NieDotyczy).dok.KSeFKomunikat(rekordKSeFKomunikat):NumerDokumentuKSeF: string(numer nadany przez KSeF — ustawiony ⇒ statusPrzyjety),NumerReferencyjnyKSeF: string,NumerReferencyjnySesjiKSeF: string,OpisBledu: string(niepusty ⇒ statusOdrzucony),Offline: bool,TokenKSeF: SysZewToken,DataPrzeslaniaKSeF,DataPrzyjeciaKSeF.dok.PosiadaKSeF: bool(ma plikImportExportKSeF),dok.PodlegaKSeF: bool,dok.QRCodeLink: string.
Snippet:
// Sprawdzenie statusu w bramce — OPERACJA ONLINE (NIE testuj jednostkowo):
var worker = new KSeFSprawdzStatusWorker();
MessageBoxInformation wynik = worker.SprawdzStatus(dok); // pobiera status z sesji wysyłki
// Odczyt zapisanego statusu i numeru — OFFLINE, w pełni testowalne:
KSeFState status = dok.StatusKSeF;
if (status == KSeFState.Przyjety)
{
string numerKSeF = dok.KSeFKomunikat.NumerDokumentuKSeF; // numer nadany przez KSeF
string nrSesji = dok.KSeFKomunikat.NumerReferencyjnySesjiKSeF;
}
else if (status == KSeFState.Odrzucony)
{
string blad = dok.KSeFKomunikat.OpisBledu; // przyczyna odrzucenia
}
Pułapki:
StatusKSeFjest kalkulowane — nie da się go ustawić; zmienia się przez samKSeFKomunikat.- Sprawdzenie statusu działa tylko, gdy
dok.StatusKSeF != Przyjetyi istniejeKSeFKomunikatz numerem referencyjnym sesji; dokument w stanieDoWyslanianie ma jeszcze czego sprawdzać. - Worker odczytuje status wszystkich dokumentów z tej samej sesji wysyłki (
NumerReferencyjnySesjiKSeF) i każdemu z nich uzupełnia numer KSeF — to operacja zbiorcza po stronie bramki. - Wywołanie
IKSeFAPIv2Service.SprawdzStatusDokumentowZSesjiwymaga sieci → NIE testuj jednostkowo. W teście weryfikuj jedynie wyliczanieStatusKSeFz różnych ustawieńKSeFKomunikat.
HANDEL-W69 — UPO, numer KSeF z duplikatu, walidacja struktury XML
Cel: trzy operacje pomocnicze KSeF: pobranie UPO (urzędowego poświadczenia odbioru) dla przyjętej
faktury, odzyskanie numeru KSeF z duplikatu (gdy bramka odrzuciła dokument kodem 440 = duplikat) oraz
walidacja struktury XML względem schematu (XSD). Walidacja XML jest offline (testowalna); pobranie
UPO jest online (NIE testuj). Pobranie numeru z duplikatu jest offline (parsuje istniejący OpisBledu).
Warianty:
| Wariant | Worker / akcja | Online? |
|---|---|---|
| Walidacja struktury XML | KSeFSprawdzXMLWorker.Check() (akcja „KSeF/Sprawdź strukturę pliku") |
OFFLINE (lokalny XSD) |
| Pobranie UPO dla dokumentu | KSeFSprawdzUPODokumentuWorker.SprawdzUPO() (akcja „KSeF/Sprawdź UPO...") |
ONLINE |
| Numer KSeF z duplikatu (błąd 440) | PobierzNumerKSeFZDuplikatuWorker.PobierzNumerDokumentuKSeF(dok) |
OFFLINE (parsuje OpisBledu) |
Pola i typy:
KSeFSprawdzXMLWorker:[Context] Dokument: DokumentHandlowy, metodavoid Check(). Ustawiadok.ImportExportKSeF.XmlValidated: ThreeStateBoolean(True/False). Walidator publiczny:KSeFSchemaVerifier.Verify(DokumentHandlowy dok)(rzuca wyjątek przy niezgodności ze schematem).KSeFSprawdzUPODokumentuWorker:[Context] Dokument,void SprawdzUPO(). Wymagadok.StatusKSeF == Przyjetyi tokenu w wersji API v2 (KSeFKomunikat.TokenKSeF.KSeFAPIv2), inaczej rzucaRowException. Zapisuje rekordKSeFUPOi datyDataPrzeslaniaKSeF/DataPrzyjeciaKSeF.PobierzNumerKSeFZDuplikatuWorker: akcjavoid PobierzNumerDokumentuKSeF(DokumentHandlowy dok). Aktywna, gdydok.KSeFKomunikat.OpisBleduzawiera „440"; z opisu wyłuskuje numer dokumentu i sesji, ustawiaNumerDokumentuKSeF/NumerReferencyjnySesjiKSeF(status przechodzi naPrzyjety).
Snippet:
// 1) Walidacja struktury XML — OFFLINE (lokalny XSD), w pełni testowalne:
var xmlWorker = new KSeFSprawdzXMLWorker { Dokument = dok };
xmlWorker.Check();
bool poprawny = dok.ImportExportKSeF.XmlValidated == ThreeStateBoolean.True;
// Alternatywnie sam weryfikator (rzuca wyjątek przy błędzie struktury):
KSeFSchemaVerifier.Verify(dok);
// 2) Numer KSeF z duplikatu — OFFLINE (parsuje OpisBledu z błędu 440):
var dupWorker = new PobierzNumerKSeFZDuplikatuWorker();
dupWorker.PobierzNumerDokumentuKSeF(dok); // ustawia NumerDokumentuKSeF, jeśli OpisBledu zawiera "440"
// 3) Pobranie UPO — OPERACJA ONLINE (NIE testuj jednostkowo):
var upoWorker = new KSeFSprawdzUPODokumentuWorker { Dokument = dok };
upoWorker.SprawdzUPO(); // wymaga StatusKSeF == Przyjety oraz API v2
Pułapki:
Check()opiera się o lokalny XSD (ImportExportKSeF.DefinicjaXmlNag.LocalXSD) — nie potrzebuje sieci, dlatego jest testowalny. Wymaga jednak wcześniej wygenerowanego XML (ImportExportKSeF.Xmlniepusty —IsEnabledCheck).SprawdzUPO()rzucaRowException, gdy dokument nie jestPrzyjetyalbo nie był wysłany w API v2 — obsłuż to przed wywołaniem. Samo pobranie UPO wymaga sieci → NIE testuj.PobierzNumerDokumentuKSeFustawia wOpisBleduznacznikDokumentHandlowy.PobranoNumerKSeFZDuplikatuOpis(link QR z duplikatu może nie działać) — to celowy efekt uboczny, nie błąd.- Walidacja statusu „440 = duplikat" działa wyłącznie na tekście
OpisBledu— jeśli opis nie zawiera „440", worker nic nie robi.
HANDEL-W70 — Import faktur z KSeF (dokumenty zakupu)
Cel: pobrać z KSeF faktury zakupowe (oraz sprzedażowe), zapisać je jako pliki KSeF (KSeFPlik) w bazie,
a następnie utworzyć z nich dokumenty zakupu. Cały proces pobierania jest online (komunikacja z bramką)
i operuje na rekordach konfiguracyjno-systemowych (KSeFZapytanieOFa, KSeFPlik), a tworzenie
dokumentów zakupu z plików KSeF realizowane jest w module księgowym — NIE testuj jednostkowo części
sieciowej.
Warianty:
| Wariant | Mechanizm |
|---|---|
| Zapytanie o faktury za okres | rekord KSeFZapytanieOFa + parametry ParametryPobieraniaFakturKSeF (DataOd, DataDo, PodmiotTworzeniaZapytaniaKSeF) |
| Pobranie paczek wyników | KSeFDownloadPartWorker.Pobierz() (akcja „Pobierz pakiety") — ONLINE; tworzy KSeFPlik |
| Kwalifikacja kierunku (zakup/sprzedaż) | wg porównania NIP z pieczątki firmy z NIP-em Podmiot1 w XML |
| Utworzenie dokumentu zakupu | z KSeFPlik (import XML do dokumentu) — obszar księgowy |
Pola i typy:
KSeFDownloadPartWorker:[Context] KSeFZapytanieOFa: KSeFZapytanieOFa, akcjaobject Pobierz(). Pobiera tylko, gdyKSeFZapytanieOFa.StatusZapytania == StatusZapytania.Przetworzonoi nie pobrano jeszcze wszystkich paczek (PobraneWszystkie).ParametryPobieraniaFakturKSeF:DataOd: DateTimeOffset,DataDo: DateTimeOffset,PodmiotTworzeniaZapytaniaKSeF,PobieranieSamofakturowania.- Wynik: rekordy
KSeFPlik(zRodzajDokumentuKSeFZapytanieOFa:Sprzedaz/Zakup/Razem) tworzone przezKSeFPlik.CreateKSefPlik(...). FormularzFA_RRjest pomijany.
Snippet:
// Pobranie paczek z wynikami zapytania — OPERACJA ONLINE (NIE testuj jednostkowo):
var worker = new KSeFDownloadPartWorker { KSeFZapytanieOFa = zapytanie };
object wynik = worker.Pobierz(); // tworzy rekordy KSeFPlik dla faktur z bramki
// Po pobraniu pliki KSeF są dostępne w module Core (KSeFPliki) i mogą zostać
// zaimportowane jako dokumenty zakupu (obszar księgowy). Kierunek (Zakup/Sprzedaz)
// kwalifikowany jest automatycznie przez porównanie NIP-u z pieczątki firmy z NIP-em
// nadawcy (Podmiot1) w pliku XML.
Pułapki:
- Pobranie paczek wymaga sieci (
IKSeFAPIv2Service.PobierzFakturyZPaczek) → NIE testuj jednostkowo. - Import opiera się o rekordy
KSeFZapytanieOFa/KSeFPlik, a nie bezpośrednio oDokumentHandlowy— dokument zakupu powstaje dopiero w kolejnym kroku (import XML), poza zakresem prostego workera na dokumencie. - Pliki o tym samym numerze KSeF są pomijane (deduplikacja po numerze), tak samo formularz
FA_RR. - Z poziomu dodatku zewnętrznego operuj na publicznych
ParametryPobieraniaFakturKSeFi statusie zapytania; testuj wyłącznie logikę przygotowania parametrów (okres, podmiot), nie samo pobieranie.
HANDEL-W71 — Fiskalizacja dokumentu (paragon fiskalny)
Cel: oznaczyć / wydrukować dokument sprzedaży jako paragon na drukarce fiskalnej. Wydruk na drukarce
to operacja sprzętowa (NIE testuj). Worker FiskalizacjaDokumentuWorker ma jednak rolę „oznacz jako
zafiskalizowane" — ustawienie SymbolKasy na zatwierdzonym dokumencie jest offline (testowalne).
Warianty:
| Wariant | Mechanizm | Sprzęt? |
|---|---|---|
| Oznacz jako zafiskalizowane | FiskalizacjaDokumentuWorker.Execute() (akcja „Narzędziowe/Oznacz jako zafiskalizowane") |
NIE (tylko ustawia SymbolKasy) |
| Symbol drukarki tekstowo | ParametryFiskalizacjiDokumentu.SymbolKasy: string (max 12) |
— |
| Symbol drukarki z listy (z bazy) | ParametryFiskalizacjiDokumentu.SymbolKasyEnum + GetListSymbolKasyEnum() |
— |
| Faktyczny wydruk fiskalny | Fiscalizer (klasa fiskalizatora) |
TAK |
Pola i typy:
FiskalizacjaDokumentuWorker:[Context] Dokument: DokumentHandlowy,[Context] Parametry: ParametryFiskalizacjiDokumentu, metodavoid Execute().ParametryFiskalizacjiDokumentu : ContextBase—SymbolKasy: string([MaxLength(12)], „Symbol drukarki"),SymbolKasyEnum: string(combo, gdy dane drukarki w bazie),GetListSymbolKasyEnum(): List<string>.- Pola dokumentu:
dok.SymbolKasy: string(ustawiane przezUstawSymbolKasy),dok.Kategoria: KategoriaHandlowa. IsVisibleExecute: tylkoSprzedaż/KorektaSprzedaży.IsEnabledExecute: dokument zatwierdzony i z pustymSymbolKasy.
Snippet:
// Oznaczenie dokumentu jako zafiskalizowanego (OFFLINE — ustawia tylko SymbolKasy):
var ctx = session.GetEmptyContext();
ctx.TryAdd(() => dok);
var parametry = new FiskalizacjaDokumentuWorker.ParametryFiskalizacjiDokumentu(ctx)
{
SymbolKasy = "DRUK1" // symbol drukarki, max 12 znaków
};
var worker = new FiskalizacjaDokumentuWorker { Dokument = dok, Parametry = parametry };
worker.Execute(); // wykona się tylko gdy dok zatwierdzony i SymbolKasy pusty
// Po operacji:
string symbol = dok.SymbolKasy; // "DRUK1"
// Faktyczny wydruk na drukarce fiskalnej — OPERACJA SPRZĘTOWA (NIE testuj):
// var fiscalizer = new Fiscalizer(dok);
// fiscalizer.Fiscalize(false);
Pułapki:
Execute()zFiskalizacjaDokumentuWorkernie drukuje — jedynie ustawiaSymbolKasyi dopisuje informację o fiskalizacji do zmian zapisu. Faktyczny wydruk realizuje klasaFiscalizer(sprzęt) → NIE testuj.- Operacja działa wyłącznie dla dokumentów zatwierdzonych o pustym
SymbolKasy(IsEnabledExecute) i kategoriiSprzedaż/KorektaSprzedaży(IsVisibleExecute). SymbolKasyjest przycinany (Trim) i ograniczony do 12 znaków; wybór z listy (SymbolKasyEnum) dostępny tylko, gdy konfiguracja trzyma dane drukarek w bazie (Config.DrukarkaFiskalna.DaneDrukarkiZapisywaneWBazie).- W teście weryfikuj jedynie ustawienie
dok.SymbolKasyi warunkiIsEnabled/IsVisible— nie symuluj wydruku.
HANDEL-W72 — E-paragon (e-mail) i ponowny wydruk paragonu
Cel: obsłużyć e-paragon (paragon w formie elektronicznej wysyłany e-mailem) oraz ponowny wydruk
paragonu na drukarce fiskalnej. Ustawienie pól e-paragonu (EParagon, adres e-mail) jest offline
(testowalne); wysyłka e-mail i wydruk na drukarce są online/sprzętowe (NIE testuj).
Warianty:
| Wariant | Mechanizm | Online/sprzęt? |
|---|---|---|
| Oznaczenie dokumentu jako e-paragon | dok.EParagon: bool, dok.EParagonAdresEmail: string |
NIE (pola) |
| Polityka e-paragonu na definicji | Definicja.OznaczJakoEParagon: OznaczJakoEParagon |
NIE |
| Odczyt danych wysłanego e-paragonu | dok.DaneEParagonu: DaneEParagonu, dok.OtworzUrlEParagonu() |
NIE (odczyt) |
| Ponowny wydruk paragonu | PonownyWydrukParagonuWorker.Drukuj() (akcja „Narzędziowe/Wydrukuj ponownie...") |
TAK (sprzęt) |
Pola i typy:
dok.EParagon: bool— czy dokument jest e-paragonem; ustawienieEParagonAdresEmailautomatycznie ustawiaEParagon(poza politykąOznaczJakoEParagon.Zawsze).dok.EParagonAdresEmail: string— adres e-mail odbiorcy e-paragonu (walidowany; przyEParagon==truenie może być pusty).Definicja.OznaczJakoEParagon: Soneta.Handel.OznaczJakoEParagon—Nigdy=0, Zawsze=1, WgKontrahenta=2.dok.DaneEParagonu: DaneEParagonu,dok.OtworzUrlEParagonu(): HyperlinkResult.PonownyWydrukParagonuWorker:[Context] Paragon: DokumentHandlowy, akcjaobject Drukuj().IsVisibleDrukuj: definicjaFiskalizowany, dokument zatwierdzony, niepustySymbolKasy.
Snippet:
// Oznaczenie dokumentu jako e-paragon i ustawienie adresu e-mail (OFFLINE — testowalne):
using (var t = session.Logout(true))
{
dok.EParagonAdresEmail = "klient@example.com"; // ustawia też EParagon = true
t.Commit();
}
session.Save();
bool jestEParagonem = dok.EParagon; // true
// Odczyt danych wysłanego e-paragonu (offline):
DaneEParagonu dane = dok.DaneEParagonu;
// Ponowny wydruk na drukarce fiskalnej — OPERACJA SPRZĘTOWA (NIE testuj jednostkowo):
var worker = new PonownyWydrukParagonuWorker { Paragon = dok };
object wynik = worker.Drukuj(); // pyta o potwierdzenie, następnie Fiscalizer.Fiscalize(false)
Pułapki:
- Ustawienie
EParagonAdresEmailma efekt uboczny: dla polityki innej niżZawszeautomatycznie ustawiaEParagon = !string.IsNullOrWhiteSpace(value). PrzyEParagon==truepusty adres e-mail nie przejdzie walidacji (EParagonVerifier/EParagonEmailVerifier). - Sama wysyłka e-paragonu e-mailem wymaga sieci, a ponowny wydruk — drukarki fiskalnej → NIE testuj
jednostkowo. Testuj jedynie ustawienie
EParagon/EParagonAdresEmaili wyliczanie politykiOznaczJakoEParagon. PonownyWydrukParagonuWorker.Drukuj()wyświetla pytanie „czy wysłać ponownie" (MessageBoxInformation) — faktyczny wydruk dzieje się w handlerzeYesHandlerprzezFiscalizer.- Ponowny wydruk dostępny tylko dla dokumentu z definicji fiskalizowanej, zatwierdzonego, z niepustym
SymbolKasy(czyli już raz zafiskalizowanego).
HANDEL-W73 — Dokument kompletacji (złożenie / rozłożenie kompletu)
Cel: obsłużyć kompletację „w locie" — rozbicie pozycji-kompletu na składniki (rozchód składników,
przychód wyrobu) wg kartoteki kompletacji. Worker DokumentKompletacjaWorker udostępnia przeliczenie
pozycji wg kartoteki, wycofujące ręczne zmiany użytkownika. To operacja w pełni lokalna (offline) —
testowalna, choć wymaga poprawnie skonfigurowanej definicji kompletacji i magazynu.
Warianty:
| Wariant | Mechanizm |
|---|---|
| Przelicz składniki/produkty wg kartoteki | DokumentKompletacjaWorker.PrzeliczWgKartoteki(dok) (akcja w menu Czynności) |
| Definicja dokumentu kompletacji | Definicja.SposobEdycjiKompletacji: SposobEdycjiKompletacji (≠ None) |
| Powiązanie składniki ↔ wyrób | dokumenty kompletacji rozchodu/przychodu z DefDokHandlowych |
| Powiązanie z obrotami | obroty rozchodowe składników i przychodowy wyrobu po Save |
Pola i typy:
DokumentKompletacjaWorker: akcjavoid PrzeliczWgKartoteki(DokumentHandlowy dokument). Wycofuje relacje podrzędne pozycji (pozycja.PodrzędneRelacje) i przelicza kompletację wg kartoteki.dok.Definicja.SposobEdycjiKompletacji: Soneta.Handel.SposobEdycjiKompletacji— gdyNone, akcja niewidoczna (IsVisiblePrzeliczWgKartoteki).- Definicje kompletacji w module:
hm.DefDokHandlowych.Kompletacja,.KompletacjaRozchód,.KompletacjaPrzychód(typuDefDokHandlowego). - Powiązania składników/wyrobu: pozycje dokumentu (
dok.Pozycje) i ich relacje (PozycjaDokHandlowego.PodrzędneRelacjetypuPozycjaRelacjiHandlowej).
Snippet:
using Soneta.Handel.Kompletacje;
// Dokument kompletacji „w locie" — definicja musi mieć SposobEdycjiKompletacji != None:
var dok = hm.DokHandlowe.WgNumer[...];
// Przeliczenie składników i wyrobu wg kartoteki kompletacji (OFFLINE, w transakcji wew. workera):
var worker = new DokumentKompletacjaWorker();
worker.PrzeliczWgKartoteki(dok); // wycofuje zmiany użytkownika, odtwarza komplet z kartoteki
session.Save(); // obroty składników (rozchód) i wyrobu (przychód) księgowane przy Save
// Sprawdzenie, czy dokument w ogóle obsługuje kompletację:
bool kompletacja = dok.Definicja.SposobEdycjiKompletacji != SposobEdycjiKompletacji.None;
Pułapki:
PrzeliczWgKartotekikasuje ręczne zmiany użytkownika w kompletacji i odtwarza komplet z kartoteki — to operacja jednokierunkowa, nie „aktualizacja przyrostowa".- Worker steruje wewnętrzną flagą
dok.BezKopiowania(włącza/wyłącza wtry/finally) — nie ustawiaj jej samodzielnie obok wywołania workera. - Akcja jest niewidoczna dla
SposobEdycjiKompletacji == Noneoraz dla dokumentu w stanieDetached/Deleted(IsVisiblePrzeliczWgKartoteki). - Obroty magazynowe (rozchód składników, przychód wyrobu) powstają dopiero po
Session.Save()— w teście zastosuj wzorzec „zapis →SaveDispose()→ odczyt na świeżej sesji" i pamiętaj o blokadzie stanu ujemnego w bazie Demo (składniki muszą mieć wcześniejszy zapisany przychód).
HANDEL-W74 — Intrastat (dane statystyczne i wyszukanie dokumentów do deklaracji)
Cel: uzupełnić na pozycjach dokumentu dane potrzebne do deklaracji Intrastat (kod CN, masa, kraj
pochodzenia, ilość w jednostkach uzupełniających) za pomocą DokumentHandlowyZmienIntrastatWorker, oraz
wyszukać dokumenty kwalifikujące się do deklaracji przywozu/wywozu za okres. Operacja jest w pełni lokalna
(offline) — testowalna.
Warianty:
| Wariant | Mechanizm |
|---|---|
| Aktualizacja danych Intrastat na pozycjach | DokumentHandlowyZmienIntrastatWorker.Update() (akcja „Aktualizuj dane dla Intrastatu ...") |
| Wybór aktualizowanych danych | DokumentHandlowyZmienIntrastatParams: KodCN, Masa, Kraj, Przelicznik (bool) |
| Rodzaj Intrastat na definicji | Definicja.Intrastat: RodzajIntrastat (NieUwzględniaj/Przywóz/Wywóz/PrzywózWPodrzędnym) |
| Typ deklaracji (przywóz/wywóz) | TypDeklaracji.IntrastatPrzywóz / IntrastatWywóz |
| Okres dokumentu do deklaracji | dok.OkresIntrastat (miesiąc deklaracji) |
Pola i typy:
DokumentHandlowyZmienIntrastatWorker: konstruktor(DokumentHandlowyZmienIntrastatParams @params)z[Context];[Context] Dokument: DokumentHandlowy,[Context(Required=false)] Dokumenty: DokumentHandlowy[],Params(read-only). Akcjaobject Update().DokumentHandlowyZmienIntrastatParams : ContextBase—KodCN: bool(„Kod CN"),Masa: bool(„Masa"),Kraj: bool(„Kraj pochodzenia"),Przelicznik: bool(„Ilość w jedn. uzupełn.").dok.Definicja.Intrastat: Soneta.Magazyny.RodzajIntrastat.dok.KierunekMagazynu: Soneta.Magazyny.KierunekPartii(kraj pochodzenia aktualizowany tylko, gdyKierunekMagazynu != Brak).- Wykonawcza metoda dokumentu:
dok.UaktualnijIntrastat(bool kodCN, bool masa, bool kraj, bool przelicznik): int(zwraca liczbę zaktualizowanych pozycji).
Snippet:
using Soneta.Deklaracje.UE;
using static Soneta.Deklaracje.UE.DokumentHandlowyZmienIntrastatWorker;
// Aktualizacja danych Intrastat na pozycjach dokumentu (OFFLINE — testowalne):
var ctx = session.GetEmptyContext();
ctx.TryAdd(() => dok);
var parametry = new DokumentHandlowyZmienIntrastatParams(ctx)
{
KodCN = true, // przepisz kod CN z kartoteki towaru
Masa = true, // przelicz masę pozycji
Kraj = true, // ustaw kraj pochodzenia
Przelicznik = true // ilość w jednostce uzupełniającej
};
var worker = new DokumentHandlowyZmienIntrastatWorker(parametry) { Dokument = dok };
worker.Update(); // aktualizuje pozycje; pomija dokumenty z Definicja.Intrastat == NieUwzględniaj
session.Save();
// Wyszukanie dokumentów do deklaracji za okres — filtr serwerowy po rodzaju Intrastatu i okresie:
var hm = session.GetHandel();
var okres = new FromTo(Date.Today.FirstDayMonth(), Date.Today.LastDayMonth());
foreach (DokumentHandlowy d in hm.DokHandlowe.WgNumer[(DokumentHandlowy d) =>
d.OkresIntrastat >= okres.From && d.OkresIntrastat <= okres.To])
{
bool przywoz = d.Definicja.Intrastat == RodzajIntrastat.Przywóz
|| d.Definicja.Intrastat == RodzajIntrastat.PrzywózWPodrzędnym;
// przywoz == true ⇒ TypDeklaracji.IntrastatPrzywóz, w przeciwnym razie IntrastatWywóz
}
Pułapki:
Update()rzucaApplicationException, gdy dokument zawiera koszty dodatkowe z podziałem wg masy (PodzialKosztuDodatkowego == Masa) a zaznaczono aktualizację masy — nie da się wtedy przeliczyć masy.- Dokumenty z
Definicja.Intrastat == RodzajIntrastat.NieUwzględniajsą pomijane (akcja niewidoczna —IsVisibleUpdate). - Kraj pochodzenia aktualizowany jest tylko, gdy
dok.KierunekMagazynu != KierunekPartii.Brak— sam parametrKraj=truenie wystarczy dla dokumentu bez ruchu magazynowego. - Jeśli istnieje już zatwierdzona deklaracja Intrastat za dany okres (
OkresIntrastat.LastDayMonth()), worker dopisze do logu ostrzeżenie, że dane nie zmienią się w zatwierdzonej deklaracji (trzeba wygenerować korektę) — aktualizacja na dokumencie i tak się wykona. - Wyszukiwanie dokumentów do deklaracji filtruj serwerowo po
OkresIntrastati rodzaju Intrastatu z definicji — nie ładuj całej tabeliDokHandlowedo pamięci (safe-code §6).