Files
soneta-erp-skills/soneta-programming/references/domeny/crm/CRM02-tworzenie.md
T

160 lines
6.3 KiB
Markdown

# CRM02 — Tworzenie, modyfikacja, usuwanie
> Wspólne fakty o typie, podstawowe typy i szablon wzorca: [../crm.md](../crm.md).
### CRM-W3 — Tworzenie kontrahenta
**Cel:** utworzyć nowy rekord kontrahenta z poprawnym minimalnym zestawem pól i wartościami domyślnymi.
**Warianty:**
| Wariant | Charakterystyka | Pola krytyczne |
|---|---|---|
| Podmiot gospodarczy krajowy | firma w PL | `StatusPodmiotu=PodmiotGospodarczy`, `RodzajPodmiotu=Krajowy`, `NIP` |
| Unijny / zagraniczny | sprzedaż wewn.-unijna / eksport | `EuVAT`, `RodzajPodmiotu=Unijny/Eksportowy` |
| Osoba fizyczna / finalny | konsument | `StatusPodmiotu=Finalny`, `PESEL` |
**Pola i typy:** `Kod: string`, `Nazwa: string`, `StatusPodmiotu: Soneta.Core.StatusPodmiotu`
(`PodmiotGospodarczy=0`, `Finalny=1`), `RodzajPodmiotu: Soneta.Core.RodzajPodmiotu`
(`Krajowy=0`, `Eksportowy=1`, `EksportowyPodróżny=2`, `Unijny=3`, `UnijnyTrójstronny=4`, `BezVAT=5`),
`PodatnikVAT: bool`, `FormaPrawna: Soneta.CRM.FormaPrawna`.
**Nadawanie kodu / numeracji:** `Kod` jest polem tekstowym ustawianym jawnie. Może być wymagana jego
unikalność (zależnie od konfiguracji modułu CRM); w razie kolizji `Save()` zgłosi `RowException` z
`DuplicateKeyException` w `InnerException`.
**Snippet:**
```csharp
var crm = session.GetCRM();
using (var t = session.Logout(editMode: true))
{
var k = new Kontrahent();
crm.Kontrahenci.AddRow(k); // najpierw dodaj do tabeli, potem ustawiaj pola
k.Kod = "FIRMA001";
k.Nazwa = "Firma XYZ Sp. z o.o.";
k.StatusPodmiotu = StatusPodmiotu.PodmiotGospodarczy;
k.RodzajPodmiotu = RodzajPodmiotu.Krajowy;
k.PodatnikVAT = true;
k.NIP = "1234563218"; // ustawienie NIP synchronizuje EuVAT
t.Commit(); // Commit() w kodzie biznesowym
}
session.Save(); // zapis do bazy; tu wykryte konflikty/duplikaty
```
**Pułapki:**
- Tworzenie **wyłącznie w transakcji** (`session.Logout(editMode: true)`). `AddRow` przed
ustawianiem pól.
- W workerze/extenderze (uruchamianym z UI) używaj `t.CommitUI()` zamiast `t.Commit()`
(safe-code, [`worker-extender.md`](../worker-extender.md)).
- `Nazwa` jest zapisywalna; `NazwaFormatowana`/`NazwaPierwszaLinia` są kalkulowane — nie ustawiaj.
- Dla podmiotu unijnego ustaw `EuVAT` (z prefiksem kraju) — platforma sama dostosuje `RodzajPodmiotu`.
- Brak `Commit()` = automatyczny rollback przy `Dispose()`.
### CRM-W4 — Modyfikacja i statusy
**Cel:** zmienić dane istniejącego kontrahenta lub jego status dostępności/handlowy.
**Warianty:**
| Wariant | Pole / operacja |
|---|---|
| Edycja danych identyfikacyjnych | `Kod`, `Nazwa`, `NIP`, … (blokada optymistyczna) |
| Ukrycie na listach | `Blokada: bool` |
| Blokada sprzedaży | `BlokadaSprzedazy: bool` |
| Zmiana formy prawnej | `FormaPrawna` (poj. lub masowo: worker `ZmienFormePrawnaKontrahentowWorker`) |
| Zastąpienie (zamiennik) | `Zamiennik: Kontrahent` (ustawia automatycznie `Blokada=true`) |
| Kopiowanie kontrahenta | worker `Soneta.CRM.KopiujKontrahentaWorker` (akcja „Kopiuj kontrahenta...") |
**Pola i typy:** `Blokada: bool`, `BlokadaSprzedazy: bool`, `FormaPrawna: Soneta.CRM.FormaPrawna`,
`Zamiennik: Soneta.CRM.Kontrahent`.
**Snippet:**
```csharp
var crm = session.GetCRM();
var k = crm.Kontrahenci.WgKodu["FIRMA001"];
if (k == null) return;
using (var t = session.Logout(editMode: true))
{
k.Nazwa = "Firma XYZ S.A.";
k.BlokadaSprzedazy = true; // zakaz wystawiania dokumentów rozchodu
k.Blokada = true; // ukrycie na listach
t.Commit();
}
session.Save();
// Kopiowanie kontrahenta — programowe użycie workera (bez UI):
var kopiarka = new KopiujKontrahentaWorker { Kontrahent = k };
using (var t = session.Logout(editMode: true))
{
Kontrahent nowy = kopiarka.Kopiuj();
t.Commit();
}
session.Save();
```
**Pułapki:**
- **Blokada optymistyczna**: konflikt edycji (ktoś inny zapisał ten rekord) wybucha w `session.Save()`
jako `RowConflictException` — obsłuż go (refresh + retry lub eskalacja), nie połykaj (safe-code §4).
- Nie nadpisuj `Kod` rekordów standardowych (`IsStandard == true`) ani incydentalnego
(`JestIncydentalny == true`).
- `Zamiennik` ma efekt uboczny — ustawienie zamiennika włącza `Blokada=true`. Do rozwiązania
„aktualnego" kontrahenta służy `Kontrahent.Coalesce(k)` (zwraca zamiennika albo sam rekord).
- Worker `KopiujKontrahentaWorker` ma property `[Context] Kontrahent` — przy ręcznym użyciu ustaw ją
przed wywołaniem `Kopiuj()`; operacja musi być w transakcji.
### CRM-W5 — Bezpieczne usuwanie
**Cel:** usunąć kontrahenta albo świadomie odmówić usunięcia, gdy istnieją powiązania.
**Warianty:**
| Wariant | Sytuacja | Zalecenie |
|---|---|---|
| Usunięcie czyste | brak dokumentów/rozrachunków/zadań/zdarzeń | dozwolone (`DeleteRow`) |
| Usunięcie zablokowane | są dokumenty/rozrachunki/zapisy | zamiast usuwać → `Blokada=true` |
| Kontrahent systemowy | `IsStandard` / `JestIncydentalny` | nie usuwać |
**Pola i typy:** `DokumentyHandlowe`, `Rozrachunki`, `Zadania`, `Zdarzenia` (kolekcje `SubTable`),
`IsStandard: bool`, `JestIncydentalny: bool`, `Blokada: bool`.
**Snippet:**
```csharp
var crm = session.GetCRM();
var k = crm.Kontrahenci.WgKodu["FIRMA001"];
if (k == null) return;
if (k.IsStandard || k.JestIncydentalny)
throw new BusException("Nie można usunąć kontrahenta systemowego.".Translate());
bool maPowiazania = !k.DokumentyHandlowe.IsEmpty || !k.Rozrachunki.IsEmpty
|| !k.Zadania.IsEmpty || !k.Zdarzenia.IsEmpty;
using (var t = session.Logout(editMode: true))
{
if (maPowiazania)
k.Blokada = true; // miękkie wycofanie zamiast usunięcia
else
k.Delete(); // twarde usunięcie tylko gdy brak powiązań
t.Commit();
}
session.Save();
```
**Pułapki:**
- Sprawdź powiązania **przed** `DeleteRow()`. Próba usunięcia powiązanego rekordu i tak zostanie
odrzucona przez integralność (wyjątek w `Save()`), ale lepiej zdecydować świadomie.
- Preferuj `Blokada=true` (kontrahent znika z list, dane pozostają) zamiast kasowania, gdy są
powiązania historyczne.
- `IsEmpty`/`Any` na kolekcji `SubTable` to **właściwości** (test serwerowy `exists …`, bez
nawiasów) — nie materializuj kolekcji do pamięci (`.ToList().Count`).
---