89 lines
3.6 KiB
JSON
89 lines
3.6 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://basicstack.de/schemas/expense/canonical/v1.0.0.json",
|
|
"title": "Expense (canonical) v1.0.0",
|
|
"description": "Kanonische Reisekostenstruktur. Bindet die Benutzerfreigabe. Technische Senken-Mappings dürfen die hier definierten Felder nicht in ihrer geschäftlichen Bedeutung ändern — insbesondere total_amount, currency und expense_items[].amount.",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["schema", "workflow_id", "employee_id", "trip", "currency", "total_amount", "expense_items", "project_assignment"],
|
|
"properties": {
|
|
"schema": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["name", "version"],
|
|
"properties": {
|
|
"name": { "const": "expense.canonical" },
|
|
"version": { "const": "1.0.0" }
|
|
}
|
|
},
|
|
"workflow_id": {
|
|
"type": "string",
|
|
"pattern": "^WF-[0-9]{4}-[0-9]{4,}$",
|
|
"description": "Prozess-ID, konstant über die gesamte Reisekostenabrechnung."
|
|
},
|
|
"employee_id": {
|
|
"type": "string",
|
|
"pattern": "^EMP-[0-9]+$",
|
|
"description": "Interne Mitarbeiter-ID. Nicht die SAP-Personalnummer — die wird senken-seitig zugeordnet."
|
|
},
|
|
"trip": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["start_date", "end_date", "destination"],
|
|
"properties": {
|
|
"start_date": { "type": "string", "format": "date" },
|
|
"end_date": { "type": "string", "format": "date" },
|
|
"destination": { "type": "string", "minLength": 1, "maxLength": 200 },
|
|
"purpose": { "type": "string", "maxLength": 500 }
|
|
}
|
|
},
|
|
"currency": {
|
|
"type": "string",
|
|
"pattern": "^[A-Z]{3}$",
|
|
"description": "ISO 4217 Währungscode (z.B. EUR)."
|
|
},
|
|
"total_amount": {
|
|
"type": "string",
|
|
"pattern": "^-?[0-9]+\\.[0-9]{2}$",
|
|
"description": "Summe aller expense_items, als Dezimal-String mit zwei Nachkommastellen (JSON Number vermeidet Rundungsfehler)."
|
|
},
|
|
"expense_items": {
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"maxItems": 200,
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["type", "amount", "incurred_on"],
|
|
"properties": {
|
|
"type": {
|
|
"type": "string",
|
|
"enum": ["HOTEL", "MEALS", "TRANSPORT_FLIGHT", "TRANSPORT_RAIL", "TRANSPORT_CAR", "PARKING", "COMMUNICATION", "CONFERENCE_FEE", "OTHER"]
|
|
},
|
|
"amount": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{2}$" },
|
|
"incurred_on": { "type": "string", "format": "date" },
|
|
"description": { "type": "string", "maxLength": 500 },
|
|
"receipt_ref": {
|
|
"type": "string",
|
|
"pattern": "^pii://[A-Za-z0-9/._-]+$",
|
|
"description": "Referenz in den PII-Store (verschlüsselter S3-Bucket), nicht der Beleg selbst."
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"project_assignment": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["project_code", "cost_center", "source_attestation_ref"],
|
|
"properties": {
|
|
"project_code": { "type": "string", "pattern": "^P-[0-9]+$" },
|
|
"cost_center": { "type": "string", "pattern": "^CC-[0-9]+$" },
|
|
"source_attestation_ref": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{64}$",
|
|
"description": "SHA-256 der Query Attestation (ds:expense-db-app / project_assignments), aus der dieses Projekt stammt. Zwingend, damit Verification nachweisen kann, dass das gewählte Projekt tatsächlich in der dem Benutzer vorgelegten Liste war."
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|