schemas: add expense.canonical, ds.expense.project_assignment, sink.db.expense (all v1.0.0) with README
This commit is contained in:
parent
989140ee88
commit
30939b777a
4 changed files with 244 additions and 0 deletions
21
schemas/README.md
Normal file
21
schemas/README.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# ProofedMesh Schemas
|
||||
|
||||
Versionierte JSON-Schemas für die kanonischen Business-, Datenquellen- und Senken-Formate. Jedes Schema trägt am Objekt-Root `schema.name` und `schema.version`, damit Attestierungen die genutzte Fassung mit-signieren können (siehe [`docs/architecture/v1.1.md`](../docs/architecture/v1.1.md#user-content-61-fachliche-integritätsprüfung--schema-versionierung-verbindlich)).
|
||||
|
||||
**Versionspolitik:** Breaking Changes bekommen eine neue Hauptversion (`v1.0.0` → `v2.0.0`). Nicht-breaking Erweiterungen eine Minor- oder Patch-Version. Alte Versionen werden niemals verändert — sie werden parallel vorgehalten, solange Attestierungen aus ihrer Laufzeit noch verifizierbar sein müssen (Default-Retention 1 Jahr, siehe PII-Retention-ADR).
|
||||
|
||||
## Vorhandene Schemas
|
||||
|
||||
| Pfad | Rolle | Verwendet in |
|
||||
| ----------------------------------------------- | ------------------------------ | -------------------------------------------------- |
|
||||
| `expense/canonical/v1.0.0.json` | Kanonische Business-Form | Approval-Attestierung (`subject`, `materials`), Agent-A-Transform-Output |
|
||||
| `ds/expense/project_assignment/v1.0.0.json` | Datenquellen-Rückgabe | Query Attestation von `ds:expense-db-app` / `project_assignments` |
|
||||
| `sink/db/expense/v1.0.0.json` | Senken-Payload | Transform-Attestierung `expense.map_to_sink` → `sink:expense-db-app` |
|
||||
|
||||
## Namensschema
|
||||
|
||||
- `expense/canonical` — kanonische (senken-unabhängige) Business-Form
|
||||
- `ds/<domain>/<resource>` — Datenquellen-Rückgabe
|
||||
- `sink/<kind>/<domain>` — Senken-Payload
|
||||
|
||||
Pfad: `<namespace>/.../v<major>.<minor>.<patch>.json`. `$id` enthält die absolute URL unter `https://basicstack.de/schemas/...` (identisch zum Dateipfad).
|
||||
50
schemas/ds/expense/project_assignment/v1.0.0.json
Normal file
50
schemas/ds/expense/project_assignment/v1.0.0.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://basicstack.de/schemas/ds/expense/project_assignment/v1.0.0.json",
|
||||
"title": "DataSource Response: project_assignments (ds:expense-db-app) v1.0.0",
|
||||
"description": "Rückgabe-Schema der Datenquelle ds:expense-db-app, Resource project_assignments. Der Datenquellen-Adapter signiert eine Query Attestation mit diesem Schema als result-Teil.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "data_source", "resource", "filters", "rows", "returned_at"],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "version"],
|
||||
"properties": {
|
||||
"name": { "const": "ds.expense.project_assignment" },
|
||||
"version": { "const": "1.0.0" }
|
||||
}
|
||||
},
|
||||
"data_source": { "const": "ds:expense-db-app" },
|
||||
"resource": { "const": "project_assignments" },
|
||||
"filters": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["employee_id"],
|
||||
"properties": {
|
||||
"employee_id": { "type": "string", "pattern": "^EMP-[0-9]+$" },
|
||||
"active_on": { "type": "string", "format": "date" }
|
||||
}
|
||||
},
|
||||
"returned_at": { "type": "string", "format": "date-time" },
|
||||
"rows": {
|
||||
"type": "array",
|
||||
"maxItems": 500,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["project_code", "project_display_name", "employee_id", "valid_from"],
|
||||
"properties": {
|
||||
"project_code": { "type": "string", "pattern": "^P-[0-9]+$" },
|
||||
"project_display_name": { "type": "string", "minLength": 1, "maxLength": 200 },
|
||||
"employee_id": { "type": "string", "pattern": "^EMP-[0-9]+$" },
|
||||
"role": { "type": "string", "maxLength": 100 },
|
||||
"default_cost_center": { "type": "string", "pattern": "^CC-[0-9]+$" },
|
||||
"valid_from": { "type": "string", "format": "date" },
|
||||
"valid_to": { "type": ["string", "null"], "format": "date" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
89
schemas/expense/canonical/v1.0.0.json
Normal file
89
schemas/expense/canonical/v1.0.0.json
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
{
|
||||
"$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."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
84
schemas/sink/db/expense/v1.0.0.json
Normal file
84
schemas/sink/db/expense/v1.0.0.json
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://basicstack.de/schemas/sink/db/expense/v1.0.0.json",
|
||||
"title": "Sink Payload: sink:expense-db-app, action expense.post v1.0.0",
|
||||
"description": "Senken-Payload für die eigene DB-App (sink:expense-db-app). Jede Transform-Attestierung von agent-sink-mapping erzeugt dieses Format aus einem expense.canonical-Dokument. Der Senken-Adapter prüft diesen Payload gegen Verification + OPA, legt dann einen Datensatz in der expenses-Tabelle an.",
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["schema", "workflow_id", "approval_id", "decision_id", "verification_ref", "canonical_payload_hash", "expense"],
|
||||
"properties": {
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["name", "version"],
|
||||
"properties": {
|
||||
"name": { "const": "sink.db.expense" },
|
||||
"version": { "const": "1.0.0" }
|
||||
}
|
||||
},
|
||||
"workflow_id": { "type": "string", "pattern": "^WF-[0-9]{4}-[0-9]{4,}$" },
|
||||
"approval_id": { "type": "string", "pattern": "^APR-[0-9]{4}-[0-9]{4,}$" },
|
||||
"decision_id": { "type": "string", "pattern": "^DEC-[0-9]{4}-[0-9]{4,}$" },
|
||||
"verification_ref": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[0-9a-f]{64}$",
|
||||
"description": "SHA-256 der Verifikationsantwort vom trust-api."
|
||||
},
|
||||
"canonical_payload_hash": {
|
||||
"type": "string",
|
||||
"pattern": "^sha256:[0-9a-f]{64}$",
|
||||
"description": "SHA-256 des originalen expense.canonical-Dokuments, über das die Benutzerfreigabe signiert hat."
|
||||
},
|
||||
"expense": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["employee_id", "project_code", "cost_center", "currency", "total_amount", "trip", "items"],
|
||||
"properties": {
|
||||
"employee_id": { "type": "string", "pattern": "^EMP-[0-9]+$" },
|
||||
"project_code": { "type": "string", "pattern": "^P-[0-9]+$" },
|
||||
"cost_center": { "type": "string", "pattern": "^CC-[0-9]+$" },
|
||||
"currency": { "type": "string", "pattern": "^[A-Z]{3}$" },
|
||||
"total_amount": { "type": "string", "pattern": "^-?[0-9]+\\.[0-9]{2}$" },
|
||||
"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", "maxLength": 200 },
|
||||
"purpose": { "type": "string", "maxLength": 500 }
|
||||
}
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"minItems": 1,
|
||||
"maxItems": 200,
|
||||
"items": {
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": ["type", "amount", "incurred_on", "gl_account"],
|
||||
"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 },
|
||||
"gl_account": {
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]{4,6}$",
|
||||
"description": "Sachkonto in unserer DB-App (nicht SAP). Agent-sink-mapping bezieht es aus einer account_assignments-Query, deren Attestierung in der Transform-Attestierung unter read_materials verlinkt sein muss."
|
||||
},
|
||||
"receipt_ref": {
|
||||
"type": "string",
|
||||
"pattern": "^pii://[A-Za-z0-9/._-]+$"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue