Prototype implementation
This commit is contained in:
parent
315143a6fc
commit
14b0bc6d01
160 changed files with 5731 additions and 42 deletions
106
vergabe_teilnahme/apps/core/services.py
Normal file
106
vergabe_teilnahme/apps/core/services.py
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
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),
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue