diff --git a/api/app/services/price_sync_service.py b/api/app/services/price_sync_service.py index 22f7847..26fe89a 100644 --- a/api/app/services/price_sync_service.py +++ b/api/app/services/price_sync_service.py @@ -154,6 +154,12 @@ async def run_catalog_price_sync(run_id: str): if is_kit: for comp in mapped_data[sku]: comp_codmat = comp["codmat"] + + # Skip components that have their own ARTICOLE_TERTI mapping + # (they'll be synced with correct cantitate_roa in individual path) + if comp_codmat in mapped_data: + continue + comp_product = products_by_sku.get(comp_codmat) if not comp_product: continue # Component not in GoMag as standalone product diff --git a/api/app/services/validation_service.py b/api/app/services/validation_service.py index 735c23d..317e45d 100644 --- a/api/app/services/validation_service.py +++ b/api/app/services/validation_service.py @@ -542,8 +542,9 @@ def sync_prices_from_order(orders, mapped_codmat_data: dict, direct_id_map: dict kit_discount_codmat = (settings or {}).get("kit_discount_codmat", "") skip_codmats = {transport_codmat, discount_codmat, kit_discount_codmat} - {""} - # Build set of kit SKUs (>1 component) - kit_skus = {sku for sku, comps in mapped_codmat_data.items() if len(comps) > 1} + # Build set of kit/bax SKUs (>1 component, or single component with cantitate_roa > 1) + kit_skus = {sku for sku, comps in mapped_codmat_data.items() + if len(comps) > 1 or (len(comps) == 1 and (comps[0].get("cantitate_roa") or 1) > 1)} updated = [] own_conn = conn is None diff --git a/api/database-scripts/06_pack_import_comenzi.pck b/api/database-scripts/06_pack_import_comenzi.pck index e2a5015..3d66e6f 100644 --- a/api/database-scripts/06_pack_import_comenzi.pck +++ b/api/database-scripts/06_pack_import_comenzi.pck @@ -66,6 +66,7 @@ -- 20.03.2026 - kit pricing extins pt reambalari single-component (cantitate_roa > 1) -- 21.03.2026 - diagnostic detaliat discount kit (id_pol, id_art, codmat in eroare) -- 21.03.2026 - fix discount amount: v_disc_amt e per-kit, nu se imparte la v_cantitate_web +-- 25.03.2026 - skip negative kit discount (markup), ROUND prices to nzecimale_pretv -- ==================================================================== CREATE OR REPLACE PACKAGE PACK_IMPORT_COMENZI AS @@ -284,6 +285,9 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS v_kit_disc_count PLS_INTEGER := 0; v_kit_disc_found BOOLEAN; + -- Zecimale pret vanzare (din optiuni firma, default 2) + v_nzec_pretv PLS_INTEGER := NVL(TO_NUMBER(pack_sesiune.getoptiunefirma(USER, 'PPRETV')), 2); + -- pljson l_json_articole CLOB := p_json_articole; v_json_arr pljson_list; @@ -492,8 +496,10 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS END IF; -- pret_ajustat = pret_cu_tva - discount_share / cantitate_roa - v_pret_ajustat := v_kit_comps(i_comp).pret_cu_tva - - (v_discount_share / v_kit_comps(i_comp).cantitate_roa); + v_pret_ajustat := ROUND( + v_kit_comps(i_comp).pret_cu_tva - + (v_discount_share / v_kit_comps(i_comp).cantitate_roa), + v_nzec_pretv); BEGIN merge_or_insert_articol( @@ -578,8 +584,8 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS v_vat_disc_alloc := v_vat_disc_alloc + v_disc_amt; END IF; - IF v_disc_amt != 0 THEN - v_unit_pret := v_disc_amt; + IF v_disc_amt > 0 THEN + v_unit_pret := ROUND(v_disc_amt, v_nzec_pretv); -- Search for existing entry with same (ptva, pret) to merge qty v_kit_disc_found := FALSE; diff --git a/api/database-scripts/09_articole_terti_050.sql b/api/database-scripts/09_articole_terti_050.sql new file mode 100644 index 0000000..e6a9f51 --- /dev/null +++ b/api/database-scripts/09_articole_terti_050.sql @@ -0,0 +1,54 @@ +-- ==================================================================== +-- 09_articole_terti_050.sql +-- Mapări ARTICOLE_TERTI cu cantitate_roa = 0.5 pentru articole +-- unde unitatea web (50 buc/set) ≠ unitatea ROA (100 buc/set). +-- +-- Efect: price sync va calcula pret_crm = pret_web / 0.5, +-- iar kit pricing va folosi prețul corect per set ROA. +-- +-- 25.03.2026 - creat pentru fix discount negativ kit pahare +-- ==================================================================== + +-- Pahar 6oz Coffee Coffee SIBA 50buc (GoMag) → 100buc/set (ROA) +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '1708828', '1708828', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '1708828' AND codmat = '1708828' AND sters = 0 +); + +-- Pahar 8oz Coffee Coffee SIBA 50buc → 100buc/set +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '528795', '528795', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '528795' AND codmat = '528795' AND sters = 0 +); + +-- Pahar 8oz Tchibo 50buc → 100buc/set +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '58', '58', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '58' AND codmat = '58' AND sters = 0 +); + +-- Pahar 7oz Lavazza SIBA 50buc → 100buc/set +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '51', '51', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '51' AND codmat = '51' AND sters = 0 +); + +-- Pahar 8oz Albastru JND 50buc → 100buc/set +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '105712338826', '105712338826', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '105712338826' AND codmat = '105712338826' AND sters = 0 +); + +-- Pahar 8oz Paris JND 50buc → 100buc/set +INSERT INTO articole_terti (sku, codmat, cantitate_roa, activ, sters, data_creare, id_util_creare) +SELECT '10573080', '10573080', 0.5, 1, 0, SYSDATE, -3 FROM dual +WHERE NOT EXISTS ( + SELECT 1 FROM articole_terti WHERE sku = '10573080' AND codmat = '10573080' AND sters = 0 +); + +COMMIT; diff --git a/docs/oracle-schema-notes.md b/docs/oracle-schema-notes.md new file mode 100644 index 0000000..6f3491f --- /dev/null +++ b/docs/oracle-schema-notes.md @@ -0,0 +1,122 @@ +# Oracle Schema Notes — MARIUSM_AUTO + +Reference pentru tabelele, procedurile și relațiile Oracle descoperite în debugging. + +## Tabele comenzi + +### COMENZI +| Coloană | Tip | Notă | +|---|---|---| +| ID_COMANDA | NUMBER (PK) | Auto-generated | +| COMANDA_EXTERNA | VARCHAR2 | Nr. comandă GoMag (ex: 481588552) | +| DATA_COMANDA | DATE | | +| ID_PART | NUMBER | FK → NOM_PARTENERI | +| PROC_DISCOUNT | NUMBER(10,4) | Discount procentual pe comandă (setat 0 la import) | +| STERS | NUMBER | Soft-delete flag | + +### COMENZI_ELEMENTE +| Coloană | Tip | Notă | +|---|---|---| +| ID_COMANDA_ELEMENT | NUMBER (PK) | Auto-generated | +| ID_COMANDA | NUMBER | FK → COMENZI | +| ID_ARTICOL | NUMBER | FK → NOM_ARTICOLE | +| ID_POL | NUMBER | FK → CRM_POLITICI_PRETURI | +| PRET | NUMBER(14,3) | Preț per unitate (cu/fără TVA per PRET_CU_TVA flag) | +| CANTITATE | NUMBER(14,3) | Cantitate (negativă pentru discount lines) | +| DISCOUNT_UNITAR | NUMBER(20,4) | Default 0 | +| PTVA | NUMBER | Procentul TVA (11, 21, etc.) | +| PRET_CU_TVA | NUMBER(1) | 1 = prețul include TVA | +| STERS | NUMBER | Soft-delete flag | + +**Discount lines**: qty negativă, pret pozitiv. Ex: qty=-1, pret=51.56 → scade 51.56 din total. + +## Tabele facturare + +### VANZARI +| Coloană | Tip | Notă | +|---|---|---| +| ID_VANZARE | NUMBER (PK) | | +| NUMAR_ACT | NUMBER | Număr factură (nract) | +| SERIE_ACT | VARCHAR2 | Serie factură | +| TIP | NUMBER | 3=factură pe bază de comandă, 1=factură simplă | +| ID_COMANDA | NUMBER | FK → COMENZI (pentru TIP=3) | +| ID_PART | NUMBER | FK → NOM_PARTENERI | +| TOTAL_FARA_TVA | NUMBER | Total calculat de pack_facturare | +| TOTAL_TVA | NUMBER | | +| TOTAL_CU_TVA | NUMBER | | +| DIFTOTFTVA | NUMBER | Diferența față de totalul trimis de client ROAFACTUARE | +| DIFTOTTVA | NUMBER | | +| STERS | NUMBER | | + +### VANZARI_DETALII +| Coloană | Tip | Notă | +|---|---|---| +| **ID_VANZARE_DET** | NUMBER (PK) | ⚠ NU `id_detaliu`! | +| ID_VANZARE | NUMBER | FK → VANZARI | +| ID_ARTICOL | NUMBER | FK → NOM_ARTICOLE | +| CANTITATE | NUMBER | | +| PRET | NUMBER | Preț de vânzare | +| PRET_ACHIZITIE | NUMBER | | +| PROC_TVAV | NUMBER | Coeficient TVA (1.21, 1.11, etc.) | +| ID_GESTIUNE | NUMBER | NULL pentru discount lines | +| CONT | VARCHAR2 | '371', NULL pentru discount lines | +| STERS | NUMBER | | + +## Tabele prețuri + +### CRM_POLITICI_PRETURI +| Coloană | Tip | Notă | +|---|---|---| +| ID_POL | NUMBER (PK) | ID politică de preț | +| PRETURI_CU_TVA | NUMBER | 1 = prețurile includ TVA | + +### CRM_POLITICI_PRET_ART +| Coloană | Tip | Notă | +|---|---|---| +| ID_POL | NUMBER | FK → CRM_POLITICI_PRETURI | +| ID_ARTICOL | NUMBER | FK → NOM_ARTICOLE | +| PRET | NUMBER | Preț de listă (cu/fără TVA per PRETURI_CU_TVA din politică) | +| PROC_TVAV | NUMBER | Coeficient TVA | + +Politici folosite: id_pol=39 (vânzare), id_pol=65 (transport). + +### ARTICOLE_TERTI +| Coloană | Tip | Notă | +|---|---|---| +| SKU | VARCHAR2 | SKU din magazin web (GoMag) | +| CODMAT | VARCHAR2 | CODMAT în ROA (FK → NOM_ARTICOLE.CODMAT) | +| CANTITATE_ROA | NUMBER | Conversie: 1 web unit = X ROA units | +| ACTIV | NUMBER | | +| STERS | NUMBER | | + +**cantitate_roa semnificații**: +- `1` → 1:1 (unitate identică web/ROA) +- `0.5` → 1 web unit (50 buc) = 0.5 ROA set (100 buc). Price sync: `pret_web / 0.5` +- `10` → bax 1000buc = 10 seturi ROA (100 buc). Kit pricing activ. +- `22.5` → bax 2250buc = 22.5 seturi ROA (100 buc). Kit pricing activ. + +## Proceduri cheie + +### PACK_COMENZI.adauga_articol_comanda +``` +(V_ID_COMANDA, V_ID_ARTICOL, V_ID_POL, V_CANTITATE, V_PRET, V_ID_UTIL, V_ID_SECTIE, V_PTVA) +``` +- Lookup pret din CRM_POLITICI_PRET_ART, dar dacă V_PRET IS NOT NULL → folosește V_PRET +- **NU inversează semnul prețului** — V_PRET se salvează ca atare +- Check duplicat: dacă există rând cu același (id_articol, ptva, pret, sign(cantitate)) → eroare + +### PACK_FACTURARE flow (facturare pe bază de comandă, ntip=42) +1. `cursor_comanda` → citește COMENZI_ELEMENTE, filtrează `SIGN(A.CANTITATE) * (A.CANTITATE - NVL(D.CANTITATE, 0)) > 0` +2. `cursor_gestiuni_articol` → verifică stoc per articol +3. `initializeaza_date_factura` → setează sesiune facturare +4. `adauga_articol_factura` (×N) → inserează în VANZARI_DETALII_TEMP +5. `scrie_factura2` → procesează temp, contabilizează +6. `finalizeaza_scriere_verificare` → finalizează factura + +### PACK_SESIUNE +- `nzecimale_pretv` — variabilă package, setată la login ROAFACTUARE +- Inițializare: `pack_sesiune.getoptiunefirma(USER, 'PPRETV')` = **2** (pe MARIUSM_AUTO) +- **Nu e setată** în context server-side (import comenzi) → folosim `getoptiunefirma` direct + +### OPTIUNI (tabel configurare) +- Coloane: `VARNAME`, `VARVALUE` (⚠ NU `cod`/`valoare`)