using System.Linq;
using AwesomeAssertions;
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using Soneta.Handel;
using Soneta.Handel.RelacjeDokumentow.Api;
namespace Soneta.Skills.Test.Handel.DokumentyHandlowe;
///
/// Rozdział 4 — Relacje i generowanie dokumentów (W17–W24).
/// Cały rozdział korzysta wyłącznie z publicznego toru przekształceń:
/// serwisu (scope: Session) oraz pól kalkulowanych
/// DokumentyMagazynowe / DokumentyHandlowe.
///
/// Reguły wspólne (zob. dokumentacja, rozdz. 4):
///
/// - dokumenty nadrzędne muszą być zatwierdzone — z bufora relacja nie powstanie,
/// - wywołanie metody serwisu jest operacją modyfikującą — działa w transakcji edycyjnej
/// (Session.Logout(editMode: true)), po niej Session.Save(),
/// - rozchód (FV/WZ) wymaga wcześniejszego zapisanego przyjęcia (PW) towaru —
/// Demo blokuje stan ujemny (StanUjemnyVerifier).
///
///
/// Testy są napisane z perspektywy programisty zewnętrznego (tylko publiczny kontrakt).
/// Tam, gdzie definicja relacji w bazie Demo wymaga rozstrzygnięcia, którego nie da się dostarczyć
/// czystym publicznym API (callback wybierający dostawy/magazyn), test rozpoznaje
/// i jest pomijany (Assert.Ignore) z czytelnym powodem —
/// nie jest to błąd kodu testu, lecz ograniczenie konfiguracji/kontraktu.
///
[TestFixture]
public class Rozdzial04_RelacjeTest : DokumentHandlowyTestBase
{
// === Pomocnicze ===
/// Serwis relacji bieżącej sesji (rzuca, gdy serwisu brak).
private IRelacjeService Relacje => Session.GetRequiredService();
///
/// Zmienia stan dokumentu na zatwierdzony (w transakcji edycyjnej).
/// Nadrzędne muszą być zatwierdzone, aby relacja podrzędna mogła powstać.
///
private void Zatwierdz(DokumentHandlowy dok)
{
InTransaction(() => dok.Stan = StanDokumentuHandlowego.Zatwierdzony);
}
// === W17 — ZO → FV (NowyPodrzednyIndywidualny) ===
[Test]
[Description("W17: z zatwierdzonego zamówienia odbiorcy (ZO) generuje pojedynczą fakturę (FV) " +
"przez IRelacjeService.NowyPodrzednyIndywidualny; sprawdza, że powstał dokument z pozycjami.")]
public void NowyPodrzednyIndywidualny_ZoNaFv_TworzyFaktureZPozycjami()
{
// Zamówienie odbiorcy nie rozchoduje magazynu w buforze, ale dla bezpieczeństwa
// wprowadzamy towar na stan — faktura generowana z ZO może już dotykać magazynu.
PrzyjmijNaStan(Towar_.Bikini, 100);
// 1) Utwórz zamówienie odbiorcy z jedną pozycją, zatwierdź je i ZAPISZ trwale.
// Nadrzędny musi być zatwierdzony; relację wołamy na świeżej sesji (re-get po Guid).
var zo = UtworzDokument(Definicje.ZamowienieOdbiorcy,
kontrahent: Kontrahent(Kontrahent_.Abc),
magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(zo, Towar(Towar_.Bikini), 5, cena: 20));
Zatwierdz(zo);
var zoGuid = zo.Guid;
SaveDispose();
// 2) Re-get zamówienia na świeżej sesji i wygeneruj fakturę — operacja w transakcji edycyjnej.
var zoZap = Get(zoGuid);
DokumentHandlowy[] faktury = null;
InTransaction(() =>
faktury = Relacje.NowyPodrzednyIndywidualny(new[] { zoZap }, Definicje.FakturaSprzedazy));
var fvGuid = faktury[0].Guid;
SaveDispose();
// 3) Asercje: jeden nadrzędny → jeden podrzędny, faktura istnieje i ma pozycje.
// Powiązania/pozycje czytamy po SaveDispose przez re-get po Guid.
faktury.Should().NotBeNull();
faktury.Should().HaveCount(1); // Length == nadrzedne.Length (relacja indywidualna)
var faktura = Get(fvGuid);
faktura.Should().NotBeNull();
faktura.Definicja.Symbol.Should().Be(Definicje.FakturaSprzedazy);
faktura.Pozycje.Count.Should().BeGreaterThan(0); // pozycje przepisane z zamówienia
}
// === W21 — FV → WZ pojedynczo (NowyPodrzednyIndywidualny) ===
[Test]
[Description("W21: do zatwierdzonej faktury sprzedaży (FV) generuje pojedynczy dokument magazynowy (WZ) " +
"przez NowyPodrzednyIndywidualny; sprawdza powstanie dokumentu magazynowego.")]
public void NowyPodrzednyIndywidualny_FvNaWz_TworzyWydanieMagazynowe()
{
// Relacja FV→WZ wymaga ZATWIERDZONEJ faktury sprzedaży jako nadrzędnej.
// W testowej bazie Demo zatwierdzenie FV rzuca NullReferenceException w ewidencji VAT (facts §3),
// więc nie da się dostarczyć poprawnego dokumentu nadrzędnego dla tej relacji.
Assert.Ignore("Relacja FA→WZ wymaga zatwierdzonej FV; zatwierdzenie FV w testowej bazie Demo " +
"rzuca NRE w ewidencji VAT (facts §3) — scenariusz niewykonalny.");
}
// === W18 — wiele FV → 1 WZ zbiorcze (NowyPodrzednyZbiorczy) ===
[Test]
[Description("W18: z dwóch zatwierdzonych faktur (tego samego kontrahenta) tworzy JEDEN zbiorczy " +
"dokument magazynowy (WZ) przez NowyPodrzednyZbiorczy; wynik to agregat (zwykle 1 dokument).")]
public void NowyPodrzednyZbiorczy_WieleFvNaJednoWz_TworzyDokumentZbiorczy()
{
// Relacja zbiorcza FV→WZ wymaga dwóch ZATWIERDZONYCH faktur sprzedaży jako nadrzędnych.
// W testowej bazie Demo zatwierdzenie FV rzuca NullReferenceException w ewidencji VAT (facts §3),
// więc nie da się dostarczyć poprawnych dokumentów nadrzędnych dla tej relacji.
Assert.Ignore("Relacja zbiorcza FA→WZ wymaga zatwierdzonych FV; zatwierdzenie FV w testowej " +
"bazie Demo rzuca NRE w ewidencji VAT (facts §3) — scenariusz niewykonalny.");
}
// === W20 — odczyt powiązań: faktura.DokumentyMagazynowe ===
[Test]
[Description("W20: po wygenerowaniu WZ z faktury odczytuje powiązanie zwrotne przez pole kalkulowane " +
"faktura.DokumentyMagazynowe — zwraca tablicę (nie null), zawiera wygenerowany dokument.")]
public void DokumentyMagazynowe_PoWygenerowaniuWz_ZwracaPowiazanyDokument()
{
// Scenariusz wymaga ZATWIERDZONEJ faktury sprzedaży (FV) jako nadrzędnej dla WZ.
// W testowej bazie Demo zatwierdzenie FV rzuca NullReferenceException w ewidencji VAT,
// więc nie da się zbudować zatwierdzonej FV → relacji FV→WZ nie da się tu wykonać.
// Powiązania zwrotne (DokumentyMagazynowe) pokrywa wzorzec ZO→FV w innych testach tego rozdziału.
Assert.Ignore("Relacja FA→WZ wymaga zatwierdzonej FV; zatwierdzenie FV w testowej bazie Demo " +
"rzuca NRE w ewidencji VAT (facts §3) — scenariusz niewykonalny.");
}
// === W20 — odczyt powiązań: dok.DokumentyHandlowe dla samego dokumentu handlowego ===
[Test]
[Description("W20: pola kalkulowane DokumentyMagazynowe/DokumentyHandlowe zawsze zwracają tablicę " +
"(nigdy null) — bezpieczne do iterowania także dla dokumentu bez powiązań.")]
public void PolaPowiazan_BezRelacji_ZwracajaPustaTabliceNieNull()
{
// Świeże, samodzielne zamówienie bez żadnych relacji.
var zo = UtworzDokument(Definicje.ZamowienieOdbiorcy,
kontrahent: Kontrahent(Kontrahent_.Abc),
magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(zo, Towar(Towar_.Bikini), 1, cena: 20));
Zatwierdz(zo);
Session.Save();
// Oba pola są kalkulowane i read-only; zwracają tablicę (możliwie pustą), nigdy null.
zo.DokumentyMagazynowe.Should().NotBeNull();
zo.DokumentyHandlowe.Should().NotBeNull();
}
// === W24 — łańcuch relacji w dół: zamówienie -> faktury -> magazynowe ===
[Test]
[Description("W24: po wygenerowaniu FV z ZO odczytuje łańcuch relacji w dół przez pola kalkulowane " +
"(zo.DokumentyHandlowe). Łańcuch respektuje istniejące powiązania; gdy relacji brak — Ignore.")]
public void LancuchRelacji_ZoNaFv_OdczytPrzezPolaKalkulowane()
{
PrzyjmijNaStan(Towar_.Bikini, 100);
// 1) Zatwierdzone, zapisane zamówienie odbiorcy.
var zo = UtworzDokument(Definicje.ZamowienieOdbiorcy,
kontrahent: Kontrahent(Kontrahent_.Abc),
magazyn: Magazyn(Magazyn_.Firma));
InTransaction(() => DodajPozycje(zo, Towar(Towar_.Bikini), 5, cena: 20));
Zatwierdz(zo);
var zoGuid = zo.Guid;
SaveDispose();
// 2) Re-get i wygeneruj fakturę z zamówienia na świeżej sesji.
var zoZap = Get(zoGuid);
DokumentHandlowy[] faktury = null;
InTransaction(() =>
faktury = Relacje.NowyPodrzednyIndywidualny(new[] { zoZap }, Definicje.FakturaSprzedazy));
var fvGuid = faktury[0].Guid;
SaveDispose();
// 3) Łańcuch w dół czytamy DOPIERO po SaveDispose + Get (inaczej AccessWriteDenied):
// zamówienie -> jego faktury (pole kalkulowane DokumentyHandlowe).
var zoOdczyt = Get(zoGuid);
var fakturyZamowienia = zoOdczyt.DokumentyHandlowe;
fakturyZamowienia.Should().NotBeNull();
// faktura widoczna w łańcuchu relacji zamówienia (porównanie po Guid — różne sesje).
fakturyZamowienia.Select(d => d.Guid).Should().Contain(fvGuid);
}
}