All checks passed!
This commit is contained in:
BIN
Binary file not shown.
@@ -0,0 +1,17 @@
|
||||
[tool.ruff]
|
||||
line-length = 100
|
||||
target-version = "py312"
|
||||
exclude = [
|
||||
"migrations",
|
||||
".venv",
|
||||
"venv",
|
||||
"__pycache__",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "I"]
|
||||
ignore = []
|
||||
|
||||
[tool.ruff.format]
|
||||
quote-style = "double"
|
||||
indent-style = "space"
|
||||
+1
-1
@@ -15,7 +15,7 @@ Including another URLconftesting test
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path, include
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls), # Admin site
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
import django
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "excel_mimic.settings")
|
||||
django.setup()
|
||||
|
||||
from sheets.models import ExcelEntry
|
||||
|
||||
|
||||
TWO_DEC = Decimal("0.00")
|
||||
|
||||
def q2(x):
|
||||
return Decimal(str(x)).quantize(TWO_DEC, rounding=ROUND_HALF_UP)
|
||||
|
||||
fields = ["korrig_druck", "nm3", "lhe", "lhe_ges"]
|
||||
|
||||
to_update = []
|
||||
total = 0
|
||||
changed_count = 0
|
||||
|
||||
qs = ExcelEntry.objects.all().only("id", *fields)
|
||||
|
||||
for e in qs.iterator(chunk_size=500):
|
||||
total += 1
|
||||
changed = False
|
||||
|
||||
for f in fields:
|
||||
val = getattr(e, f)
|
||||
if val is not None:
|
||||
new = q2(val)
|
||||
if new != val:
|
||||
setattr(e, f, new)
|
||||
changed = True
|
||||
|
||||
if changed:
|
||||
to_update.append(e)
|
||||
changed_count += 1
|
||||
|
||||
if len(to_update) >= 500:
|
||||
ExcelEntry.objects.bulk_update(to_update, fields)
|
||||
to_update = []
|
||||
print(f"processed={total}, changed={changed_count}")
|
||||
|
||||
if to_update:
|
||||
ExcelEntry.objects.bulk_update(to_update, fields)
|
||||
|
||||
print(f"DONE. processed={total}, changed={changed_count}")
|
||||
+1
-1
@@ -5,4 +5,4 @@ class SheetsConfig(AppConfig):
|
||||
name = 'sheets'
|
||||
|
||||
def ready(self):
|
||||
import sheets.signals
|
||||
pass
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
from django import forms
|
||||
from .models import ExcelEntry, Betriebskosten, Institute
|
||||
from .models import ExcelEntry, Betriebskosten
|
||||
|
||||
|
||||
|
||||
|
||||
+7
-8
@@ -1,7 +1,10 @@
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models import DecimalField, Value
|
||||
class Institute(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
|
||||
@@ -73,8 +76,7 @@ class CellReference(models.Model):
|
||||
class Meta:
|
||||
unique_together = ['source_cell', 'target_cell']
|
||||
|
||||
from decimal import Decimal
|
||||
from django.db import models
|
||||
|
||||
|
||||
class Betriebskosten(models.Model):
|
||||
KOSTENTYP_CHOICES = [
|
||||
@@ -263,10 +265,7 @@ class BetriebskostenSummary(models.Model):
|
||||
umlage_personal = models.DecimalField(max_digits=12, decimal_places=2, default=0)
|
||||
|
||||
def recalculate(self):
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models import DecimalField, Value
|
||||
from .models import Betriebskosten
|
||||
|
||||
|
||||
items = Betriebskosten.objects.all()
|
||||
|
||||
@@ -291,7 +290,7 @@ class BetriebskostenSummary(models.Model):
|
||||
self.save()
|
||||
|
||||
# models.py
|
||||
from django.db import models
|
||||
|
||||
|
||||
class AbrechnungCell(models.Model):
|
||||
"""
|
||||
|
||||
-5041
File diff suppressed because it is too large
Load Diff
@@ -1,353 +0,0 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="spreadsheet-container">
|
||||
|
||||
<style>
|
||||
.spreadsheet-container {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.sheet-navigation {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.sheet-navigation h2 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sheet-navigation a,
|
||||
.sheet-navigation span {
|
||||
text-decoration: none;
|
||||
color: #007bff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.sheet-navigation .disabled-link {
|
||||
color: #aaa;
|
||||
cursor: default;
|
||||
}
|
||||
.indent {
|
||||
padding-left: 18px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
width: 60px;
|
||||
}
|
||||
.top-tables {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
flex: 1;
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.table-container h3 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.sheet-table,
|
||||
.spreadsheet-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.sheet-table th,
|
||||
.sheet-table td,
|
||||
.spreadsheet-table th,
|
||||
.spreadsheet-table td {
|
||||
border: 1px solid #dee2e6;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.sheet-table th,
|
||||
.spreadsheet-table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.row-label {
|
||||
background-color: #f9f9f9;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.number-cell {
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sheet-table tbody tr:nth-child(even),
|
||||
.spreadsheet-table tbody tr:nth-child(even) {
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.sheet-table tbody tr:hover,
|
||||
.spreadsheet-table tbody tr:hover {
|
||||
background-color: #f1f3f5;
|
||||
}
|
||||
|
||||
.summary-row {
|
||||
font-weight: bold;
|
||||
background-color: #e8f4f8;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="sheet-navigation">
|
||||
<a href="{% url 'clients_list' %}">← Helium Output Übersicht</a>
|
||||
<h2>
|
||||
{% with first=window.0 last=window|last %}
|
||||
Halbjahres-Bilanz ({{ first.1 }}/{{ first.0 }} – {{ last.1 }}/{{ last.0 }})
|
||||
{% endwith %}
|
||||
</h2>
|
||||
{% with first=window.0 %}
|
||||
<a href="{% url 'monthly_sheet' first.0 first.1 %}">Monatsblätter</a>
|
||||
{% endwith %}
|
||||
</div>
|
||||
|
||||
<div class="top-tables">
|
||||
<!-- LEFT TABLE (Top Left – AG Vogel / AG Halfm / IKP) -->
|
||||
<div class="table-container">
|
||||
<h3>Top Left – Halbjahresbilanz</h3>
|
||||
<table class="sheet-table spreadsheet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bezeichnung</th>
|
||||
{% for c in clients_left %}
|
||||
<th>{{ c }}</th>
|
||||
{% endfor %}
|
||||
<th>Σ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows_left %}
|
||||
<tr>
|
||||
<td>{{ row.label }}</td>
|
||||
{% for v in row.values %}
|
||||
<td class="number-cell">
|
||||
{% if row.is_percent and v is not None %}
|
||||
{{ v|floatformat:4 }}
|
||||
{% elif v is not None %}
|
||||
{{ v|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="number-cell">
|
||||
{% if row.is_percent and row.total %}
|
||||
{{ row.total|floatformat:4 }}
|
||||
{% elif row.total is not None %}
|
||||
{{ row.total|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- RIGHT TABLE (Top Right – Dr. Fohrer / AG Buntk. / etc.) -->
|
||||
<div class="table-container">
|
||||
<h3>Top Right – Halbjahresbilanz</h3>
|
||||
<table class="sheet-table spreadsheet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bezeichnung</th>
|
||||
{% for c in clients_right %}
|
||||
<th>{{ c }}</th>
|
||||
{% endfor %}
|
||||
<th>Σ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows_right %}
|
||||
<tr>
|
||||
<td>{{ row.label }}</td>
|
||||
{% for v in row.values %}
|
||||
<td class="number-cell">
|
||||
{% if row.is_text_row %}
|
||||
{{ v }}
|
||||
{% elif row.is_percent and v is not None %}
|
||||
{{ v|floatformat:4 }}
|
||||
{% elif v is not None %}
|
||||
{{ v|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% endfor %}
|
||||
<td class="number-cell">
|
||||
{% if row.is_text_row %}
|
||||
{{ row.total }}
|
||||
{% elif row.is_percent and row.total %}
|
||||
{{ row.total|floatformat:4 }}
|
||||
{% elif row.total is not None %}
|
||||
{{ row.total|floatformat:2 }}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-container overall-summary-table">
|
||||
<h3>Summe</h3>
|
||||
<table class="sheet-table spreadsheet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Bezeichnung</th>
|
||||
<th>Σ</th>
|
||||
<th>Licht-wiese</th>
|
||||
<th>Chemie</th>
|
||||
<th>MaWi</th>
|
||||
<th>M3</th>
|
||||
</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>
|
||||
|
||||
<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>
|
||||
</table>
|
||||
</div>
|
||||
<div class="table-container" style="margin-top: 20px;">
|
||||
<h3>Bottom Table 1 – Bilanz (read-only)</h3>
|
||||
|
||||
<table class="sheet-table spreadsheet-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Gasspeicher</th>
|
||||
<th>Volumen</th>
|
||||
<th>bar</th>
|
||||
<th>korrigiert</th>
|
||||
<th>Nm³</th>
|
||||
<th>Lit. LHe</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for r in bottom1_rows %}
|
||||
<tr class="{% if r.is_total %}summary-row{% endif %}">
|
||||
<td class="row-label {% if r.indent %}indent{% endif %}">{{ r.label }}</td>
|
||||
<td class="number-cell">{{ r.volume|floatformat:1 }}</td>
|
||||
<td class="number-cell">{{ r.bar|floatformat:0 }}</td>
|
||||
<td class="number-cell">{{ r.korr|floatformat:1 }}</td>
|
||||
<td class="number-cell">{{ r.nm3|floatformat:0 }}</td>
|
||||
<td class="number-cell">{{ r.lhe|floatformat:0 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="table-container bottom-table-2">
|
||||
<h3>Bottom Table 2 – Verbraucherbestand L-He (read-only)</h3>
|
||||
|
||||
<table class="sheet-table spreadsheet-table">
|
||||
<tbody>
|
||||
<!-- Row 38 -->
|
||||
<tr>
|
||||
<td class="row-label" colspan="5">Verbraucherbestand</td>
|
||||
<td class="readonly-cell">{{ bottom2.j38|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.k38|floatformat:2 }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 39 -->
|
||||
<tr>
|
||||
<td class="row-label">+ Anlage:</td>
|
||||
<td>Gefäss 2,5:</td>
|
||||
<td class="readonly-cell">{{ bottom2.g39|floatformat:2 }}</td>
|
||||
<td>Gefäss 1,0:</td>
|
||||
<td class="readonly-cell">{{ bottom2.i39|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.j39|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.k39|floatformat:2 }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 40 -->
|
||||
<tr>
|
||||
<td class="row-label">+ Kaltgas:</td>
|
||||
<td>Gefäss 2,5:</td>
|
||||
<td class="readonly-cell">{{ bottom2.g40|default_if_none:""|floatformat:2 }}</td>
|
||||
<td>Gefäss 1,0:</td>
|
||||
<td class="readonly-cell">{{ bottom2.i40|default_if_none:""|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.j40|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.k40|floatformat:2 }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 43 -->
|
||||
<tr>
|
||||
<td class="row-label" colspan="5">Bestand flüssig He</td>
|
||||
<td class="readonly-cell">{{ bottom2.j43|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.k43|floatformat:2 }}</td>
|
||||
</tr>
|
||||
|
||||
<!-- Row 44 -->
|
||||
<tr>
|
||||
<td class="row-label" colspan="5">Gesamtbestand neu:</td>
|
||||
<td class="readonly-cell">{{ bottom2.j44|floatformat:2 }}</td>
|
||||
<td class="readonly-cell">{{ bottom2.k44|floatformat:2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div> {# closes .spreadsheet-container #}
|
||||
{% endblock %}
|
||||
-4401
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,14 +1,10 @@
|
||||
# sheets/services/halfyear_calc.py
|
||||
from __future__ import annotations
|
||||
from django.db.models.functions import Coalesce
|
||||
from decimal import Decimal
|
||||
from typing import Dict, Any
|
||||
from django.shortcuts import redirect, render
|
||||
from decimal import Decimal
|
||||
from sheets.models import (
|
||||
Client, SecondTableEntry, Institute, ExcelEntry,
|
||||
Betriebskosten, MonthlySheet, Cell, CellReference, MonthlySummary ,BetriebskostenSummary,AbrechnungCell
|
||||
)
|
||||
Client, SecondTableEntry, ExcelEntry,
|
||||
MonthlySheet, Cell, MonthlySummary)
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models import DecimalField, Value
|
||||
@@ -172,7 +168,6 @@ def build_halfyear_window(interval_year: int, start_month: int):
|
||||
window.append((y, m))
|
||||
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.
|
||||
@@ -268,8 +263,7 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st
|
||||
|
||||
|
||||
chosen_sheet_bottom2, bottom2_inputs = pick_bottom2_from_window(window, sheets_by_ym, prev_sheet)
|
||||
bottom2_g39 = bottom2_inputs["g39"]
|
||||
bottom2_i39 = bottom2_inputs["i39"]
|
||||
|
||||
# ----------------------------
|
||||
# HALF-YEAR BOTTOM TABLE 1 (Bilanz) - Read only
|
||||
# ----------------------------
|
||||
@@ -351,7 +345,6 @@ def compute_halfyear_context(interval_year: int, interval_start: int) -> Dict[st
|
||||
"nm3": nm3_sum_27_35,
|
||||
"lhe": lhe_sum_27_35,
|
||||
})
|
||||
start_sheet = sheets_by_ym.get((start_year, start_month))
|
||||
# ------------------------------------------------------------
|
||||
# Bottom Table 2 (Halbjahres Bilanz) – server-side recalcBottom2()
|
||||
# ------------------------------------------------------------
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
|
||||
+2
-2
@@ -27,10 +27,10 @@ urlpatterns = [
|
||||
path('set-halfyear-interval/', set_halfyear_interval, name='set_halfyear_interval'),
|
||||
path('halfyear-bilanz/', views.halfyear_balance_view, name='halfyear_balance'),
|
||||
path('sheet/<int:year>/<int:month>/', MonthlySheetView.as_view(), name='monthly_sheet'),
|
||||
|
||||
path('settings/halfyear/', halfyear_settings, name='halfyear_settings'),
|
||||
path('sheet/<int:year>/<int:month>/', views.MonthlySheetView.as_view(), name='monthly_sheet'),
|
||||
path('summary/<int:year>/<int:start_month>/', views.SummarySheetView.as_view(), name='summary_sheet'),
|
||||
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-cells/", SaveCellsView.as_view(), name="save_cells"),
|
||||
path('save-month-summary/', SaveMonthSummaryView.as_view(), name='save_month_summary'),
|
||||
path('calculate/', views.CalculateView.as_view(), name='calculate'),
|
||||
|
||||
|
||||
+34
-96
@@ -4,24 +4,21 @@ from django.views.decorators.csrf import csrf_exempt
|
||||
from django.db.models import Sum, Value, DecimalField
|
||||
from django.http import JsonResponse
|
||||
from django.db.models import Q
|
||||
from django.db.models import Count
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from django.apps import apps
|
||||
from datetime import date, datetime
|
||||
from decimal import InvalidOperation
|
||||
from datetime import datetime
|
||||
from sheets.services.halfyear_calc import compute_halfyear_context
|
||||
import calendar
|
||||
from django.utils import timezone
|
||||
import re
|
||||
from django.views.generic import TemplateView, View
|
||||
from .models import (
|
||||
Client, SecondTableEntry, Institute, ExcelEntry,
|
||||
Betriebskosten, MonthlySheet, Cell, CellReference, MonthlySummary ,BetriebskostenSummary,AbrechnungCell
|
||||
Betriebskosten, MonthlySheet, Cell, MonthlySummary ,BetriebskostenSummary,AbrechnungCell
|
||||
)
|
||||
from django.db.models import Sum
|
||||
from django.urls import reverse
|
||||
from django.db.models.functions import Coalesce
|
||||
from .forms import BetriebskostenForm
|
||||
from django.utils.dateparse import parse_date
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
import json
|
||||
FIRST_SHEET_YEAR = 2025
|
||||
FIRST_SHEET_MONTH = 1
|
||||
@@ -368,13 +365,7 @@ def get_bestand_kannen_for_month(sheet, client_name: str) -> Decimal:
|
||||
"""
|
||||
return get_top_left_value(sheet, client_name, row_index=BESTAND_KANNEN_ROW_INDEX)
|
||||
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models import DecimalField, Value
|
||||
|
||||
from .models import MonthlySheet, SecondTableEntry, Client, Cell
|
||||
from django.shortcuts import redirect, render
|
||||
|
||||
# You already have HALFYEAR_CLIENTS for the left table (AG Vogel, AG Halfm, IKP)
|
||||
HALFYEAR_CLIENTS = ["AG Vogel", "AG Halfm", "IKP"]
|
||||
@@ -463,7 +454,7 @@ def get_top_left_value(sheet, client_name: str, row_index: int) -> Decimal:
|
||||
return Decimal('0')
|
||||
def get_group_clients(group_key):
|
||||
"""Return queryset of clients that belong to a logical group."""
|
||||
from .models import Client # local import to avoid circulars
|
||||
|
||||
|
||||
group = CLIENT_GROUPS.get(group_key)
|
||||
if not group:
|
||||
@@ -473,9 +464,7 @@ def get_group_clients(group_key):
|
||||
|
||||
def calculate_summation(sheet, table_type, row_index, sum_column_index):
|
||||
"""Calculate summation for a row, with special handling for % row"""
|
||||
from decimal import Decimal
|
||||
from .models import Cell
|
||||
|
||||
|
||||
try:
|
||||
# Special case: top_left, % row (Excel B20 -> row_index 19)
|
||||
if table_type == 'top_left' and row_index == 19:
|
||||
@@ -533,9 +522,7 @@ def evaluate_formula(formula, values_dict):
|
||||
Safely evaluate a formula like "10 + 9" where numbers are row indices
|
||||
values_dict: {row_index: decimal_value}
|
||||
"""
|
||||
from decimal import Decimal
|
||||
import re
|
||||
|
||||
|
||||
try:
|
||||
# Create a copy of the formula to work with
|
||||
expr = formula
|
||||
@@ -568,9 +555,7 @@ class MonthlySheetView(TemplateView):
|
||||
|
||||
def populate_helium_input_to_top_right(self, sheet):
|
||||
"""Populate bezug data from SecondTableEntry to top-right table (row 8 = Excel row 12)"""
|
||||
from .models import SecondTableEntry, Cell, Client
|
||||
from django.db.models.functions import Coalesce
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
year = sheet.year
|
||||
month = sheet.month
|
||||
@@ -632,10 +617,7 @@ class MonthlySheetView(TemplateView):
|
||||
return True
|
||||
def calculate_bezug_from_entries(self, sheet, year, month):
|
||||
"""Calculate B11 (Bezug) from SecondTableEntry for all clients - ONLY for non-start sheets"""
|
||||
from .models import SecondTableEntry, Cell, Client
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# Check if this is the start sheet
|
||||
if year == 2025 and month == 1:
|
||||
@@ -664,7 +646,7 @@ class MonthlySheetView(TemplateView):
|
||||
b11_cell.save()
|
||||
|
||||
# Also trigger dependent calculations
|
||||
from .views import SaveCellsView
|
||||
|
||||
save_view = SaveCellsView()
|
||||
save_view.calculate_top_left_dependents(sheet, b11_cell)
|
||||
# In MonthlySheetView.get_context_data() method, update the TOP_RIGHT_CLIENTS and row count:
|
||||
@@ -673,7 +655,7 @@ class MonthlySheetView(TemplateView):
|
||||
|
||||
return True
|
||||
def get_context_data(self, **kwargs):
|
||||
from decimal import Decimal
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
year = self.kwargs.get('year', datetime.now().year)
|
||||
month = self.kwargs.get('month', datetime.now().month)
|
||||
@@ -702,9 +684,7 @@ class MonthlySheetView(TemplateView):
|
||||
# Recalculate dependents once when opening the sheet
|
||||
# ----------------------------------------------------
|
||||
|
||||
from .views import SaveCellsView
|
||||
from .models import Cell
|
||||
|
||||
|
||||
save_view = SaveCellsView()
|
||||
|
||||
# ---- TOP LEFT ----
|
||||
@@ -768,8 +748,7 @@ class MonthlySheetView(TemplateView):
|
||||
# Update row counts in build_group_rows function
|
||||
def build_group_rows(sheet, table_type, client_names):
|
||||
"""Build rows for display in monthly sheet."""
|
||||
from decimal import Decimal
|
||||
from .models import Cell
|
||||
|
||||
MERGED_ROWS = {2, 3, 5, 6, 7, 9, 10, 12, 14, 15}
|
||||
MERGED_PAIRS = [
|
||||
("Dr. Fohrer", "AG Buntk."),
|
||||
@@ -1085,7 +1064,7 @@ class MonthlySheetView(TemplateView):
|
||||
prev_year = year
|
||||
prev_month = month - 1
|
||||
|
||||
from .models import MonthlySheet, Cell, Client
|
||||
|
||||
|
||||
prev_sheet = MonthlySheet.objects.filter(
|
||||
year=prev_year,
|
||||
@@ -1146,8 +1125,7 @@ class MonthlySheetView(TemplateView):
|
||||
- AG Alff + AG Gutfl. share the SAME value (from previous month's AG Alff or AG Gutfl.)
|
||||
M3 clients just copy their own value.
|
||||
"""
|
||||
from .models import MonthlySheet, Cell, Client
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# Do nothing on first sheet
|
||||
if year == FIRST_SHEET_YEAR and month == FIRST_SHEET_MONTH:
|
||||
@@ -1284,7 +1262,7 @@ def get_factor_value(table_type, row_index):
|
||||
|
||||
def recalculate_stand_der_gaszahler(self, sheet):
|
||||
"""Recalculate Stand der Gaszähler for all client pairs"""
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# For Dr. Fohrer and AG Buntk. (L & M columns)
|
||||
try:
|
||||
@@ -1456,7 +1434,7 @@ class AbrechnungView(TemplateView):
|
||||
EDITABLE_ROW_KEYS = {"ghe_bezug", "betrag", "gutschriften"}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
from sheets.services.halfyear_calc import compute_halfyear_context
|
||||
|
||||
context = super().get_context_data(**kwargs)
|
||||
|
||||
interval_year = self.request.session.get("halfyear_year")
|
||||
@@ -1496,22 +1474,6 @@ class AbrechnungView(TemplateView):
|
||||
umlage_personal_total = d(bs.umlage_personal) if bs else Decimal("0")
|
||||
|
||||
# ---- helper: sum lhe_output in the 6-month window for a list of client names ----
|
||||
def sum_output_for_clients_exact(client_names):
|
||||
total = Decimal("0")
|
||||
if not client_names:
|
||||
return total
|
||||
|
||||
for (y, m) in window:
|
||||
total += SecondTableEntry.objects.filter(
|
||||
client__name__in=client_names,
|
||||
date__year=y,
|
||||
date__month=m,
|
||||
).aggregate(
|
||||
total=Coalesce(Sum("lhe_output"), Value(0, output_field=DecimalField()))
|
||||
)["total"] or Decimal("0")
|
||||
|
||||
return total
|
||||
# (NOTE: the date filter above is “wide”; if you prefer exact (y,m) matching, use the loop version below)
|
||||
def sum_output_for_clients_exact(client_names):
|
||||
total = Decimal("0")
|
||||
if not client_names:
|
||||
@@ -1920,13 +1882,7 @@ class AbrechnungView(TemplateView):
|
||||
GROUP_IJKL = ["orgchem_thiele", "phychem_m3_buntkow", "orgchem_fohrer", "mawi_m3_gutfl", "mawi_alff"]
|
||||
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 (prefer computed, fallback to stored)."""
|
||||
if (row_key, col_key) in computed:
|
||||
@@ -2104,7 +2060,7 @@ class RechnungView(TemplateView):
|
||||
# 3) Heliumverluste (l) from Halbjahresbilanz total Verbraucherverluste
|
||||
# (sum all clients left+right)
|
||||
# ------------------------------------------------------------
|
||||
from sheets.services.halfyear_calc import compute_halfyear_context
|
||||
|
||||
half_ctx = compute_halfyear_context(interval_year, interval_start)
|
||||
|
||||
heliumverluste_l = Decimal("0")
|
||||
@@ -2223,14 +2179,7 @@ class SaveAbrechnungCellsView(View):
|
||||
)
|
||||
|
||||
return JsonResponse({"ok": True})
|
||||
def build_halfyear_window(interval_year: int, interval_start_month: int):
|
||||
window = []
|
||||
for offset in range(6):
|
||||
total_index = (interval_start_month - 1) + offset
|
||||
y = interval_year + (total_index // 12)
|
||||
m = (total_index % 12) + 1
|
||||
window.append((y, m))
|
||||
return window
|
||||
|
||||
class SaveCellsView(View):
|
||||
|
||||
def calculate_bottom_3_dependents(self, sheet):
|
||||
@@ -2296,7 +2245,7 @@ class SaveCellsView(View):
|
||||
cell = get_cell(r, c)
|
||||
return dec(cell.value if cell else None)
|
||||
|
||||
f47 = get_val(1, 0)
|
||||
|
||||
g47 = get_val(1, 1)
|
||||
i47 = get_val(1, 2)
|
||||
i50 = get_val(4, 2)
|
||||
@@ -2410,7 +2359,6 @@ class SaveCellsView(View):
|
||||
# If conversion fails, treat as empty
|
||||
new_value = None
|
||||
|
||||
old_value = cell.value
|
||||
cell.value = new_value
|
||||
cell.save()
|
||||
|
||||
@@ -2486,10 +2434,7 @@ class SaveCellsView(View):
|
||||
14: Verbraucherverluste (Liter L-He) - calculated
|
||||
15: % - calculated
|
||||
"""
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum, Count
|
||||
from django.db.models.functions import Coalesce
|
||||
from .models import Client, Cell, ExcelEntry, SecondTableEntry
|
||||
|
||||
|
||||
TOP_RIGHT_CLIENTS = [
|
||||
"Dr. Fohrer", # L
|
||||
@@ -2820,9 +2765,7 @@ class SaveCellsView(View):
|
||||
- Nm³ (col 3): SUM Nm³ rows 0–8
|
||||
- Lit. LHe (col 4): SUM Lit. LHe rows 0–8
|
||||
"""
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from .models import Cell
|
||||
|
||||
|
||||
updated_cells = []
|
||||
|
||||
DATA_ROWS = list(range(0, 9)) # 0–8
|
||||
@@ -2961,9 +2904,7 @@ class SaveCellsView(View):
|
||||
|
||||
def calculate_top_left_dependents(self, sheet, changed_cell):
|
||||
"""Calculate dependent cells in top_left table"""
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
|
||||
client_id = changed_cell.client_id
|
||||
updated_cells = []
|
||||
@@ -2986,7 +2927,7 @@ class SaveCellsView(View):
|
||||
if cell and cell.value is not None:
|
||||
try:
|
||||
return Decimal(str(cell.value))
|
||||
except:
|
||||
except Exception:
|
||||
return Decimal('0')
|
||||
return Decimal('0')
|
||||
|
||||
@@ -3041,13 +2982,13 @@ class SaveCellsView(View):
|
||||
})
|
||||
|
||||
# 4. B11 = Sum of LHe Output from SecondTableEntry for this client/month - Bezug
|
||||
from .models import SecondTableEntry
|
||||
|
||||
client = changed_cell.client
|
||||
|
||||
# Calculate total LHe output for this client in this month
|
||||
# 4. B11 = Bezug (Liter L-He)
|
||||
# For start sheet: manual entry, for other sheets: auto-calculated from SecondTableEntry
|
||||
from .models import SecondTableEntry
|
||||
|
||||
client = changed_cell.client
|
||||
|
||||
b11_cell = cell_dict.get(8) # row_index 8 = Excel B11 (UI row 9)
|
||||
@@ -3258,7 +3199,7 @@ class SaveCellsView(View):
|
||||
|
||||
def recalculate_top_left_table(self, sheet, client_id):
|
||||
"""Recalculate the top-left table for a specific client"""
|
||||
from decimal import Decimal
|
||||
|
||||
|
||||
# Get all cells for this client in top_left table
|
||||
cells = Cell.objects.filter(
|
||||
@@ -3429,7 +3370,7 @@ class SummarySheetView(TemplateView):
|
||||
year = int(self.kwargs.get('year', datetime.now().year))
|
||||
|
||||
# Get 6 monthly sheets
|
||||
months = [(year, m) for m in range(start_month, start_month + 6)]
|
||||
|
||||
sheets = MonthlySheet.objects.filter(
|
||||
year=year,
|
||||
month__in=list(range(start_month, start_month + 6))
|
||||
@@ -3605,7 +3546,7 @@ def set_halfyear_interval(request):
|
||||
return redirect('clients_list')
|
||||
# Table One View (ExcelEntry)
|
||||
def table_one_view(request):
|
||||
from .models import ExcelEntry, Client, Institute
|
||||
|
||||
|
||||
# --- Base queryset for the main Helium Input table ---
|
||||
base_entries = ExcelEntry.objects.all().select_related('client', 'client__institute')
|
||||
@@ -4129,9 +4070,7 @@ def betriebskosten_list(request):
|
||||
|
||||
summary = get_summary()
|
||||
summary.recalculate()
|
||||
from decimal import Decimal
|
||||
from django.db.models import Sum, DecimalField, Value
|
||||
from django.db.models.functions import Coalesce
|
||||
|
||||
|
||||
interval_year = request.session.get("halfyear_year")
|
||||
interval_start = request.session.get("halfyear_start_month")
|
||||
@@ -4287,7 +4226,7 @@ def get_summary():
|
||||
summary, created = BetriebskostenSummary.objects.get_or_create(id=1)
|
||||
return summary
|
||||
|
||||
from django.http import JsonResponse
|
||||
|
||||
|
||||
def update_personalkosten(request):
|
||||
if request.method == "POST":
|
||||
@@ -4345,8 +4284,7 @@ class CheckSheetView(View):
|
||||
|
||||
|
||||
|
||||
|
||||
return JsonResponse(result)
|
||||
|
||||
class TestFormulaView(View):
|
||||
def get(self, request):
|
||||
# Test the formula evaluation directly
|
||||
|
||||
Reference in New Issue
Block a user