generated from coulomb/repo-seed
Prototype implementation
This commit is contained in:
0
vergabe_teilnahme/apps/ausschreibungen/__init__.py
Normal file
0
vergabe_teilnahme/apps/ausschreibungen/__init__.py
Normal file
11
vergabe_teilnahme/apps/ausschreibungen/admin.py
Normal file
11
vergabe_teilnahme/apps/ausschreibungen/admin.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Ausschreibung
|
||||
|
||||
|
||||
@admin.register(Ausschreibung)
|
||||
class AusschreibungAdmin(admin.ModelAdmin):
|
||||
list_display = ['titel', 'ausschreiber', 'status', 'abgabe_bis', 'teilnahmeentscheidung']
|
||||
list_filter = ['status', 'teilnahmeentscheidung', 'vergabeart']
|
||||
search_fields = ['titel', 'ausschreiber', 'vergabenummer']
|
||||
date_hierarchy = 'abgabe_bis'
|
||||
5
vergabe_teilnahme/apps/ausschreibungen/apps.py
Normal file
5
vergabe_teilnahme/apps/ausschreibungen/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class AusschreibungenConfig(AppConfig):
|
||||
name = 'vergabe_teilnahme.apps.ausschreibungen'
|
||||
@@ -0,0 +1,53 @@
|
||||
# Generated by Django 6.0.5 on 2026-05-08 10:32
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Ausschreibung',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('titel', models.CharField(max_length=400)),
|
||||
('ausschreiber', models.CharField(max_length=300)),
|
||||
('vergabeplattform', models.CharField(blank=True, max_length=200)),
|
||||
('vergabenummer', models.CharField(blank=True, max_length=100)),
|
||||
('vergabeart', models.CharField(blank=True, choices=[('oeffentlich', 'Öffentliche Ausschreibung'), ('beschraenkt', 'Beschränkte Ausschreibung'), ('freihanding', 'Freihändige Vergabe'), ('wettbewerb', 'Wettbewerb'), ('rahmenvertrag', 'Rahmenvertrag'), ('sonstige', 'Sonstige')], max_length=30)),
|
||||
('status', models.PositiveSmallIntegerField(choices=[(1, 'Recherche & Unterlagen'), (2, 'Teilnahmeentscheidung'), (3, 'Detaillierte Durchsicht'), (4, 'Klärungsphase'), (5, 'Preismodell'), (6, 'Unterlagen finalisieren'), (7, 'Abgabe'), (8, 'Zuschlag / Nachbetrachtung'), (9, 'Abgegeben'), (10, 'Gewonnen'), (11, 'Verloren'), (12, 'Aufgehoben'), (13, 'Zurückgezogen')], default=1)),
|
||||
('teilnahmeentscheidung', models.CharField(choices=[('offen', 'Offen'), ('teilnahme', 'Teilnahme'), ('ablehnung', 'Ablehnung')], default='offen', max_length=20)),
|
||||
('entscheidungsbegruendung', models.TextField(blank=True)),
|
||||
('veroeffentlichungsdatum', models.DateField(blank=True, null=True)),
|
||||
('bieterfragen_bis', models.DateField(blank=True, null=True)),
|
||||
('abgabe_bis', models.DateTimeField(blank=True, null=True)),
|
||||
('bindefrist', models.DateField(blank=True, null=True)),
|
||||
('leistungsbeschreibung', models.TextField(blank=True)),
|
||||
('branche', models.CharField(blank=True, max_length=200)),
|
||||
('schlagwoerter', models.CharField(blank=True, max_length=500)),
|
||||
('geschaetztes_volumen', models.DecimalField(blank=True, decimal_places=2, max_digits=14, null=True)),
|
||||
('laufzeit', models.CharField(blank=True, max_length=100)),
|
||||
('optionen', models.TextField(blank=True)),
|
||||
('fundstelle_url', models.URLField(blank=True, max_length=1000)),
|
||||
('unterlagen_erhalten', models.BooleanField(default=False)),
|
||||
('unterlagen_erhalten_am', models.DateField(blank=True, null=True)),
|
||||
('erstellt_am', models.DateTimeField(auto_now_add=True)),
|
||||
('geaendert_am', models.DateTimeField(auto_now=True)),
|
||||
('bid_manager', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='verwaltete_ausschreibungen', to=settings.AUTH_USER_MODEL)),
|
||||
('team', models.ManyToManyField(blank=True, related_name='team_ausschreibungen', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Ausschreibung',
|
||||
'verbose_name_plural': 'Ausschreibungen',
|
||||
'ordering': ['-erstellt_am'],
|
||||
},
|
||||
),
|
||||
]
|
||||
107
vergabe_teilnahme/apps/ausschreibungen/models.py
Normal file
107
vergabe_teilnahme/apps/ausschreibungen/models.py
Normal file
@@ -0,0 +1,107 @@
|
||||
from datetime import date
|
||||
|
||||
from django.db import models
|
||||
|
||||
from vergabe_teilnahme.apps.core.models import FlexibleModel
|
||||
|
||||
|
||||
class Ausschreibung(FlexibleModel):
|
||||
STATUS_CHOICES = [
|
||||
(1, 'Recherche & Unterlagen'),
|
||||
(2, 'Teilnahmeentscheidung'),
|
||||
(3, 'Detaillierte Durchsicht'),
|
||||
(4, 'Klärungsphase'),
|
||||
(5, 'Preismodell'),
|
||||
(6, 'Unterlagen finalisieren'),
|
||||
(7, 'Abgabe'),
|
||||
(8, 'Zuschlag / Nachbetrachtung'),
|
||||
(9, 'Abgegeben'),
|
||||
(10, 'Gewonnen'),
|
||||
(11, 'Verloren'),
|
||||
(12, 'Aufgehoben'),
|
||||
(13, 'Zurückgezogen'),
|
||||
]
|
||||
TEILNAHME_CHOICES = [
|
||||
('offen', 'Offen'),
|
||||
('teilnahme', 'Teilnahme'),
|
||||
('ablehnung', 'Ablehnung'),
|
||||
]
|
||||
VERGABEART_CHOICES = [
|
||||
('oeffentlich', 'Öffentliche Ausschreibung'),
|
||||
('beschraenkt', 'Beschränkte Ausschreibung'),
|
||||
('freihanding', 'Freihändige Vergabe'),
|
||||
('wettbewerb', 'Wettbewerb'),
|
||||
('rahmenvertrag', 'Rahmenvertrag'),
|
||||
('sonstige', 'Sonstige'),
|
||||
]
|
||||
|
||||
titel = models.CharField(max_length=400)
|
||||
ausschreiber = models.CharField(max_length=300)
|
||||
vergabeplattform = models.CharField(max_length=200, blank=True)
|
||||
vergabenummer = models.CharField(max_length=100, blank=True)
|
||||
vergabeart = models.CharField(max_length=30, choices=VERGABEART_CHOICES, blank=True)
|
||||
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES, default=1)
|
||||
teilnahmeentscheidung = models.CharField(
|
||||
max_length=20, choices=TEILNAHME_CHOICES, default='offen'
|
||||
)
|
||||
entscheidungsbegruendung = models.TextField(blank=True)
|
||||
|
||||
# Fristen
|
||||
veroeffentlichungsdatum = models.DateField(null=True, blank=True)
|
||||
bieterfragen_bis = models.DateField(null=True, blank=True)
|
||||
abgabe_bis = models.DateTimeField(null=True, blank=True)
|
||||
bindefrist = models.DateField(null=True, blank=True)
|
||||
|
||||
# Verantwortlichkeiten
|
||||
bid_manager = models.ForeignKey(
|
||||
'accounts.Mitarbeiter', on_delete=models.SET_NULL,
|
||||
null=True, blank=True, related_name='verwaltete_ausschreibungen'
|
||||
)
|
||||
team = models.ManyToManyField(
|
||||
'accounts.Mitarbeiter', blank=True, related_name='team_ausschreibungen'
|
||||
)
|
||||
|
||||
# Beschreibung & Klassifizierung
|
||||
leistungsbeschreibung = models.TextField(blank=True)
|
||||
branche = models.CharField(max_length=200, blank=True)
|
||||
schlagwoerter = models.CharField(max_length=500, blank=True)
|
||||
geschaetztes_volumen = models.DecimalField(max_digits=14, decimal_places=2, null=True, blank=True)
|
||||
laufzeit = models.CharField(max_length=100, blank=True)
|
||||
optionen = models.TextField(blank=True)
|
||||
|
||||
# Herkunft & Dokumente
|
||||
fundstelle_url = models.URLField(max_length=1000, blank=True)
|
||||
unterlagen_erhalten = models.BooleanField(default=False)
|
||||
unterlagen_erhalten_am = models.DateField(null=True, blank=True)
|
||||
|
||||
# Timestamps
|
||||
erstellt_am = models.DateTimeField(auto_now_add=True)
|
||||
geaendert_am = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-erstellt_am']
|
||||
verbose_name = 'Ausschreibung'
|
||||
verbose_name_plural = 'Ausschreibungen'
|
||||
|
||||
def __str__(self):
|
||||
return self.titel
|
||||
|
||||
@property
|
||||
def ist_aktiv(self):
|
||||
return 1 <= self.status <= 9
|
||||
|
||||
@property
|
||||
def naechste_frist(self):
|
||||
heute = date.today()
|
||||
kandidaten = []
|
||||
if self.bieterfragen_bis and self.bieterfragen_bis >= heute:
|
||||
kandidaten.append(self.bieterfragen_bis)
|
||||
if self.abgabe_bis:
|
||||
abgabe = self.abgabe_bis.date() if hasattr(self.abgabe_bis, 'date') else self.abgabe_bis
|
||||
if abgabe >= heute:
|
||||
kandidaten.append(abgabe)
|
||||
return min(kandidaten) if kandidaten else None
|
||||
|
||||
@property
|
||||
def phase_nummer(self):
|
||||
return min(8, max(1, self.status))
|
||||
3
vergabe_teilnahme/apps/ausschreibungen/tests.py
Normal file
3
vergabe_teilnahme/apps/ausschreibungen/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
vergabe_teilnahme/apps/ausschreibungen/urls.py
Normal file
9
vergabe_teilnahme/apps/ausschreibungen/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
|
||||
from . import views
|
||||
|
||||
app_name = 'ausschreibungen'
|
||||
|
||||
urlpatterns = [
|
||||
path('', views.dashboard, name='dashboard'),
|
||||
]
|
||||
7
vergabe_teilnahme/apps/ausschreibungen/views.py
Normal file
7
vergabe_teilnahme/apps/ausschreibungen/views.py
Normal file
@@ -0,0 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def dashboard(request):
|
||||
return render(request, 'ausschreibungen/dashboard.html', {
|
||||
'breadcrumbs': [{'label': 'Übersicht', 'url': None}],
|
||||
})
|
||||
Reference in New Issue
Block a user