Files
roa2web-service-auto/docs/service-auto/tabele-service-auto.md
Claude Agent 43484db45e docs(service-auto): ground truth audit v3 from MARIUSM_AUTO production
Add real production sources as authoritative reference (supersedes
vfp_roaauto/Scripturi_instalare/packages.sql which is for a different
product — devize producție, not service auto):

- mariusm_ddl_export.sql: 5127 lines DDL from DBMS_METADATA (tables,
  views, triggers) of MARIUSM_AUTO schema
- pack_auto.pck: main business package (17 procedures)
- PACK_FACTURARE.pck, PACK_SESIUNE.pck, PACK_CONTAFIN.pck,
  PACK_COMENZI.pck: dependency packages
- export_ddl.sql: SQL export helper using DBMS_METADATA + DBMS_OUTPUT
  with discovery via ALL_OBJECTS LIKE patterns

Rewrite tabele-service-auto.md v3 (~600 lines) fully grounded in
production sources. Map all flows end-to-end:

- Create (pack_auto.dev_adauga_lucrare) → NOM_LUCRARI + DEV_ORDL
- Normare (dev_adauga_operatie) → DEV_OPER + DEV_OPER_MECANICI
- Validate ops (dev_valideaza_operatii) → DEV_OPER.VALIDAT
- Validate order (dev_valideaza_comanda) → DEV_ORDL.VALIDAT + CALENDAR
- Archive (dev_arhiveaza_comanda) → DEV_ORDL.INCHIS_FORTAT
- Bonuri consum: generic ROA (ointroduceri.prg tip=3) → RUL.id_lucrare
- Facturare: pack_facturare.* + pack_auto.actualizeaza_deviz

Key business semantics confirmed by Marius 2026-04-11:

- DEV_TIP_DEVIZ.inch_validare = 1 means validation alone closes the
  order (no closing note). inch_validare = 0 means additional closing
  required (via invoice for billable types, or 711=332 journal entry
  for internal types). View AUTO_LISTARE_MAN_TOT_COM has the exact
  "closed" condition as (validat=1 AND inch_validare=1) OR
  (facturat=1 AND inch_validare=0).
- Live DEV_TIP_DEVIZ values: 1=POST GARANTIE, 2=GARANTIE, 3=REGIE,
  4=PREGATIRE, 5=REGIE 2, 6=PRODUCTIE, 7=CONSTATARE. REGIE/PRODUCTIE/
  CONSTATARE have inch_validare=1 (internal, closed at validation).
- DEV_OPER for service auto contains only manopera (id_norme). The
  id_articol/id_rul_aux columns exist in DDL for another product that
  shares the table but are not populated by pack_auto.
- Real materials consumed on an order live in RUL tagged by id_lucrare,
  not in DEV_OPER. DEV_ESTIMARI_REP is a separate pre-sale estimate
  (both manopera and materiale lines) given to the client, independent
  of the real manopera (DEV_OPER) and real materials (RUL).

Plan Correction 13 (claude-main-design-20260411-rethink.md):

- Invalidate Scripturi_instalare references
- Confirm NOM_LUCRARI ← DEV_ORDL inheritance pattern
- Confirm pack_sesiune.dev_idLucrare/dev_idOrdl populated by triggers
- Refine prototype SP (Option 3) template based on real schema
- Timeline unchanged, scope wall reconfirmed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-05 09:37:09 +00:00

883 lines
48 KiB
Markdown

# Tabele + SP-uri Service Auto — MARIUSM_AUTO ground truth (v3)
> **Status:** v3 complet rescris 2026-04-11 pe baza sursă producție.
>
> **Surse (toate din `docs/service-auto/`):**
> - `mariusm_ddl_export.sql` — DDL exportat din producție (tabele, views, triggere)
> - `pack_auto.pck` — pachet principal service auto
> - `PACK_FACTURARE.pck` — pachet facturare
> - `PACK_SESIUNE.pck` — session state (dev_idLucrare, dev_idOrdl, getAn/getLuna)
> - `PACK_CONTAFIN.pck` — financial utilities (getCotaTVAStandard, get_idFact)
> - `PACK_COMENZI.pck` — **pentru ROA ERP base comenzi**, NU service auto
>
> **Versiuni anterioare invalidate:**
> - v1/v2 a referit `vfp_roaauto/Scripturi_instalare/packages.sql` → acel fișier era pentru
> **alt produs** ("devize producție"), NU service auto.
> - v2 a presupus că tabela e `comenzi`/`vcomenzi` → view-ul `VCOMENZI` din MARIUSM_AUTO
> **nu e pentru service auto** (e pentru ROA ERP base, tabela sursă `comenzi` + `nom_masini` +
> `id_codclient` + field `interna` cu valori `INTERNA/EXTERNA/GESTIUNE/SECTIE`).
> - Service auto = **`DEV_ORDL` + `NOM_LUCRARI`** (pattern de inheritance), cu view-uri
> dedicate prefixate `AUTO_*`.
---
## 1. Model de entități — ierarhie ROA + extensie ROAAUTO
```
┌──────────────────────────────────────────────────────────────────────────┐
│ NOM_LUCRARI (ROA ERP base — header generic al oricărei "lucrări"/comenzi) │
│ id_lucrare PK, nrord, id_mod, facturata, id_fact, id_tata │
│ TRIGGER TRG_NOM_LUCRARI_BEFOINS: NEXTVAL → id_lucrare │
│ pack_sesiune.dev_idLucrare := ID │
└────────────────────────────────┬─────────────────────────────────────────┘
│ FK id_lucrare (FK_DEV_ORDL_002)
┌──────────────────────────────────────────────────────────────────────────┐
│ DEV_ORDL (ROAAUTO — extensie service auto) │
│ id_ordl PK, id_lucrare FK, id_tip FK, id_masiniclient FK, │
│ id_inspector, id_asigurator, id_part, id_part_ref, id_agent, │
│ validat, inchis_fortat, proc_tvav, kmint, ore_functionare, │
│ solicitari_client CLOB, observatii, defectiuni, nr_dosar, ... │
│ TRIGGER TRG_DEV_ORDL_BEFOINS: NEXTVAL → id_ordl │
│ pack_sesiune.dev_idOrdl := ID │
└──┬───────────────────────────────────────────────┬───────────────────────┘
│ │
│ FK id_ordl │ id_lucrare (NU FK direct,
▼ │ tag logic prin coloană)
┌─────────────────────────────────────┐ ┌─────────────────────────────┐
│ DEV_OPER │ │ RUL (ROA generic warehouse) │
│ (NUMAI manoperă pentru service auto)│ │ id_rul PK, id_lucrare, │
│ id_oper PK, id_ordl FK, │ │ id_articol, cante, pretv, │
│ id_norme FK → DEV_NOM_NORME, │ │ id_set, id_fact, ... │
│ timpn, pret, validat, │ │ │
│ id_util, dataora, sters, nou │ │ Populat de "Bon consum" │
│ │ │ (ointroduceri.prg tip=3) │
│ Coloane id_articol / id_rul_aux: │ │ cu ID_LUCRARE tag. │
│ EXISTĂ în DDL (pentru alt produs │ └─────────────────────────────┘
│ "producție" care partajează schema),│
│ dar NU sunt populate de service auto│
└────┬────────────────────────────────┘
│ FK id_oper
┌──────────────────────────────────────────────────────────────────────────┐
│ DEV_OPER_MECANICI — distribuția timpului pe mecanici │
│ id_opermecanic PK, id_oper FK, id_mecanic FK, ore │
└──────────────────────────────────────────────────────────────────────────┘
```
**Materialele consumate pe o comandă NU sunt în `DEV_OPER`.** Sunt în **`RUL`** (tabela
generică ROA de rulaje / mișcări de stoc), legate de comandă prin coloana `RUL.ID_LUCRARE`.
Flow-ul e:
1. User face "Bon consum" în `ointroduceri.prg` (tip=3) → selectează articole din stoc +
tag-uiește comandă (id_lucrare) → salvare
2. Generic ROA warehouse scrie rânduri în `RUL` cu `ID_LUCRARE = X`, `SCD = '3xx'`,
`SCC = '6xx'` (consum de stoc)
3. La afișarea devizului și la facturare, materialele se citesc ca `SUM(cante * pretv)
FROM RUL WHERE id_lucrare = X`
**Notă business Marius 2026-04-11:** este posibil ca `DEV_OPER` să fie folosit de alt program
de producție care are și materiale în DEV_OPER (cu `id_articol` + `id_rul_aux` populate),
dar **pentru service auto ROAAUTO, `DEV_OPER` conține doar operații de manoperă** — fiecare
linie are `id_norme` non-NULL și `id_articol` NULL.
**Separat, deviz estimativ (pre-sale, ofertă către client):**
```
DEV_ESTIMARI_REP (deviz estimativ — ofertă pentru client înainte de a începe lucrarea)
id_dev_estimare_rep PK, id_lucrare FK,
id_norme (manoperă estimată) SAU id_articol (material estimat),
cantitate, pret, pret_cu_tva, cota_tva, id_sectie, id_valuta, ...
```
**Cele trei flux-uri paralele și independente pe o lucrare (`id_lucrare`):**
| # | Tabel | Ce conține | Când se populează |
|---|---|---|---|
| 1 | `DEV_ESTIMARI_REP` | Deviz estimativ (manoperă + materiale estimate) dat ca ofertă clientului | **Înainte** de a începe lucrarea |
| 2 | `DEV_OPER` | Manopera **reală** executată (doar `id_norme`, service auto) | **În timpul** lucrării |
| 3 | `RUL` cu `id_lucrare` | Materialele **reale** consumate (bonuri de consum din stoc) | **În timpul** lucrării |
Nu există sincronizare automată: estimarea e doar o ofertă pre-sale, iar lucrarea reală
(manoperă + materiale) poate devia de la ea. Comparațiile estimat vs. real se fac în
rapoarte analitice, nu în flux-ul operațional.
---
## 2. DEV_ORDL — schema reală producție
Exportat via `DBMS_METADATA.GET_DDL` pe 2026-04-11 din MARIUSM_AUTO.
**37 coloane**, FK-uri active:
| Coloană | Tip | Null | Note |
|---|---|---|---|
| `id_ordl` | `NUMBER(10)` | NN | PK, alocat de trigger din `SEQ_DEV_ORDL` |
| `luna` | `NUMBER(2)` | NN | Luna contabilă (0 default) |
| `an` | `NUMBER(4)` | NN | Anul contabil (0 default) |
| `id_inspector` | `NUMBER(5)` | Y | FK → `DEV_NOM_INSPECTORI` |
| `id_lucrare` | `NUMBER(10)` | NN | **FK → `NOM_LUCRARI` (FK_DEV_ORDL_002)** |
| `termen` | `DATE` | Y | Termen execuție (default `SYSDATE + gnTermenZileService`, de obicei +10 zile) |
| `datai` | `DATE` | Y | Data intrării |
| `orai` / `orae` | `NUMBER(5)` | Y | Ora intrare/ieșire |
| `datae` | `DATE` | Y | Data ieșirii |
| `validat` | `NUMBER(1)` | NN | 0/1 |
| `id_util_ad` | `NUMBER(5)` | NN | User creator |
| `id_util_valid` | `NUMBER(5)` | Y | User care a validat |
| `dataoraad` | `DATE` | Y | Audit: momentul creării |
| `dataoravalid` | `DATE` | Y | Audit: momentul validării |
| `id_utils` / `dataoras` | | Y | Audit: ștergere logică |
| `id_masiniclient` | `NUMBER(5)` | Y | **FK → `DEV_MASINICLIENTI`** |
| `id_asigurator` | `NUMBER(5)` | Y | FK → `DEV_NOM_ASIGURATORI` |
| `sters` | `NUMBER(1)` | NN | Soft delete |
| `id_tip` | `NUMBER(5)` | NN | **FK → `DEV_TIP_DEVIZ`** (default 1) |
| `inchis_fortat` | `NUMBER(1)` | NN | 0/1 — arhivat fără validare |
| `kmint` | `NUMBER(10)` | Y | Km la bord intrare |
| `nr_dosar` | `VARCHAR2(40)` | Y | Nr. dosar daună (pentru garanție asigurator) |
| `solicitari_client` | `CLOB` | Y | Text denormalizat: numele normelor solicitate (agregat prin `STRINGAGG` din `DEV_NOM_NORME` la crearea comenzii) |
| `proc_tvav` | `NUMBER(10,4)` | Y | Procent TVA (citit din `pack_contafin.getCotaTVAStandard(luna, an)` la creare) |
| `id_part` | `NUMBER(10)` | Y | **FK → `NOM_PARTENERI`** — clientul **direct pe comandă** (nu doar prin mașină) |
| `coada_deviz` | `NUMBER(1)` | NN | Flag "în coada devizelor" |
| `observatii` | `VARCHAR2(1000)` | Y | |
| `data_curs` | `DATE` | Y | Pentru comenzi în valută |
| `in_lucru` | `NUMBER(1)` | NN | Flag "comanda în lucru" |
| `id_valuta_deviz` | `NUMBER(10)` | Y | FK → `NOM_VALUTE` |
| `facturezmix` | `NUMBER(1)` | NN | Flag "facturare mixtă" |
| `data_in_lucru` / `dataorainlucru` | `DATE` | Y | Audit trecere în lucru |
| `id_agent` | `NUMBER(10)` | Y | FK → `NOM_PARTENERI` (agent vânzări) |
| `id_util_inchis` / `dataorainchis` | | Y | Audit "închidere forțată" (arhivare) |
| `id_part_ref` | `NUMBER(10)` | Y | FK → `NOM_PARTENERI` (partener referent) |
| `ore_functionare` | `NUMBER(10)` | Y | Pentru vehicule utilitare (ore motor în loc de km) |
| `defectiuni` | `VARCHAR2(200)` | Y | |
**PK:** `PK_DEV_ORDL` pe `id_ordl`.
**FK active în producție:**
- `FK_DEV_ORDL_001` → `DEV_NOM_INSPECTORI(id_inspector)` ENABLE
- `FK_DEV_ORDL_002` → `NOM_LUCRARI(id_lucrare)` **ENABLE** ← constraint critic
- `FK_DEV_ORDL_003` → `NOM_PARTENERI(id_part)` ENABLE (client direct)
- `FK_DEV_ORDL_004` → `NOM_VALUTE(id_valuta)` ENABLE
- `FK_DEV_ORDL_005` → `NOM_PARTENERI(id_agent)` ENABLE
- `FK_DEV_ORDL_006` → `NOM_PARTENERI(id_part_ref)` ENABLE
**Nu există FK pe `id_masiniclient`** (probabil pentru a permite comenzi pentru mașini
temporare sau fără înmatriculare persistentă).
**Coloane `AVANS`, `VALCTVA`, `TIP_INCAS`, `ID_CTR`, `ID_FACT`, `ID_CONTRACT`** care apăreau
în v2 DDL **NU există** în producție reală. Sunt artefacte ale scripturilor de instalare
vechi care au fost abandonate.
---
## 3. NOM_LUCRARI — header parent (ROA ERP base)
```sql
NOM_LUCRARI (
id_lucrare NUMBER(10) NN PK,
nrord VARCHAR2(50),
sters NUMBER(1) NN default 0,
id_mod NUMBER(20) NN default 0, -- 1200 pentru service auto
inactiv NUMBER(1) NN default 0,
facturata NUMBER(1) NN default 0,
id_tata NUMBER(10), -- parent hierarchy
id_fact NUMBER(20) -- populat la facturare
)
```
**Trigger `TRG_NOM_LUCRARI_BEFOINS`:**
```sql
select SEQ_NOM_LUCRARI.NEXTVAL into :new.ID_LUCRARE from dual;
pack_sesiune.dev_idLucrare := :new.id_lucrare; -- <-- session state pentru pack_auto
```
**Implicație pentru roa2web:** după `INSERT INTO nom_lucrari ... RETURNING id_lucrare INTO v_local`,
ai id-ul în două locuri simultan — în `v_local` (prin RETURNING) și în
`pack_sesiune.dev_idLucrare` (prin trigger). Poți folosi oricare. `pack_auto.dev_adauga_lucrare`
folosește intern `pack_sesiune.dev_idLucrare` pentru step-ul 2 (INSERT în `DEV_ORDL`).
**Trigger `TRG_NOM_LUCRARI_BEFOUPD`:** cheamă `pack_audit.verifica_val(...)` pentru fiecare
câmp modificat → toate UPDATE-urile pe `NOM_LUCRARI` sunt **audit trail-uite automat**
într-un pachet separat `pack_audit` (nu am încă sursa, nu e în scope imediat).
---
## 4. DEV_OPER — liniile de operații manoperă (doar)
**Important:** pentru service auto ROAAUTO, `DEV_OPER` conține **numai operații de manoperă**.
Deși schema fizică are coloanele `id_articol`, `id_rul_aux`, `id_dev_estimare_rep` (probabil
pentru compatibilitate cu alt produs de producție care partajează aceeași tabelă), **SP-urile
`pack_auto.*` pentru service auto nu le populează**. Verificare în body-ul `dev_adauga_operatie`
și `dev_adauga_oper_fact`: ambele inserează doar `id_norme`, nu `id_articol`.
```sql
DEV_OPER (
id_oper NUMBER(10) NN PK,
id_ordl NUMBER(10) NN FK → DEV_ORDL,
id_norme NUMBER(10) FK → DEV_NOM_NORME -- SERVICE AUTO: mereu non-NULL
timpn NUMBER(8,3) NN default 0, -- ore manoperă
pret NUMBER(17,4) NN default 0, -- preț oră (lei/oră)
datai DATE NN default SYSDATE,
id_sectie NUMBER(5) FK → NOM_SECTII,
id_util NUMBER(5), -- validator (populat la validare)
dataora DATE, -- momentul validării
id_utili NUMBER(5), -- creator (introducător)
id_utils NUMBER(5), -- user care a șters
dataoras DATE,
sters NUMBER(1) NN default 0,
validat NUMBER(1) NN default 0,
nou NUMBER(1) NN default 0, -- flag heritage-currency RON/ROL
id_fact NUMBER(20) FK → DOCUMENTE -- populat la facturare
-- Coloanele următoare există în DDL dar NU sunt folosite de service auto:
-- id_articol NUMBER(20) FK → NOM_ARTICOLE (pentru alt produs: producție)
-- id_dev_estimare_rep NUMBER(20) FK → DEV_ESTIMARI_REP (pentru alt produs)
-- id_rul_aux NUMBER(20) FK → RUL_AUXILIAR (pentru alt produs)
)
```
**Materialele consumate NU sunt aici.** Sunt în **`RUL.id_lucrare`** (vezi secțiunea 8
pentru flux-ul de bon consum).
**Câmpul `nou`** = flag heritage-currency (RON denominat 2005+ vs vechiul ROL). Setat
automat în SP-uri pe baza comparației lună curentă vs `pack_sesiune.GET_ANRON()` /
`GET_LUNARON()`. Pentru orice lucrare modernă (post-2005) → mereu `nou = 1`. View-urile
`AUTO_VOPER` conțin un `CASE` care împarte `pret` la 10000 pentru valorile ROL vechi.
Artefact istoric, zero impact asupra prototype-ului nou.
**Trigger `TRG_DEV_OPER_BEFOINS`:**
```sql
select SEQ_dev_oper.NEXTVAL into :new.id_oper from dual;
```
(Nu populează session state — deosebire față de DEV_ORDL.)
---
## 5. DEV_ESTIMARI_REP — deviz estimativ pentru client (pre-sale)
**Rol:** deviz estimativ pe care service-ul îl oferă clientului **înainte** ca lucrarea
să înceapă — o "ofertă" care conține estimarea de manoperă și materiale pentru o reparație
propusă. **Este complet separat de lucrarea reală.**
```
DEV_ESTIMARI_REP ≠ DEV_OPER ≠ RUL (id_lucrare)
(deviz estimativ (manopera reală (bonuri consum materiale
către client, cronometrată pe reale pe comandă, din stoc,
manopera + materiale) comandă) din warehouse generic ROA)
```
Cele **trei flux-uri sunt independente**:
1. **DEV_ESTIMARI_REP** — înainte de lucrare, utilizatorul face o estimare pentru client
cu linii mixte (manoperă estimată + materiale estimate). Clientul primește devizul,
decide dacă aprobă lucrarea.
2. **DEV_OPER** — în timpul lucrării, se înregistrează manopera efectiv executată
(doar operații cu `id_norme`, timp real, preț manoperă).
3. **RUL cu `id_lucrare`** — în paralel, bonurile de consum reale scot materiale din
stoc tag-uite pe comandă.
**Nu există sincronizare automată** între estimare și real — un client poate aproba un
deviz estimativ de 1000 lei, dar lucrarea reală să se termine la 1200 lei (fie pentru
că s-a găsit ceva în plus, fie pentru că au fost piese mai scumpe decât s-a estimat).
Comparația real vs. estimat se face probabil la facturare sau în raporte.
**Schema:** fiecare linie e fie manoperă estimată (`id_norme` NN) fie material estimat
(`id_articol` NN):
```sql
DEV_ESTIMARI_REP (
id_dev_estimare_rep NUMBER(20) NN PK,
id_lucrare NUMBER(20) NN FK → NOM_LUCRARI,
id_norme NUMBER(20) FK → DEV_NOM_NORME,
id_articol NUMBER(20) FK → NOM_ARTICOLE,
cantitate NUMBER(20,4) NN,
pret NUMBER(20,4) NN default 0,
pret_cu_tva NUMBER(1) NN default 0,
cota_tva NUMBER(10),
id_sectie NUMBER(10) FK → NOM_SECTII,
id_pol NUMBER(20) FK → CRM_POLITICI_PRETURI,
id_dev_estimare_prod NUMBER(20) FK → DEV_ESTIMARI_PRODUSE,
id_articol_grupare NUMBER(20),
cu_pierderi NUMBER(1) NN default 0,
id_valuta NUMBER(10),
semifabricat_ales / semifabricat_id NUMBER(10),
id_util / dataora, id_utils / dataoras, sters
)
```
**View-ul UI:** `AUTO_VESTIMARI_REP` — denormalizează `dev_estimari_rep` cu `dev_nom_norme` +
`nom_articole`, calculează `pretftva` / `pretctva` / `valftva` / `valtva` / `valoare` prin
`pack_sesiune.calculeaza_pret_*` + `calculeaza_total_*`.
**SP-uri pentru estimare** (din `pack_auto`):
- `adauga_manopera_de(id_lucrare, id_norme, cantitate, pret, pret_cu_tva, cota_tva, id_util)`
- `adauga_material_de(id_lucrare, id_articol, cantitate, pret, pret_cu_tva, cota_tva, id_util)`
- `modifica_pret_de`, `modifica_cota_tva_de`, `modifica_cantitate_de`, `sterge_articol_de`
**NOT IN SCOPE pentru prototype** — estimarea e un sub-flux pre-sale dedicat.
---
## 6. DEV_TIP_DEVIZ — enum tip comandă (cu flag închidere prin validare)
```sql
DEV_TIP_DEVIZ (
id_tip NUMBER(5) NN PK,
denumire VARCHAR2(50) NN,
sters NUMBER(1) NN default 0,
inch_validare NUMBER(1) NN default 1 -- 1 = închidere prin validare, 0 = alt mod
)
```
**Coloana `inch_validare`** e cheia pentru a înțelege flux-ul post-validare. Default = 1
(închidere implicită prin validare). Setată per tip-comandă prin
`pack_auto.setOptiuneInchidere(v_tip, v_inchidere_validare)` și citită prin
`pack_auto.getOptiuneInchidere(v_tip) RETURN NUMBER`.
**Valori live confirmate 2026-04-11 de Marius (`SELECT id_tip, denumire, sters, inch_validare
FROM MARIUSM_AUTO.DEV_TIP_DEVIZ ORDER BY id_tip`):**
| id_tip | denumire | prefix | sters | inch_validare | Natura comenzii | Închidere reală |
|---|---|---|---|---|---|---|
| 1 | POST GARANTIE | *(fără)* | 0 | **0** | Se facturează client direct | prin factură (`pack_facturare.*`) → `facturat=1` |
| 2 | GARANTIE | G | 0 | **0** | Se facturează asigurator prin dosar daună | prin factură → `facturat=1` |
| 3 | REGIE | R | 0 | **1** | Auto proprii — NU se facturează | **validare directă** — `validat=1` suficient |
| 4 | PREGATIRE | P | 0 | **0** | Se facturează | prin factură → `facturat=1` |
| 5 | REGIE 2 | *(fără)* | 0 | **0** | Se facturează | prin factură → `facturat=1` |
| 6 | PRODUCTIE | PR | 0 | **1** | Producție internă — NU se facturează | **validare directă** — `validat=1` suficient |
| 7 | CONSTATARE | C | 0 | **1** | Doar diagnostic — NU se facturează | **validare directă** — `validat=1` suficient |
Prefixele sunt construite în VFP la `oproceduri_devize.prg:108-120` (hardcodate în client,
nu în DB).
**Semantica `inch_validare` (corectată 2026-04-11 după clarificare iterativă Marius):**
Flag-ul decide **dacă validarea comenzii e suficientă pentru a o considera închisă, sau
e necesară încă o notă contabilă suplimentară**.
- **`inch_validare = 1`** → **Validarea ESTE închiderea.** Comanda se consideră închisă
prin simplul fapt că `DEV_ORDL.VALIDAT = 1`. **NU** se generează nicio notă contabilă
suplimentară. Folosit tipic pentru comenzi interne (REGIE = auto proprii, PRODUCTIE,
CONSTATARE) unde nu se facturează nimic și nu se dorește impact contabil la închidere.
- **`inch_validare = 0`** → **Validarea NU e închiderea.** Comanda rămâne "deschisă"
contabil chiar și după `VALIDAT = 1`. E nevoie de o notă contabilă suplimentară pentru
a o închide real:
- Pentru tipuri **facturabile** (GARANTIE, POST GARANTIE, PREGATIRE, REGIE 2) → nota
vine prin **facturare** (`pack_facturare.*`), care setează și `facturat = 1` pe flux-ul
de agregate.
- Pentru tipuri **nefacturabile** (teoretic REGIE/PRODUCTIE dacă ar fi configurate așa) →
se generează o notă contabilă `711 = 332` care recunoaște venitul intern și închide
stocul "servicii în curs de execuție".
**Dovada în cod — view-urile `AUTO_LISTARE_MAN_TOT_COM` și `AUTO_LISTARE_MAN_TOT_DESF`**
(`mariusm_ddl_export.sql:1795-1800` și `:1842-1847`) au condiția **exactă** pentru "comandă
închisă în luna curentă":
```sql
WHERE a.sters = 0
AND ( (extract(month from a.dataoravalid) = pack_sesiune.getluna()
AND extract(year from a.dataoravalid) = pack_sesiune.getan()
AND a.validat = 1
AND e.inch_validare = 1) -- inchis prin validare singura
OR
(extract(month from b.dataact) = pack_sesiune.getluna()
AND extract(year from b.dataact) = pack_sesiune.getan()
AND b.facturat = 1
AND e.inch_validare = 0)) -- inchis prin facturare/nota
```
Cu alte cuvinte, view-ul consideră o comandă "închisă" DOAR dacă:
- **`inch_validare = 1` + `VALIDAT = 1`** (calea A: validarea închide) **SAU**
- **`inch_validare = 0` + `FACTURAT = 1`** (calea B: trebuie factură/notă de închidere)
**Configurare live MARIUSM_AUTO (2026-04-11):**
| Tip comandă | Configurare | Efect |
|---|---|---|
| REGIE, PRODUCTIE, CONSTATARE | `inch_validare = 1` | Validarea **e** închiderea. Zero notă contabilă. |
| POST GARANTIE, GARANTIE, PREGATIRE, REGIE 2 | `inch_validare = 0` | Validarea nu e suficientă. Trebuie factură. |
Marius poate schimba configurarea per tip — ex: dacă schimbă REGIE la `inch_validare = 0`,
atunci comenzile REGIE nu se mai consideră închise la validare, ci necesită o notă contabilă
`711 = 332` suplimentară înainte ca `FACTURAT = 1` să apară în agregat.
**`dev_arhiveaza_comanda` este o procedură complet separată** (setează `INCHIS_FORTAT = 1`),
apelată doar pentru a abandona forțat o comandă — nu e legată de `inch_validare`. Folosit
doar pentru cazuri excepționale (client a renunțat, constatare fără urmare tehnică, etc.).
**Implicație pentru prototype (creare only):** irelevant pentru scope wall. **Critic pentru
phase 2+** când se implementează închiderea comenzii: UI-ul trebuie să știe că validarea
singură NU închide în toate cazurile, și să navigheze la facturare sau notă contabilă de
închidere în funcție de `tip.inch_validare`.
---
## 7. Pachetul principal `pack_auto` — map procedure → tabel
Map complet (din `docs/service-auto/pack_auto.pck`):
### 7.1. Create comandă
| Procedură | Operație |
|---|---|
| `dev_adauga_lucrare` (15 params, overload scurt) | Wrapper peste versiunea lungă. Apelează versiunea cu 17 params cu defaults pe `pnOreFct`, `pcObservatii`, `pcDefectiuni`. |
| `dev_adauga_lucrare` (17 params, overload lung) | **Flux real:** (1) INSERT `NOM_LUCRARI(nrord, id_mod=1200)`; (2) agregă `solicitari_client` din `DEV_NOM_NORME.DENOP` pentru id-urile din CSV; (3) INSERT `DEV_ORDL` cu toate câmpurile; (4) UPDATE `DEV_MASINICLIENTI` SET `kmint`, `ore_functionare`. Verifică mai întâi că `NRORD` nu e duplicat pe `NOM_LUCRARI` (aruncă `ORA-20000`). |
**Dependențe runtime ale versiunii lungi:**
- `pack_sesiune.dev_idLucrare` — populat de `TRG_NOM_LUCRARI_BEFOINS`, citit intern la step 3
- `pack_contafin.getCotaTVAStandard(luna, an)` — pentru `proc_tvav` (citit din `CALENDAR` / config lunar)
- `STRINGAGG(DENOP)` agregat peste `DEV_NOM_NORME.ID_NORME IN (...)` unde `...` provine din parsarea CSV
- (Oracle 19c+ are `STRINGAGG` ca user-defined aggregate; verifică dacă există în MARIUSM_AUTO)
### 7.2. Normare operații
| Procedură | Operație |
|---|---|
| `dev_adauga_operatie(v_gcs, id_sectie, id_ordl, pret, id_util, timpn, id_norme, csv_id_mecanic, an?, luna?)` | **(1)** Citește `DEV_ORDL.VALIDAT` → dacă = 1, aruncă `ORA-20000 Comanda validata nu se mai poate modifica`. **(2)** INSERT `DEV_OPER` cu `validat=0, nou=1`. **(3)** Sparge `csv_id_mecanic` pe `;` → insert multiplu în `DEV_OPER_MECANICI` cu `ore = timpn / count(mecanici)`. |
| `dev_actualizeaza_operatie(v_gcs, camp, valoare_noua, id_oper, luna, an)` | Verifică `VALIDAT=0`. Actualizează `PRET` sau `TIMPN`. Suportă tranziția RON/ROL: dacă `DATAI < get_lunaron()` și luna-țintă ≥ get_lunaron() atunci `valoare_noua * 10000`. |
| `dev_valideaza_operatii(v_gcs, id_ordl, id_util)` | Verifică mai întâi `DEV_ORDL.VALIDAT = 0`. Setează `VALIDAT=1, ID_UTIL, DATAORA=SYSDATE` pe toate liniile din `DEV_OPER WHERE id_ordl = X AND sters=0 AND validat=0`. |
| `valideaza_operatie(schema, validat, id_oper, id_util)` | Setează manual `VALIDAT` pe o singură operație. |
| `sterge_operatie(schema, id_oper, id_util)` | Soft delete: `sters=1, dataoras=SYSDATE`. |
| `modifica_sectie(schema, id_sectie, id_oper, id_util)` | UPDATE secție pe operație. |
| `dev_distribuie_timp_n(v_luna, v_an)` | **MERGE** peste `DEV_OPER_MECANICI`: pentru operațiile unde `sum(ore_mecanici) <> timpn`, redistribuie uniform. Folosit ca housekeeping periodic. |
**View-ul UI pentru normare:** `AUTO_NORMARE_COMENZI` — filtrat pe
`inchis_fortat = 0 AND validat = 0` (din `AUTO_COMENZI_VALIDATE`).
### 7.3. Validare comandă
| Procedură | Operație |
|---|---|
| `dev_valideaza_comanda(v_gcs, id_ordl, id_util, an?, luna?)` | **(1)** Verifică `DEV_OPER WHERE id_ordl = X AND sters=0 AND validat=0` → dacă există, aruncă `ORA-20000 Mai sunt operatii nevalidate`. **(2)** Calculează `ldData` = SYSDATE sau ultima zi a lunii vechi. **(3)** Verifică `CALENDAR` că luna e deschisă. **(4)** UPDATE `DEV_ORDL SET VALIDAT=1, ID_UTIL_VALID, DATAORAVALID=ldData, PROC_TVAV = pack_contafin.getCotaTVAStandard(luna, an)`. |
| `dev_invalideaza_comanda(v_gcs, id_ordl, id_utils)` | Reverse: `VALIDAT=0, DATAORAVALID=NULL`, recalculează `PROC_TVAV` din luna/an curentă a comenzii. |
| `dev_arhiveaza_comanda(id_ordl, id_util)` | **Alternativă la validare** — pentru tipuri cu `inch_validare=0`. Verifică `CALENDAR` luna deschisă, setează `INCHIS_FORTAT=1, ID_UTIL_INCHIS, DATAORAINCHIS = SYSDATE`. |
| `dev_dezarhiveaza_comanda(id_ordl)` | Reverse: `INCHIS_FORTAT=0`. |
| `dev_sterge_comanda(schema, id_util, id_ordl, id_lucrare)` | Verifică că `RUL WHERE id_lucrare = X AND sters=0` e gol la sum(cante*pretv). Verifică `DEV_OPER WHERE id_ordl = X AND sters=0` = 0. Setează `sters=1` pe `DEV_ORDL` și `inactiv=1` pe `NOM_LUCRARI`. |
**View-ul UI:** `AUTO_VALIDARE_COMENZI` — JOIN peste `AUTO_COMENZI_VALIDATE` +
`AUTO_VORDL_MAN` (manoperă agregată) + `AUTO_VORDL_MAT` (materiale agregate) +
`AUTO_VORDL_FACTURI` (info factură emisă).
**Flux de validare condiționat:** tipul comenzii (`dev_tip_deviz.inch_validare`) decide care
dintre `dev_valideaza_comanda` (dacă `inch_validare=1`) sau `dev_arhiveaza_comanda`
(dacă `inch_validare=0`) se apelează la închiderea comenzii. View-ul `AUTO_COMENZI_VALIDATE`
are chiar o logică "time-aware": consideră o comandă validată **doar dacă** `dataoravalid`
e ≤ luna curentă din `pack_sesiune.getluna() + pack_sesiune.getan() * 12`. Validările
în viitor apar ca `validat=0` în view, ceea ce e intenționat pentru izolare contabilă
pe lună.
### 7.4. Facturare
| Procedură | Operație |
|---|---|
| `dev_adauga_oper_fact(schema, id_ordl, id_sectie, id_norme, total_f_tva, timpn, ore, id_util, id_mecanic)` | Folosit pentru tipuri speciale ITP/SPALARE/DIVERSE unde se adaugă o operație "sintetică" direct la facturare. Setează `DEV_ORDL.VALIDAT=1` + INSERT în `DEV_OPER` + `DEV_OPER_MECANICI`. `V_PRET = total_f_tva / timpn` (calcul reverse pentru a păstra total exact). |
| `actualizeaza_deviz(proc_tvav, csv_id_ordl, id_set)` | **Apelat la final de facturare**. (1) Ia ultimul `ID_FACT` din `ACT WHERE cod = current_cod AND scd NOT LIKE '5%'`. (2) UPDATE `DEV_ORDL SET PROC_TVAV` pentru id-urile din CSV. (3) UPDATE `RUL SET ID_FACT = v_id_fact WHERE id_set <> 229 AND id_lucrare IN (...)` (index hint `IDX_RUL_001`). (4) Pentru `id_set` în (31003-31011), UPDATE și `NOM_LUCRARI SET ID_FACT`. |
| `citeste_sume_stornare(csv_id_lucrare, delim, cursor OUT)` | Citește sume storno avans pentru listele CSV de lucrări. |
| `citeste_date_avans(csv_id_lucrare, delim, cursor OUT)` | Citește avansurile pentru facturare. |
| `citeste_discount_contract / salveaza_discount_contract` | Citește / salvează discount-uri contract per partener + sucursală. |
**Flux facturare real (VFP `factureaza_comanda` → `factureaza_deviz` →
`pack_facturare.*`):**
```
1. [pack_auto.dev_adauga_lucrare] — uneori se creează o comandă ad-hoc pentru facturi
ITP/SPALARE/DIVERSE (altfel se pleacă de la o comandă existentă)
2. User completează date factură (client, valoare, TVA, incasare) în VFP form
3. [lans(lnIdSet, ...)] — VFP generează note contabile pentru factură în cursor actactan
4. [verificare_note_contabile()] — validare locală VFP
5. [pack_auto.dev_adauga_oper_fact] — pentru tipuri ITP/SPALARE/DIVERSE, adaugă operația sintetică
6. [factureaza_deviz]:
a. [pack_facturare.initializeaza_date_factura(...)] → INSERT în VANZARI header (20+ param)
b. pentru fiecare articol în crsvanztemp:
[pack_facturare.adauga_articol_factura_deviz(...)] → INSERT în VANZARI_DETALII
c. [oscrie_in_fisiere(...)] — scrie cursor actactan în ACT (registrul jurnal)
d. [pack_facturare.scrie_in_vanzari(...)] → finalizare VANZARI (incasare, discount, delegat)
apelează în aceeași tranzacție:
[pack_auto.actualizeaza_deviz(proc_tvav, csv_id_ordl, id_set)]
→ UPDATE DEV_ORDL.PROC_TVAV + RUL.ID_FACT + NOM_LUCRARI.ID_FACT (pentru id_set specifice)
e. MERGE INTO vanzari SET id_fact FROM act WHERE cod=v.cod AND scd='4111'
7. COMMIT sau ROLLBACK
```
**Tabele atinse la facturare:** `VANZARI`, `VANZARI_DETALII`, `ACT`, `DEV_ORDL.PROC_TVAV`,
`RUL.ID_FACT` (pentru materiale consumate), `NOM_LUCRARI.ID_FACT`, `ACT.ID_FACT`
(prin pack_facturare). **IN SCOPE abia pentru phase 3-4**, după ce prototype-ul probează
crearea unei comenzi simple.
### 7.5. Alte operații
- `modifica_date_comanda(id_ordl, id_tip, kmint, ore_functionare, id_util)` — edit rapid
- `modifica_tip_comanda(schema, id_tip, id_ordl, id_util)` — schimbă tip
- `modifica_client_comanda(schema, id_ordl, id_masiniclient, id_util)` — schimbă mașina
---
## 8. Bonuri consum = **flow generic ROA, în `RUL.id_lucrare`**
`pack_auto` **nu are** nicio procedură pentru bonuri consum. Materialele consumate pe
comandă sunt **100% în tabela generică `RUL`** (rulaje ROA), tag-uite prin
`RUL.ID_LUCRARE = X`. Flux-ul:
1. VFP `ointroduceri.prg` cu `tip=3` (BON CONSUM) deschide un form de introducere articole
din stoc → user selectează linii + alege comanda (id_lucrare) → salvare.
2. Generic ROA warehouse SP generează rânduri în `RUL` cu:
- `ID_LUCRARE = X` (tag-ul comenzii)
- `SCD = '3xx'` (contul de stoc), `SCC = '6xx'` sau `'332'` (contul de destinație)
- `CANTE`, `PRETV` (cantitate + preț unitar)
- `ID_SET` care tipizează operațiunea de stoc
3. **La afișarea devizului** (view-urile `AUTO_VORDL_MAT`, `AUTO_VALIDARE_COMENZI`),
materialele se citesc ca agregat din `MV_ORDL_MAT` (materialized view peste `RUL`)
pe `id_lucrare`.
4. **La facturare**, `factureaza_deviz` citește suma materialelor din cursoarele `actactan`
+ `vanzari` (generate de `lans(lnIdSet...)` local în VFP), apoi
`pack_auto.actualizeaza_deviz` face `UPDATE RUL SET ID_FACT = :v_id_fact
WHERE ID_LUCRARE IN (...) AND ID_SET <> 229` — marchează materialele ca facturate.
**Impact asupra DEV_OPER:** zero. Pentru service auto, `DEV_OPER` conține **doar manoperă**;
materialele nu intră în DEV_OPER deloc.
**Implicație pentru prototype:** Bonul de consum este **out of scope**. Flux-ul atinge
pachete generice ROA (neauditate) + tabela `RUL` (nu e în scheme ROAAUTO dedicate). Dacă
phase 2 cere "service auto complet", bonul de consum va fi un sub-modul separat cu propriul
audit asupra pachetului generic ROA `pack_intrari_iesiri` (sau similar).
**Observație specială pentru tipurile nefacturabile (`inch_validare = 1` în live MARIUSM_AUTO):**
REGIE/PRODUCTIE/CONSTATARE se închid **prin simpla validare** — zero notă contabilă.
Bonurile de consum materiale rămân în `RUL` cu `ID_FACT = NULL` permanent (fără a fi legate
de vreo factură), dar comanda e considerată "închisă" în agregatele de raportare. Impactul
contabil al materialelor consumate pe aceste comenzi se face prin flux-ul generic ROA de
rulaje (stocul e deja scăzut prin `scd='3xx', scc='6xx'` la momentul bonului de consum).
**Dacă Marius ar schimba REGIE la `inch_validare = 0`** (scenariu teoretic), atunci pentru
a închide o comandă REGIE ar trebui:
1. Validare (`VALIDAT = 1`) — doar un pas intermediar
2. O notă contabilă suplimentară `711 = 332` (care recunoaște venitul intern aferent
materialelor deja consumate) — această notă trebuie să seteze `facturat = 1` sau un
mecanism echivalent ca să apară în agregatele view-ului
3. Abia atunci view-ul `AUTO_LISTARE_MAN_TOT_COM` consideră comanda "închisă"
Acest scenariu nu e activ în configurarea curentă, dar e permis de schema.
---
## 9. View-uri cheie pentru UI service auto
Toate definite în `mariusm_ddl_export.sql` (secțiunea VIEWS). Cele relevante pentru ecrane:
| View | Rol | JOIN-uri principale |
|---|---|---|
| `AUTO_COMENZI_VALIDATE` | Base view — "comenzi cu time-aware validat flag" (validat = 1 doar dacă `dataoravalid` ≤ luna curentă session) | `dev_ordl` + `syn_utilizatori` + `nom_lucrari` |
| `AUTO_NORMARE_COMENZI` | Listare comenzi care așteaptă normare operații. **Filtru tipic:** `inchis_fortat=0 AND validat=0` | `auto_comenzi_validate` + `auto_vordl_facturi` + `dev_masiniclienti` + `nom_parteneri` + `dev_tip_deviz` |
| `AUTO_VALIDARE_COMENZI` | Listare comenzi în curs de validare + manopera/materiale agregate | `auto_comenzi_validate` + `auto_vordl_man` + `auto_vordl_mat` + `auto_vordl_facturi` + lookup-uri |
| `AUTO_ORDL_FACTURARE` | Listare comenzi pregătite pentru facturare | `dev_ordl` + `nom_lucrari` + `dev_masiniclienti` + `nom_parteneri` + `dev_nom_masini` + `dev_nom_marci` + `dev_nom_asiguratori` + `dev_nom_inspectori` WHERE `sters = 0` |
| `AUTO_VORDL` | Listare completă comandă (toate câmpurile expuse) | ~10 JOIN-uri |
| `AUTO_VORDL_MAN` | Agregat manoperă pe comandă (sum după `pack_sesiune` RON/ROL) | derivat |
| `AUTO_VORDL_MAT` | Agregat materiale pe comandă — **folosește `MV_ORDL_MAT` (materialized view)** cu pack_sesiune currency logic | `MV_ORDL_MAT` grupat pe `id_lucrare` |
| `AUTO_VORDL_FACTURI` | Facturile emise per `id_lucrare` | derivat |
| `AUTO_VESTIMARI_REP` | Linii estimare pentru un deviz pre-sale | `dev_estimari_rep` + `dev_nom_norme` + `nom_articole` + `syn_utilizatori` |
| `AUTO_VOPER` | Liniile de operație cu lookup norme + ansamblu + mașină + secție + mecanic | `dev_oper` + `dev_nom_norme` + `dev_nom_ansamble` + `dev_nom_masini` + `nom_sectii` + `syn_utilizatori` |
| `AUTO_VOPER_DETALII_M` | Detalii mecanici per operație | `dev_oper_mecanici` + `dev_mecanici` + `nom_parteneri` |
| `AUTO_VMASINICLIENTI` | Lista mașini + clienți pentru dropdown-uri | `dev_masiniclienti` + `dev_nom_masini` + `dev_nom_marci` + `nom_parteneri` |
| `AUTO_VNORME` | Catalog norme manoperă pentru picker | `dev_nom_norme` + `dev_nom_masini` + `dev_nom_ansamble` |
**Notă `pack_sesiune` currency logic:** multe view-uri (`AUTO_VOPER`, `AUTO_VORDL_MAT`)
conțin `CASE WHEN pack_sesiune.getAn()*12 + getLuna() >= GET_ANRON()*12 + GET_LUNARON() THEN ron ELSE rol END`.
Acesta e un heritage-currency switch (RON denominat 2005 vs vechiul ROL) — după 2005 toate
înregistrările noi sunt RON, deci în practică întotdeauna se ia branch-ul RON. **Pentru
prototype modern, acest CASE e un artefact istoric** care nu trebuie replicat — noi inserăm
direct RON fără switch.
---
## 10. Flux end-to-end real (VFP → DB)
### 10.1. Creare comandă
```
[VFP: frm_gencom] Submit
VFP SQL: begin pack_auto.dev_adauga_lucrare(
gcS, gnAn, gnLuna, gnIdUtil,
pcNr, -- 'G12345' / 'R...'
pnIdInsp,
pnIdAsig, pcNrDosar,
pnIdMC, pnKmInt, pnOreFct,
to_date(pdTermen, 'DD/MM/YYYY'),
pntipcom,
pcSirIDOperatiiClient,
pcObservatii, pcDefectiuni,
pnIdPartRef,
?@pnIdOrdl); end;
PACK_AUTO.DEV_ADAUGA_LUCRARE (overload 17 params):
1. SELECT COUNT(*) FROM NOM_LUCRARI WHERE sters=0 AND nrord=pcNr
→ IF > 0 THEN RAISE ORA-20000 'Mai exista o lucrare...'
2. INSERT INTO NOM_LUCRARI (nrord, id_mod) VALUES (pcNr, 1200)
→ TRG_NOM_LUCRARI_BEFOINS populează :new.id_lucrare din SEQ_NOM_LUCRARI
+ pack_sesiune.dev_idLucrare := :new.id_lucrare
3. IF pcSirIdOperatii IS NOT NULL THEN
SELECT STRINGAGG(DENOP) INTO v_solicitari_client FROM DEV_NOM_NORME
WHERE id_norme IN (<parsed CSV>)
4. INSERT INTO DEV_ORDL (an, luna, id_inspector, id_lucrare, datai,
id_util_ad, id_masiniclient, id_asigurator, id_tip, kmint,
ore_functionare, termen, nr_dosar, dataoraad, solicitari_client,
observatii, defectiuni, proc_tvav, id_part_ref)
VALUES (tnAn, tnLuna, pnIdInsp,
pack_sesiune.dev_idLucrare, -- <-- citit din session state populat la pasul 2
SYSDATE, tnIdUtil, pnIdMC, pnIdAsig, pnTipCom, pnKmInt,
pnOreFct, pdTermen, pcNrDosar, SYSDATE, v_solicitari_client,
pcObservatii, pcDefectiuni,
pack_contafin.getCotaTVAStandard(tnLuna, tnAn),
decode(pnIdPartRef, 0, NULL, pnIdPartRef))
RETURNING id_ordl INTO pnIdOrdl
→ TRG_DEV_ORDL_BEFOINS populează :new.id_ordl din SEQ_DEV_ORDL
+ pack_sesiune.dev_idOrdl := :new.id_ordl
5. UPDATE DEV_MASINICLIENTI SET kmint, ore_functionare WHERE id_masiniclient=pnIdMC
```
**Nu touch-uiește ACT, nici RUL, nici VANZARI.** Facturare e un flux separat.
### 10.2. Normare operații (adaugă manoperă pe o comandă existentă)
```
[VFP: normare_comanda()] deschide FRM_NOM_COM cu cursor AUTO_NORMARE_COMENZI
User selectează o comandă + alege norme din AUTO_VNORME + alocă mecanici
VFP SQL: begin pack_auto.dev_adauga_operatie(
gcS, pnid_sectie, pnIdOrdl, pnpret, gnIdUtil,
pntimpn, pnid_norme, pcsir_idmecanic); end;
PACK_AUTO.DEV_ADAUGA_OPERATIE:
1. SELECT VALIDAT FROM DEV_ORDL WHERE id_ordl = X
→ IF validat=1 THEN RAISE ORA-20000 'Comanda validata nu mai poate fi modificata'
2. INSERT INTO DEV_OPER (id_ordl, id_sectie, timpn, pret, id_norme,
id_utili, nou, datai, dataora)
VALUES (..., 1, SYSDATE, SYSDATE)
RETURNING id_oper INTO v_id_oper
→ TRG_DEV_OPER_BEFOINS populează :new.id_oper din SEQ_dev_oper
3. PARSE csv_id_mecanic pe ';' → FOR i IN 1..count LOOP
INSERT INTO DEV_OPER_MECANICI (id_oper, id_mecanic, ore)
VALUES (v_id_oper, id_mec_i, timpn / count)
```
### 10.3. Validare operații (marchează fiecare operație ca "gata")
```
[VFP user click "Valideaza operatii"] pe o comandă
VFP SQL: begin pack_auto.dev_valideaza_operatii(gcS, pnIdOrdl, gnIdUtil); end;
PACK_AUTO.DEV_VALIDEAZA_OPERATII:
1. SELECT VALIDAT FROM DEV_ORDL WHERE id_ordl = X
→ IF validat=1 THEN RAISE ORA-20000 'Comanda validata...'
2. UPDATE DEV_OPER SET validat=1, id_util, dataora=SYSDATE
WHERE id_ordl = X AND sters=0 AND validat=0
```
**Alternativ, linie cu linie:**
```
begin pack_auto.dev_valideaza_operatie(gcS, pn_id_oper, gnIdUtil); end;
→ UPDATE DEV_OPER SET VALIDAT=1 WHERE id_oper=X
```
### 10.4. Validare comandă (finalizează comanda după ce toate operațiile sunt validate)
```
[VFP user click "Valideaza comanda"]
VFP SQL: begin pack_auto.dev_valideaza_comanda(gcS, pnIdOrdl, gnIdUtil, an?, luna?); end;
PACK_AUTO.DEV_VALIDEAZA_COMANDA:
1. SELECT COUNT(*) FROM DEV_OPER WHERE id_ordl = X AND sters=0 AND validat=0
→ IF > 0 THEN RAISE ORA-20000 'Mai sunt operatii nevalidate'
2. Calculează ldData: SYSDATE dacă luna curentă = luna_comandă, else LAST_DAY
3. SELECT COUNT(*) FROM CALENDAR WHERE an=.. AND luna=..
→ IF 0 THEN RAISE ORA-20000 'Nu puteti valida deoarece nu este deschisa luna'
4. UPDATE DEV_ORDL SET VALIDAT=1, ID_UTIL_VALID, DATAORAVALID=ldData,
PROC_TVAV = pack_contafin.getCotaTVAStandard(luna, an)
WHERE id_ordl = X
```
### 10.5. Închidere forțată (arhivare — pentru tipuri cu `inch_validare=0`)
```
[VFP user click "Inchide comanda" pe tip POST_GARANTIE/etc.]
VFP SQL: begin pack_auto.dev_arhiveaza_comanda(pnIdOrdl, gnIdUtil); end;
PACK_AUTO.DEV_ARHIVEAZA_COMANDA:
1. Verifică CALENDAR luna curentă deschisă
2. UPDATE DEV_ORDL SET INCHIS_FORTAT=1, ID_UTIL_INCHIS, DATAORAINCHIS=SYSDATE
```
### 10.6. Facturare
Vezi secțiunea 7.4 de mai sus. IN SCOPE abia pentru phase 3-4.
### 10.7. Bonuri consum
Vezi secțiunea 8. Out of scope — e flux generic ROA.
---
## 11. Labelurile UI (Romanian)
| Câmp DB | Label UI (din VFP ground truth) |
|---|---|
| `id_tip` | **Tip comandă** |
| `nrinmat` (via masiniclienti) | **Număr înmatriculare** / **Mașină** |
| `id_part` (via masiniclienti.id_partener) | **Client** |
| `solicitari_client` (derivat din norme) | **Operații solicitate** |
| `kmint` | **Km la bord** |
| `ore_functionare` | **Ore funcționare** (pt utilitare) |
| `termen` | **Termen execuție** |
| `observatii` | **Observații** |
| `defectiuni` | **Defecțiuni** |
| `nr_dosar` | **Nr. dosar** (doar pentru tip=garanție) |
| `id_inspector` | **Inspector** |
| `id_asigurator` | **Asigurator** |
| `id_part_ref` | **Partener referent** |
| `id_agent` | **Agent vânzări** |
**Toast-uri (convenție ROA2WEB din `ReceiptCreateUnifiedView.vue`):**
- Success: `summary="Comandă creată"`, `detail="Nr ${nr_comanda}"`
- Business error: `summary="Validare"`, `detail=<mesaj SP după strip prefix ORA-20xxx>`
- Infra: `summary="Eroare conexiune"` (503) / `"Eroare internă"` (500)
---
## 12. Implicații pentru prototype-ul roa2web
### 12.1. Scope wall reconfirmat
Prototype-ul scrie **1 SP minimal pentru 1 acțiune = creare comandă**. Tot restul (normare,
validare operații/comandă, arhivare, facturare, bonuri consum, estimare) e **out of scope**.
### 12.2. Opțiunea 3 (SP nou minimal) — actualizată
Față de v2, acum știm **exact** ce face `pack_auto.dev_adauga_lucrare` versiunea lungă.
Versiunea minimală a SP-ului nostru va:
```sql
CREATE OR REPLACE PROCEDURE MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP(
p_id_tip IN NUMBER, -- FK dev_tip_deviz.id_tip
p_id_masiniclient IN NUMBER, -- FK dev_masiniclienti.id_masiniclient
p_kmint IN NUMBER,
p_termen IN DATE,
p_id_util IN NUMBER,
p_nr_comanda IN VARCHAR2, -- generat în Python cu prefix (G/R/P/PR/C)
p_id_ordl OUT NUMBER,
p_numar_out OUT VARCHAR2
) AS
v_id_lucrare NUMBER;
v_nr_existent NUMBER;
v_now DATE := SYSDATE;
BEGIN
-- 1. Duplicate-check (paritate cu pack_auto)
SELECT COUNT(*) INTO v_nr_existent
FROM MARIUSM_AUTO.NOM_LUCRARI
WHERE sters = 0 AND nrord = p_nr_comanda;
IF v_nr_existent > 0 THEN
RAISE_APPLICATION_ERROR(-20001,
'Mai exista o comanda cu numarul ' || p_nr_comanda);
END IF;
-- 2. Parent INSERT (trigger-ul alocă id_lucrare + populează pack_sesiune.dev_idLucrare)
INSERT INTO MARIUSM_AUTO.NOM_LUCRARI (nrord, id_mod)
VALUES (p_nr_comanda, 1200)
RETURNING id_lucrare INTO v_id_lucrare;
-- 3. Child INSERT minimal — doar coloane obligatorii + clientul (prin masiniclient)
INSERT INTO MARIUSM_AUTO.DEV_ORDL (
an, luna,
id_lucrare,
datai, dataoraad,
id_util_ad,
id_masiniclient,
id_tip,
kmint,
termen
) VALUES (
EXTRACT(YEAR FROM v_now), EXTRACT(MONTH FROM v_now),
v_id_lucrare,
v_now, v_now,
p_id_util,
p_id_masiniclient,
p_id_tip,
p_kmint,
p_termen
) RETURNING id_ordl INTO p_id_ordl;
p_numar_out := p_nr_comanda;
END;
/
```
**~30 linii PL/SQL, două INSERT-uri, zero dependency pe `pack_contafin` / `STRINGAGG` / `pack_sesiune`** — minim absolut ca să probeze ipoteza #1 (Python + oracledb + OUT params + PL/SQL procedure call pe un INSERT dual cu RETURNING).
**Trigger-ele rulează oricum** — `pack_sesiune.dev_idLucrare` și `dev_idOrdl` vor fi populate, dar nu le citim. E trivial de demonstrat că e safe: pattern-ul Oracle `RETURNING INTO` e standard.
**Coloane SĂRITE pe care SP-ul real le are:**
- `id_inspector`, `id_asigurator` — folosite doar pentru service cu asigurator
- `nr_dosar`, `pcNrDosar` — dosar daună
- `ore_functionare`, `pnOreFct` — pentru utilitare
- `solicitari_client` (agregat din STRINGAGG) — derivat din CSV, dispensabil pentru prototype
- `observatii`, `defectiuni` — free text opțional
- `id_part_ref` — referent
- `proc_tvav` (via `pack_contafin.getCotaTVAStandard`) — dacă e NOT NULL vor primi default implicit (NULL); dacă schema real e NOT NULL, adăugăm valoarea 19 hardcodat
**De verificat în săpt 3:** rulează `DESC MARIUSM_AUTO.DEV_ORDL` sau din `mariusm_ddl_export.sql`
linia 565 — `PROC_TVAV` e `NUMBER(10,4)` nullable, deci OK.
### 12.3. GRANTS pentru ROA_WEB user
```sql
CREATE USER ROA_WEB IDENTIFIED BY "...";
GRANT CONNECT, CREATE SESSION TO ROA_WEB;
GRANT EXECUTE ON MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP TO ROA_WEB;
-- Pentru read-only selectors (ipoteza #3 = grants model funcționează)
GRANT SELECT ON MARIUSM_AUTO.AUTO_VMASINICLIENTI TO ROA_WEB;
GRANT SELECT ON MARIUSM_AUTO.DEV_TIP_DEVIZ TO ROA_WEB;
```
### 12.4. Test negativ pentru ipoteza #3
```sql
-- Connected as ROA_WEB:
INSERT INTO MARIUSM_AUTO.DEV_ORDL ... ; -- așteptat: ORA-00942
SELECT * FROM MARIUSM_AUTO.DEV_ORDL WHERE ROWNUM < 2; -- așteptat: ORA-00942
EXEC MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP(...); -- așteptat: succes + row inserted
```
---
## 13. Gap-uri rămase (de acoperit după sapt 3)
- `PACK_SESIUNE.pck` citit — de confirmat că `dev_idLucrare`, `dev_idOrdl`, `getAn`, `getLuna`,
`GET_ANRON`, `GET_LUNARON`, `get_cod`, `get_id_util` sunt toate exported și nu ascund
ceva surprinzător (nu am citit încă body-ul, doar referințe).
- `PACK_CONTAFIN.pck` citit — de confirmat `getCotaTVAStandard(luna, an)` și `get_idFact()`.
- `PACK_FACTURARE.pck` citit — necesar doar când intrăm în flux-ul de facturare (phase 3-4).
- `DEV_TIP_DEVIZ.inch_validare` per rând — rulează query-ul de verificare:
```sql
SELECT id_tip, denumire, sters, inch_validare FROM MARIUSM_AUTO.DEV_TIP_DEVIZ ORDER BY id_tip;
```
- `MV_ORDL_MAT` materialized view — de verificat refresh schedule; dacă refresh e "on commit"
atunci UPDATE-urile pe RUL sunt imediate în AUTO_VORDL_MAT; dacă e "on demand" cu DBMS_JOB
atunci e latency în UI.
- `pack_audit.verifica_val` — folosit de toate trigger-ele `_BEFOUPD`. E dependency ascuns
pentru orice UPDATE pe `NOM_LUCRARI`, `DEV_ORDL`, `DEV_OPER`, etc. Nu e folosit la INSERT,
deci prototype-ul (care doar inserează) nu-l atinge.
---
## 14. Referințe
- **Ground truth DDL:** `docs/service-auto/mariusm_ddl_export.sql` (5127 linii)
- **Pachete production:** `pack_auto.pck`, `PACK_FACTURARE.pck`, `PACK_SESIUNE.pck`,
`PACK_CONTAFIN.pck`, `PACK_COMENZI.pck` (toate în `docs/service-auto/`)
- **VFP consumer code:** `gitea.romfast.ro/romfast/vfp_roaauto/Programe/oproceduri_devize.prg`
(`generare_com`, `normare_comanda`, `factureaza_comanda`, `factureaza_deviz`)
- **Manual utilizator:** `docs/service-auto/manual_ROAAUTO.pdf`
- **Plan master:** `docs/service-auto/claude-main-design-20260411-rethink.md`