fix(sync): prevent kit/bax price sync from overwriting individual CRM prices
Three code paths could overwrite CRM list prices with wrong values when web unit (50 buc) differs from ROA unit (100 buc): - price_sync_service: kit path now skips components that have their own ARTICOLE_TERTI mapping (individual path handles them with correct ÷0.5) - validation_service: sync_prices_from_order now skips bax SKUs (cantitate_roa > 1) in addition to multi-component kits - pack_import_comenzi: skip negative kit discount (markup), ROUND prices to nzecimale_pretv decimals Also adds: - SQL script for 6 ARTICOLE_TERTI mappings (cantitate_roa=0.5) for cup articles where web=50buc, ROA=100buc/set - Oracle schema reference documentation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -154,6 +154,12 @@ async def run_catalog_price_sync(run_id: str):
|
|||||||
if is_kit:
|
if is_kit:
|
||||||
for comp in mapped_data[sku]:
|
for comp in mapped_data[sku]:
|
||||||
comp_codmat = comp["codmat"]
|
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)
|
comp_product = products_by_sku.get(comp_codmat)
|
||||||
if not comp_product:
|
if not comp_product:
|
||||||
continue # Component not in GoMag as standalone product
|
continue # Component not in GoMag as standalone product
|
||||||
|
|||||||
@@ -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", "")
|
kit_discount_codmat = (settings or {}).get("kit_discount_codmat", "")
|
||||||
skip_codmats = {transport_codmat, discount_codmat, kit_discount_codmat} - {""}
|
skip_codmats = {transport_codmat, discount_codmat, kit_discount_codmat} - {""}
|
||||||
|
|
||||||
# Build set of kit SKUs (>1 component)
|
# 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}
|
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 = []
|
updated = []
|
||||||
own_conn = conn is None
|
own_conn = conn is None
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
-- 20.03.2026 - kit pricing extins pt reambalari single-component (cantitate_roa > 1)
|
-- 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 - 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
|
-- 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
|
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_count PLS_INTEGER := 0;
|
||||||
v_kit_disc_found BOOLEAN;
|
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
|
-- pljson
|
||||||
l_json_articole CLOB := p_json_articole;
|
l_json_articole CLOB := p_json_articole;
|
||||||
v_json_arr pljson_list;
|
v_json_arr pljson_list;
|
||||||
@@ -492,8 +496,10 @@ CREATE OR REPLACE PACKAGE BODY PACK_IMPORT_COMENZI AS
|
|||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
-- pret_ajustat = pret_cu_tva - discount_share / cantitate_roa
|
-- pret_ajustat = pret_cu_tva - discount_share / cantitate_roa
|
||||||
v_pret_ajustat := v_kit_comps(i_comp).pret_cu_tva -
|
v_pret_ajustat := ROUND(
|
||||||
(v_discount_share / v_kit_comps(i_comp).cantitate_roa);
|
v_kit_comps(i_comp).pret_cu_tva -
|
||||||
|
(v_discount_share / v_kit_comps(i_comp).cantitate_roa),
|
||||||
|
v_nzec_pretv);
|
||||||
|
|
||||||
BEGIN
|
BEGIN
|
||||||
merge_or_insert_articol(
|
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;
|
v_vat_disc_alloc := v_vat_disc_alloc + v_disc_amt;
|
||||||
END IF;
|
END IF;
|
||||||
|
|
||||||
IF v_disc_amt != 0 THEN
|
IF v_disc_amt > 0 THEN
|
||||||
v_unit_pret := v_disc_amt;
|
v_unit_pret := ROUND(v_disc_amt, v_nzec_pretv);
|
||||||
|
|
||||||
-- Search for existing entry with same (ptva, pret) to merge qty
|
-- Search for existing entry with same (ptva, pret) to merge qty
|
||||||
v_kit_disc_found := FALSE;
|
v_kit_disc_found := FALSE;
|
||||||
|
|||||||
54
api/database-scripts/09_articole_terti_050.sql
Normal file
54
api/database-scripts/09_articole_terti_050.sql
Normal file
@@ -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;
|
||||||
122
docs/oracle-schema-notes.md
Normal file
122
docs/oracle-schema-notes.md
Normal file
@@ -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`)
|
||||||
Reference in New Issue
Block a user