From cfbb5c2fa27045982141bca4abfee87ec4138b74 Mon Sep 17 00:00:00 2001 From: Mohamad Hesham Jenbaz Date: Mon, 13 Apr 2026 13:41:28 +0200 Subject: [PATCH] updated --- db.sqlite3 | Bin 4030464 -> 4034560 bytes sheets/services/halfyear_calc.py | 237 +++++++++---- sheets/templates/clients_table.html | 4 +- sheets/templates/halfyear_balance.html | 87 ++--- sheets/templates/monthly_sheet.html | 67 +++- sheets/urls.py | 9 +- sheets/views.py | 464 ++++++++++--------------- 7 files changed, 465 insertions(+), 403 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index 35a321aee73008d256796efbe9788bb74bc8cc4c..3567da489e7621464bf8b165be44d8330c77a7b7 100644 GIT binary patch delta 37731 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 delta 35692 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 rJgsB^&5x%$l--rGftfu3*MI diff --git a/sheets/services/halfyear_calc.py b/sheets/services/halfyear_calc.py index 705809f..e3ba478 100644 --- a/sheets/services/halfyear_calc.py +++ b/sheets/services/halfyear_calc.py @@ -13,8 +13,37 @@ from django.db.models import Sum from django.db.models.functions import Coalesce from django.db.models import DecimalField, Value HALFYEAR_CLIENTS = ["AG Vogel", "AG Halfm", "IKP"] -TR_RUECKF_FLUESSIG_ROW = 2 # confirmed by your March value 172.840560 +TR_RUECKF_FLUESSIG_ROW = 2 TR_BESTAND_KANNEN_ROW = 5 +GASBESTAND_ROW_INDEX = 9 +GASBESTAND_COL_NM3 = 3 + +# In top_left / top_right, "Bestand in Kannen-1 (Lit. L-He)" is row_index 5 +BESTAND_KANNEN_ROW_INDEX = 5 +HALFYEAR_RIGHT_CLIENTS = [ + "Dr. Fohrer", + "AG Buntk.", + "AG Alff", + "AG Gutfl.", + "M3 Thiele", + "M3 Buntkowsky", + "M3 Gutfleisch", + "Merck" +] +BOTTOM1_COL_VOLUME = 0 +BOTTOM1_COL_BAR = 1 +BOTTOM1_COL_KORR = 2 +BOTTOM1_COL_NM3 = 3 +BOTTOM1_COL_LHE = 4 +BOTTOM2_ROW_ANLAGE = 0 +BOTTOM2_COL_G39 = 0 # "Gefäss 2,5" (cell id shows column_index=0) +BOTTOM2_COL_I39 = 1 # "Gefäss 1,0" (cell id shows column_index=1) +BOTTOM2_ROW_INPUTS = { + "g39": (0, 0), # row_index=0, column_index=0 (your G39) + "i39": (0, 1), # row_index=0, column_index=1 (your I39) +} +FACTOR_NM3_TO_LHE = Decimal("0.75") +RIGHT_CLIENT_INDEX = {name: idx for idx, name in enumerate(HALFYEAR_RIGHT_CLIENTS)} def get_top_right_value(sheet, client_name: str, row_index: int) -> Decimal: """ Read a numeric value from the top_right table of a MonthlySheet for @@ -128,34 +157,7 @@ def pick_sheet_by_gasbestand(window, sheets_by_ym, prev_sheet): return sheet return prev_sheet # NEW: clients for the top-right half-year table -GASBESTAND_ROW_INDEX = 9 # <-- adjust if your bottom_1 has a different row index -GASBESTAND_COL_NM3 = 3 # <-- adjust to the column index for Nm³ in bottom_1 -# In top_left / top_right, "Bestand in Kannen-1 (Lit. L-He)" is row_index 5 -BESTAND_KANNEN_ROW_INDEX = 5 -HALFYEAR_RIGHT_CLIENTS = [ - "Dr. Fohrer", - "AG Buntk.", - "AG Alff", - "AG Gutfl.", - "M3 Thiele", - "M3 Buntkowsky", - "M3 Gutfleisch", -] -BOTTOM1_COL_VOLUME = 0 -BOTTOM1_COL_BAR = 1 -BOTTOM1_COL_KORR = 2 -BOTTOM1_COL_NM3 = 3 -BOTTOM1_COL_LHE = 4 -BOTTOM2_ROW_ANLAGE = 0 -BOTTOM2_COL_G39 = 0 # "Gefäss 2,5" (cell id shows column_index=0) -BOTTOM2_COL_I39 = 1 # "Gefäss 1,0" (cell id shows column_index=1) -BOTTOM2_ROW_INPUTS = { - "g39": (0, 0), # row_index=0, column_index=0 (your G39) - "i39": (0, 1), # row_index=0, column_index=1 (your I39) -} -FACTOR_NM3_TO_LHE = Decimal("0.75") -RIGHT_CLIENT_INDEX = {name: idx for idx, name in enumerate(HALFYEAR_RIGHT_CLIENTS)} def build_halfyear_window(interval_year: int, start_month: int): """ @@ -171,7 +173,53 @@ def build_halfyear_window(interval_year: int, start_month: int): return window # Import ONLY models + pure helpers here from sheets.models import MonthlySheet, Cell, Client, SecondTableEntry, BetriebskostenSummary +def sum_right_row_without_duplicates(label: str, clients_right: list[str], values: list): + """ + For specific rows, clients are logically merged, so we must only count one copy. + """ + # rows where the PAIRS are merged: + # - Bestand in Kannen-1 + # - Summe Bestand + # - Best. in Kannen Vormonat + merged_pair_labels = { + "Bestand in Kannen-1 (Lit. L-He)", + "Summe Bestand (Lit. L-He)", + "Best. in Kannen Vormonat (Lit. L-He)", + "Verbraucherverluste (Liter L-He)", + # ✅ Sammel is also duplicated for the merged PAIRS (Fohrer+Buntk, Alff+Gutfl) + "Sammelrückführungen (Lit. L-He)", + "Sammelrückführung (Lit. L-He)", # safety (singular) +} + + merged_m3_labels = { + # ✅ Sammel is duplicated for the merged M3 triple + "Sammelrückführungen (Lit. L-He)", + "Sammelrückführung (Lit. L-He)", # safety (singular) + } + + skip_indices = set() + + # skip second column of each merged PAIR + if label in merged_pair_labels: + for right_name in ("AG Buntk.", "AG Gutfl."): + if right_name in clients_right: + skip_indices.add(clients_right.index(right_name)) + + # skip 2nd+3rd of the merged TRIPLE + if label in merged_m3_labels: + for name in ("M3 Buntkowsky", "M3 Gutfleisch"): + if name in clients_right: + skip_indices.add(clients_right.index(name)) + + total = Decimal("0") + for i, v in enumerate(values): + if i in skip_indices: + continue + if v in (None, ""): + continue + total += Decimal(str(v).replace(",", ".")) + return total def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[str, Any]: """ Returns a context dict with the SAME keys your current halfyear_balance.html expects. @@ -227,8 +275,8 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st # ---------------------------- chosen_sheet_bottom1 = pick_sheet_by_gasbestand(window, sheets_by_ym, prev_sheet) - # IMPORTANT: define which bottom_1 row_index corresponds to Excel rows 27..35 - # If your bottom_1 starts at Excel row 27 => row_index 0 == Excel 27 + # define which bottom_1 row_index corresponds to Excel rows 27..35 + # If bottom_1 starts at Excel row 27 => row_index 0 == Excel 27 # then row_index = excel_row - 27 BOTTOM1_EXCEL_START_ROW = 27 @@ -671,6 +719,20 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st right_data["M3 Gutfleisch"]['sammel'] = group3_total def safe_div(a: Decimal, b: Decimal) -> Decimal: return (a / b) if b != 0 else Decimal("0") + + # Merck: Sammelrückführung = total helium input (ExcelEntry.lhe_ges) over the 6-month window + merck_sammel_total = Decimal("0") + for (y, m) in window: + qs = ExcelEntry.objects.filter( + client__name="Merck", + date__year=y, + date__month=m, + ).aggregate( + total=Coalesce(Sum("lhe_ges"), Value(0, output_field=DecimalField())) + ) + merck_sammel_total += Decimal(str(qs["total"])) + + right_data["Merck"]["sammel"] = merck_sammel_total # --- Rückführung flüssig (Lit. L-He) for Halbjahres-Bilanz top-right --- # Uses your exact formulas. @@ -741,6 +803,9 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st right_data["M3 Thiele"]["bestand_kannen"] = pick_bestand_top_right("M3 Thiele") right_data["M3 Buntkowsky"]["bestand_kannen"] = pick_bestand_top_right("M3 Buntkowsky") right_data["M3 Gutfleisch"]["bestand_kannen"] = pick_bestand_top_right("M3 Gutfleisch") + # Merck should stay empty in Bestand in Kannen-1 + right_data["Merck"]["bestand_kannen"] = None + right_data["Merck"]["best_kannen_vormonat"] = None # Summe Bestand = same as previous row for cname in RIGHT_CLIENTS: @@ -807,9 +872,23 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st b11 = right_data[cname]['summe_bestand'] # Excel: P13+P12-P11 etc. right_data[cname]['rueckf_soll'] = b13 + b12 - b11 - + # Merck: only selected rows should have values in Top Right Halbjahresbilanz + right_data["Merck"]["stand_prev_share"] = None + right_data["Merck"]["rueckf_fluessig"] = None + right_data["Merck"]["sonder"] = None + right_data["Merck"]["bestand_kannen"] = None + right_data["Merck"]["summe_bestand"] = None + right_data["Merck"]["best_kannen_vormonat"] = None + right_data["Merck"]["rueckf_soll"] = None + right_data["Merck"]["verluste"] = None + right_data["Merck"]["fuellungen_warm"] = None + right_data["Merck"]["kaltgas_rueckgabe"] = None # --- Verluste (Soll-Rückf.) (Lit. L-He) = B14 - B6 - B7 --- for cname in RIGHT_CLIENTS: + if cname == "Merck": + right_data[cname]['verluste'] = None + continue + b14 = right_data[cname]['rueckf_soll'] b6 = right_data[cname]['rueckf_fluessig'] b7 = right_data[cname]['sonder'] @@ -829,17 +908,22 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st b13 = right_data[cname]['bezug'] right_data[cname]['kaltgas_rueckgabe'] = b13 * factor - # --- Verbraucherverluste (Liter L-He) = Verluste - Kaltgas Rückgabe --- + # --- Verbraucherverluste (Liter L-He) --- for cname in RIGHT_CLIENTS: - b15 = right_data[cname]['verluste'] - b17 = right_data[cname]['kaltgas_rueckgabe'] - right_data[cname]['verbraucherverluste'] = b15 - b17 + if cname == "Merck": + bezug = right_data[cname].get("bezug") or Decimal("0") + sammel = right_data[cname].get("sammel") or Decimal("0") + right_data[cname]["verbraucherverluste"] = bezug - sammel + else: + b15 = right_data[cname]['verluste'] + b17 = right_data[cname]['kaltgas_rueckgabe'] + right_data[cname]['verbraucherverluste'] = b15 - b17 # --- % = Verbraucherverluste / Bezug --- for cname in RIGHT_CLIENTS: - bezug = right_data[cname]['bezug'] - verb = right_data[cname]['verbraucherverluste'] - if bezug != 0: + bezug = right_data[cname].get('bezug') or Decimal("0") + verb = right_data[cname].get('verbraucherverluste') + if bezug != 0 and verb is not None: right_data[cname]['percent'] = verb / bezug else: right_data[cname]['percent'] = None @@ -910,50 +994,78 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st def safe_pct(verb, bez): return (verb / bez) if bez != 0 else None + def sum_right_key(label_for_merge: str, clients: list[str], key: str) -> Decimal: + vals = [right_data[c].get(key) for c in clients] + return sum_right_row_without_duplicates(label_for_merge, clients, vals) rows_sum = [] def d(x): return x if isinstance(x, Decimal) else Decimal("0") - for label, key in SUM_TABLE_ROWS: + for row_index, (label, key) in enumerate(SUM_TABLE_ROWS): if key == "factor_row": lichtwiese = chemie = mawi = m3 = total = Decimal("0.06") elif key == "percent": - # Right totals - rw_bez = sum(d(right_data[c].get("bezug")) for c in RIGHT_ALL) - rw_verb = sum(d(right_data[c].get("verbraucherverluste")) for c in RIGHT_ALL) + # We want % = Verbraucherverluste / Bezug (for each group) + + # LEFT totals (no merge on left) + left_bez = sum(d(client_data_left[c].get("bezug")) for c in LEFT_ALL) + left_verb = sum(d(client_data_left[c].get("verbraucherverluste")) for c in LEFT_ALL) + + # RIGHT totals (must skip merged duplicates!) + rw_bez = sum(d(right_data[c].get("bezug")) for c in RIGHT_ALL) + + rw_verb = sum_right_key("Verbraucherverluste (Liter L-He)", RIGHT_ALL, "verbraucherverluste") + lichtwiese = safe_pct(rw_verb, rw_bez) - # Chemie - ch_bez = sum(d(right_data[c].get("bezug")) for c in RIGHT_GROUPS["chemie"]) - ch_verb = sum(d(right_data[c].get("verbraucherverluste")) for c in RIGHT_GROUPS["chemie"]) + # Chemie group (Fohrer+Buntk) + ch_clients = RIGHT_GROUPS["chemie"] + ch_bez = sum_right_key("Bezug (Liter L-He)", ch_clients, "bezug") + ch_verb = sum_right_key("Verbraucherverluste (Liter L-He)", ch_clients, "verbraucherverluste") chemie = safe_pct(ch_verb, ch_bez) - # MaWi - mw_bez = sum(d(right_data[c].get("bezug")) for c in RIGHT_GROUPS["mawi"]) - mw_verb = sum(d(right_data[c].get("verbraucherverluste")) for c in RIGHT_GROUPS["mawi"]) + # MaWi group (Alff+Gutfl) + mw_clients = RIGHT_GROUPS["mawi"] + mw_bez = sum_right_key("Bezug (Liter L-He)", mw_clients, "bezug") + mw_verb = sum_right_key("Verbraucherverluste (Liter L-He)", mw_clients, "verbraucherverluste") mawi = safe_pct(mw_verb, mw_bez) - # M3 - m3_bez = sum(d(right_data[c].get("bezug")) for c in RIGHT_GROUPS["m3"]) - m3_verb = sum(d(right_data[c].get("verbraucherverluste")) for c in RIGHT_GROUPS["m3"]) + # M3 group (triple) + m3_clients = RIGHT_GROUPS["m3"] + m3_bez = sum_right_key("Bezug (Liter L-He)", m3_clients, "bezug") + m3_verb = sum_right_key("Verbraucherverluste (Liter L-He)", m3_clients, "verbraucherverluste") m3 = safe_pct(m3_verb, m3_bez) - # Σ column = (left verb + right verb) / (left bez + right bez) - left_bez = sum(d(client_data_left[c].get("bezug")) for c in LEFT_ALL) - left_verb = sum(d(client_data_left[c].get("verbraucherverluste")) for c in LEFT_ALL) + # Overall Σ (%) total = safe_pct(left_verb + rw_verb, left_bez + rw_bez) + else: - # normal rows = sums - lichtwiese = sum(d(right_data[c].get(key)) for c in RIGHT_ALL) - chemie = sum(d(right_data[c].get(key)) for c in RIGHT_GROUPS["chemie"]) - mawi = sum(d(right_data[c].get(key)) for c in RIGHT_GROUPS["mawi"]) - m3 = sum(d(right_data[c].get(key)) for c in RIGHT_GROUPS["m3"]) + # normal rows = sums (BUT skip duplicates for merged logical cells) + rw_values = [right_data[c].get(key) for c in RIGHT_ALL] + lichtwiese = sum_right_row_without_duplicates(label, RIGHT_ALL, rw_values) + + ch_clients = RIGHT_GROUPS["chemie"] + ch_values = [right_data[c].get(key) for c in ch_clients] + chemie = sum_right_row_without_duplicates(label, ch_clients, ch_values) + + mw_clients = RIGHT_GROUPS["mawi"] + mw_values = [right_data[c].get(key) for c in mw_clients] + mawi = sum_right_row_without_duplicates(label, mw_clients, mw_values) + + m3_clients = RIGHT_GROUPS["m3"] + m3_values = [right_data[c].get(key) for c in m3_clients] + m3 = sum_right_row_without_duplicates(label, m3_clients, m3_values) left_total = sum(d(client_data_left[c].get(key)) for c in LEFT_ALL) - total = left_total + lichtwiese + # Merck should count ONLY in the overall total, not in Lichtwiese + merck_val = d(right_data["Merck"].get(key)) + + total = left_total + lichtwiese + merck_val + + # ✅ THIS MUST BE OUTSIDE THE IF/ELIF/ELSE rows_sum.append({ "row_index": row_index, "label": label, @@ -964,6 +1076,7 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st "m3": m3, "is_percent": (key == "percent"), }) + def find_sum_row(rows, label_startswith: str): for r in rows: if str(r.get("label", "")).strip().startswith(label_startswith): @@ -999,8 +1112,8 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st bottom2["k44"] = k44 bottom2["j44"] = j44 - def d(x): - return x if isinstance(x, Decimal) else Decimal("0") + def d_or_none(x): + return x if isinstance(x, Decimal) else None # ---- Bottom2: J38/K38 depend on rows_sum (overall summary), so do it HERE ---- k38 = Decimal("0") diff --git a/sheets/templates/clients_table.html b/sheets/templates/clients_table.html index 28078dc..4af89dc 100644 --- a/sheets/templates/clients_table.html +++ b/sheets/templates/clients_table.html @@ -3,10 +3,10 @@ {% block content %}
-

Helium Output Yearly Summary

+

Heliumabgabe - Halbjahresübersicht

{% csrf_token %} -

Global 6-Month Interval

+

Allgemeines 6-Monats-Intervall