generated from coulomb/repo-seed
107 lines
3.2 KiB
Python
107 lines
3.2 KiB
Python
from datetime import date
|
|
from decimal import Decimal
|
|
|
|
PHASEN = [
|
|
(1, 'Recherche & Unterlagen'),
|
|
(2, 'Teilnahmeentscheidung'),
|
|
(3, 'Detaillierte Durchsicht'),
|
|
(4, 'Bieterfragen & Klärung'),
|
|
(5, 'Preismodell'),
|
|
(6, 'Unterlagen finalisieren'),
|
|
(7, 'Abgabe'),
|
|
(8, 'Zuschlag / Nachbetrachtung'),
|
|
]
|
|
|
|
# Maps Ausschreibung.status integer to phase number
|
|
STATUS_TO_PHASE = {
|
|
1: 1, 2: 1, # Recherche
|
|
3: 2, # Teilnahmeentscheidung
|
|
4: 3, 5: 3, # Durchsicht
|
|
6: 4, 7: 4, # Bieterfragen
|
|
8: 5, # Preise
|
|
9: 6, # Finalisierung
|
|
10: 7, 11: 7, # Abgabe
|
|
12: 8, 13: 8, # Nachbetrachtung
|
|
}
|
|
|
|
|
|
def build_phase_nav(ausschreibung, current_url=''):
|
|
aktuelle_phase = STATUS_TO_PHASE.get(ausschreibung.status, 1)
|
|
base = f'/ausschreibungen/{ausschreibung.pk}'
|
|
phase_urls = {
|
|
1: f'{base}/',
|
|
2: f'{base}/teilnahmeentscheidung/',
|
|
3: f'{base}/anforderungen/',
|
|
4: f'{base}/bieterfragen/',
|
|
5: f'{base}/preise/',
|
|
6: f'{base}/dokumente/',
|
|
7: f'{base}/abgabe/',
|
|
8: f'{base}/nachbetrachtung/',
|
|
}
|
|
return [
|
|
{
|
|
'nummer': num,
|
|
'name': name,
|
|
'url': phase_urls[num],
|
|
'aktiv': num == aktuelle_phase,
|
|
'erledigt': num < aktuelle_phase,
|
|
'warnung': False,
|
|
}
|
|
for num, name in PHASEN
|
|
]
|
|
|
|
|
|
def get_deadline_warnings(ausschreibung):
|
|
"""Gibt Liste von Warnungen für nahende Fristen zurück."""
|
|
warnings = []
|
|
heute = date.today()
|
|
if ausschreibung.bieterfragen_bis:
|
|
delta = (ausschreibung.bieterfragen_bis - heute).days
|
|
if delta <= 3:
|
|
warnings.append({
|
|
'typ': 'bieterfragen',
|
|
'tage': delta,
|
|
'farbe': 'red' if delta <= 1 else 'amber',
|
|
})
|
|
if ausschreibung.abgabe_bis:
|
|
abgabe_date = (
|
|
ausschreibung.abgabe_bis.date()
|
|
if hasattr(ausschreibung.abgabe_bis, 'date')
|
|
else ausschreibung.abgabe_bis
|
|
)
|
|
delta = (abgabe_date - heute).days
|
|
if delta <= 14:
|
|
warnings.append({
|
|
'typ': 'abgabe',
|
|
'tage': delta,
|
|
'farbe': 'red' if delta <= 3 else 'amber',
|
|
})
|
|
return warnings
|
|
|
|
|
|
def gewichteter_durchschnitt(preispunkte, feld='einzelpreis'):
|
|
"""Berechnet gewichteten Durchschnitt für Preispunkte.
|
|
|
|
Punkte mit Gewicht 0,0 werden ausgeschlossen.
|
|
Gibt None zurück wenn keine verwertbaren Punkte vorhanden.
|
|
"""
|
|
relevante = [
|
|
p for p in preispunkte
|
|
if getattr(p, feld) is not None and p.vergleichsgewicht > 0
|
|
]
|
|
if not relevante:
|
|
return None
|
|
summe_gewichte = sum(p.vergleichsgewicht for p in relevante)
|
|
if summe_gewichte == 0:
|
|
return None
|
|
summe = sum(getattr(p, feld) * p.vergleichsgewicht for p in relevante)
|
|
werte = [getattr(p, feld) for p in relevante]
|
|
return {
|
|
'wert': summe / summe_gewichte,
|
|
'summe_gewichte': summe_gewichte,
|
|
'anzahl': len(relevante),
|
|
'minimum': min(werte),
|
|
'maximum': max(werte),
|
|
'ungewichtet': sum(werte) / len(werte),
|
|
}
|