ProofedMesh/schemas/expense/canonical/v1.1.0.json

126 lines
5.6 KiB
JSON
Raw Normal View History

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://basicstack.de/schemas/expense/canonical/v1.1.0.json",
"title": "Expense (canonical) v1.1.0",
"description": "Kanonische Reisekostenstruktur. Additive gegenüber v1.0.0: expense_items[] erhalten ein optionales allocations[]-Array, mit dem einzelne Zeilen auf mehrere Projekte prozentual oder absolut aufgeteilt werden können. Das top-level project_assignment ist optional (für Vorgänge ohne Zeilen-Splits) — es gilt als Default-Zuordnung für alle items ohne allocations. OPA erzwingt: pro Item entweder top-level project_assignment oder item.allocations, nicht beides zugleich; alle Allocations eines Items im selben Modus; Summenerhalt (percentage=100.00 bzw. amount=item.amount).",
"type": "object",
"additionalProperties": false,
"required": ["schema", "workflow_id", "employee_id", "trip", "currency", "total_amount", "expense_items"],
"properties": {
"schema": {
"type": "object",
"additionalProperties": false,
"required": ["name", "version"],
"properties": {
"name": { "const": "expense.canonical" },
"version": { "const": "1.1.0" }
}
},
"workflow_id": {
"type": "string",
"pattern": "^WF-[0-9]{4}-[0-9]{4,}$"
},
"employee_id": {
"type": "string",
"pattern": "^EMP-[0-9]+$"
},
"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}$"
},
"total_amount": {
"type": "string",
"pattern": "^-?[0-9]+\\.[0-9]{2}$"
},
"project_assignment": {
"description": "Optional. Default-Projektzuordnung für alle items ohne eigenes allocations[]. Wenn ein Item eigene allocations hat, wird dieser Default für dieses Item NICHT angewendet.",
"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}$"
}
}
},
"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/._-]+$"
},
"allocations": {
"description": "Optional. Aufteilung dieser Zeile auf ein oder mehrere Projekte. Wenn gesetzt, überschreibt das top-level project_assignment für dieses Item. Alle Entries desselben Items MÜSSEN denselben mode haben (OPA-Regel). Bei mode=percentage: Summe der percentage == 100.00. Bei mode=absolute: Summe der amount == item.amount.",
"type": "array",
"minItems": 1,
"maxItems": 50,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["project_code", "cost_center", "mode", "source_attestation_ref"],
"properties": {
"project_code": { "type": "string", "pattern": "^P-[0-9]+$" },
"cost_center": { "type": "string", "pattern": "^CC-[0-9]+$" },
"mode": { "enum": ["percentage", "absolute"] },
"percentage": {
"type": "string",
"pattern": "^(100\\.00|[0-9]{1,2}\\.[0-9]{2})$",
"description": "Vorhanden wenn mode=percentage; abwesend sonst. Zwei Nachkommastellen, 0.00..100.00."
},
"amount": {
"type": "string",
"pattern": "^-?[0-9]+\\.[0-9]{2}$",
"description": "Vorhanden wenn mode=absolute; abwesend sonst."
},
"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 project_code+cost_center stammt."
}
},
"allOf": [
{
"if": { "properties": { "mode": { "const": "percentage" } }, "required": ["mode"] },
"then": { "required": ["percentage"], "not": { "required": ["amount"] } }
},
{
"if": { "properties": { "mode": { "const": "absolute" } }, "required": ["mode"] },
"then": { "required": ["amount"], "not": { "required": ["percentage"] } }
}
]
}
}
}
}
}
}
}