Analitic cu sold pe latura opusa: FACTURA cu suma negativa

Analiticul unui cont cu parteneri al carui sold e pe latura opusa laturii
fixate in cod primeste rand FACTURA, cu suma negativa pe latura
formularului (BALANTA si sold). Nu mai ramane nimic pe NEREPARTIZAT.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BdA5a3ECKFD6Citf1ptPZu
This commit is contained in:
2026-09-21 00:14:07 +03:00
parent 92dc8c7b32
commit 4c59a103c9
7 changed files with 158 additions and 139 deletions

View File

@@ -19,8 +19,9 @@ Forma randurilor (revizuit 20.09.2026, vezi pct. 14-15):
5121/5124 devine rand BALANTA de diferenta (analitic nou, fara FACTURA); la
ceilalti parteneri se include in BALANTA agregat. NU se mai emite FACTURA de
diferenta (o rezolva formularul, prin NEREPARTIZAT).
- analiticul cu sold pe latura opusa celei fixate de formular (':379') nu primeste
FACTURA; ramane doar in BALANTA si apare ca NEREPARTIZAT.
- analiticul cu sold pe latura opusa celei fixate de formular (':379') primeste
FACTURA, cu suma scrisa cu semn negativ pe latura formularului (atat in BALANTA
cat si in sold-ul FACTURA); soldul ramane pe latura corecta si nu apare NEREPARTIZAT.
"""
import csv
@@ -155,8 +156,8 @@ def rand(header, **kw):
return r
def _semn_sold(net):
return "creditor" if net < 0 else "debitor"
def este_latura_formular(net, side):
return (net > 0) == (side == "D")
def genereaza(rows, header):
@@ -183,7 +184,7 @@ def genereaza(rows, header):
out = []
leaf_map = {} # frunza -> randurile dedicate (folosite la dubluri)
leaf_rep = set() # toate frunzele reprezentate (inclusiv in BALANTA agregat)
sarite = [] # (cont, analitic, sold_semnat, motiv)
negative = [] # (cont, analitic, sold_semnat, latura_formular)
def add(r, leaf=None):
out.append(r)
@@ -220,21 +221,25 @@ def genereaza(rows, header):
for k in direct:
kd = by[k]
kac = split_cont(k)[1]
add(rand(header, TIP="BALANTA", cont=p, acont=kac, numar=1,
nume=kd["denumire"], totdeb=round(kd["total_d"], 2),
totcred=round(kd["total_c"], 2)), leaf=k)
net = round(kd["total_d"] - kd["total_c"], 2)
if net != 0 and not este_latura_formular(net, side):
neg = -abs(net)
negative.append((p, k, neg, side))
if side == "D":
td, tc = neg, None
else:
td, tc = None, neg
else:
td, tc = round(kd["total_d"], 2), round(kd["total_c"], 2)
add(rand(header, TIP="BALANTA", cont=p, acont=kac, numar=1,
nume=kd["denumire"], totdeb=td, totcred=tc), leaf=k)
if net == 0:
continue
if (net > 0) == (side == "D"):
add(rand(header, TIP="FACTURA", cont=p, acont=kac,
numar=k.replace(".", ""),
nume=kd["denumire"] or f"FURNIZOR {k}",
cod_fiscal=k, sold=abs(net), explicatia=side))
else:
sarite.append((p, k, net,
f"sold {_semn_sold(net)} vs latura {side} "
f"a formularului"))
sold = abs(net) if este_latura_formular(net, side) else -abs(net)
add(rand(header, TIP="FACTURA", cont=p, acont=kac,
numar=k.replace(".", ""),
nume=kd["denumire"] or f"FURNIZOR {k}",
cod_fiscal=k, sold=sold, explicatia=side))
if p in diff_by_parent:
d = diff_by_parent[p]
ac = acont_diferenta(p, direct)
@@ -246,8 +251,20 @@ def genereaza(rows, header):
# 401/4092/4111/461/462/4551 si 5311
if direct:
base_d = sum(by[k]["total_d"] for k in direct)
base_c = sum(by[k]["total_c"] for k in direct)
base_d = 0.0
base_c = 0.0
for k in direct:
net = round(by[k]["total_d"] - by[k]["total_c"], 2)
if net != 0 and not este_latura_formular(net, side):
neg = -abs(net)
negative.append((p, k, neg, side))
if side == "D":
base_d += neg
else:
base_c += neg
else:
base_d += by[k]["total_d"]
base_c += by[k]["total_c"]
d = diff_by_parent.get(p, 0.0)
add(rand(header, TIP="BALANTA", cont=p, acont=None, numar=1,
nume=by[p]["denumire"],
@@ -258,33 +275,33 @@ def genereaza(rows, header):
if net == 0:
leaf_rep.add(k)
continue
if (net > 0) == (side == "D"):
add(rand(header, TIP="FACTURA", cont=p, acont=None,
numar=k.replace(".", ""),
nume=by[k]["denumire"] or f"FURNIZOR {k}",
cod_fiscal=k, sold=abs(net), explicatia=side),
leaf=k)
else:
sarite.append((p, k, net,
f"sold {_semn_sold(net)} vs latura {side} "
f"a formularului"))
leaf_rep.add(k)
sold = abs(net) if este_latura_formular(net, side) else -abs(net)
add(rand(header, TIP="FACTURA", cont=p, acont=None,
numar=k.replace(".", ""),
nume=by[k]["denumire"] or f"FURNIZOR {k}",
cod_fiscal=k, sold=sold, explicatia=side),
leaf=k)
else:
net = round(by[p]["total_d"] - by[p]["total_c"], 2)
add(rand(header, TIP="BALANTA", cont=p, acont=None, numar=1,
nume=by[p]["denumire"], totdeb=round(by[p]["total_d"], 2),
totcred=round(by[p]["total_c"], 2)), leaf=p)
if net != 0:
if (net > 0) == (side == "D"):
nume = "CENTRALA" if p == "5311" else by[p]["denumire"]
add(rand(header, TIP="FACTURA", cont=p, acont=None,
numar=p, nume=nume, cod_fiscal=p, sold=abs(net),
explicatia=side))
if net != 0 and not este_latura_formular(net, side):
neg = -abs(net)
negative.append((p, p, neg, side))
if side == "D":
td, tc = neg, None
else:
sarite.append((p, p, net,
f"sold {_semn_sold(net)} vs latura {side} "
f"a formularului"))
return out, diffs, leaf_map, leaf_rep, sarite
td, tc = None, neg
else:
td = round(by[p]["total_d"], 2)
tc = round(by[p]["total_c"], 2)
add(rand(header, TIP="BALANTA", cont=p, acont=None, numar=1,
nume=by[p]["denumire"], totdeb=td, totcred=tc), leaf=p)
if net != 0:
nume = "CENTRALA" if p == "5311" else by[p]["denumire"]
sold = abs(net) if este_latura_formular(net, side) else -abs(net)
add(rand(header, TIP="FACTURA", cont=p, acont=None,
numar=p, nume=nume, cod_fiscal=p, sold=sold,
explicatia=side))
return out, diffs, leaf_map, leaf_rep, negative
def scrie_xlsx(header, rows, path):
@@ -302,7 +319,7 @@ def scrie_xlsx(header, rows, path):
def verifica(firma, csv_path, xlsx_path, rows, out, diffs, leaf_map, leaf_rep,
sarite, header):
negative, header):
by, children = arbore(rows)
leaves = [c for c in by if c not in children]
diff_by_parent = dict(diffs)
@@ -334,24 +351,19 @@ def verifica(firma, csv_path, xlsx_path, rows, out, diffs, leaf_map, leaf_rep,
semn = 1 if latura_factura(r["cont"]) == "D" else -1
part_rows[r["cont"]] = part_rows.get(r["cont"], 0.0) + semn * (r["sold"] or 0)
sarite_by_cont = {}
for cont, _k, s, _motiv in sarite:
sarite_by_cont[cont] = round(sarite_by_cont.get(cont, 0.0) + s, 2)
part_verif = []
for p in sorted({c.split(".")[0] for c in by if este_partener(c)}):
net_sint = round(by[p]["total_d"] - by[p]["total_c"], 2)
suma = round(part_rows.get(p, 0.0), 2)
sarit = round(sarite_by_cont.get(p, 0.0), 2)
direct = round(diff_by_parent.get(p, 0.0), 2)
asteptat = round(sarit + direct, 2)
asteptat = direct
diferenta = round(net_sint - suma, 2)
ok = abs(diferenta - asteptat) <= 0.005
part_verif.append((p, suma, net_sint, sarit, direct, asteptat, diferenta, ok))
part_verif.append((p, suma, net_sint, direct, asteptat, diferenta, ok))
ok_bal = abs(net) <= 0.005
ok_leaves = not pierdute and not duplicate_leaf
ok_part = all(x[7] for x in part_verif)
ok_part = all(x[6] for x in part_verif)
ok_lung = not lungimi and not dubluri_bal
L = []
@@ -392,30 +404,33 @@ def verifica(firma, csv_path, xlsx_path, rows, out, diffs, leaf_map, leaf_rep,
L.append("Formularul scade din randul BALANTA suma randurilor FACTURA de pe "
"acelasi `(cont, acont, latura)` si lasa restul pe partenerul "
"`NEREPARTIZAT`. Deci: `net sintetic - SUM(sold FACTURA cu semn) = "
"analitice sarite + postare directa pe sintetic`.")
"postare directa pe sintetic`.")
L.append("")
L.append("| cont | SUM(sold FACTURA) | net sintetic | analitice sarite | postare directa | NEREPARTIZAT asteptat | diferenta | rezultat |")
L.append("|---|---|---|---|---|---|---|---|")
for p, suma, net_sint, sarit, direct, asteptat, diferenta, ok in part_verif:
L.append(f"| {p} | {suma} | {net_sint} | {sarit} | {direct} | {asteptat} "
L.append("| cont | SUM(sold FACTURA) | net sintetic | postare directa | NEREPARTIZAT asteptat | diferenta | rezultat |")
L.append("|---|---|---|---|---|---|---|")
for p, suma, net_sint, direct, asteptat, diferenta, ok in part_verif:
L.append(f"| {p} | {suma} | {net_sint} | {direct} | {asteptat} "
f"| {diferenta} | {'OK' if ok else '**ESEC**'} |")
L.append("")
L.append(f"Rezultat: {'OK' if ok_part else '**ESEC**'}")
L.append("")
L.append("## 4. Analitice sarite (sold pe latura opusa formularului)")
L.append("## 4. Analitice cu semn negativ (sold pe latura opusa formularului)")
L.append("")
L.append("Latura FACTURA e fixa in cod (`:379`): `401,404,462,419,408,4551` -> "
"CREDIT, orice alt cont -> DEBIT. Un analitic cu sold pe latura opusa nu "
"primeste rand FACTURA; ramane in BALANTA si formularul ii pune "
"`NEREPARTIZAT`.")
"CREDIT, orice alt cont -> DEBIT. Un analitic cu sold pe latura opusa "
"primeste totusi rand FACTURA, iar suma se scrie cu **semn negativ pe "
"latura formularului**, atat in randul BALANTA cat si in `sold`-ul "
"randului FACTURA. Soldul ramane pe latura corecta, balanta ramane "
"inchisa, iar randul FACTURA consuma exact randul BALANTA (fara "
"`NEREPARTIZAT`).")
L.append("")
if not sarite:
if not negative:
L.append("Niciunul.")
else:
L.append("| cont | analitic | sold (semnat) | motiv |")
L.append("| cont | analitic | sold (semnat) | latura formularului |")
L.append("|---|---|---|---|")
for cont, k, s, motiv in sarite:
L.append(f"| {cont} | {k} | {s} | {motiv} |")
for cont, k, s, side in negative:
L.append(f"| {cont} | {k} | {s} | {side} |")
L.append("")
L.append("## 5. Lungimi si dubluri")
L.append("")
@@ -459,7 +474,7 @@ def verifica(firma, csv_path, xlsx_path, rows, out, diffs, leaf_map, leaf_rep,
"totdeb": totdeb, "totcred": totcred, "net": net,
"pierdute": pierdute, "dubluri_leaf": duplicate_leaf,
"dubluri_bal": list(dubluri_bal), "lungimi": lungimi,
"part_verif": part_verif, "sarite": sarite}
"part_verif": part_verif, "negative": negative}
def main():
@@ -467,11 +482,11 @@ def main():
assert len(header) == 39, f"cap de tabel neasteptat: {len(header)} coloane"
for firma, csv_path in FIRME:
rows = citeste_balanta(csv_path)
out, diffs, leaf_map, leaf_rep, sarite = genereaza(rows, header)
out, diffs, leaf_map, leaf_rep, negative = genereaza(rows, header)
xlsx_path = f"init_{firma}_2025_12.xlsx"
scrie_xlsx(header, out, xlsx_path)
rez = verifica(firma, csv_path, xlsx_path, rows, out, diffs, leaf_map,
leaf_rep, sarite, header)
leaf_rep, negative, header)
print(f"=== {firma} ===")
print(f" rows: {len(out)} totdeb={rez['totdeb']} totcred={rez['totcred']} net={rez['net']}")
print(f" balanta={'OK' if rez['bal'] else 'ESEC'} "
@@ -479,8 +494,8 @@ def main():
f"parteneri={'OK' if rez['part'] else 'ESEC'} "
f"lungimi={'OK' if rez['lung'] else 'ESEC'}")
print(f" scris {xlsx_path} si docs/verificare_{firma}.md")
if rez["sarite"]:
print(f" analitice sarite: {rez['sarite']}")
if rez["negative"]:
print(f" analitice cu semn negativ: {rez['negative']}")
if rez["pierdute"] or rez["dubluri_leaf"] or rez["dubluri_bal"] or rez["lungimi"]:
print(" ATENTIE:", rez["pierdute"], rez["dubluri_leaf"],
rez["dubluri_bal"], rez["lungimi"])