Files
soneta-erp-skills/soneta-programming/references/domeny/crm/CRM01-wyszukiwanie.md
T

5.2 KiB

CRM01 — Wyszukiwanie i identyfikacja

Wspólne fakty o typie, podstawowe typy i szablon wzorca: ../crm.md.

CRM-W1 — Wyszukiwanie kontrahenta

Cel: odnaleźć istniejącego kontrahenta po wybranym kluczu, zanim zaczniemy go modyfikować lub zanim utworzymy nowy rekord.

Warianty:

Wariant Klucz Uwaga
Po kodzie Kod indeks WgKodu, klucz unikalny — zwraca pojedynczy rekord
Po nazwie / fragmencie Nazwa indeks WgNazwy (nieunikalny) lub SubTable[pattern]
Po NIP / EU VAT NIP, EuVAT normalizacja: Nip.Flat / EuVat.Flat przed porównaniem
Po adresie Adres.* miejscowość, kod pocztowy, ulica
Po PESEL / REGON / KRS PESEL, REGON, KRS osoby fizyczne / podmioty
Dedup przed dodaniem NIP sprawdzenie, czy podmiot już istnieje
Kontrahent incydentalny JestIncydentalny systemowy rekord (Kontrahent.INCYDENTALNY)

Pola i typy: Kod: string, NIP: string, EuVAT: string, Nazwa: string, Adres: Soneta.Core.Adres, PESEL/REGON/KRS: string, JestIncydentalny: bool.

Snippet:

var crm = session.GetCRM();

// 1. Po kodzie — klucz unikalny, zwraca pojedynczy rekord lub null
Kontrahent poKodzie = crm.Kontrahenci.WgKodu["ABC"];

// 2. Po nazwie — indeks nieunikalny, zwraca zbiór; bierzemy pierwszy
Kontrahent poNazwie = crm.Kontrahenci.WgNazwy["Firma XYZ"].FirstOrDefault();

// 3. Po NIP — filtr serwerowy; warunek aplikujemy na indeksie. Porównania tekstowe są case-insensitive
var nip = Nip.Flat("123-456-32-18");           // usuwa myślniki
Kontrahent poNip = crm.Kontrahenci.WgNIP[(Kontrahent k) => k.NIP == nip].FirstOrDefault();

// 4. Po fragmencie nazwy / mieście — serwerowy LIKE (warunek na indeksie WgNazwy)
foreach (Kontrahent k in crm.Kontrahenci.WgNazwy[(Kontrahent k) =>
             k.Nazwa.Contains("bud") && k.Adres.Miejscowosc == "Kraków"])
{
    // ...
}

// 5. Dedup przed dodaniem nowego kontrahenta
bool juzIstnieje = crm.Kontrahenci.WgNIP[(Kontrahent k) => k.NIP == nip].Any();

Pułapki:

  • WgKodu[...] zwraca pojedynczy rekord (klucz unikalny) — może być null. WgNazwy[...] zwraca zbiór (klucz nieunikalny), trzeba .FirstOrDefault()/iterację.
  • Nie iteruj całej tabeli Kontrahenci z if w pamięci — to tabela kartotekowa (rośnie z biznesem). Filtruj przez warunek aplikowany na indeksie, np. crm.Kontrahenci.WgKodu[(Kontrahent k) => …] (warunek wykonywany przez SQL). Indeksator samej tabeli (crm.Kontrahenci[…]) służy do dostępu po ID/kluczu, nie przyjmuje wyrażenia LINQ. Patrz rowcondition.md i safe-code.md §6.
  • W RowCondition (wyrażeniu LINQ) wolno użyć tylko pól bazodanowych. NazwaFormatowana, KodKraju, Platnik są kalkulowane → rzucą LinqConditionException.
  • Porównania tekstowe w warunku są case-insensitive — nie dubluj ToLower().
  • Przed porównaniem NIP/EU VAT normalizuj wejście (Nip.Flat, EuVat.Flat), bo w bazie bywają formaty z myślnikami i bez.

CRM-W2 — Walidacja NIP / REGON / EU VAT

Cel: sprawdzić poprawność NIP/REGON (suma kontrolna) i EU VAT (format/kraj) przed zapisem, niezależnie od weryfikacji online (CRM-W15).

Warianty:

Wariant Wejście Metoda publiczna
NIP krajowy 10 cyfr lub DDD-DDD-DD-DD Soneta.Core.Nip.Test(string)
REGON 9/14 9 lub 14 cyfr Soneta.Core.Regon.Test(string)
EU VAT prefiks kraju + numer Soneta.Core.EuVat.Test(string, ISessionable)
Normalizacja usunięcie myślników/spacji Nip.Flat, Nip.Format, EuVat.Flat
Rozbicie EU VAT kraj + numer EuVat.Split(value, out country, out nip)

Pola i typy: NIP: string, REGON: string, EuVAT: string. Walidatory są statyczne; EuVat.Test wymaga ISessionable (sprawdza listę krajów UE w bazie).

Snippet:

// Walidatory rzucają NullReferenceException dla null — najpierw odsiej puste wejście.
if (!nip.IsNullOrEmpty() && Nip.Test(nip)) { /* NIP poprawny */ }
if (!regon.IsNullOrEmpty() && Regon.Test(regon)) { /* REGON poprawny */ }
if (!euVat.IsNullOrEmpty() && EuVat.Test(euVat, session)) { /* EU VAT poprawny */ }

// Rozbicie EU VAT "PL1234563218" -> kraj "PL", numer "1234563218"
EuVat.Split(euVat, out string kodKraju, out string numer);

// Walidacja w event-handlerze zapisu (rzut PRZED Commit/Save):
if (!kontrahent.NIP.IsNullOrEmpty() && !Nip.Test(kontrahent.NIP))
    throw new RowException(kontrahent, "Nieprawidłowy NIP".Translate(), nameof(kontrahent.NIP));

Pułapki:

  • Nip.Test, Regon.Test, EuVat.Test rzucają NullReferenceException dla null (odwołują się do .Length). Zawsze najpierw sprawdź IsNullOrEmpty.
  • To walidacja formatu/sumy kontrolnej, a nie weryfikacja w MF/VIES — patrz CRM-W15.
  • Komunikaty walidacyjne rzucaj jako RowException(row, "…".Translate(), nameof(Pole)) przed Commit() (safe-code §5.1). Wyjątek po Commit() nie wycofa zmiany z sesji.
  • Ustawienie NIP/EuVAT na samym Kontrahent uruchamia wbudowaną synchronizację (NIP↔EuVAT, auto-zmiana RodzajPodmiotu) — własna walidacja jest dodatkiem, nie zastępstwem.