cleanup: remove 5 duplicate scripts from scripts/

Removed scripts covered by more complete alternatives:
- match_by_price.py, match_invoices.py → covered by match_all.py
- compare_detail.py → covered by match_all.py
- reset_sqlite.py → covered by delete_imported.py (includes Oracle + dry-run)
- debug_match.py → one-off hardcoded debug script

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-03-17 12:23:40 +00:00
parent 7a2408e310
commit 06f8fa5842
5 changed files with 0 additions and 1335 deletions

View File

@@ -1,325 +0,0 @@
"""
Generate detailed comparison CSV: GoMag orders vs Oracle invoices
Side-by-side view for manual analysis.
"""
import oracledb
import os
import sys
import sqlite3
import csv
from difflib import SequenceMatcher
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
os.environ['PATH'] = r'C:\app\Server\product\18.0.0\dbhomeXE\bin' + ';' + os.environ.get('PATH','')
oracledb.init_oracle_client()
# --- Load GoMag orders ---
db = sqlite3.connect(r'C:\gomag-vending\api\data\import.db')
db.row_factory = sqlite3.Row
c = db.cursor()
c.execute("""
SELECT order_number, order_date, customer_name, status,
id_comanda, order_total, billing_name, shipping_name
FROM orders ORDER BY order_date DESC
""")
orders = [dict(r) for r in c.fetchall()]
for order in orders:
c.execute("""
SELECT sku, product_name, quantity, price, vat, mapping_status
FROM order_items WHERE order_number = ? ORDER BY sku
""", (order['order_number'],))
order['items'] = [dict(r) for r in c.fetchall()]
db.close()
print(f"Loaded {len(orders)} GoMag orders")
# --- Load Oracle invoices ---
conn = oracledb.connect(user='VENDING', password='ROMFASTSOFT', dsn='ROA')
cur = conn.cursor()
min_date = min(str(o['order_date'])[:10] for o in orders)
max_date = max(str(o['order_date'])[:10] for o in orders)
cur.execute("""
SELECT v.id_vanzare, v.numar_act, v.serie_act,
TO_CHAR(v.data_act, 'YYYY-MM-DD') as data_act,
v.total_fara_tva, v.total_cu_tva, v.id_part,
p.denumire as partener, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.sters = 0
AND v.data_act >= TO_DATE(:1, 'YYYY-MM-DD') - 3
AND v.data_act <= TO_DATE(:2, 'YYYY-MM-DD') + 3
AND v.total_cu_tva > 0
ORDER BY v.data_act DESC
""", [min_date, max_date])
invoices = []
inv_map = {}
for r in cur:
inv = {
'id_vanzare': r[0], 'numar_act': r[1], 'serie_act': r[2] or '',
'data_act': r[3], 'total_fara_tva': float(r[4] or 0),
'total_cu_tva': float(r[5] or 0), 'id_part': r[6],
'partener': ((r[7] or '') + ' ' + (r[8] or '')).strip(),
'items': [],
}
invoices.append(inv)
inv_map[inv['id_vanzare']] = inv
# Batch fetch details
inv_ids = [inv['id_vanzare'] for inv in invoices]
for i in range(0, len(inv_ids), 500):
batch = inv_ids[i:i+500]
placeholders = ",".join([f":d{j}" for j in range(len(batch))])
params = {f"d{j}": did for j, did in enumerate(batch)}
cur.execute(f"""
SELECT vd.id_vanzare, vd.id_articol, a.codmat, a.denumire,
vd.cantitate, vd.pret, vd.pret_cu_tva, vd.proc_tvav
FROM vanzari_detalii vd
LEFT JOIN nom_articole a ON vd.id_articol = a.id_articol
WHERE vd.id_vanzare IN ({placeholders}) AND vd.sters = 0
ORDER BY vd.id_vanzare, vd.id_articol
""", params)
for r in cur:
inv_map[r[0]]['items'].append({
'id_articol': r[1], 'codmat': r[2], 'denumire': r[3],
'cantitate': float(r[4] or 0), 'pret': float(r[5] or 0),
'pret_cu_tva': float(r[6] or 0), 'tva_pct': float(r[7] or 0),
})
conn.close()
print(f"Loaded {len(invoices)} Oracle invoices")
# --- Fuzzy matching orders → invoices ---
def normalize_name(name):
if not name:
return ''
n = name.strip().upper()
for old, new in [('S.R.L.', 'SRL'), ('S.R.L', 'SRL'), ('SC ', ''), ('PFA ', ''), ('PF ', '')]:
n = n.replace(old, new)
return n
def name_similarity(n1, n2):
nn1 = normalize_name(n1)
nn2 = normalize_name(n2)
if not nn1 or not nn2:
return 0
sim1 = SequenceMatcher(None, nn1, nn2).ratio()
words1 = nn1.split()
if len(words1) >= 2:
reversed1 = ' '.join(reversed(words1))
sim2 = SequenceMatcher(None, reversed1, nn2).ratio()
return max(sim1, sim2)
return sim1
matches = []
used_invoices = set()
orders_sorted = sorted(orders, key=lambda o: -(o['order_total'] or 0))
for order in orders_sorted:
best_match = None
best_score = 0
order_date = str(order['order_date'])[:10]
order_total = order['order_total'] or 0
order_name = order['customer_name'] or ''
for inv in invoices:
if inv['id_vanzare'] in used_invoices:
continue
try:
od = int(order_date.replace('-',''))
id_ = int(inv['data_act'].replace('-',''))
date_diff = abs(od - id_)
except:
continue
if date_diff > 3:
continue
total_diff = abs(order_total - inv['total_cu_tva'])
total_pct = total_diff / max(order_total, 0.01) * 100
if total_pct > 15 and total_diff > 15:
continue
sim = name_similarity(order_name, inv['partener'])
sim2 = name_similarity(order.get('billing_name') or '', inv['partener'])
sim3 = name_similarity(order.get('shipping_name') or '', inv['partener'])
sim = max(sim, sim2, sim3)
date_score = 1 if date_diff == 0 else (0.7 if date_diff == 1 else (0.4 if date_diff == 2 else 0.2))
total_score = 1 - min(total_pct / 100, 1)
score = sim * 0.45 + total_score * 0.40 + date_score * 0.15
if score > best_score:
best_score = score
best_match = inv
if best_match and best_score > 0.45:
matches.append({'order': order, 'invoice': best_match, 'score': best_score})
used_invoices.add(best_match['id_vanzare'])
else:
matches.append({'order': order, 'invoice': None, 'score': 0})
# Sort by order date
matches.sort(key=lambda m: str(m['order']['order_date']), reverse=True)
# --- Write detailed comparison CSV ---
out_dir = r'C:\gomag-vending\scripts\output'
os.makedirs(out_dir, exist_ok=True)
with open(os.path.join(out_dir, 'comparatie_detaliata.csv'), 'w', newline='', encoding='utf-8-sig') as f:
w = csv.writer(f, delimiter=';')
w.writerow([
'NR_COMANDA_GOMAG', 'DATA_COMANDA', 'CLIENT_GOMAG', 'STATUS_IMPORT',
'TOTAL_COMANDA_GOMAG',
'NR_ARTICOLE_GOMAG', 'SKU_GOMAG', 'PRODUS_GOMAG', 'QTY_GOMAG', 'PRET_GOMAG', 'TVA_GOMAG',
'LINIE_TOTAL_GOMAG',
'|',
'FACTURA_ROA', 'DATA_FACTURA', 'CLIENT_ROA', 'TOTAL_FACTURA_ROA',
'NR_ARTICOLE_ROA', 'CODMAT_ROA', 'PRODUS_ROA', 'QTY_ROA', 'PRET_ROA', 'TVA_ROA',
'LINIE_TOTAL_ROA',
'|',
'MATCH_SCORE', 'DIFF_TOTAL', 'SKU_EQ_CODMAT',
])
for m in matches:
o = m['order']
inv = m['invoice']
go_items = o['items']
roa_items = inv['items'] if inv else []
# Filter out transport/discount for comparison count
roa_real = [ri for ri in roa_items if ri['codmat'] not in ('TRANSPORT', 'DISCOUNT', None, '') and ri['cantitate'] > 0]
roa_extra = [ri for ri in roa_items if ri['codmat'] in ('TRANSPORT', 'DISCOUNT') or ri['cantitate'] < 0]
max_lines = max(len(go_items), len(roa_items), 1)
order_total = o['order_total'] or 0
inv_total = inv['total_cu_tva'] if inv else 0
diff_total = round(order_total - inv_total, 2) if inv else ''
for idx in range(max_lines):
gi = go_items[idx] if idx < len(go_items) else None
ri = roa_items[idx] if idx < len(roa_items) else None
# GoMag side
if idx == 0:
go_order = o['order_number']
go_date = str(o['order_date'])[:10]
go_client = o['customer_name'] or ''
go_status = o['status']
go_total = order_total
go_nr_art = len(go_items)
else:
go_order = ''
go_date = ''
go_client = ''
go_status = ''
go_total = ''
go_nr_art = ''
if gi:
go_sku = gi['sku'] or ''
go_prod = gi['product_name'] or ''
go_qty = gi['quantity']
go_price = gi['price']
go_vat = gi['vat']
go_line_total = round(gi['quantity'] * gi['price'], 2)
else:
go_sku = go_prod = go_qty = go_price = go_vat = go_line_total = ''
# ROA side
if idx == 0 and inv:
roa_fact = f"{inv['serie_act']}{inv['numar_act']}"
roa_date = inv['data_act']
roa_client = inv['partener']
roa_total = inv_total
roa_nr_art = len(roa_items)
else:
roa_fact = ''
roa_date = ''
roa_client = ''
roa_total = ''
roa_nr_art = ''
if ri:
roa_codmat = ri['codmat'] or ''
roa_prod = ri['denumire'] or ''
roa_qty = ri['cantitate']
roa_price = ri['pret']
roa_vat = ri['tva_pct']
roa_line_total = round(ri['cantitate'] * ri['pret'], 2) if ri['cantitate'] > 0 else round(-ri['cantitate'] * ri['pret'], 2)
else:
roa_codmat = roa_prod = roa_qty = roa_price = roa_vat = roa_line_total = ''
# Match indicators
if idx == 0:
score = round(m['score'], 2) if m['score'] else ''
diff = diff_total
else:
score = ''
diff = ''
# Check SKU == CODMAT
sku_eq = ''
if gi and ri and go_sku and roa_codmat:
if go_sku == roa_codmat:
sku_eq = 'DA'
else:
sku_eq = ''
w.writerow([
go_order, go_date, go_client, go_status,
go_total,
go_nr_art, go_sku, go_prod, go_qty, go_price, go_vat,
go_line_total,
'|',
roa_fact, roa_date, roa_client, roa_total,
roa_nr_art, roa_codmat, roa_prod, roa_qty, roa_price, roa_vat,
roa_line_total,
'|',
score, diff, sku_eq,
])
# Empty separator row before unmatched invoice summary
w.writerow([])
w.writerow(['--- FACTURI ROA FARA COMANDA GOMAG ---'])
w.writerow([])
unmatched_inv = [inv for inv in invoices if inv['id_vanzare'] not in used_invoices]
unmatched_inv.sort(key=lambda x: x['data_act'], reverse=True)
for inv in unmatched_inv:
for idx, ri in enumerate(inv['items']):
if idx == 0:
w.writerow([
'', '', '', '', '', '', '', '', '', '', '', '',
'|',
f"{inv['serie_act']}{inv['numar_act']}", inv['data_act'],
inv['partener'], inv['total_cu_tva'],
len(inv['items']),
ri['codmat'] or '', ri['denumire'] or '',
ri['cantitate'], ri['pret'], ri['tva_pct'],
round(ri['cantitate'] * ri['pret'], 2),
'|', '', '', '',
])
else:
w.writerow([
'', '', '', '', '', '', '', '', '', '', '', '',
'|',
'', '', '', '',
'',
ri['codmat'] or '', ri['denumire'] or '',
ri['cantitate'], ri['pret'], ri['tva_pct'],
round(ri['cantitate'] * ri['pret'], 2),
'|', '', '', '',
])
print(f"\nDone!")
print(f"Matched: {sum(1 for m in matches if m['invoice'])} / {len(orders)} orders")
print(f"Unmatched invoices: {len(unmatched_inv)}")
print(f"\nOutput: {os.path.join(out_dir, 'comparatie_detaliata.csv')}")
print(f"Open in Excel (separator: ;, encoding: UTF-8)")

View File

@@ -1,112 +0,0 @@
"""Debug matching for specific order 480102897"""
import oracledb
import os
import sys
import sqlite3
from difflib import SequenceMatcher
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
os.environ['PATH'] = r'C:\app\Server\product\18.0.0\dbhomeXE\bin' + ';' + os.environ.get('PATH','')
oracledb.init_oracle_client()
# Get GoMag order
db = sqlite3.connect(r'C:\gomag-vending\api\data\import.db')
db.row_factory = sqlite3.Row
c = db.cursor()
c.execute("SELECT * FROM orders WHERE order_number = '480102897'")
order = dict(c.fetchone())
c.execute("SELECT * FROM order_items WHERE order_number = '480102897'")
items = [dict(r) for r in c.fetchall()]
db.close()
print(f"=== GoMag Order 480102897 ===")
print(f" Client: {order['customer_name']}")
print(f" Billing: {order['billing_name']}")
print(f" Shipping: {order['shipping_name']}")
print(f" Date: {order['order_date']}")
print(f" Total: {order['order_total']}")
print(f" Status: {order['status']}")
print(f" Items ({len(items)}):")
for it in items:
print(f" SKU={it['sku']:20s} qty={it['quantity']:6.1f} price={it['price']:8.2f} {it['product_name']}")
# Now search Oracle for ALL invoices around that date with similar total
conn = oracledb.connect(user='VENDING', password='ROMFASTSOFT', dsn='ROA')
cur = conn.cursor()
order_date = str(order['order_date'])[:10]
order_total = order['order_total']
print(f"\n=== Oracle invoices near date {order_date}, total ~{order_total} ===")
cur.execute("""
SELECT v.id_vanzare, v.numar_act, v.serie_act,
TO_CHAR(v.data_act, 'YYYY-MM-DD') as data_act,
v.total_cu_tva, v.id_part,
p.denumire as partener, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.sters = 0
AND v.data_act >= TO_DATE(:1, 'YYYY-MM-DD') - 3
AND v.data_act <= TO_DATE(:2, 'YYYY-MM-DD') + 3
AND v.total_cu_tva > 0
ORDER BY ABS(v.total_cu_tva - :3), v.data_act
""", [order_date, order_date, order_total])
print(f"{'NR_FACT':>8s} {'SERIE':>5s} {'DATA':>12s} {'TOTAL_CU':>10s} {'DIFF':>10s} {'PARTENER':40s}")
candidates = []
for r in cur:
total = float(r[4] or 0)
diff = total - order_total
partener = ((r[6] or '') + ' ' + (r[7] or '')).strip()
print(f"{str(r[1]):>8s} {str(r[2] or ''):>5s} {r[3]:>12s} {total:10.2f} {diff:+10.2f} {partener:40s}")
candidates.append({'numar_act': r[1], 'serie_act': r[2], 'data_act': r[3],
'total': total, 'partener': partener, 'id_vanzare': r[0]})
if len(candidates) >= 20:
break
# Also search by client name
print(f"\n=== Oracle invoices by name 'STOICA' or 'LIVIU' near that date ===")
cur.execute("""
SELECT v.id_vanzare, v.numar_act, v.serie_act,
TO_CHAR(v.data_act, 'YYYY-MM-DD') as data_act,
v.total_cu_tva,
p.denumire as partener, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.sters = 0
AND v.data_act >= TO_DATE(:1, 'YYYY-MM-DD') - 5
AND v.data_act <= TO_DATE(:2, 'YYYY-MM-DD') + 5
AND (UPPER(p.denumire) LIKE '%STOICA%' OR UPPER(p.prenume) LIKE '%STOICA%'
OR UPPER(p.denumire) LIKE '%LIVIU%' OR UPPER(p.prenume) LIKE '%LIVIU%'
OR UPPER(p.denumire) LIKE '%SLM%')
ORDER BY v.data_act DESC
""", [order_date, order_date])
for r in cur:
total = float(r[4] or 0)
partener = ((r[5] or '') + ' ' + (r[6] or '')).strip()
print(f" {str(r[1]):>8s} {str(r[2] or ''):>5s} {r[3]:>12s} {total:10.2f} {partener}")
# Show details of invoice 4035
print(f"\n=== Details of invoice VM4035 ===")
cur.execute("""
SELECT v.id_vanzare, TO_CHAR(v.data_act, 'YYYY-MM-DD'), v.total_cu_tva,
p.denumire, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.numar_act = 4035 AND v.serie_act = 'VM' AND v.sters = 0
""")
row = cur.fetchone()
if row:
print(f" Date: {row[1]}, Total: {float(row[2]):.2f}, Client: {row[3]} {row[4]}")
cur.execute("""
SELECT a.codmat, a.denumire, vd.cantitate, vd.pret, vd.pret_cu_tva
FROM vanzari_detalii vd
LEFT JOIN nom_articole a ON vd.id_articol = a.id_articol
WHERE vd.id_vanzare = :1 AND vd.sters = 0
""", [row[0]])
for r in cur:
print(f" COD={r[0] or '':20s} qty={float(r[2]):6.1f} pret={float(r[3]):8.2f} pretcu={float(r[4]):8.2f} {r[1]}")
conn.close()

View File

@@ -1,414 +0,0 @@
"""
Match GoMag SKUs → ROA id_articol by matching order lines on unit price.
For each matched order-invoice pair, compare lines by price to discover mappings.
Output: SQL for nom_articole codmat updates + CSV for ARTICOLE_TERTI mappings.
"""
import oracledb
import os
import sys
import sqlite3
import csv
from collections import defaultdict
from difflib import SequenceMatcher
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
os.environ['PATH'] = r'C:\app\Server\product\18.0.0\dbhomeXE\bin' + ';' + os.environ.get('PATH','')
oracledb.init_oracle_client()
# --- Load GoMag orders ---
db = sqlite3.connect(r'C:\gomag-vending\api\data\import.db')
db.row_factory = sqlite3.Row
c = db.cursor()
c.execute("SELECT order_number, order_date, customer_name, status, order_total FROM orders ORDER BY order_date DESC")
orders = [dict(r) for r in c.fetchall()]
for order in orders:
c.execute("SELECT sku, product_name, quantity, price, vat FROM order_items WHERE order_number = ? ORDER BY sku", (order['order_number'],))
order['items'] = [dict(r) for r in c.fetchall()]
db.close()
print(f"Loaded {len(orders)} GoMag orders")
# --- Load Oracle invoices ---
conn = oracledb.connect(user='VENDING', password='ROMFASTSOFT', dsn='ROA')
cur = conn.cursor()
min_date = min(str(o['order_date'])[:10] for o in orders)
max_date = max(str(o['order_date'])[:10] for o in orders)
cur.execute("""
SELECT v.id_vanzare, v.numar_act, v.serie_act,
TO_CHAR(v.data_act, 'YYYY-MM-DD') as data_act,
v.total_fara_tva, v.total_cu_tva, v.id_part,
p.denumire as partener, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.sters = 0 AND v.data_act >= TO_DATE(:1, 'YYYY-MM-DD') - 3
AND v.data_act <= TO_DATE(:2, 'YYYY-MM-DD') + 3 AND v.total_cu_tva > 0
ORDER BY v.data_act DESC
""", [min_date, max_date])
invoices = []
inv_map = {}
for r in cur:
inv = {
'id_vanzare': r[0], 'numar_act': r[1], 'serie_act': r[2] or '',
'data_act': r[3], 'total_fara_tva': float(r[4] or 0),
'total_cu_tva': float(r[5] or 0), 'id_part': r[6],
'partener': ((r[7] or '') + ' ' + (r[8] or '')).strip(),
'items': [],
}
invoices.append(inv)
inv_map[inv['id_vanzare']] = inv
inv_ids = [inv['id_vanzare'] for inv in invoices]
for i in range(0, len(inv_ids), 500):
batch = inv_ids[i:i+500]
placeholders = ",".join([f":d{j}" for j in range(len(batch))])
params = {f"d{j}": did for j, did in enumerate(batch)}
cur.execute(f"""
SELECT vd.id_vanzare, vd.id_articol, a.codmat, a.denumire,
vd.cantitate, vd.pret, vd.pret_cu_tva, vd.proc_tvav
FROM vanzari_detalii vd
LEFT JOIN nom_articole a ON vd.id_articol = a.id_articol
WHERE vd.id_vanzare IN ({placeholders}) AND vd.sters = 0
ORDER BY vd.id_vanzare, vd.id_articol
""", params)
for r in cur:
inv_map[r[0]]['items'].append({
'id_articol': r[1], 'codmat': r[2], 'denumire': r[3],
'cantitate': float(r[4] or 0), 'pret': float(r[5] or 0),
'pret_cu_tva': float(r[6] or 0), 'tva_pct': float(r[7] or 0),
})
print(f"Loaded {len(invoices)} Oracle invoices")
# --- Match orders → invoices (same as before) ---
def normalize_name(name):
if not name:
return ''
n = name.strip().upper()
for old, new in [('S.R.L.', 'SRL'), ('S.R.L', 'SRL'), ('SC ', ''), ('PFA ', ''), ('PF ', '')]:
n = n.replace(old, new)
return n
def name_similarity(n1, n2):
nn1 = normalize_name(n1)
nn2 = normalize_name(n2)
if not nn1 or not nn2:
return 0
sim1 = SequenceMatcher(None, nn1, nn2).ratio()
words1 = nn1.split()
if len(words1) >= 2:
sim2 = SequenceMatcher(None, ' '.join(reversed(words1)), nn2).ratio()
return max(sim1, sim2)
return sim1
matches = []
used_invoices = set()
orders_sorted = sorted(orders, key=lambda o: -(o['order_total'] or 0))
for order in orders_sorted:
best_match = None
best_score = 0
order_date = str(order['order_date'])[:10]
order_total = order['order_total'] or 0
for inv in invoices:
if inv['id_vanzare'] in used_invoices:
continue
try:
date_diff = abs(int(order_date.replace('-','')) - int(inv['data_act'].replace('-','')))
except:
continue
if date_diff > 3:
continue
total_diff = abs(order_total - inv['total_cu_tva'])
total_pct = total_diff / max(order_total, 0.01) * 100
if total_pct > 15 and total_diff > 15:
continue
sim = name_similarity(order['customer_name'] or '', inv['partener'])
date_score = 1 if date_diff == 0 else (0.7 if date_diff == 1 else (0.4 if date_diff == 2 else 0.2))
total_score = 1 - min(total_pct / 100, 1)
score = sim * 0.45 + total_score * 0.40 + date_score * 0.15
if score > best_score:
best_score = score
best_match = inv
if best_match and best_score > 0.45:
matches.append({'order': order, 'invoice': best_match, 'score': best_score})
used_invoices.add(best_match['id_vanzare'])
print(f"Matched: {len(matches)} orders → invoices")
# --- Match line items by PRICE ---
# For each matched pair, match GoMag items → ROA items by line total (qty * price)
# Discovery: SKU → (id_articol, codmat, denumire, qty_ratio)
# Collect all discovered mappings: sku → list of observations
sku_observations = defaultdict(list)
for m in matches:
o = m['order']
inv = m['invoice']
go_items = o['items']
# Exclude transport/discount from ROA
roa_items = [ri for ri in inv['items'] if ri['cantitate'] > 0
and ri['codmat'] not in ('TRANSPORT', 'DISCOUNT')]
roa_transport = [ri for ri in inv['items']
if ri['codmat'] in ('TRANSPORT', 'DISCOUNT') or ri['cantitate'] < 0]
go_remaining = list(range(len(go_items)))
roa_remaining = list(range(len(roa_items)))
item_matches = []
# Pass 1: match by line total (qty * unit_price_fara_tva)
for gi_idx in list(go_remaining):
gi = go_items[gi_idx]
go_line = gi['quantity'] * gi['price'] # cu TVA
go_line_fara = go_line / (1 + gi['vat']/100) if gi['vat'] else go_line
for ri_idx in list(roa_remaining):
ri = roa_items[ri_idx]
roa_line = ri['cantitate'] * ri['pret'] # fara TVA
if abs(go_line_fara - roa_line) < 0.50:
item_matches.append((gi_idx, [ri_idx]))
go_remaining.remove(gi_idx)
roa_remaining.remove(ri_idx)
break
# Pass 2: match by unit price (for items where qty might differ but price is same)
for gi_idx in list(go_remaining):
gi = go_items[gi_idx]
go_price_fara = gi['price'] / (1 + gi['vat']/100) if gi['vat'] else gi['price']
for ri_idx in list(roa_remaining):
ri = roa_items[ri_idx]
if abs(go_price_fara - ri['pret']) < 0.02:
item_matches.append((gi_idx, [ri_idx]))
go_remaining.remove(gi_idx)
roa_remaining.remove(ri_idx)
break
# Pass 3: 1:1 positional if same count remaining
if len(go_remaining) == 1 and len(roa_remaining) == 1:
item_matches.append((go_remaining[0], [roa_remaining[0]]))
go_remaining = []
roa_remaining = []
# Pass 4: 1:N — one GoMag item matches multiple ROA items by combined total
for gi_idx in list(go_remaining):
gi = go_items[gi_idx]
go_line_fara = (gi['quantity'] * gi['price']) / (1 + gi['vat']/100) if gi['vat'] else gi['quantity'] * gi['price']
if len(roa_remaining) >= 2:
for i_pos, ri_idx1 in enumerate(roa_remaining):
for ri_idx2 in roa_remaining[i_pos+1:]:
ri1 = roa_items[ri_idx1]
ri2 = roa_items[ri_idx2]
combined = ri1['cantitate'] * ri1['pret'] + ri2['cantitate'] * ri2['pret']
if abs(go_line_fara - combined) < 1.0:
item_matches.append((gi_idx, [ri_idx1, ri_idx2]))
go_remaining.remove(gi_idx)
roa_remaining.remove(ri_idx1)
roa_remaining.remove(ri_idx2)
break
else:
continue
break
# Record observations
for gi_idx, ri_indices in item_matches:
gi = go_items[gi_idx]
ris = [roa_items[i] for i in ri_indices]
if len(ris) == 1:
ri = ris[0]
qty_ratio = ri['cantitate'] / gi['quantity'] if gi['quantity'] else 1
sku_observations[gi['sku']].append({
'type': 'simple' if abs(qty_ratio - round(qty_ratio)) < 0.01 and abs(qty_ratio - 1) < 0.01 else 'repack',
'id_articol': ri['id_articol'],
'codmat': ri['codmat'],
'denumire': ri['denumire'],
'go_qty': gi['quantity'],
'roa_qty': ri['cantitate'],
'qty_ratio': round(qty_ratio, 4),
'go_price': gi['price'],
'roa_pret': ri['pret'],
'product_name': gi['product_name'],
'order': o['order_number'],
'factura': f"VM{inv['numar_act']}",
})
else:
# Complex set
go_line_fara = (gi['quantity'] * gi['price']) / (1 + gi['vat']/100) if gi['vat'] else gi['quantity'] * gi['price']
for ri in ris:
ri_line = ri['cantitate'] * ri['pret']
pct = round(ri_line / go_line_fara * 100, 2) if go_line_fara else 0
qty_ratio = ri['cantitate'] / gi['quantity'] if gi['quantity'] else 1
sku_observations[gi['sku']].append({
'type': 'set',
'id_articol': ri['id_articol'],
'codmat': ri['codmat'],
'denumire': ri['denumire'],
'go_qty': gi['quantity'],
'roa_qty': ri['cantitate'],
'qty_ratio': round(qty_ratio, 4),
'procent_pret': pct,
'go_price': gi['price'],
'roa_pret': ri['pret'],
'product_name': gi['product_name'],
'order': o['order_number'],
'factura': f"VM{inv['numar_act']}",
})
conn.close()
# --- Analyze observations: find consistent mappings ---
print(f"\n{'='*80}")
print(f"ANALYSIS: {len(sku_observations)} unique SKUs with observations")
print(f"{'='*80}")
# For each SKU, check if all observations agree on the same id_articol
simple_update = {} # SKU → {id_articol, codmat, denumire} — for nom_articole UPDATE
repack_csv = {} # (SKU, codmat) → {cantitate_roa} — for ARTICOLE_TERTI
set_csv = {} # (SKU, codmat) → {cantitate_roa, procent_pret}
inconsistent = {} # SKU → list of conflicting observations
already_has_codmat = {} # SKU already equals codmat
for sku, obs_list in sorted(sku_observations.items()):
# Group by id_articol
by_articol = defaultdict(list)
for obs in obs_list:
by_articol[obs['id_articol']].append(obs)
# Check if any observation shows SKU == CODMAT already
if any(obs.get('codmat') == sku for obs in obs_list):
already_has_codmat[sku] = obs_list[0]
continue
# Filter to types
types = set(obs['type'] for obs in obs_list)
if 'set' in types:
# Complex set — collect all components
components = {}
for obs in obs_list:
if obs['type'] == 'set':
key = obs['id_articol']
if key not in components:
components[key] = obs
# Check consistency across observations
if len(components) >= 2:
for art_id, obs in components.items():
codmat = obs['codmat'] or f"ID:{art_id}"
set_csv[(sku, codmat)] = {
'id_articol': art_id,
'cantitate_roa': obs['qty_ratio'],
'procent_pret': obs['procent_pret'],
'denumire': obs['denumire'],
'product_name': obs['product_name'],
}
continue
if len(by_articol) == 1:
# All observations point to same article
art_id = list(by_articol.keys())[0]
obs = by_articol[art_id][0]
# Check qty ratios are consistent
ratios = [o['qty_ratio'] for o in by_articol[art_id]]
avg_ratio = sum(ratios) / len(ratios)
if all(abs(r - avg_ratio) < 0.01 for r in ratios):
if abs(avg_ratio - 1.0) < 0.01:
# Simple 1:1
simple_update[sku] = {
'id_articol': art_id,
'codmat_actual': obs['codmat'],
'denumire': obs['denumire'],
'product_name': obs['product_name'],
'observations': len(by_articol[art_id]),
}
else:
# Repackaging
codmat = obs['codmat'] or f"ID:{art_id}"
repack_csv[(sku, codmat)] = {
'id_articol': art_id,
'cantitate_roa': round(avg_ratio, 3),
'denumire': obs['denumire'],
'product_name': obs['product_name'],
'observations': len(by_articol[art_id]),
}
else:
inconsistent[sku] = obs_list
else:
# Multiple different articles for same SKU across orders
if len(by_articol) == 1:
pass # handled above
else:
inconsistent[sku] = obs_list
# --- Output ---
out_dir = r'C:\gomag-vending\scripts\output'
os.makedirs(out_dir, exist_ok=True)
print(f"\n{'='*80}")
print(f"RESULTS")
print(f"{'='*80}")
print(f"\n--- Already mapped (SKU == CODMAT): {len(already_has_codmat)} ---")
print(f"\n--- Simple 1:1 → UPDATE nom_articole SET codmat = SKU: {len(simple_update)} ---")
for sku, info in sorted(simple_update.items()):
print(f" {sku:25s} → id_articol={info['id_articol']:6d} codmat_actual='{info['codmat_actual'] or ''}' [{info['denumire'][:40]}] ({info['observations']} obs)")
print(f"\n--- Repackaging → ARTICOLE_TERTI: {len(repack_csv)} ---")
for (sku, codmat), info in sorted(repack_csv.items()):
print(f" {sku:25s}{codmat:15s} x{info['cantitate_roa']} id_art={info['id_articol']} [{info['denumire'][:35]}] ({info['observations']} obs)")
print(f"\n--- Complex sets → ARTICOLE_TERTI: {len(set_csv)} ---")
for (sku, codmat), info in sorted(set_csv.items()):
print(f" {sku:25s}{codmat:15s} {info['procent_pret']:6.2f}% x{info['cantitate_roa']} [{info['denumire'][:35]}]")
print(f"\n--- Inconsistent (different articles across orders): {len(inconsistent)} ---")
for sku, obs_list in sorted(inconsistent.items()):
arts = set((o['id_articol'], o['denumire'][:30]) for o in obs_list)
print(f" {sku:25s}{len(arts)} different articles: {'; '.join(f'id={a[0]}({a[1]})' for a in arts)}")
# Write SQL for simple updates
with open(os.path.join(out_dir, 'update_codmat.sql'), 'w', encoding='utf-8') as f:
f.write("-- UPDATE nom_articole: set codmat = GoMag SKU for 1:1 mappings\n")
f.write("-- Generated from invoice-order matching\n")
f.write("-- VERIFY BEFORE RUNNING!\n\n")
for sku, info in sorted(simple_update.items()):
f.write(f"-- {info['product_name'][:60]}{info['denumire'][:60]}\n")
f.write(f"-- Current codmat: '{info['codmat_actual'] or ''}' | {info['observations']} order matches\n")
f.write(f"UPDATE nom_articole SET codmat = '{sku}' WHERE id_articol = {info['id_articol']} AND sters = 0;\n\n")
# Write CSV for repackaging (ARTICOLE_TERTI format)
with open(os.path.join(out_dir, 'repack_mappings.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'codmat', 'cantitate_roa', 'procent_pret', 'id_articol', 'product_name_gomag', 'denumire_roa', 'observations'])
for (sku, codmat), info in sorted(repack_csv.items()):
w.writerow([sku, codmat, info['cantitate_roa'], 100, info['id_articol'], info['product_name'], info['denumire'], info['observations']])
# Write CSV for sets
with open(os.path.join(out_dir, 'set_mappings.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'codmat', 'cantitate_roa', 'procent_pret', 'id_articol', 'product_name_gomag', 'denumire_roa'])
for (sku, codmat), info in sorted(set_csv.items()):
w.writerow([sku, codmat, info['cantitate_roa'], info['procent_pret'], info['id_articol'], info['product_name'], info['denumire']])
# Write inconsistent for manual review
with open(os.path.join(out_dir, 'inconsistent_skus.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'product_name', 'id_articol', 'codmat', 'denumire_roa', 'qty_ratio', 'type', 'order', 'factura'])
for sku, obs_list in sorted(inconsistent.items()):
for obs in obs_list:
w.writerow([sku, obs['product_name'], obs['id_articol'], obs['codmat'] or '',
obs['denumire'], obs['qty_ratio'], obs['type'], obs['order'], obs['factura']])
print(f"\nOutput written to {out_dir}:")
print(f" update_codmat.sql - {len(simple_update)} SQL updates for nom_articole")
print(f" repack_mappings.csv - {len(repack_csv)} repackaging mappings")
print(f" set_mappings.csv - {len(set_csv)} complex set mappings")
print(f" inconsistent_skus.csv - {len(inconsistent)} SKUs needing manual review")

View File

@@ -1,442 +0,0 @@
"""
Match GoMag orders (SQLite) with manual invoices (Oracle vanzari)
by date + client name + total value.
Then compare line items to discover SKU → CODMAT mappings.
"""
import oracledb
import os
import sys
import sqlite3
import csv
from difflib import SequenceMatcher
# Fix Windows console encoding
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
os.environ['PATH'] = r'C:\app\Server\product\18.0.0\dbhomeXE\bin' + ';' + os.environ.get('PATH','')
oracledb.init_oracle_client()
# --- Step 1: Get GoMag orders from SQLite ---
print("=" * 80)
print("STEP 1: Loading GoMag orders from SQLite")
print("=" * 80)
db = sqlite3.connect(r'C:\gomag-vending\api\data\import.db')
db.row_factory = sqlite3.Row
c = db.cursor()
# Get orders with status IMPORTED or ALREADY_IMPORTED
c.execute("""
SELECT order_number, order_date, customer_name, status,
id_comanda, order_total, billing_name, shipping_name
FROM orders
WHERE status IN ('IMPORTED', 'ALREADY_IMPORTED')
AND order_date >= date('now', '-10 days')
ORDER BY order_date DESC
""")
orders = [dict(r) for r in c.fetchall()]
# Get order items
for order in orders:
c.execute("""
SELECT sku, product_name, quantity, price, vat, mapping_status
FROM order_items
WHERE order_number = ?
ORDER BY sku
""", (order['order_number'],))
order['items'] = [dict(r) for r in c.fetchall()]
db.close()
print(f"Loaded {len(orders)} GoMag orders")
for o in orders:
print(f" {o['order_number']:>10s} | {str(o['order_date'])[:10]} | {(o['customer_name'] or '')[:35]:35s} | {o['order_total'] or 0:10.2f} | {len(o['items'])} items")
# --- Step 2: Get Oracle invoices (vanzari) with detail lines ---
print()
print("=" * 80)
print("STEP 2: Loading Oracle invoices (vanzari + detalii)")
print("=" * 80)
conn = oracledb.connect(user='VENDING', password='ROMFASTSOFT', dsn='ROA')
cur = conn.cursor()
# Get vanzari header + partner name
cur.execute("""
SELECT v.id_vanzare, v.numar_act, v.serie_act,
TO_CHAR(v.data_act, 'YYYY-MM-DD') as data_act,
v.total_fara_tva, v.total_cu_tva, v.id_part,
p.denumire as partener, p.prenume
FROM vanzari v
LEFT JOIN nom_parteneri p ON v.id_part = p.id_part
WHERE v.sters = 0 AND v.data_act >= SYSDATE - 10
ORDER BY v.data_act DESC
""")
invoices = []
for r in cur:
inv = {
'id_vanzare': r[0],
'numar_act': r[1],
'serie_act': r[2],
'data_act': r[3],
'total_fara_tva': float(r[4] or 0),
'total_cu_tva': float(r[5] or 0),
'id_part': r[6],
'partener': (r[7] or '') + (' ' + r[8] if r[8] else ''),
}
invoices.append(inv)
# Get detail lines for each invoice
for inv in invoices:
cur.execute("""
SELECT vd.id_articol, a.codmat, a.denumire,
vd.cantitate, vd.pret, vd.pret_cu_tva, vd.proc_tvav
FROM vanzari_detalii vd
LEFT JOIN nom_articole a ON vd.id_articol = a.id_articol
WHERE vd.id_vanzare = :1 AND vd.sters = 0
ORDER BY vd.id_articol
""", [inv['id_vanzare']])
inv['items'] = []
for r in cur:
inv['items'].append({
'id_articol': r[0],
'codmat': r[1],
'denumire': r[2],
'cantitate': float(r[3] or 0),
'pret': float(r[4] or 0),
'pret_cu_tva': float(r[5] or 0),
'tva_pct': float(r[6] or 0),
})
conn.close()
print(f"Loaded {len(invoices)} Oracle invoices")
for inv in invoices:
print(f" {inv['serie_act']}{str(inv['numar_act']):>6s} | {inv['data_act']} | {inv['partener'][:35]:35s} | {inv['total_cu_tva']:10.2f} | {len(inv['items'])} items")
# --- Step 3: Fuzzy matching ---
print()
print("=" * 80)
print("STEP 3: Matching orders → invoices (date + name + total)")
print("=" * 80)
def normalize_name(name):
if not name:
return ''
return name.strip().upper().replace('S.R.L.', 'SRL').replace('S.R.L', 'SRL')
def name_similarity(n1, n2):
return SequenceMatcher(None, normalize_name(n1), normalize_name(n2)).ratio()
matches = []
unmatched_orders = []
used_invoices = set()
for order in orders:
best_match = None
best_score = 0
order_date = str(order['order_date'])[:10]
order_total = order['order_total'] or 0
order_name = order['customer_name'] or ''
for inv in invoices:
if inv['id_vanzare'] in used_invoices:
continue
# Date match (must be same day or +/- 1 day)
inv_date = inv['data_act']
date_diff = abs(
(int(order_date.replace('-','')) - int(inv_date.replace('-','')))
)
if date_diff > 1:
continue
# Total match (within 5% or 5 lei)
total_diff = abs(order_total - inv['total_cu_tva'])
total_pct = total_diff / max(order_total, 0.01) * 100
if total_pct > 5 and total_diff > 5:
continue
# Name similarity
sim = name_similarity(order_name, inv['partener'])
# Score: name similarity (0-1) + total closeness (0-1) + date match (0-1)
total_score = sim * 0.5 + (1 - min(total_pct/100, 1)) * 0.4 + (1 if date_diff == 0 else 0.5) * 0.1
if total_score > best_score:
best_score = total_score
best_match = inv
if best_match and best_score > 0.3:
matches.append({
'order': order,
'invoice': best_match,
'score': best_score,
})
used_invoices.add(best_match['id_vanzare'])
else:
unmatched_orders.append(order)
print(f"\nMatched: {len(matches)} | Unmatched orders: {len(unmatched_orders)}")
print()
for m in matches:
o = m['order']
inv = m['invoice']
print(f" ORDER {o['order_number']} ({(o['customer_name'] or '')[:25]}, {o['order_total']:.2f})")
print(f" ↔ FACT {inv['serie_act']}{inv['numar_act']} ({inv['partener'][:25]}, {inv['total_cu_tva']:.2f}) [score={m['score']:.2f}]")
print()
if unmatched_orders:
print("Unmatched orders:")
for o in unmatched_orders:
print(f" {o['order_number']} | {(o['customer_name'] or '')[:35]} | {o['order_total'] or 0:.2f}")
# --- Step 4: Compare line items for matched pairs ---
print()
print("=" * 80)
print("STEP 4: Line item comparison for matched orders")
print("=" * 80)
simple_mappings = [] # SKU → CODMAT, same qty/price → update nom_articole
repack_mappings = [] # SKU → CODMAT, different qty → ARTICOLE_TERTI
complex_mappings = [] # 1 SKU → N CODMATs → ARTICOLE_TERTI with procent_pret
unresolved = [] # Cannot determine mapping
for m in matches:
o = m['order']
inv = m['invoice']
go_items = o['items']
roa_items = inv['items']
print(f"\n--- ORDER {o['order_number']} ↔ FACT {inv['serie_act']}{inv['numar_act']} ---")
print(f" GoMag: {len(go_items)} items | ROA: {len(roa_items)} items")
# Show items side by side
print(f" GoMag items:")
for gi in go_items:
print(f" SKU={gi['sku']:20s} qty={gi['quantity']:6.1f} price={gi['price']:10.2f} [{gi['product_name'][:40]}]")
print(f" ROA items:")
for ri in roa_items:
print(f" COD={str(ri['codmat'] or ''):20s} qty={ri['cantitate']:6.1f} pret={ri['pret']:10.4f} [{(ri['denumire'] or '')[:40]}]")
# Try matching by price (unit price with TVA)
# GoMag price is usually with TVA, ROA pret can be fara TVA
# Let's try both
go_remaining = list(range(len(go_items)))
roa_remaining = list(range(len(roa_items)))
item_matches = []
# First pass: exact 1:1 by total value (qty * price)
for gi_idx in list(go_remaining):
gi = go_items[gi_idx]
go_total = gi['quantity'] * gi['price']
go_total_fara = go_total / (1 + gi['vat']/100) if gi['vat'] else go_total
for ri_idx in list(roa_remaining):
ri = roa_items[ri_idx]
roa_total = ri['cantitate'] * ri['pret']
roa_total_cu = ri['cantitate'] * ri['pret_cu_tva']
# Match by total (fara TVA or cu TVA)
if (abs(go_total_fara - roa_total) < 0.5 or
abs(go_total - roa_total_cu) < 0.5 or
abs(go_total - roa_total) < 0.5):
item_matches.append((gi_idx, [ri_idx]))
go_remaining.remove(gi_idx)
roa_remaining.remove(ri_idx)
break
# Second pass: 1:N matching (one GoMag item → multiple ROA items)
for gi_idx in list(go_remaining):
gi = go_items[gi_idx]
go_total = gi['quantity'] * gi['price']
go_total_fara = go_total / (1 + gi['vat']/100) if gi['vat'] else go_total
# Try combinations of remaining ROA items
if len(roa_remaining) >= 2:
# Try pairs
for i, ri_idx1 in enumerate(roa_remaining):
for ri_idx2 in roa_remaining[i+1:]:
ri1 = roa_items[ri_idx1]
ri2 = roa_items[ri_idx2]
combined_total = ri1['cantitate'] * ri1['pret'] + ri2['cantitate'] * ri2['pret']
combined_total_cu = ri1['cantitate'] * ri1['pret_cu_tva'] + ri2['cantitate'] * ri2['pret_cu_tva']
if (abs(go_total_fara - combined_total) < 1.0 or
abs(go_total - combined_total_cu) < 1.0):
item_matches.append((gi_idx, [ri_idx1, ri_idx2]))
go_remaining.remove(gi_idx)
roa_remaining.remove(ri_idx1)
roa_remaining.remove(ri_idx2)
break
else:
continue
break
# Report matches
for gi_idx, ri_indices in item_matches:
gi = go_items[gi_idx]
ris = [roa_items[i] for i in ri_indices]
if len(ris) == 1:
ri = ris[0]
# Same quantity?
if abs(gi['quantity'] - ri['cantitate']) < 0.01:
# Simple 1:1
entry = {
'sku': gi['sku'],
'codmat': ri['codmat'],
'id_articol': ri['id_articol'],
'product_name': gi['product_name'],
'denumire': ri['denumire'],
'go_qty': gi['quantity'],
'roa_qty': ri['cantitate'],
'go_price': gi['price'],
'roa_pret': ri['pret'],
'order': o['order_number'],
'factura': f"{inv['serie_act']}{inv['numar_act']}",
}
simple_mappings.append(entry)
print(f" ✓ SIMPLE: {gi['sku']}{ri['codmat']} (qty {gi['quantity']}={ri['cantitate']})")
else:
# Repackaging
cantitate_roa = ri['cantitate'] / gi['quantity'] if gi['quantity'] else 1
entry = {
'sku': gi['sku'],
'codmat': ri['codmat'],
'id_articol': ri['id_articol'],
'cantitate_roa': round(cantitate_roa, 3),
'product_name': gi['product_name'],
'denumire': ri['denumire'],
'go_qty': gi['quantity'],
'roa_qty': ri['cantitate'],
'order': o['order_number'],
'factura': f"{inv['serie_act']}{inv['numar_act']}",
}
repack_mappings.append(entry)
print(f" ✓ REPACK: {gi['sku']}{ri['codmat']} (qty {gi['quantity']}{ri['cantitate']}, ratio={cantitate_roa:.3f})")
else:
# Complex set
go_total = gi['quantity'] * gi['price']
go_total_fara = go_total / (1 + gi['vat']/100) if gi['vat'] else go_total
for ri in ris:
ri_total = ri['cantitate'] * ri['pret']
pct = round(ri_total / go_total_fara * 100, 2) if go_total_fara else 0
entry = {
'sku': gi['sku'],
'codmat': ri['codmat'],
'id_articol': ri['id_articol'],
'cantitate_roa': ri['cantitate'] / gi['quantity'] if gi['quantity'] else 1,
'procent_pret': pct,
'product_name': gi['product_name'],
'denumire': ri['denumire'],
'order': o['order_number'],
'factura': f"{inv['serie_act']}{inv['numar_act']}",
}
complex_mappings.append(entry)
print(f" ✓ SET: {gi['sku']}{ri['codmat']} ({pct}%)")
# Unresolved
for gi_idx in go_remaining:
gi = go_items[gi_idx]
unresolved.append({
'sku': gi['sku'],
'product_name': gi['product_name'],
'quantity': gi['quantity'],
'price': gi['price'],
'order': o['order_number'],
'factura': f"{inv['serie_act']}{inv['numar_act']}",
'roa_remaining': [roa_items[i] for i in roa_remaining],
})
print(f" ? UNRESOLVED: {gi['sku']} ({gi['product_name'][:40]})")
# --- Step 5: Summary and output ---
print()
print("=" * 80)
print("STEP 5: SUMMARY")
print("=" * 80)
# Deduplicate mappings
seen_simple = {}
for m in simple_mappings:
key = (m['sku'], m['codmat'])
if key not in seen_simple:
seen_simple[key] = m
seen_repack = {}
for m in repack_mappings:
key = (m['sku'], m['codmat'])
if key not in seen_repack:
seen_repack[key] = m
seen_complex = {}
for m in complex_mappings:
key = (m['sku'], m['codmat'])
if key not in seen_complex:
seen_complex[key] = m
print(f"\nSimple 1:1 (update nom_articole.codmat = SKU): {len(seen_simple)} unique")
for key, m in seen_simple.items():
print(f" {m['sku']:25s}{m['codmat']:15s} | {m['product_name'][:35]}{(m['denumire'] or '')[:35]}")
print(f"\nRepackaging (ARTICOLE_TERTI with cantitate_roa): {len(seen_repack)} unique")
for key, m in seen_repack.items():
print(f" {m['sku']:25s}{m['codmat']:15s} x{m['cantitate_roa']} | {m['product_name'][:30]}{(m['denumire'] or '')[:30]}")
print(f"\nComplex sets (ARTICOLE_TERTI with procent_pret): {len(seen_complex)} unique")
for key, m in seen_complex.items():
print(f" {m['sku']:25s}{m['codmat']:15s} {m['procent_pret']}% | {m['product_name'][:30]}{(m['denumire'] or '')[:30]}")
print(f"\nUnresolved: {len(unresolved)}")
for u in unresolved:
print(f" {u['sku']:25s} | {u['product_name'][:40]} | order={u['order']}")
# --- Write CSVs ---
out_dir = r'C:\gomag-vending\scripts\output'
os.makedirs(out_dir, exist_ok=True)
# Simple mappings CSV (for verification before SQL update)
with open(os.path.join(out_dir, 'simple_mappings.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'codmat', 'id_articol', 'product_name_gomag', 'denumire_roa', 'go_qty', 'roa_qty', 'go_price', 'roa_pret', 'order', 'factura'])
for m in seen_simple.values():
w.writerow([m['sku'], m['codmat'], m['id_articol'], m['product_name'], m['denumire'], m['go_qty'], m['roa_qty'], m['go_price'], m['roa_pret'], m['order'], m['factura']])
# Repackaging CSV (for ARTICOLE_TERTI import)
with open(os.path.join(out_dir, 'repack_mappings.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'codmat', 'cantitate_roa', 'procent_pret', 'product_name_gomag', 'denumire_roa'])
for m in seen_repack.values():
w.writerow([m['sku'], m['codmat'], m['cantitate_roa'], 100, m['product_name'], m['denumire']])
# Complex sets CSV (for ARTICOLE_TERTI import)
with open(os.path.join(out_dir, 'complex_mappings.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'codmat', 'cantitate_roa', 'procent_pret', 'product_name_gomag', 'denumire_roa'])
for m in seen_complex.values():
w.writerow([m['sku'], m['codmat'], round(m['cantitate_roa'], 3), m['procent_pret'], m['product_name'], m['denumire']])
# Unresolved CSV
with open(os.path.join(out_dir, 'unresolved.csv'), 'w', newline='', encoding='utf-8') as f:
w = csv.writer(f)
w.writerow(['sku', 'product_name', 'quantity', 'price', 'order', 'factura', 'roa_remaining_items'])
for u in unresolved:
roa_str = '; '.join([f"{r['codmat']}({r['cantitate']}x{r['pret']:.2f})" for r in u['roa_remaining']])
w.writerow([u['sku'], u['product_name'], u['quantity'], u['price'], u['order'], u['factura'], roa_str])
# SQL script for simple mappings (update nom_articole)
with open(os.path.join(out_dir, 'update_codmat.sql'), 'w', encoding='utf-8') as f:
f.write("-- Simple SKU → CODMAT: set SKU as CODMAT in nom_articole\n")
f.write("-- VERIFY BEFORE RUNNING!\n\n")
for m in seen_simple.values():
codmat = m['codmat']
sku = m['sku']
f.write(f"-- {m['product_name'][:50]}{m['denumire'][:50]}\n")
f.write(f"UPDATE nom_articole SET codmat = '{sku}' WHERE codmat = '{codmat}' AND sters = 0;\n\n")
print(f"\nOutput written to {out_dir}:")
print(f" simple_mappings.csv - {len(seen_simple)} rows (verify, then run update_codmat.sql)")
print(f" repack_mappings.csv - {len(seen_repack)} rows (import via /api/mappings/import-csv)")
print(f" complex_mappings.csv - {len(seen_complex)} rows (import via /api/mappings/import-csv)")
print(f" unresolved.csv - {len(unresolved)} rows (manual review needed)")
print(f" update_codmat.sql - SQL for simple mappings")

View File

@@ -1,42 +0,0 @@
"""Reset imported orders in SQLite back to SKIPPED after Oracle deletion"""
import sys, sqlite3
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
db = sqlite3.connect(r'C:\gomag-vending\api\data\import.db')
c = db.cursor()
# Show before
c.execute("SELECT order_number, customer_name, id_comanda, status FROM orders WHERE status = 'IMPORTED'")
rows = c.fetchall()
print(f"Orders to reset: {len(rows)}")
for r in rows:
print(f" {r[0]} | {r[1]} | id_comanda={r[2]} | {r[3]}")
# Reset
c.execute("""
UPDATE orders SET
status = 'SKIPPED',
id_comanda = NULL,
id_partener = NULL,
id_adresa_facturare = NULL,
id_adresa_livrare = NULL,
error_message = NULL,
factura_serie = NULL,
factura_numar = NULL,
factura_total_fara_tva = NULL,
factura_total_tva = NULL,
factura_total_cu_tva = NULL,
factura_data = NULL,
invoice_checked_at = NULL
WHERE status = 'IMPORTED'
""")
print(f"\nReset: {c.rowcount} orders → SKIPPED")
db.commit()
# Verify
c.execute("SELECT status, COUNT(*) FROM orders GROUP BY status")
print("\nStatus after reset:")
for r in c.fetchall():
print(f" {r[0]:20s} {r[1]}")
db.close()