This commit is contained in:
seb
2026-10-16 13:09:25 +02:00
parent a560a78f4d
commit e083d66a75
2 changed files with 309 additions and 159 deletions

View File

@@ -1,14 +1,13 @@
VFS Drafts: Collaborative Quote, Order, and Delivery Editing VFS Drafts: Collaborative Quote and Order Editing
# Overview # Overview
The virtual directory `drafts/` in VFS allows the LLM agent and the frontend user to collaborate in real time on in-progress commercial documents. The virtual directory `drafts/` in VFS allows the LLM agent and the frontend user to collaborate in real time on in-progress commercial documents.
Drafts are stateful forms maintained in the frontend application and synchronized with the backend. Drafts are stateful forms maintained in the frontend application and synchronized with the backend.
There are three virtual draft files: There are two virtual draft files:
1. `drafts/quote` : Sales Quote / Angebot (Customer sales proposal) 1. `drafts/quote` : Sales Quote / Angebot (Customer sales proposal)
2. `drafts/order` : Sales Order / Auftrag & Bestellung (Customer order confirmation) 2. `drafts/order` : Sales Order / Auftrag & Bestellung (Customer order confirmation)
3. `drafts/delivery` : Goods Inbound / Wareneingang & Lieferschein (Supplier delivery receipt)
--- ---
@@ -31,7 +30,7 @@ Every draft file contains a root object with a `manifest` property conforming to
"properties": { "properties": {
"kind": { "kind": {
"type": "string", "type": "string",
"enum": ["quote", "order", "delivery"], "enum": ["quote", "order"],
"description": "Document type" "description": "Document type"
}, },
"mode": { "mode": {
@@ -50,7 +49,7 @@ Every draft file contains a root object with a `manifest` property conforming to
}, },
"partyId": { "partyId": {
"type": ["integer", "null"], "type": ["integer", "null"],
"description": "Customer entity.id for quotes/orders; supplier.id for deliveries" "description": "Customer entity.id for quotes and orders"
}, },
"number": { "number": {
"type": ["string", "null"], "type": ["string", "null"],
@@ -381,157 +380,3 @@ Used for customer order confirmations and sales orders (Aufträge & Bestellungen
} }
} }
``` ```
---
## 4. Goods Inbound Delivery Draft (`drafts/delivery`)
Used for inbound delivery receipts from suppliers (Wareneingang & Lieferscheine).
### Complete Delivery JSON Schema
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DeliveryDraft",
"type": "object",
"required": ["manifest"],
"properties": {
"manifest": {
"type": "object",
"required": ["kind", "mode", "lines"],
"properties": {
"kind": { "type": "string", "const": "delivery" },
"mode": { "type": "string", "enum": ["create", "edit"] },
"id": { "type": ["integer", "null"] },
"date": {
"type": ["string", "null"],
"format": "date",
"description": "Delivery arrival / receipt date (Lieferdatum, YYYY-MM-DD)"
},
"partyId": {
"type": ["integer", "null"],
"description": "Supplier ID (supplier.id / entity_id)"
},
"number": {
"type": ["string", "null"],
"description": "Delivery manifest number (allocated by system sequence upon creation)"
},
"externalNumber": {
"type": ["string", "null"],
"description": "Supplier delivery slip number (Lieferschein-Nr. des Lieferanten)"
},
"comment": {
"type": ["string", "null"],
"description": "Warehouse receipt remarks or gate notes"
},
"lines": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "amount", "net", "taxrate"],
"properties": {
"pos": {
"type": "integer",
"minimum": 1,
"description": "Position line number (1, 2, 3...)"
},
"sku": {
"type": ["string", "null"],
"description": "Article SKU (internal SKU or matched supplier SKU)"
},
"name": {
"type": "string",
"description": "Delivered article name"
},
"amount": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Delivered quantity received into warehouse"
},
"pu": {
"type": "number",
"minimum": 1,
"default": 1,
"description": "Packaging unit / VPE (default 1)"
},
"net": {
"type": "number",
"minimum": 0,
"description": "Purchase cost net per unit in EUR (EK netto)"
},
"gross": {
"type": ["number", "null"],
"description": "Purchase cost gross per unit in EUR (EK brutto)"
},
"taxrate": {
"type": "number",
"minimum": 0,
"description": "Input tax percentage (e.g. 19 or 7)"
},
"rebate": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "Supplier discount percentage"
},
"unit": {
"type": ["string", "null"],
"description": "Unit of measurement"
},
"comment": {
"type": ["string", "null"],
"description": "Batch number, lot, condition, or storage bin note"
}
}
}
}
}
}
}
}
```
### Complete Delivery Example
```json
{
"manifest": {
"kind": "delivery",
"mode": "create",
"id": null,
"date": "2026-10-14",
"partyId": 12,
"number": null,
"externalNumber": "LS-SUP-99412",
"comment": "Ware unbeschädigt auf Palette 3 angeliefert.",
"lines": [
{
"pos": 1,
"sku": "42001257",
"name": "1 Pot 8L System inkl. 47L Wasserfass",
"amount": 20,
"pu": 1,
"net": 38.50,
"gross": 45.82,
"taxrate": 19,
"rebate": 0,
"unit": "Stück",
"comment": "Palette A-04"
},
{
"pos": 2,
"sku": "42000009",
"name": "Autopot Easy2Grow Erweiterungsset 2Pot",
"amount": 50,
"pu": 1,
"net": 24.00,
"gross": 28.56,
"taxrate": 19,
"rebate": 3,
"unit": "Stück",
"comment": "Karton 1 bis 5"
}
]
}
}
```

View File

@@ -0,0 +1,305 @@
Process Incoming Product and Invoices: Extraction & Delivery Drafts
# Overview
This skill guides the automated and interactive processing of inbound supplier documents (invoices / *Rechnungen* and delivery notes / *Lieferscheine*).
It covers:
1. **Document Data Extraction**: Extracting structured metadata, packaging units (VPE), units of measurement, line items, and notes from document text/images via the `output_manifest` tool.
2. **Goods Inbound Delivery Draft (`drafts/delivery`)**: Storing and maintaining the inbound receipt in the virtual file system (`drafts/delivery`) for real-time collaboration with the user in the UI.
---
# 1. Document Extraction Rules & Prompt Logic
When reading or processing an invoice or delivery note, follow these core rules:
## General Extraction Rules
- **Document Type (`manifestType`)**: `"rechnung"` or `"lieferschein"`. For `"lieferschein"`, prices (`eknetto`) and taxes (`mwst`) must be `null`.
- **Exclude Non-Product Lines**: Exclude freight, shipping, postage, pallet charges, packaging disposal fees, and handling surcharges. Only include physical inventory products.
- **Line Items**: Extract `position`, `count`, `vpe` (default: 1), `unit`, `articleName`, `liefArtNr`, `mwst` (7, 19, or null), and `eknetto` per piece in euro cents (or null).
- **Supplier Matching**: Match the supplier against known company suppliers to identify `lieferant_id` and `lieferantName`. For unknown suppliers, extract `lieferantName` as string and set `lieferant_id` to `null`.
- **Notes Strictly in German**: Record all notes, ambiguities, packaging assumptions, or excluded line details in German. These notes assist a human reviewer in double-checking results.
- **Delta / Batching Updates**: The `output_manifest` tool consists of three optional components: `basedata`, `notes`, and `items`. Only include a component when it carries genuinely new or changed data; omit unchanged components.
- Include `basedata` ONLY when at least one field is currently null/empty or value actually changes.
- Include `items` ONLY when new product line items are being added.
- Include `notes` ONLY when there is a genuinely new noteworthy detail.
## VPE (Verpackungseinheit) Logic
VPE represents the number of individually sellable units inside a bulk item. If not explicitly split for individual sale, default to 1.
- `"20 Boxen á 10 Stück"` -> `vpe = 20`.
- `"10 Gläser á 100 Filter"` -> `vpe = 10` (the 10 glasses are sold individually).
- `"100 Stück"` -> `vpe = 1` (sold as one bulk pack).
- Typical sellable VPEs are containers like *Beutel*, *Gläser*, *Boxen*, *Hefte*, not loose sub-components.
- In `"Y Hefte á X Blatt"` the VPE is NOT X, but `Y`.
- A "Display" represents a VPE: `"20er Display"` means `vpe = 20`.
- `eknetto` is the price of one full VPE.
## Unit (ME) Rules
- The unit (ME) must ALWAYS refer to the individual sellable item (e.g. `Stk.`, `Beutel`, `Flasche`, `Box`, `Set`). It must **NEVER** be `"VE"` or `"VPE"`.
- A packaging size like `"VE100"` means the bulk pack contains 100 individual items: set `vpe = 100` and `unit = "Stk."` (or the relevant individual unit).
- Example: `"Einhängesiebe Stahl, VE100"` -> `vpe = 100`, `unit = "Stk."`, `count = 1` (one bulk pack of 100 sieves).
---
# 2. Extraction Tool Specification (`output_manifest`)
The extraction engine outputs structured findings via the `output_manifest` tool call.
```json
{
"name": "output_manifest",
"description": "Stores extracted invoice/delivery-note data: header metadata (basedata), notes, and product line items. Each component is strictly optional — only include the ones that carry new data.",
"strict": false,
"parameters": {
"type": "object",
"additionalProperties": false,
"required": [],
"properties": {
"basedata": {
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"date": { "type": ["string", "null"], "description": "Document date (ISO 8601, YYYY-MM-DD)" },
"lieferant_id": { "type": ["number", "null"], "description": "Lieferant ID if matched, else null" },
"lieferantName": { "type": "string", "description": "Supplier name" },
"manifestNr": { "type": ["string", "null"], "description": "Lieferscheinnummer or Rechnungsnummer" },
"manifestType": {
"anyOf": [
{ "type": "string", "enum": ["rechnung", "lieferschein"] },
{ "type": "null" }
],
"description": "Document type"
}
}
},
{ "type": "null" }
],
"description": "Header/metadata. Only include fields that are currently missing or changed."
},
"notes": {
"anyOf": [
{ "type": "array", "items": { "type": "string" } },
{ "type": "null" }
],
"description": "New noteworthy details (in German), for a human to double-check the result."
},
"items": {
"anyOf": [
{
"type": "object",
"additionalProperties": false,
"properties": {
"numberOfItemsToAdd": { "type": "number", "description": "Number of NEW line items being added in this call." },
"items": {
"type": "array",
"description": "Invoice/delivery product line items",
"items": {
"type": "object",
"additionalProperties": false,
"required": ["position", "count", "vpe", "unit", "articleName", "liefArtNr", "mwst", "eknetto"],
"properties": {
"position": { "type": "number", "description": "Position number" },
"count": { "type": "number", "description": "Quantity (Menge)" },
"vpe": { "type": "number", "description": "Packaging unit size (VPE). Default: 1." },
"unit": { "type": "string", "description": "Unit of the individual sellable item. NEVER 'VE' or 'VPE'." },
"articleName": { "type": "string", "description": "Article name" },
"liefArtNr": { "type": "string", "description": "Supplier article number" },
"mwst": {
"anyOf": [{ "type": "string", "enum": ["7", "19"] }, { "type": "null" }],
"description": "VAT rate percentage; null if delivery note or missing"
},
"eknetto": { "type": ["number", "null"], "description": "Net purchase price per VPE in euro cents; null if missing" }
}
}
}
}
},
{ "type": "null" }
],
"description": "New product line items."
}
}
}
}
```
---
# 3. Goods Inbound Delivery Draft (`drafts/delivery`)
Inbound deliveries are staged and edited as drafts in the virtual file system under `drafts/delivery`.
This allows the agent and frontend user to review, correct, and finalize stock receipts and purchase costs before committing them into the inventory ledger.
## Field Mapping from Extracted Manifest to Delivery Draft
- **`date`**: Document date (`YYYY-MM-DD`).
- **`partyId`**: Supplier ID (`lieferant_id` / `entity.id`).
- **`externalNumber`**: Supplier delivery slip number or invoice number (`manifestNr`).
- **`comment`**: Human-readable notes, inspection remarks, storage location, or extraction caveats.
- **`lines`**:
- `pos`: 1-based position line number.
- `sku`: Internal article SKU or matched supplier SKU (`liefArtNr`).
- `name`: Delivered product name (`articleName`).
- `amount`: Quantity delivered (`count`).
- `pu`: Packaging unit size (`vpe`, default `1`).
- `net`: Purchase cost net per unit in EUR (`eknetto / 100`, or `0` for delivery notes without prices).
- `gross`: Purchase cost gross per unit in EUR (`net * (1 + taxrate/100)`).
- `taxrate`: Input tax rate percentage (`19`, `7`, or `0`).
- `rebate`: Supplier discount percentage (default `0`).
- `unit`: Individual sellable unit (e.g. `Stück`, `Beutel`, `Flasche`).
- `comment`: Specific batch, lot, or packaging comment.
---
## Delivery Draft JSON Schema
```json
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DeliveryDraft",
"type": "object",
"required": ["manifest"],
"properties": {
"manifest": {
"type": "object",
"required": ["kind", "mode", "lines"],
"properties": {
"kind": { "type": "string", "const": "delivery" },
"mode": { "type": "string", "enum": ["create", "edit"] },
"id": { "type": ["integer", "null"] },
"date": {
"type": ["string", "null"],
"format": "date",
"description": "Delivery arrival / receipt date (Lieferdatum, YYYY-MM-DD)"
},
"partyId": {
"type": ["integer", "null"],
"description": "Supplier ID (supplier.id / entity_id)"
},
"number": {
"type": ["string", "null"],
"description": "Delivery manifest number (allocated by system sequence upon creation)"
},
"externalNumber": {
"type": ["string", "null"],
"description": "Supplier delivery slip number (Lieferschein-Nr. des Lieferanten)"
},
"comment": {
"type": ["string", "null"],
"description": "Warehouse receipt remarks or gate notes"
},
"lines": {
"type": "array",
"items": {
"type": "object",
"required": ["name", "amount", "net", "taxrate"],
"properties": {
"pos": {
"type": "integer",
"minimum": 1,
"description": "Position line number (1, 2, 3...)"
},
"sku": {
"type": ["string", "null"],
"description": "Article SKU (internal SKU or matched supplier SKU)"
},
"name": {
"type": "string",
"description": "Delivered article name"
},
"amount": {
"type": "number",
"exclusiveMinimum": 0,
"description": "Delivered quantity received into warehouse"
},
"pu": {
"type": "number",
"minimum": 1,
"default": 1,
"description": "Packaging unit / VPE (default 1)"
},
"net": {
"type": "number",
"minimum": 0,
"description": "Purchase cost net per unit in EUR (EK netto)"
},
"gross": {
"type": ["number", "null"],
"description": "Purchase cost gross per unit in EUR (EK brutto)"
},
"taxrate": {
"type": "number",
"minimum": 0,
"description": "Input tax percentage (e.g. 19 or 7)"
},
"rebate": {
"type": "number",
"minimum": 0,
"maximum": 100,
"default": 0,
"description": "Supplier discount percentage"
},
"unit": {
"type": ["string", "null"],
"description": "Unit of measurement"
},
"comment": {
"type": ["string", "null"],
"description": "Batch number, lot, condition, or storage bin note"
}
}
}
}
}
}
}
}
```
---
## Delivery Draft Complete Example
```json
{
"manifest": {
"kind": "delivery",
"mode": "create",
"id": null,
"date": "2026-10-14",
"partyId": 12,
"number": null,
"externalNumber": "LS-SUP-99412",
"comment": "Ware unbeschädigt auf Palette 3 angeliefert.",
"lines": [
{
"pos": 1,
"sku": "42001257",
"name": "1 Pot 8L System inkl. 47L Wasserfass",
"amount": 20,
"pu": 1,
"net": 38.50,
"gross": 45.82,
"taxrate": 19,
"rebate": 0,
"unit": "Stück",
"comment": "Palette A-04"
},
{
"pos": 2,
"sku": "42000009",
"name": "Autopot Easy2Grow Erweiterungsset 2Pot",
"amount": 50,
"pu": 1,
"net": 24.00,
"gross": 28.56,
"taxrate": 19,
"rebate": 3,
"unit": "Stück",
"comment": "Karton 1 bis 5"
}
]
}
}
```