paperwork: Add Audits for Waivers and Certifications
This commit is contained in:
parent
b462e6b18f
commit
250c96f1c2
@ -1,3 +1,4 @@
|
|||||||
|
from django import forms
|
||||||
from django.core import mail
|
from django.core import mail
|
||||||
from django.contrib import admin, messages
|
from django.contrib import admin, messages
|
||||||
from django.db.models import Value
|
from django.db.models import Value
|
||||||
@ -8,15 +9,34 @@ from .models import (
|
|||||||
Department,
|
Department,
|
||||||
CertificationDefinition,
|
CertificationDefinition,
|
||||||
Certification,
|
Certification,
|
||||||
|
CertificationAudit,
|
||||||
CertificationVersion,
|
CertificationVersion,
|
||||||
InstructorOrVendor,
|
InstructorOrVendor,
|
||||||
SpecialProgram,
|
SpecialProgram,
|
||||||
Waiver,
|
Waiver,
|
||||||
|
WaiverAudit,
|
||||||
)
|
)
|
||||||
from .forms import CertificationForm
|
from .forms import CertificationForm
|
||||||
from .certification_emails import all_certification_emails
|
from .certification_emails import all_certification_emails
|
||||||
|
|
||||||
|
|
||||||
|
class AlwaysChangedModelForm(forms.models.ModelForm):
|
||||||
|
"""By always returning true even unchanged inlines will get validated and saved."""
|
||||||
|
|
||||||
|
def has_changed(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractAuditInline(admin.TabularInline):
|
||||||
|
extra = 0
|
||||||
|
form = AlwaysChangedModelForm
|
||||||
|
|
||||||
|
def get_formset(self, request, obj=None, **kwargs):
|
||||||
|
formset = super().get_formset(request, obj, **kwargs)
|
||||||
|
formset.form.base_fields["user"].initial = request.user
|
||||||
|
return formset
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Department)
|
@admin.register(Department)
|
||||||
class DepartmentAdmin(admin.ModelAdmin):
|
class DepartmentAdmin(admin.ModelAdmin):
|
||||||
search_fields = ["name"]
|
search_fields = ["name"]
|
||||||
@ -64,6 +84,10 @@ class CertificationDefinitionAdmin(admin.ModelAdmin):
|
|||||||
return obj.latest_version().semantic_version()
|
return obj.latest_version().semantic_version()
|
||||||
|
|
||||||
|
|
||||||
|
class CertificationAuditInline(AbstractAuditInline):
|
||||||
|
model = CertificationAudit
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Certification)
|
@admin.register(Certification)
|
||||||
class CertificationAdmin(admin.ModelAdmin):
|
class CertificationAdmin(admin.ModelAdmin):
|
||||||
form = CertificationForm
|
form = CertificationForm
|
||||||
@ -74,6 +98,7 @@ class CertificationAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
autocomplete_fields = ["member"]
|
autocomplete_fields = ["member"]
|
||||||
exclude = ["shop_lead_notified"]
|
exclude = ["shop_lead_notified"]
|
||||||
|
inlines = [CertificationAuditInline]
|
||||||
|
|
||||||
def get_queryset(self, request):
|
def get_queryset(self, request):
|
||||||
qs = super().get_queryset(request)
|
qs = super().get_queryset(request)
|
||||||
@ -112,6 +137,10 @@ class CertificationAdmin(admin.ModelAdmin):
|
|||||||
def certification_department(self, obj):
|
def certification_department(self, obj):
|
||||||
return obj.certification_version.definition.department
|
return obj.certification_version.definition.department
|
||||||
|
|
||||||
|
@admin.display(description="Latest Audit")
|
||||||
|
def latest_audit(self, obj):
|
||||||
|
return obj.audits.latest()
|
||||||
|
|
||||||
list_display = [
|
list_display = [
|
||||||
"certification_name",
|
"certification_name",
|
||||||
"name",
|
"name",
|
||||||
@ -121,6 +150,7 @@ class CertificationAdmin(admin.ModelAdmin):
|
|||||||
"date",
|
"date",
|
||||||
"shop_lead_notified",
|
"shop_lead_notified",
|
||||||
"certified_by",
|
"certified_by",
|
||||||
|
"latest_audit",
|
||||||
]
|
]
|
||||||
list_display_links = [
|
list_display_links = [
|
||||||
"certification_name",
|
"certification_name",
|
||||||
@ -129,6 +159,7 @@ class CertificationAdmin(admin.ModelAdmin):
|
|||||||
list_filter = [
|
list_filter = [
|
||||||
"certification_version__definition__department",
|
"certification_version__definition__department",
|
||||||
("shop_lead_notified", admin.EmptyFieldListFilter),
|
("shop_lead_notified", admin.EmptyFieldListFilter),
|
||||||
|
("audits", admin.EmptyFieldListFilter),
|
||||||
]
|
]
|
||||||
|
|
||||||
actions = ["send_notifications"]
|
actions = ["send_notifications"]
|
||||||
@ -188,10 +219,13 @@ class SpecialProgramAdmin(admin.ModelAdmin):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class WaiverAuditInline(AbstractAuditInline):
|
||||||
|
model = WaiverAudit
|
||||||
|
|
||||||
|
|
||||||
@admin.register(Waiver)
|
@admin.register(Waiver)
|
||||||
class WaiverAdmin(admin.ModelAdmin):
|
class WaiverAdmin(admin.ModelAdmin):
|
||||||
search_fields = ["name"]
|
search_fields = ["name"]
|
||||||
|
|
||||||
list_display = [
|
list_display = [
|
||||||
"name",
|
"name",
|
||||||
"date",
|
"date",
|
||||||
@ -201,7 +235,17 @@ class WaiverAdmin(admin.ModelAdmin):
|
|||||||
"guardian_name",
|
"guardian_name",
|
||||||
"guardian_relation",
|
"guardian_relation",
|
||||||
"guardian_date",
|
"guardian_date",
|
||||||
|
"latest_audit",
|
||||||
]
|
]
|
||||||
|
list_filter = [
|
||||||
|
"waiver_version",
|
||||||
|
("audits", admin.EmptyFieldListFilter),
|
||||||
|
]
|
||||||
|
inlines = [WaiverAuditInline]
|
||||||
|
|
||||||
|
@admin.display(description="Latest Audit")
|
||||||
|
def latest_audit(self, obj):
|
||||||
|
return obj.audits.latest()
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(CmsRedRiverVeteransScholarship)
|
admin.site.register(CmsRedRiverVeteransScholarship)
|
||||||
|
90
paperwork/migrations/0012_waiveraudit_certificationaudit.py
Normal file
90
paperwork/migrations/0012_waiveraudit_certificationaudit.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
# Generated by Django 4.2 on 2023-04-10 05:34
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
("paperwork", "0011_alter_certificationversion_options"),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="WaiverAudit",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("date", models.DateField(default=datetime.date.today, unique=True)),
|
||||||
|
("good", models.BooleanField(default=False)),
|
||||||
|
("notes", models.CharField(blank=True, max_length=255)),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"waiver",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="audits",
|
||||||
|
to="paperwork.waiver",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"ordering": ["date"],
|
||||||
|
"get_latest_by": ["date"],
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name="CertificationAudit",
|
||||||
|
fields=[
|
||||||
|
(
|
||||||
|
"id",
|
||||||
|
models.BigAutoField(
|
||||||
|
auto_created=True,
|
||||||
|
primary_key=True,
|
||||||
|
serialize=False,
|
||||||
|
verbose_name="ID",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
("date", models.DateField(default=datetime.date.today, unique=True)),
|
||||||
|
("good", models.BooleanField(default=False)),
|
||||||
|
("notes", models.CharField(blank=True, max_length=255)),
|
||||||
|
(
|
||||||
|
"certification",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name="audits",
|
||||||
|
to="paperwork.certification",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"user",
|
||||||
|
models.ForeignKey(
|
||||||
|
on_delete=django.db.models.deletion.PROTECT,
|
||||||
|
to=settings.AUTH_USER_MODEL,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
"ordering": ["date"],
|
||||||
|
"get_latest_by": ["date"],
|
||||||
|
"abstract": False,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
@ -1,13 +1,30 @@
|
|||||||
|
import datetime
|
||||||
import re
|
import re
|
||||||
|
|
||||||
from semver import VersionInfo
|
from semver import VersionInfo
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import OuterRef, Q, ExpressionWrapper, Subquery
|
from django.db.models import OuterRef, Q, ExpressionWrapper, Subquery
|
||||||
|
from django.conf import settings
|
||||||
from django.core.validators import RegexValidator
|
from django.core.validators import RegexValidator
|
||||||
|
|
||||||
from membershipworks.models import Member, Flag as MembershipWorksFlag
|
from membershipworks.models import Member, Flag as MembershipWorksFlag
|
||||||
|
|
||||||
|
|
||||||
|
class AbstractAudit(models.Model):
|
||||||
|
date = models.DateField(default=datetime.date.today, unique=True)
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
|
||||||
|
good = models.BooleanField(default=False)
|
||||||
|
notes = models.CharField(max_length=255, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{'Good' if self.good else 'Bad'} audit at {self.date} by {self.user}"
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
abstract = True
|
||||||
|
ordering = ["date"]
|
||||||
|
get_latest_by = ["date"]
|
||||||
|
|
||||||
|
|
||||||
class CmsRedRiverVeteransScholarship(models.Model):
|
class CmsRedRiverVeteransScholarship(models.Model):
|
||||||
serial = models.AutoField(primary_key=True)
|
serial = models.AutoField(primary_key=True)
|
||||||
program_name = models.CharField(db_column="Program Name", max_length=255)
|
program_name = models.CharField(db_column="Program Name", max_length=255)
|
||||||
@ -202,6 +219,12 @@ class Certification(models.Model):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
class CertificationAudit(AbstractAudit):
|
||||||
|
certification = models.ForeignKey(
|
||||||
|
Certification, on_delete=models.CASCADE, related_name="audits"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class InstructorOrVendor(models.Model):
|
class InstructorOrVendor(models.Model):
|
||||||
serial = models.AutoField(primary_key=True)
|
serial = models.AutoField(primary_key=True)
|
||||||
name = models.CharField(db_column="Name", max_length=255)
|
name = models.CharField(db_column="Name", max_length=255)
|
||||||
@ -282,3 +305,7 @@ class Waiver(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
db_table = "Waivers"
|
db_table = "Waivers"
|
||||||
|
|
||||||
|
|
||||||
|
class WaiverAudit(AbstractAudit):
|
||||||
|
waiver = models.ForeignKey(Waiver, on_delete=models.CASCADE, related_name="audits")
|
||||||
|
Loading…
Reference in New Issue
Block a user