feat(pricing): kit/pachet pricing with price list lookup, replace procent_pret
- Oracle PL/SQL: kit pricing logic with Mode A (distributed discount) and
Mode B (separate discount line), dual policy support, PRETURI_CU_TVA flag
- Eliminate procent_pret from entire stack (Oracle, Python, JS, HTML)
- New settings: kit_pricing_mode, kit_discount_codmat, price_sync_enabled
- Settings UI: cards for Kit Pricing and Price Sync configuration
- Mappings UI: kit badges with lazy-loaded component prices from price list
- Price sync from orders: auto-update ROA prices when web prices differ
- Catalog price sync: new service to sync all GoMag product prices to ROA
- Kit component price validation: pre-check prices before import
- New endpoint GET /api/mappings/{sku}/prices for component price display
- New endpoints POST /api/price-sync/start, GET status, GET history
- DDL script 07_drop_procent_pret.sql (run after deploy confirmation)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -465,6 +465,7 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
if item.sku in validation["mapped"]:
|
||||
mapped_skus_in_orders.add(item.sku)
|
||||
|
||||
mapped_codmat_data = {}
|
||||
if mapped_skus_in_orders:
|
||||
mapped_codmat_data = await asyncio.to_thread(
|
||||
validation_service.resolve_mapped_codmats, mapped_skus_in_orders, conn
|
||||
@@ -501,6 +502,33 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
# Pass codmat_policy_map to import via app_settings
|
||||
if codmat_policy_map:
|
||||
app_settings["_codmat_policy_map"] = codmat_policy_map
|
||||
|
||||
# ── Kit component price validation ──
|
||||
kit_pricing_mode = app_settings.get("kit_pricing_mode")
|
||||
if kit_pricing_mode and mapped_codmat_data:
|
||||
id_pol_prod = int(app_settings.get("id_pol_productie") or 0) or None
|
||||
kit_missing = await asyncio.to_thread(
|
||||
validation_service.validate_kit_component_prices,
|
||||
mapped_codmat_data, id_pol, id_pol_prod, conn
|
||||
)
|
||||
if kit_missing:
|
||||
kit_skus_missing = set(kit_missing.keys())
|
||||
for sku, missing_codmats in kit_missing.items():
|
||||
_log_line(run_id, f"Kit {sku}: prețuri lipsă pentru {', '.join(missing_codmats)}")
|
||||
new_truly = []
|
||||
for order in truly_importable:
|
||||
order_skus = {item.sku for item in order.items}
|
||||
if order_skus & kit_skus_missing:
|
||||
missing_list = list(order_skus & kit_skus_missing)
|
||||
skipped.append((order, missing_list))
|
||||
else:
|
||||
new_truly.append(order)
|
||||
truly_importable = new_truly
|
||||
|
||||
# Mode B config validation
|
||||
if kit_pricing_mode == "separate_line":
|
||||
if not app_settings.get("kit_discount_codmat"):
|
||||
_log_line(run_id, "EROARE: Kit mode 'separate_line' dar kit_discount_codmat nu e configurat!")
|
||||
finally:
|
||||
await asyncio.to_thread(database.pool.release, conn)
|
||||
|
||||
@@ -565,6 +593,28 @@ async def run_sync(id_pol: int = None, id_sectie: int = None, run_id: str = None
|
||||
})
|
||||
_log_line(run_id, f"#{order.number} [{order.date or '?'}] {customer} → OMIS (lipsa: {', '.join(missing_skus)})")
|
||||
await sqlite_service.save_orders_batch(skipped_batch)
|
||||
|
||||
# ── Price sync from orders ──
|
||||
if app_settings.get("price_sync_enabled") == "1":
|
||||
try:
|
||||
all_sync_orders = truly_importable + already_in_roa
|
||||
direct_id_map = validation.get("direct_id_map", {})
|
||||
id_pol_prod = int(app_settings.get("id_pol_productie") or 0) or None
|
||||
price_updates = await asyncio.to_thread(
|
||||
validation_service.sync_prices_from_order,
|
||||
all_sync_orders, mapped_codmat_data,
|
||||
direct_id_map, codmat_policy_map, id_pol,
|
||||
id_pol_productie=id_pol_prod,
|
||||
settings=app_settings
|
||||
)
|
||||
if price_updates:
|
||||
_log_line(run_id, f"Sync prețuri: {len(price_updates)} prețuri actualizate")
|
||||
for pu in price_updates:
|
||||
_log_line(run_id, f" {pu['codmat']}: {pu['old_price']:.2f} → {pu['new_price']:.2f}")
|
||||
except Exception as e:
|
||||
_log_line(run_id, f"Eroare sync prețuri din comenzi: {e}")
|
||||
logger.error(f"Price sync error: {e}")
|
||||
|
||||
_update_progress("skipped", f"Skipped {skipped_count}",
|
||||
0, len(truly_importable),
|
||||
{"imported": 0, "skipped": skipped_count, "errors": 0, "already_imported": already_imported_count})
|
||||
|
||||
Reference in New Issue
Block a user