updated
This commit is contained in:
@@ -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")
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<!-- Title -->
|
||||
<h1 class="page-title">Helium Output Yearly Summary</h1>
|
||||
<h1 class="page-title">Heliumabgabe - Halbjahresübersicht</h1>
|
||||
<form method="post" action="{% url 'set_halfyear_interval' %}" class="interval-form">
|
||||
{% csrf_token %}
|
||||
<h3>Global 6-Month Interval</h3>
|
||||
<h3>Allgemeines 6-Monats-Intervall</h3>
|
||||
|
||||
<label>Year:</label>
|
||||
<select name="year">
|
||||
|
||||
@@ -222,52 +222,55 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for r in rows_sum %}
|
||||
<tr data-row-index="{{ r.row_index }}">
|
||||
<tr>
|
||||
<td>{{ r.label }}</td>
|
||||
<td class="sum-col sum-cell">
|
||||
{% if r.is_percent %}
|
||||
{{ r.total|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.total|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<tbody>
|
||||
{% for r in rows_sum %}
|
||||
<tr data-row-index="{{ r.row_index }}">
|
||||
<td>{{ r.label }}</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.lichtwiese|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.lichtwiese|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="sum-col sum-cell">
|
||||
{% if r.is_percent %}
|
||||
{{ r.total|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.total|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.chemie|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.chemie|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.lichtwiese|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.lichtwiese|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.chemie|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.chemie|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.mawi|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.mawi|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.m3|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.m3|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.mawi|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.mawi|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
||||
<td class="sum-col">
|
||||
{% if r.is_percent %}
|
||||
{{ r.m3|floatformat:2 }}%
|
||||
{% else %}
|
||||
{{ r.m3|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-container" style="margin-top: 20px;">
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Top Tables Container -->
|
||||
<div class="top-section"></div>
|
||||
<div class="top-tables">
|
||||
<!-- Left Table (18 rows × clients) -->
|
||||
<div class="table-container top-left-table">
|
||||
@@ -114,7 +115,9 @@
|
||||
|
||||
|
||||
<td class="sum-cell">
|
||||
{{ row.sum|default_if_none:"" }}
|
||||
{% if row.sum is not None %}
|
||||
{{ row.sum|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
@@ -219,7 +222,11 @@
|
||||
{% endfor %}
|
||||
|
||||
<td class="sum-cell">
|
||||
{{ row.sum|default_if_none:"" }}
|
||||
<td class="sum-cell">
|
||||
{% if row.sum is not None %}
|
||||
{{ row.sum|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endwith %}
|
||||
@@ -242,13 +249,18 @@
|
||||
<tr data-row-index="{{ row.row_index }}">
|
||||
<td class="row-label">{{ row.label }}</td>
|
||||
<td class="sum-cell overall-sum-cell">
|
||||
{{ row.sum|default_if_none:"" }}
|
||||
<td class="sum-cell">
|
||||
{% if row.sum is not None %}
|
||||
{{ row.sum|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Bottom Tables -->
|
||||
<div class="bottom-tables">
|
||||
<div class="table-container bottom-table-1">
|
||||
@@ -320,7 +332,9 @@
|
||||
data-row="{{ forloop.parentloop.counter0 }}"
|
||||
data-col="{{ forloop.counter0 }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="readonly-cell"
|
||||
@@ -328,7 +342,9 @@
|
||||
data-table="bottom_1"
|
||||
data-row="{{ forloop.parentloop.counter0 }}"
|
||||
data-col="{{ forloop.counter0 }}">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
|
||||
@@ -339,7 +355,9 @@
|
||||
data-table="bottom_1"
|
||||
data-row="{{ forloop.parentloop.counter0 }}"
|
||||
data-col="{{ forloop.counter0 }}">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@@ -379,7 +397,9 @@
|
||||
data-col="0"
|
||||
data-client-id="{{ cell.client.id|default:'' }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
<td>Gefäss 1,0</td>
|
||||
@@ -392,7 +412,9 @@
|
||||
data-col="1"
|
||||
data-client-id="{{ cell.client.id|default:'' }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
<td id="bt2-j39" class="readonly-cell"></td>
|
||||
@@ -454,7 +476,9 @@
|
||||
<td id="bt3-j46"
|
||||
class="readonly-cell calculated-cell"
|
||||
data-cell-id="{{ cell.id|default:'' }}">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
|
||||
@@ -490,7 +514,9 @@
|
||||
data-col="1"
|
||||
data-cell-id="{{ cell.id|default:'' }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
|
||||
@@ -503,7 +529,9 @@
|
||||
data-col="2"
|
||||
data-cell-id="{{ cell.id|default:'' }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
|
||||
@@ -546,7 +574,9 @@
|
||||
data-col="2"
|
||||
data-client-id="{{ cell.client.id|default:'' }}"
|
||||
contenteditable="true">
|
||||
{{ cell.value|default:"" }}
|
||||
{% if cell.value is not None %}
|
||||
{{ cell.value|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endwith %}
|
||||
|
||||
@@ -800,6 +830,12 @@ $(document).ready(function() {
|
||||
const v = parseFloat(t);
|
||||
return isNaN(v) ? null : v;
|
||||
}
|
||||
function formatNumber(value) {
|
||||
if (value === null || value === undefined || value === '') return '';
|
||||
const num = parseFloat(String(value).replace(',', '.'));
|
||||
if (isNaN(num)) return value;
|
||||
return num.toFixed(2);
|
||||
}
|
||||
|
||||
// Recalculate Sum cell (last column) for a given row
|
||||
function recalcRowSum($row) {
|
||||
@@ -837,7 +873,7 @@ $(document).ready(function() {
|
||||
});
|
||||
|
||||
if (hasValue) {
|
||||
$row.find('.sum-cell').text(total);
|
||||
$row.find('.sum-cell').text(total.toFixed(2));
|
||||
} else {
|
||||
$row.find('.sum-cell').text('');
|
||||
}
|
||||
@@ -1243,8 +1279,9 @@ $(document).ready(function() {
|
||||
const targetCell = $('[data-cell-id="' + cellData.id + '"]');
|
||||
if (!targetCell.length) return;
|
||||
|
||||
targetCell.text(cellData.value || '');
|
||||
targetCell.data('original-value', cellData.value || '');
|
||||
const formattedValue = formatNumber(cellData.value);
|
||||
targetCell.text(formattedValue);
|
||||
targetCell.data('original-value', formattedValue);
|
||||
|
||||
if (cellData.is_calculated) {
|
||||
targetCell.addClass('calculated-cell');
|
||||
|
||||
+4
-5
@@ -1,5 +1,5 @@
|
||||
from django.urls import path
|
||||
from .views import DebugTopRightView
|
||||
|
||||
from .views import SaveCellsView , SaveMonthSummaryView, halfyear_settings,MonthlySheetView, monthly_sheet_root,set_halfyear_interval,AbrechnungView, SaveAbrechnungCellsView
|
||||
from . import views
|
||||
from .views import RechnungView
|
||||
@@ -19,9 +19,9 @@ urlpatterns = [
|
||||
path("abrechnung/autosave/", SaveAbrechnungCellsView.as_view(), name="abrechnung_autosave"),
|
||||
path("abrechnung/save/", SaveAbrechnungCellsView.as_view(), name="save_abrechnung"),
|
||||
path('check-sheets/', views.CheckSheetView.as_view(), name='check_sheets'),
|
||||
path('quick-debug/', views.QuickDebugView.as_view(), name='quick_debug'),
|
||||
|
||||
path('test-formula/', views.TestFormulaView.as_view(), name='test_formula'),
|
||||
path('simple-debug/', views.SimpleDebugView.as_view(), name='simple_debug'),
|
||||
|
||||
path("rechnung/", RechnungView.as_view(), name="rechnung"),
|
||||
path('sheet/', monthly_sheet_root, name='monthly_sheet_root'),
|
||||
path('set-halfyear-interval/', set_halfyear_interval, name='set_halfyear_interval'),
|
||||
@@ -33,6 +33,5 @@ urlpatterns = [
|
||||
path("save-cells/", SaveCellsView.as_view(), name="save_cells"), path("save-cells/", SaveCellsView.as_view(), name="save_cells"),path('save-month-summary/', SaveMonthSummaryView.as_view(), name='save_month_summary'),
|
||||
path('save-month-summary/', SaveMonthSummaryView.as_view(), name='save_month_summary'),
|
||||
path('calculate/', views.CalculateView.as_view(), name='calculate'),
|
||||
path('debug-calculation/', views.DebugCalculationView.as_view(), name='debug_calculation'),
|
||||
path('debug-top-right/', DebugTopRightView.as_view(), name='debug_top_right'),
|
||||
|
||||
]
|
||||
+187
-277
@@ -59,6 +59,12 @@ CLIENT_GROUPS = {
|
||||
'M3 Gutfleisch',
|
||||
],
|
||||
},
|
||||
'merck': {
|
||||
'label': 'Merck',
|
||||
'names': [
|
||||
'Merck',
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
# Add this CALCULATION_CONFIG at the top of views.py
|
||||
@@ -275,12 +281,12 @@ def build_halfyear_window(interval_year: int, start_month: int):
|
||||
# Halbjahres-Bilanz helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# You can adjust these indices if needed.
|
||||
|
||||
# Assuming:
|
||||
# - bottom_1.table has row "Gasbestand" at some fixed row index,
|
||||
# and columns: ... Nm³, Lit. LHe
|
||||
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
|
||||
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
|
||||
@@ -382,6 +388,7 @@ HALFYEAR_RIGHT_CLIENTS = [
|
||||
"M3 Thiele",
|
||||
"M3 Buntkowsky",
|
||||
"M3 Gutfleisch",
|
||||
"Merck",
|
||||
]
|
||||
BOTTOM1_COL_VOLUME = 0
|
||||
BOTTOM1_COL_BAR = 1
|
||||
@@ -461,6 +468,7 @@ def get_group_clients(group_key):
|
||||
group = CLIENT_GROUPS.get(group_key)
|
||||
if not group:
|
||||
return Client.objects.none()
|
||||
|
||||
return Client.objects.filter(name__in=group['names'])
|
||||
|
||||
def calculate_summation(sheet, table_type, row_index, sum_column_index):
|
||||
@@ -575,6 +583,7 @@ class MonthlySheetView(TemplateView):
|
||||
"M3 Thiele", # Column index 4 (P)
|
||||
"M3 Buntkowsky", # Column index 5 (Q)
|
||||
"M3 Gutfleisch", # Column index 6 (R)
|
||||
"Merck",
|
||||
]
|
||||
|
||||
# For each client in top-right table
|
||||
@@ -735,6 +744,7 @@ class MonthlySheetView(TemplateView):
|
||||
"M3 Thiele",
|
||||
"M3 Buntkowsky",
|
||||
"M3 Gutfleisch",
|
||||
"Merck",
|
||||
]
|
||||
current_summary = MonthlySummary.objects.filter(sheet=sheet).first()
|
||||
|
||||
@@ -765,6 +775,10 @@ class MonthlySheetView(TemplateView):
|
||||
("Dr. Fohrer", "AG Buntk."),
|
||||
("AG Alff", "AG Gutfl."),
|
||||
]
|
||||
|
||||
MERGED_TRIPLES = [
|
||||
("M3 Thiele", "M3 Buntkowsky", "M3 Gutfleisch"),
|
||||
]
|
||||
rows = []
|
||||
|
||||
# Determine row count
|
||||
@@ -813,13 +827,29 @@ class MonthlySheetView(TemplateView):
|
||||
has_value = False
|
||||
|
||||
merged_second_indices = set()
|
||||
|
||||
if table_type == 'top_right' and row_idx in MERGED_ROWS:
|
||||
|
||||
# Handle pairs
|
||||
for left_name, right_name in MERGED_PAIRS:
|
||||
try:
|
||||
right_idx = client_names.index(right_name)
|
||||
merged_second_indices.add(right_idx)
|
||||
except ValueError:
|
||||
# client not in this table; just ignore
|
||||
pass
|
||||
|
||||
# Handle M3 triple group
|
||||
MERGED_TRIPLES = [
|
||||
("M3 Thiele", "M3 Buntkowsky", "M3 Gutfleisch"),
|
||||
]
|
||||
|
||||
for a, b, c in MERGED_TRIPLES:
|
||||
try:
|
||||
b_idx = client_names.index(b)
|
||||
c_idx = client_names.index(c)
|
||||
merged_second_indices.add(b_idx)
|
||||
merged_second_indices.add(c_idx)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
for col_idx, cell in enumerate(display_cells):
|
||||
@@ -1250,100 +1280,8 @@ def get_factor_value(table_type, row_index):
|
||||
# Save Cells View
|
||||
# views.py - Updated SaveCellsView
|
||||
# views.py - Update SaveCellsView class
|
||||
def debug_cell_values(self, sheet, client_id):
|
||||
"""Debug method to check cell values"""
|
||||
from .models import Cell
|
||||
cells = Cell.objects.filter(
|
||||
sheet=sheet,
|
||||
table_type='top_left',
|
||||
client_id=client_id
|
||||
).order_by('row_index')
|
||||
|
||||
debug_info = {}
|
||||
for cell in cells:
|
||||
debug_info[f"row_{cell.row_index}"] = {
|
||||
'value': str(cell.value) if cell.value else 'None',
|
||||
'ui_row': cell.row_index + 1,
|
||||
'excel_ref': f"B{cell.row_index + 3}"
|
||||
}
|
||||
|
||||
return debug_info
|
||||
class DebugCalculationView(View):
|
||||
"""Debug view to test calculations directly"""
|
||||
def get(self, request):
|
||||
sheet_id = request.GET.get('sheet_id', 1)
|
||||
client_name = request.GET.get('client', 'AG Vogel')
|
||||
|
||||
try:
|
||||
sheet = MonthlySheet.objects.get(id=sheet_id)
|
||||
client = Client.objects.get(name=client_name)
|
||||
|
||||
# Get SaveCellsView instance
|
||||
save_view = SaveCellsView()
|
||||
|
||||
# Create a dummy cell to trigger calculations
|
||||
dummy_cell = Cell.objects.filter(
|
||||
sheet=sheet,
|
||||
table_type='top_left',
|
||||
client=client,
|
||||
row_index=0 # B3
|
||||
).first()
|
||||
|
||||
if not dummy_cell:
|
||||
return JsonResponse({'error': 'No cells found for this client'})
|
||||
|
||||
# Trigger calculation
|
||||
updated = save_view.calculate_top_left_dependents(sheet, dummy_cell)
|
||||
|
||||
# Get updated cell values
|
||||
cells = Cell.objects.filter(
|
||||
sheet=sheet,
|
||||
table_type='top_left',
|
||||
client=client
|
||||
).order_by('row_index')
|
||||
|
||||
cell_data = []
|
||||
for cell in cells:
|
||||
cell_data.append({
|
||||
'row_index': cell.row_index,
|
||||
'ui_row': cell.row_index + 1,
|
||||
'excel_ref': f"B{cell.row_index + 3}",
|
||||
'value': str(cell.value) if cell.value else 'None',
|
||||
'description': self.get_row_description(cell.row_index)
|
||||
})
|
||||
|
||||
return JsonResponse({
|
||||
'sheet': f"{sheet.year}-{sheet.month:02d}",
|
||||
'client': client.name,
|
||||
'cells': cell_data,
|
||||
'updated_count': len(updated),
|
||||
'calculation': 'B5 = IF(B3>0; B3-B4; 0)'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': str(e)}, status=400)
|
||||
|
||||
def get_row_description(self, row_index):
|
||||
"""Get description for row index"""
|
||||
descriptions = {
|
||||
0: "B3: Stand der Gaszähler (Nm³)",
|
||||
1: "B4: Stand der Gaszähler (Vormonat) (Nm³)",
|
||||
2: "B5: Gasrückführung (Nm³)",
|
||||
3: "B6: Rückführung flüssig (Lit. L-He)",
|
||||
4: "B7: Sonderrückführungen (Lit. L-He)",
|
||||
5: "B8: Bestand in Kannen-1 (Lit. L-He)",
|
||||
6: "B9: Summe Bestand (Lit. L-He)",
|
||||
7: "B10: Best. in Kannen Vormonat (Lit. L-He)",
|
||||
8: "B11: Bezug (Liter L-He)",
|
||||
9: "B12: Rückführ. Soll (Lit. L-He)",
|
||||
10: "B13: Verluste (Soll-Rückf.) (Lit. L-He)",
|
||||
11: "B14: Füllungen warm (Lit. L-He)",
|
||||
12: "B15: Kaltgas Rückgabe (Lit. L-He) – Faktor",
|
||||
13: "B16: Faktor",
|
||||
14: "B17: Verbraucherverluste (Liter L-He)",
|
||||
15: "B18: %"
|
||||
}
|
||||
return descriptions.get(row_index, f"Row {row_index}")
|
||||
|
||||
|
||||
def recalculate_stand_der_gaszahler(self, sheet):
|
||||
"""Recalculate Stand der Gaszähler for all client pairs"""
|
||||
from decimal import Decimal
|
||||
@@ -1443,82 +1381,7 @@ def recalculate_stand_der_gaszahler(self, sheet):
|
||||
ag_gutfl_row0.save()
|
||||
except Exception as e:
|
||||
print(f"Error recalculating Stand der Gaszähler for AG Alff/AG Gutfl.: {e}")
|
||||
# In your SaveCellsView class in views.py
|
||||
class DebugTopRightView(View):
|
||||
"""Debug view to check top_right calculations"""
|
||||
def get(self, request):
|
||||
sheet_id = request.GET.get('sheet_id', 1)
|
||||
client_name = request.GET.get('client', 'Dr. Fohrer')
|
||||
|
||||
try:
|
||||
sheet = MonthlySheet.objects.get(id=sheet_id)
|
||||
client = Client.objects.get(name=client_name)
|
||||
|
||||
# Get all cells for this client in top_right
|
||||
cells = Cell.objects.filter(
|
||||
sheet=sheet,
|
||||
table_type='top_right',
|
||||
client=client
|
||||
).order_by('row_index')
|
||||
|
||||
cell_data = []
|
||||
descriptions = {
|
||||
0: "Stand der Gaszähler (Vormonat)",
|
||||
1: "Gasrückführung (Nm³)",
|
||||
2: "Rückführung flüssig",
|
||||
3: "Sonderrückführungen",
|
||||
4: "Sammelrückführungen",
|
||||
5: "Bestand in Kannen-1",
|
||||
6: "Summe Bestand",
|
||||
7: "Best. in Kannen Vormonat",
|
||||
8: "Same as row 9 from prev sheet",
|
||||
9: "Bezug",
|
||||
10: "Rückführ. Soll",
|
||||
11: "Verluste",
|
||||
12: "Füllungen warm",
|
||||
13: "Kaltgas Rückgabe",
|
||||
14: "Faktor 0.06",
|
||||
15: "Verbraucherverluste",
|
||||
16: "%"
|
||||
}
|
||||
|
||||
for cell in cells:
|
||||
cell_data.append({
|
||||
'row_index': cell.row_index,
|
||||
'ui_row': cell.row_index + 1,
|
||||
'description': descriptions.get(cell.row_index, f"Row {cell.row_index}"),
|
||||
'value': str(cell.value) if cell.value else 'Empty',
|
||||
'cell_id': cell.id
|
||||
})
|
||||
|
||||
# Test calculation
|
||||
row3_cell = cells.filter(row_index=3).first()
|
||||
row5_cell = cells.filter(row_index=5).first()
|
||||
row6_cell = cells.filter(row_index=6).first()
|
||||
|
||||
calculation_info = {
|
||||
'row3_value': str(row3_cell.value) if row3_cell and row3_cell.value else '0',
|
||||
'row5_value': str(row5_cell.value) if row5_cell and row5_cell.value else '0',
|
||||
'row6_value': str(row6_cell.value) if row6_cell and row6_cell.value else '0',
|
||||
'expected_sum': '0'
|
||||
}
|
||||
|
||||
if row3_cell and row5_cell and row6_cell:
|
||||
row3_val = Decimal(str(row3_cell.value)) if row3_cell.value else Decimal('0')
|
||||
row5_val = Decimal(str(row5_cell.value)) if row5_cell.value else Decimal('0')
|
||||
expected = row3_val + row5_val
|
||||
calculation_info['expected_sum'] = str(expected)
|
||||
calculation_info['is_correct'] = row6_cell.value == expected
|
||||
|
||||
return JsonResponse({
|
||||
'sheet': f"{sheet.year}-{sheet.month}",
|
||||
'client': client.name,
|
||||
'cells': cell_data,
|
||||
'calculation': calculation_info
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({'error': str(e)}, status=400)
|
||||
|
||||
ABRECHNUNG_COL_CLIENTS = {
|
||||
# first 3 columns
|
||||
"pkm_vogel": ["AG Vogel"],
|
||||
@@ -1582,7 +1445,12 @@ class AbrechnungView(TemplateView):
|
||||
("rechnungsbetrag", "Rechnungsbetrag", "EUR"),
|
||||
("eff_lhe_preis", "eff. L-He-Preis", "EUR/L"),
|
||||
]
|
||||
|
||||
SUMMARY_ROWS = {
|
||||
"bezogen_menge",
|
||||
"kaltg_warmfuell",
|
||||
"he_verbrauch",
|
||||
"lhe_verluste",
|
||||
}
|
||||
|
||||
# Editable rows = your yellow rows
|
||||
EDITABLE_ROW_KEYS = {"ghe_bezug", "betrag", "gutschriften"}
|
||||
@@ -1706,9 +1574,12 @@ class AbrechnungView(TemplateView):
|
||||
def sum_all_cols(values_dict):
|
||||
return sum(v for k, v in values_dict.items() if k != "gesamt_summe")
|
||||
|
||||
bezogen["gesamt_summe"] = sum_all_cols(bezogen)
|
||||
warmfills["gesamt_summe"] = sum_all_cols(warmfills)
|
||||
|
||||
bezogen["gesamt_summe"] = (
|
||||
d(bezogen.get("chemie")) + d(bezogen.get("mawi")) + d(bezogen.get("physik"))
|
||||
)
|
||||
warmfills["gesamt_summe"] = (
|
||||
d(warmfills.get("chemie")) + d(warmfills.get("mawi")) + d(warmfills.get("physik"))
|
||||
)
|
||||
# ---- Row: Bezogen. Menge ----
|
||||
for col_key in bezogen:
|
||||
computed[("bezogen_menge", col_key)] = bezogen[col_key]
|
||||
@@ -1765,12 +1636,30 @@ class AbrechnungView(TemplateView):
|
||||
for col_key in he_verbrauch:
|
||||
if col_key in first3:
|
||||
row1_vals[col_key] = instandhaltung * safe_div(he_verbrauch[col_key], physik_hv) / Decimal("3")
|
||||
|
||||
elif col_key in next3:
|
||||
row1_vals[col_key] = instandhaltung / Decimal("9")
|
||||
|
||||
elif col_key in next2:
|
||||
row1_vals[col_key] = instandhaltung / Decimal("6")
|
||||
|
||||
# ✅ NEW: summary columns
|
||||
elif col_key in ("chemie", "mawi"):
|
||||
row1_vals[col_key] = instandhaltung / Decimal("3")
|
||||
|
||||
elif col_key == "physik":
|
||||
row1_vals[col_key] = (
|
||||
d(row1_vals.get("pkm_vogel")) +
|
||||
d(row1_vals.get("iap_halfmann")) +
|
||||
d(row1_vals.get("ikp"))
|
||||
)
|
||||
|
||||
elif col_key == "gesamt_summe":
|
||||
row1_vals[col_key] = sum(row1_vals.get(k, Decimal("0")) for k in first3)
|
||||
# keep whatever you want here; example: chemie+mawi+physik
|
||||
row1_vals[col_key] = (
|
||||
d(row1_vals.get("chemie")) + d(row1_vals.get("mawi")) + d(row1_vals.get("physik"))
|
||||
)
|
||||
|
||||
else:
|
||||
row1_vals[col_key] = Decimal("0")
|
||||
|
||||
@@ -1820,23 +1709,21 @@ class AbrechnungView(TemplateView):
|
||||
elif col_key == "chemie":
|
||||
v = (
|
||||
d(verbrauch_right.get("Dr. Fohrer")) +
|
||||
d(verbrauch_right.get("AG Buntk.")) +
|
||||
d(verbrauch_right.get("M3 Buntkowsky")) +
|
||||
d(verbrauch_right.get("M3 Thiele"))
|
||||
d(verbrauch_right.get("AG Buntk."))
|
||||
)
|
||||
|
||||
elif col_key == "mawi":
|
||||
# MaWi = MaWi Gutfl + MaWi Alff (Abrechnung columns)
|
||||
v = (
|
||||
d(verbrauch_right.get("AG Alff")) +
|
||||
d(verbrauch_right.get("AG Gutfl.")) +
|
||||
d(verbrauch_right.get("M3 Gutfleisch"))
|
||||
d(lhe_verluste.get("AG Gutfl")) +
|
||||
d(lhe_verluste.get("mawi_alff"))
|
||||
)
|
||||
|
||||
elif col_key == "physik":
|
||||
v = (
|
||||
d(verbrauch_left.get("AG Vogel")) +
|
||||
d(verbrauch_left.get("AG Halfm")) +
|
||||
d(verbrauch_left.get("IKP"))
|
||||
v = (
|
||||
d(verbrauch_right.get("M3 Thiele")) +
|
||||
d(verbrauch_right.get("M3 Buntkowsky")) +
|
||||
d(verbrauch_right.get("M3 Gutfleisch"))
|
||||
)
|
||||
|
||||
elif col_key == "gesamt_summe":
|
||||
@@ -1851,14 +1738,41 @@ class AbrechnungView(TemplateView):
|
||||
|
||||
|
||||
# --- Gesamt-summe ---
|
||||
lhe_verluste["gesamt_summe"] = sum(v for k, v in lhe_verluste.items())
|
||||
lhe_verluste["gesamt_summe"] = (
|
||||
d(lhe_verluste.get("chemie")) + d(lhe_verluste.get("mawi")) + d(lhe_verluste.get("physik"))
|
||||
)
|
||||
computed[("lhe_verluste", "gesamt_summe")] = lhe_verluste["gesamt_summe"]
|
||||
|
||||
|
||||
# ---- Row: 3 Umlage Heliumkosten = heliumkosten * (LHe-Verluste col / LHe-Verluste gesamt_summe) ----
|
||||
for col_key in lhe_verluste:
|
||||
computed[("umlage_heliumkosten_3", col_key)] = heliumkosten * safe_div(lhe_verluste[col_key], lhe_verluste["gesamt_summe"])
|
||||
FIRST8 = [
|
||||
"pkm_vogel",
|
||||
"iap_halfmann",
|
||||
"ikp",
|
||||
"orgchem_thiele",
|
||||
"phychem_m3_buntkow",
|
||||
"orgchem_fohrer",
|
||||
"mawi_m3_gutfl",
|
||||
"mawi_alff",
|
||||
]
|
||||
|
||||
den = sum(d(lhe_verluste.get(k)) for k in FIRST8) # denominator = sum of first 8 LHe–Verluste
|
||||
|
||||
# 1) compute first 8 columns
|
||||
for k in FIRST8:
|
||||
computed[("umlage_heliumkosten_3", k)] = heliumkosten * safe_div(d(lhe_verluste.get(k)), den)
|
||||
|
||||
# 2) compute the 3 summary columns from those first 8 (so they stay consistent)
|
||||
# 2) compute the 3 summary columns directly from their LHe–Verluste shares (Excel logic)
|
||||
computed[("umlage_heliumkosten_3", "chemie")] = heliumkosten * safe_div(d(lhe_verluste.get("chemie")), den)
|
||||
computed[("umlage_heliumkosten_3", "mawi")] = heliumkosten * safe_div(d(lhe_verluste.get("mawi")), den)
|
||||
computed[("umlage_heliumkosten_3", "physik")] = heliumkosten * safe_div(d(lhe_verluste.get("physik")), den)
|
||||
|
||||
|
||||
# 3) last column = sum of first 8 columns (your requirement)
|
||||
computed[("umlage_heliumkosten_3", "gesamt_summe")] = sum(
|
||||
d(computed.get(("umlage_heliumkosten_3", k))) for k in FIRST8
|
||||
)
|
||||
# ---- Row: 4-Kosten He-Gas-Bezug = Umlage Heliumkosten * Bezugskosten-GasHe ----
|
||||
for col_key, _label in self.COLUMNS:
|
||||
ghe = d(value_map.get(("ghe_bezug", col_key)))
|
||||
@@ -1901,7 +1815,17 @@ class AbrechnungView(TemplateView):
|
||||
elif col_key in ("chemie", "mawi", "physik"):
|
||||
v = safe_div(he_verbrauch[col_key], sum3) * umlage_personal_total
|
||||
elif col_key == "gesamt_summe":
|
||||
v = sum(d(computed.get(("umlage_personal_5", k))) for k in he_verbrauch if k != "gesamt_summe")
|
||||
# sum of the FIRST 8 columns only (exclude chemie/mawi/physik/gesamt_summe)
|
||||
v = (
|
||||
d(computed.get(("umlage_personal_5", "pkm_vogel"))) +
|
||||
d(computed.get(("umlage_personal_5", "iap_halfmann"))) +
|
||||
d(computed.get(("umlage_personal_5", "ikp"))) +
|
||||
d(computed.get(("umlage_personal_5", "orgchem_thiele"))) +
|
||||
d(computed.get(("umlage_personal_5", "phychem_m3_buntkow"))) +
|
||||
d(computed.get(("umlage_personal_5", "orgchem_fohrer"))) +
|
||||
d(computed.get(("umlage_personal_5", "mawi_m3_gutfl"))) +
|
||||
d(computed.get(("umlage_personal_5", "mawi_alff")))
|
||||
)
|
||||
else:
|
||||
v = Decimal("0")
|
||||
computed[("umlage_personal_5", col_key)] = v
|
||||
@@ -1932,6 +1856,19 @@ class AbrechnungView(TemplateView):
|
||||
else:
|
||||
sonstiges_text[col_key] = "Nachzahlung"
|
||||
# overwrite display values for non-editable computed rows
|
||||
FIRST4 = {"bezogen_menge", "kaltg_warmfuell", "anzahl_15", "he_verbrauch"}
|
||||
FIRST8 = ["pkm_vogel", "iap_halfmann", "ikp", "orgchem_thiele",
|
||||
"phychem_m3_buntkow", "orgchem_fohrer", "mawi_m3_gutfl", "mawi_alff"]
|
||||
|
||||
for rk, _label, _unit in self.ROWS:
|
||||
if rk in FIRST4:
|
||||
computed[(rk, "gesamt_summe")] = (
|
||||
d(computed.get((rk, "chemie"))) +
|
||||
d(computed.get((rk, "mawi"))) +
|
||||
d(computed.get((rk, "physik")))
|
||||
)
|
||||
else:
|
||||
computed[(rk, "gesamt_summe")] = sum(d(computed.get((rk, ck))) for ck in FIRST8)
|
||||
non_editable_formula_rows = {
|
||||
"bezogen_menge",
|
||||
"kaltg_warmfuell",
|
||||
@@ -1981,19 +1918,31 @@ class AbrechnungView(TemplateView):
|
||||
# -------------------------------
|
||||
|
||||
GROUP_IJKL = ["orgchem_thiele", "phychem_m3_buntkow", "orgchem_fohrer", "mawi_m3_gutfl", "mawi_alff"]
|
||||
GROUP_NOP = GROUP_IJKL + ["chemie", "mawi", "physik"]
|
||||
GROUP_NOP = ["chemie", "mawi", "physik"]
|
||||
GROUP_STADT = ["pkm_vogel", "iap_halfmann", "ikp"]
|
||||
def nop_cols_for_row(row_key: str):
|
||||
# For gutschriften, NOP should sum the 5 IJKL client columns
|
||||
if row_key == "gutschriften":
|
||||
return GROUP_IJKL
|
||||
# otherwise NOP is the 3 summary columns (Chemie/MaWi/Physik)
|
||||
return GROUP_NOP
|
||||
|
||||
def val(row_key, col_key):
|
||||
"""Get the final numeric value for a cell (computed if available, else stored)."""
|
||||
v = value_map.get((row_key, col_key))
|
||||
return d(v)
|
||||
"""Get the final numeric value for a cell (prefer computed, fallback to stored)."""
|
||||
if (row_key, col_key) in computed:
|
||||
return d(computed.get((row_key, col_key)))
|
||||
return d(value_map.get((row_key, col_key)))
|
||||
|
||||
def sum_group(row_key, cols):
|
||||
return sum(val(row_key, ck) for ck in cols)
|
||||
|
||||
right_rows = []
|
||||
|
||||
def nop_cols_for_row(row_key: str):
|
||||
# For gutschriften, NOP should sum the 5 IJKL client columns
|
||||
if row_key == "gutschriften":
|
||||
return GROUP_IJKL
|
||||
# otherwise NOP is the 3 summary columns
|
||||
return GROUP_NOP
|
||||
# These are your normal rows (same order as UI)
|
||||
# We will create one summary row per Abrechnung row_key.
|
||||
for row_key, label, unit in self.ROWS:
|
||||
@@ -2011,14 +1960,27 @@ class AbrechnungView(TemplateView):
|
||||
continue
|
||||
|
||||
ijkl = sum_group(row_key, GROUP_IJKL)
|
||||
nop = sum_group(row_key, GROUP_NOP)
|
||||
if row_key == "umlage_heliumkosten_3":
|
||||
nop = (
|
||||
val(row_key, "chemie") +
|
||||
val(row_key, "mawi") +
|
||||
val(row_key, "physik")
|
||||
)
|
||||
else:
|
||||
nop = sum_group(row_key, nop_cols_for_row(row_key))
|
||||
stadt = sum_group(row_key, GROUP_STADT)
|
||||
check = nop + stadt
|
||||
|
||||
# Special rule: Rechnungsbetrag row gets +Betrag +Gutschriften (for NOP only)
|
||||
if row_key == "rechnungsbetrag":
|
||||
nop_extra = sum_group("betrag", GROUP_NOP) + sum_group("gutschriften", GROUP_NOP)
|
||||
nop = nop + nop_extra
|
||||
# Rechnungsbetrag (NOP) = (Chemie+MaWi+Physik Rechnungsbetrag)
|
||||
# + (Chemie+MaWi+Physik Betrag)
|
||||
# + (IJKL Gutschriften) <-- because you want gutschriften NOP from IJKL
|
||||
nop = (
|
||||
sum_group("rechnungsbetrag", GROUP_NOP)
|
||||
+ sum_group("betrag", GROUP_NOP)
|
||||
+ sum_group("gutschriften", GROUP_IJKL)
|
||||
)
|
||||
check = nop + stadt
|
||||
|
||||
right_rows.append({
|
||||
@@ -2180,7 +2142,7 @@ class RechnungView(TemplateView):
|
||||
|
||||
bs = BetriebskostenSummary.objects.first()
|
||||
instandhaltung = d(bs.instandhaltung) if bs else Decimal("0")
|
||||
personalkosten = d(bs.personalkosten) if bs else Decimal("0")
|
||||
personalkosten = d(bs.umlage_personal) if bs else Decimal("0")
|
||||
|
||||
preis_eur_pro_l = (instandhaltung / interval_total_output_lhe) if interval_total_output_lhe != 0 else Decimal("0")
|
||||
|
||||
@@ -2537,6 +2499,7 @@ class SaveCellsView(View):
|
||||
"M3 Thiele", # P
|
||||
"M3 Buntkowsky", # Q
|
||||
"M3 Gutfleisch", # R
|
||||
"Merck"
|
||||
]
|
||||
|
||||
# Define merged pairs
|
||||
@@ -2553,6 +2516,7 @@ class SaveCellsView(View):
|
||||
"fohrer_buntk": ["Dr. Fohrer", "AG Buntk."],
|
||||
"alff_gutfl": ["AG Alff", "AG Gutfl."],
|
||||
"m3": ["M3 Thiele", "M3 Buntkowsky", "M3 Gutfleisch"],
|
||||
"merck": ["Merck"],
|
||||
}
|
||||
|
||||
year = sheet.year
|
||||
@@ -2797,6 +2761,26 @@ class SaveCellsView(View):
|
||||
else:
|
||||
prozent = Decimal('0')
|
||||
set_val(m3_client, 15, prozent, is_calculated=True)
|
||||
# 9. Simple client(s): only Bezug, Sammelrückführungen, Verbraucherverluste = Sammel - Bezug
|
||||
SIMPLE_RIGHT_CLIENTS = ["Merck"] # use the exact database name here
|
||||
|
||||
for client_name in SIMPLE_RIGHT_CLIENTS:
|
||||
# Bezug (row 8)
|
||||
bezug = get_val(client_name, 8)
|
||||
|
||||
# Sammelrückführungen (row 4)
|
||||
sammel = get_val(client_name, 4)
|
||||
|
||||
# Verbraucherverluste (row 14) = Sammelrückführungen - Bezug
|
||||
verbrauch = bezug - sammel
|
||||
set_val(client_name, 14, verbrauch, is_calculated=True)
|
||||
|
||||
# % (row 15) = Verbraucherverluste / Bezug
|
||||
if bezug != 0:
|
||||
prozent = verbrauch / bezug
|
||||
else:
|
||||
prozent = Decimal("0")
|
||||
set_val(client_name, 15, prozent, is_calculated=True)
|
||||
|
||||
return updated_cells
|
||||
|
||||
@@ -4360,41 +4344,7 @@ class CheckSheetView(View):
|
||||
})
|
||||
|
||||
|
||||
class QuickDebugView(View):
|
||||
def get(self, request):
|
||||
# Get ALL sheets
|
||||
sheets = MonthlySheet.objects.all().order_by('year', 'month')
|
||||
|
||||
result = {
|
||||
'sheets': []
|
||||
}
|
||||
|
||||
for sheet in sheets:
|
||||
sheet_info = {
|
||||
'id': sheet.id,
|
||||
'display': f"{sheet.year}-{sheet.month:02d}",
|
||||
'url': f"/sheet/{sheet.year}/{sheet.month}/", # CHANGED THIS LINE
|
||||
'sheet_url_pattern': 'sheet/{year}/{month}/', # Add this for clarity
|
||||
}
|
||||
|
||||
# Count cells with data for first client in top_left table
|
||||
first_client = Client.objects.first()
|
||||
if first_client:
|
||||
test_cells = sheet.cells.filter(
|
||||
client=first_client,
|
||||
table_type='top_left',
|
||||
row_index__in=[8, 9, 10] # Rows 9, 10, 11
|
||||
).order_by('row_index')
|
||||
|
||||
cell_values = {}
|
||||
for cell in test_cells:
|
||||
cell_values[f"row_{cell.row_index}"] = str(cell.value) if cell.value else "Empty"
|
||||
|
||||
sheet_info['test_cells'] = cell_values
|
||||
else:
|
||||
sheet_info['test_cells'] = "No clients"
|
||||
|
||||
result['sheets'].append(sheet_info)
|
||||
|
||||
|
||||
return JsonResponse(result)
|
||||
class TestFormulaView(View):
|
||||
@@ -4417,46 +4367,6 @@ class TestFormulaView(View):
|
||||
})
|
||||
|
||||
|
||||
class SimpleDebugView(View):
|
||||
"""Simplest debug view to check if things are working"""
|
||||
def get(self, request):
|
||||
sheet_id = request.GET.get('sheet_id', 1)
|
||||
|
||||
try:
|
||||
sheet = MonthlySheet.objects.get(id=sheet_id)
|
||||
|
||||
# Get first client
|
||||
client = Client.objects.first()
|
||||
if not client:
|
||||
return JsonResponse({'error': 'No clients found'})
|
||||
|
||||
# Check a few cells
|
||||
cells = Cell.objects.filter(
|
||||
sheet=sheet,
|
||||
client=client,
|
||||
table_type='top_left',
|
||||
row_index__in=[8, 9, 10]
|
||||
).order_by('row_index')
|
||||
|
||||
cell_data = []
|
||||
for cell in cells:
|
||||
cell_data.append({
|
||||
'row_index': cell.row_index,
|
||||
'ui_row': cell.row_index + 1,
|
||||
'value': str(cell.value) if cell.value is not None else 'Empty',
|
||||
'cell_id': cell.id
|
||||
})
|
||||
|
||||
return JsonResponse({
|
||||
'sheet': f"{sheet.year}-{sheet.month}",
|
||||
'sheet_id': sheet.id,
|
||||
'client': client.name,
|
||||
'cells': cell_data,
|
||||
'note': 'Row 8 = UI Row 9, Row 9 = UI Row 10, Row 10 = UI Row 11'
|
||||
})
|
||||
|
||||
except MonthlySheet.DoesNotExist:
|
||||
return JsonResponse({'error': f'Sheet with id {sheet_id} not found'})
|
||||
|
||||
def halfyear_settings(request):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user