A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
395 lines
16 KiB
Go
395 lines
16 KiB
Go
package funding
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// ============================================================================
|
|
// Constants / Enums
|
|
// ============================================================================
|
|
|
|
// FundingProgram represents the type of funding program
|
|
type FundingProgram string
|
|
|
|
const (
|
|
FundingProgramDigitalPakt1 FundingProgram = "DIGITALPAKT_1"
|
|
FundingProgramDigitalPakt2 FundingProgram = "DIGITALPAKT_2"
|
|
FundingProgramLandesfoerderung FundingProgram = "LANDESFOERDERUNG"
|
|
FundingProgramSchultraeger FundingProgram = "SCHULTRAEGER"
|
|
FundingProgramSonstige FundingProgram = "SONSTIGE"
|
|
)
|
|
|
|
// ApplicationStatus represents the workflow status
|
|
type ApplicationStatus string
|
|
|
|
const (
|
|
ApplicationStatusDraft ApplicationStatus = "DRAFT"
|
|
ApplicationStatusInProgress ApplicationStatus = "IN_PROGRESS"
|
|
ApplicationStatusReview ApplicationStatus = "REVIEW"
|
|
ApplicationStatusSubmitted ApplicationStatus = "SUBMITTED"
|
|
ApplicationStatusApproved ApplicationStatus = "APPROVED"
|
|
ApplicationStatusRejected ApplicationStatus = "REJECTED"
|
|
ApplicationStatusArchived ApplicationStatus = "ARCHIVED"
|
|
)
|
|
|
|
// FederalState represents German federal states
|
|
type FederalState string
|
|
|
|
const (
|
|
FederalStateNI FederalState = "NI" // Niedersachsen
|
|
FederalStateNRW FederalState = "NRW" // Nordrhein-Westfalen
|
|
FederalStateBAY FederalState = "BAY" // Bayern
|
|
FederalStateBW FederalState = "BW" // Baden-Wuerttemberg
|
|
FederalStateHE FederalState = "HE" // Hessen
|
|
FederalStateSN FederalState = "SN" // Sachsen
|
|
FederalStateTH FederalState = "TH" // Thueringen
|
|
FederalStateSA FederalState = "SA" // Sachsen-Anhalt
|
|
FederalStateBB FederalState = "BB" // Brandenburg
|
|
FederalStateMV FederalState = "MV" // Mecklenburg-Vorpommern
|
|
FederalStateSH FederalState = "SH" // Schleswig-Holstein
|
|
FederalStateHH FederalState = "HH" // Hamburg
|
|
FederalStateHB FederalState = "HB" // Bremen
|
|
FederalStateBE FederalState = "BE" // Berlin
|
|
FederalStateSL FederalState = "SL" // Saarland
|
|
FederalStateRP FederalState = "RP" // Rheinland-Pfalz
|
|
)
|
|
|
|
// SchoolType represents different school types
|
|
type SchoolType string
|
|
|
|
const (
|
|
SchoolTypeGrundschule SchoolType = "GRUNDSCHULE"
|
|
SchoolTypeHauptschule SchoolType = "HAUPTSCHULE"
|
|
SchoolTypeRealschule SchoolType = "REALSCHULE"
|
|
SchoolTypeGymnasium SchoolType = "GYMNASIUM"
|
|
SchoolTypeGesamtschule SchoolType = "GESAMTSCHULE"
|
|
SchoolTypeOberschule SchoolType = "OBERSCHULE"
|
|
SchoolTypeFoerderschule SchoolType = "FOERDERSCHULE"
|
|
SchoolTypeBerufsschule SchoolType = "BERUFSSCHULE"
|
|
SchoolTypeBerufskolleg SchoolType = "BERUFSKOLLEG"
|
|
SchoolTypeFachoberschule SchoolType = "FACHOBERSCHULE"
|
|
SchoolTypeBerufliches SchoolType = "BERUFLICHES_GYMNASIUM"
|
|
SchoolTypeSonstige SchoolType = "SONSTIGE"
|
|
)
|
|
|
|
// CarrierType represents the school carrier type
|
|
type CarrierType string
|
|
|
|
const (
|
|
CarrierTypePublic CarrierType = "PUBLIC" // Oeffentlich
|
|
CarrierTypePrivate CarrierType = "PRIVATE" // Privat
|
|
CarrierTypeChurch CarrierType = "CHURCH" // Kirchlich
|
|
CarrierTypeNonProfit CarrierType = "NON_PROFIT" // Gemeinnuetzig
|
|
)
|
|
|
|
// BudgetCategory represents categories for budget items
|
|
type BudgetCategory string
|
|
|
|
const (
|
|
BudgetCategoryNetwork BudgetCategory = "NETWORK" // Netzwerk/Verkabelung
|
|
BudgetCategoryWLAN BudgetCategory = "WLAN" // WLAN-Infrastruktur
|
|
BudgetCategoryDevices BudgetCategory = "DEVICES" // Endgeraete
|
|
BudgetCategoryPresentation BudgetCategory = "PRESENTATION" // Praesentationstechnik
|
|
BudgetCategorySoftware BudgetCategory = "SOFTWARE" // Software-Lizenzen
|
|
BudgetCategoryServer BudgetCategory = "SERVER" // Server/Rechenzentrum
|
|
BudgetCategoryServices BudgetCategory = "SERVICES" // Dienstleistungen
|
|
BudgetCategoryTraining BudgetCategory = "TRAINING" // Schulungen
|
|
BudgetCategorySonstige BudgetCategory = "SONSTIGE" // Sonstige
|
|
)
|
|
|
|
// ============================================================================
|
|
// Main Entities
|
|
// ============================================================================
|
|
|
|
// FundingApplication represents a funding application
|
|
type FundingApplication struct {
|
|
ID uuid.UUID `json:"id"`
|
|
TenantID uuid.UUID `json:"tenant_id"`
|
|
ApplicationNumber string `json:"application_number"` // e.g., DP2-NI-2026-00123
|
|
Title string `json:"title"`
|
|
FundingProgram FundingProgram `json:"funding_program"`
|
|
Status ApplicationStatus `json:"status"`
|
|
|
|
// Wizard State
|
|
CurrentStep int `json:"current_step"`
|
|
TotalSteps int `json:"total_steps"`
|
|
WizardData map[string]interface{} `json:"wizard_data,omitempty"`
|
|
|
|
// School Information
|
|
SchoolProfile *SchoolProfile `json:"school_profile,omitempty"`
|
|
|
|
// Project Information
|
|
ProjectPlan *ProjectPlan `json:"project_plan,omitempty"`
|
|
Budget *Budget `json:"budget,omitempty"`
|
|
Timeline *ProjectTimeline `json:"timeline,omitempty"`
|
|
|
|
// Financial Summary
|
|
RequestedAmount float64 `json:"requested_amount"`
|
|
OwnContribution float64 `json:"own_contribution"`
|
|
ApprovedAmount *float64 `json:"approved_amount,omitempty"`
|
|
|
|
// Attachments
|
|
Attachments []Attachment `json:"attachments,omitempty"`
|
|
|
|
// Audit Trail
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
SubmittedAt *time.Time `json:"submitted_at,omitempty"`
|
|
CreatedBy uuid.UUID `json:"created_by"`
|
|
UpdatedBy uuid.UUID `json:"updated_by"`
|
|
}
|
|
|
|
// SchoolProfile contains school information
|
|
type SchoolProfile struct {
|
|
Name string `json:"name"`
|
|
SchoolNumber string `json:"school_number"` // Official school number
|
|
Type SchoolType `json:"type"`
|
|
FederalState FederalState `json:"federal_state"`
|
|
Address Address `json:"address"`
|
|
ContactPerson ContactPerson `json:"contact_person"`
|
|
StudentCount int `json:"student_count"`
|
|
TeacherCount int `json:"teacher_count"`
|
|
ClassCount int `json:"class_count"`
|
|
CarrierType CarrierType `json:"carrier_type"`
|
|
CarrierName string `json:"carrier_name"`
|
|
CarrierAddress *Address `json:"carrier_address,omitempty"`
|
|
Infrastructure *InfrastructureStatus `json:"infrastructure,omitempty"`
|
|
}
|
|
|
|
// Address represents a postal address
|
|
type Address struct {
|
|
Street string `json:"street"`
|
|
HouseNo string `json:"house_no"`
|
|
PostalCode string `json:"postal_code"`
|
|
City string `json:"city"`
|
|
Country string `json:"country,omitempty"`
|
|
}
|
|
|
|
// ContactPerson represents a contact person
|
|
type ContactPerson struct {
|
|
Salutation string `json:"salutation,omitempty"` // Herr/Frau
|
|
Title string `json:"title,omitempty"` // Dr., Prof.
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
Position string `json:"position,omitempty"` // Schulleitung, IT-Beauftragter
|
|
Email string `json:"email"`
|
|
Phone string `json:"phone,omitempty"`
|
|
}
|
|
|
|
// InfrastructureStatus describes current IT infrastructure
|
|
type InfrastructureStatus struct {
|
|
HasWLAN bool `json:"has_wlan"`
|
|
WLANCoverage int `json:"wlan_coverage"` // Percentage 0-100
|
|
HasStructuredCabling bool `json:"has_structured_cabling"`
|
|
InternetBandwidth string `json:"internet_bandwidth"` // e.g., "100 Mbit/s"
|
|
DeviceCount int `json:"device_count"` // Current devices
|
|
HasServerRoom bool `json:"has_server_room"`
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
// ProjectPlan describes the project
|
|
type ProjectPlan struct {
|
|
ProjectName string `json:"project_name"`
|
|
Summary string `json:"summary"` // Kurzbeschreibung
|
|
Goals string `json:"goals"` // Projektziele
|
|
DidacticConcept string `json:"didactic_concept"` // Paedagogisches Konzept
|
|
MEPReference string `json:"mep_reference,omitempty"` // Medienentwicklungsplan Bezug
|
|
DataProtection string `json:"data_protection"` // Datenschutzkonzept
|
|
MaintenancePlan string `json:"maintenance_plan"` // Wartungs-/Betriebskonzept
|
|
TargetGroups []string `json:"target_groups"` // e.g., ["Schueler", "Lehrer"]
|
|
SubjectsAffected []string `json:"subjects_affected,omitempty"` // Betroffene Faecher
|
|
}
|
|
|
|
// Budget represents the financial plan
|
|
type Budget struct {
|
|
TotalCost float64 `json:"total_cost"`
|
|
RequestedFunding float64 `json:"requested_funding"`
|
|
OwnContribution float64 `json:"own_contribution"`
|
|
OtherFunding float64 `json:"other_funding"`
|
|
FundingRate float64 `json:"funding_rate"` // 0.90 = 90%
|
|
BudgetItems []BudgetItem `json:"budget_items"`
|
|
IsWithinLimits bool `json:"is_within_limits"`
|
|
Justification string `json:"justification,omitempty"` // Begruendung
|
|
}
|
|
|
|
// BudgetItem represents a single budget line item
|
|
type BudgetItem struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Position int `json:"position"` // Order number
|
|
Category BudgetCategory `json:"category"`
|
|
Description string `json:"description"`
|
|
Manufacturer string `json:"manufacturer,omitempty"`
|
|
ProductName string `json:"product_name,omitempty"`
|
|
Quantity int `json:"quantity"`
|
|
UnitPrice float64 `json:"unit_price"`
|
|
TotalPrice float64 `json:"total_price"`
|
|
IsFundable bool `json:"is_fundable"` // Foerderfahig Ja/Nein
|
|
FundingSource string `json:"funding_source"` // digitalpakt, eigenanteil, sonstige
|
|
Notes string `json:"notes,omitempty"`
|
|
}
|
|
|
|
// ProjectTimeline represents project schedule
|
|
type ProjectTimeline struct {
|
|
PlannedStart time.Time `json:"planned_start"`
|
|
PlannedEnd time.Time `json:"planned_end"`
|
|
Milestones []Milestone `json:"milestones,omitempty"`
|
|
ProjectPhase string `json:"project_phase,omitempty"` // Current phase
|
|
}
|
|
|
|
// Milestone represents a project milestone
|
|
type Milestone struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description,omitempty"`
|
|
DueDate time.Time `json:"due_date"`
|
|
CompletedAt *time.Time `json:"completed_at,omitempty"`
|
|
Status string `json:"status"` // planned, in_progress, completed
|
|
}
|
|
|
|
// Attachment represents an uploaded file
|
|
type Attachment struct {
|
|
ID uuid.UUID `json:"id"`
|
|
FileName string `json:"file_name"`
|
|
FileType string `json:"file_type"` // pdf, docx, xlsx, jpg, png
|
|
FileSize int64 `json:"file_size"` // bytes
|
|
Category string `json:"category"` // angebot, mep, nachweis, sonstiges
|
|
Description string `json:"description,omitempty"`
|
|
StoragePath string `json:"-"` // Internal path, not exposed
|
|
UploadedAt time.Time `json:"uploaded_at"`
|
|
UploadedBy uuid.UUID `json:"uploaded_by"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Wizard Step Data
|
|
// ============================================================================
|
|
|
|
// WizardStep represents a single wizard step
|
|
type WizardStep struct {
|
|
Number int `json:"number"`
|
|
Title string `json:"title"`
|
|
Description string `json:"description"`
|
|
Fields []string `json:"fields"` // Field IDs for this step
|
|
IsCompleted bool `json:"is_completed"`
|
|
IsRequired bool `json:"is_required"`
|
|
HelpContext string `json:"help_context"` // Context for LLM assistant
|
|
}
|
|
|
|
// WizardProgress tracks wizard completion
|
|
type WizardProgress struct {
|
|
CurrentStep int `json:"current_step"`
|
|
TotalSteps int `json:"total_steps"`
|
|
CompletedSteps []int `json:"completed_steps"`
|
|
StepValidation map[int][]string `json:"step_validation,omitempty"` // Errors per step
|
|
FormData map[string]interface{} `json:"form_data"`
|
|
LastSavedAt time.Time `json:"last_saved_at"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// BreakPilot Presets
|
|
// ============================================================================
|
|
|
|
// ProductPreset represents a BreakPilot product preset
|
|
type ProductPreset struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
BudgetItems []BudgetItem `json:"budget_items"`
|
|
AutoFill map[string]interface{} `json:"auto_fill"`
|
|
DataProtection string `json:"data_protection"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// Export Structures
|
|
// ============================================================================
|
|
|
|
// ExportDocument represents a generated document
|
|
type ExportDocument struct {
|
|
Type string `json:"type"` // antragsschreiben, kostenplan, datenschutz
|
|
Format string `json:"format"` // pdf, docx, xlsx
|
|
FileName string `json:"file_name"`
|
|
GeneratedAt time.Time `json:"generated_at"`
|
|
ContentHash string `json:"content_hash"`
|
|
StoragePath string `json:"-"`
|
|
}
|
|
|
|
// ExportBundle represents a ZIP bundle of all documents
|
|
type ExportBundle struct {
|
|
ID uuid.UUID `json:"id"`
|
|
ApplicationID uuid.UUID `json:"application_id"`
|
|
Documents []ExportDocument `json:"documents"`
|
|
GeneratedAt time.Time `json:"generated_at"`
|
|
DownloadURL string `json:"download_url"`
|
|
ExpiresAt time.Time `json:"expires_at"`
|
|
}
|
|
|
|
// ============================================================================
|
|
// LLM Assistant
|
|
// ============================================================================
|
|
|
|
// AssistantMessage represents a chat message with the assistant
|
|
type AssistantMessage struct {
|
|
Role string `json:"role"` // user, assistant, system
|
|
Content string `json:"content"`
|
|
Step int `json:"step,omitempty"` // Current wizard step
|
|
}
|
|
|
|
// AssistantRequest for asking questions
|
|
type AssistantRequest struct {
|
|
ApplicationID uuid.UUID `json:"application_id"`
|
|
Question string `json:"question"`
|
|
CurrentStep int `json:"current_step"`
|
|
Context map[string]interface{} `json:"context,omitempty"`
|
|
History []AssistantMessage `json:"history,omitempty"`
|
|
}
|
|
|
|
// AssistantResponse from the assistant
|
|
type AssistantResponse struct {
|
|
Answer string `json:"answer"`
|
|
Suggestions []string `json:"suggestions,omitempty"`
|
|
References []string `json:"references,omitempty"` // Links to help resources
|
|
FormFills map[string]interface{} `json:"form_fills,omitempty"` // Suggested form values
|
|
}
|
|
|
|
// ============================================================================
|
|
// API Request/Response Types
|
|
// ============================================================================
|
|
|
|
// CreateApplicationRequest for creating a new application
|
|
type CreateApplicationRequest struct {
|
|
Title string `json:"title"`
|
|
FundingProgram FundingProgram `json:"funding_program"`
|
|
FederalState FederalState `json:"federal_state"`
|
|
PresetID string `json:"preset_id,omitempty"` // Optional BreakPilot preset
|
|
}
|
|
|
|
// UpdateApplicationRequest for updating an application
|
|
type UpdateApplicationRequest struct {
|
|
Title *string `json:"title,omitempty"`
|
|
WizardData map[string]interface{} `json:"wizard_data,omitempty"`
|
|
CurrentStep *int `json:"current_step,omitempty"`
|
|
}
|
|
|
|
// SaveWizardStepRequest for saving a wizard step
|
|
type SaveWizardStepRequest struct {
|
|
Step int `json:"step"`
|
|
Data map[string]interface{} `json:"data"`
|
|
Complete bool `json:"complete"` // Mark step as complete
|
|
}
|
|
|
|
// ApplicationListResponse for list endpoints
|
|
type ApplicationListResponse struct {
|
|
Applications []FundingApplication `json:"applications"`
|
|
Total int `json:"total"`
|
|
Page int `json:"page"`
|
|
PageSize int `json:"page_size"`
|
|
}
|
|
|
|
// ExportRequest for export endpoints
|
|
type ExportRequest struct {
|
|
Format string `json:"format"` // zip, pdf, docx
|
|
Documents []string `json:"documents"` // Which documents to include
|
|
Language string `json:"language"` // de, en
|
|
}
|