ProofedMesh/schemas/expense/canonical/v1.2.0.json
CTO Agent 27c45b356a
Some checks failed
validate / Rego (opa fmt + opa test) (push) Successful in 12s
validate / JSON Schema (ajv) (push) Has been cancelled
schemas: add ds/receipts v1.0.0, ds/per_diem v1.0.0, expense.canonical v1.2.0, sink.db.expense v1.2.0 (+ examples)
2026-10-25 17:07:20 +00:00

147 lines
6.6 KiB
JSON

{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://basicstack.de/schemas/expense/canonical/v1.2.0.json",
"title": "Expense (canonical) v1.2.0",
"description": "Additive gegenüber v1.1.0: (1) pro Item optional receipt_content_hash + receipt_attestation_ref, damit die Belegbindung in die Signatur-Kette eingeht. (2) Neue Item-Typen PER_DIEM_FULL, PER_DIEM_PARTIAL, PER_DIEM_ARRIVAL, PER_DIEM_DEPARTURE für vom per-diem-calculator erzeugte Pauschalen. (3) Pro Item optional computed_from — Rückbindung an die Transform- und Query-Attestationen, aus denen das Item deterministisch gerechnet wurde.",
"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.2.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 },
"countries_visited": {
"type": "array",
"minItems": 1,
"maxItems": 20,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["country", "from_date", "to_date"],
"properties": {
"country": { "type": "string", "pattern": "^[A-Z]{2}$" },
"city": { "type": ["string","null"], "maxLength": 100 },
"from_date": { "type": "string", "format": "date" },
"to_date": { "type": "string", "format": "date" }
}
}
}
}
},
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
"total_amount": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{2}$" },
"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}$" }
}
},
"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",
"PER_DIEM_FULL", "PER_DIEM_PARTIAL", "PER_DIEM_ARRIVAL", "PER_DIEM_DEPARTURE"
]
},
"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/._-]+$" },
"receipt_content_hash": {
"type": "string",
"pattern": "^sha256:[0-9a-f]{64}$",
"description": "SHA-256 des hochgeladenen Belegs. Wird vom Receipt-Adapter beim Upload geliefert und ins Item eingetragen; vom Approval mit-signiert."
},
"receipt_attestation_ref": {
"type": "string",
"pattern": "^sha256:[0-9a-f]{64}$",
"description": "SHA-256 der Receipt Attestation (ingest). Verification kann so prüfen, dass genau dieser Beleg vorlag."
},
"computed_from": {
"type": "object",
"additionalProperties": false,
"required": ["transform_attestation_ref"],
"description": "Nur gesetzt für deterministisch berechnete Items (z.B. PER_DIEM_*). Bindet an die Transform-Attestation, die dieses Item erzeugt hat, und optional an die Rate-Query-Attestation.",
"properties": {
"transform_attestation_ref": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
"rate_query_attestation_ref": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" },
"rate_id": { "type": "string", "pattern": "^PDR-[0-9]+$" }
}
},
"allocations": {
"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})$" },
"amount": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{2}$" },
"source_attestation_ref": { "type": "string", "pattern": "^sha256:[0-9a-f]{64}$" }
},
"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"] } }
}
]
}
}
},
"allOf": [
{
"if": {
"properties": {
"type": {
"enum": ["PER_DIEM_FULL", "PER_DIEM_PARTIAL", "PER_DIEM_ARRIVAL", "PER_DIEM_DEPARTURE"]
}
},
"required": ["type"]
},
"then": { "required": ["computed_from"] }
}
]
}
}
}
}