@kevludwig/eve-lexware-office 0.1.4 → 0.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/extension/lib/purchase.mjs +2 -2
- package/dist/extension/lib/types.d.ts +1 -1
- package/dist/extension/lib/with-card.d.ts +9 -0
- package/dist/extension/lib/with-card.mjs +25 -0
- package/dist/extension/skills/eingangsrechnung/SKILL.md +2 -1
- package/dist/extension/tools/create_customer.mjs +4 -3
- package/dist/extension/tools/create_invoice.mjs +2 -1
- package/dist/extension/tools/create_order_confirmation.mjs +2 -1
- package/dist/extension/tools/create_purchase_invoice.mjs +5 -4
- package/dist/extension/tools/create_quotation.mjs +2 -1
- package/dist/extension/tools/send_payment_reminder.mjs +3 -2
- package/package.json +1 -1
|
@@ -46,7 +46,7 @@ function totalsOf(groups = []) {
|
|
|
46
46
|
net: round2(gross - tax)
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
const ATTACHMENT_PATH = /^\/workspace\/attachments\/[0-9a-f]{8,64}\/[\w.-]+$/;
|
|
49
|
+
const ATTACHMENT_PATH = /^\/workspace\/(?:\.eve\/)?attachments\/[0-9a-f]{8,64}\/[\w.-]+$/;
|
|
50
50
|
const isAllowedAttachmentPath = (path) => ATTACHMENT_PATH.test(path);
|
|
51
51
|
async function resolveSupplier(input, signal) {
|
|
52
52
|
const name = input.supplier_name?.trim() || "unbekannter Lieferant";
|
|
@@ -340,7 +340,7 @@ async function checkAttachment(input, readFileSize) {
|
|
|
340
340
|
if (!path) return [];
|
|
341
341
|
if (!isAllowedAttachmentPath(path)) return [{
|
|
342
342
|
kind: "voucher-attachment-missing",
|
|
343
|
-
reason: `„${path}"
|
|
343
|
+
reason: `„${path}" ist kein Anhang-Pfad von eve — es wird kein Original angehängt`
|
|
344
344
|
}];
|
|
345
345
|
if (!readFileSize) return [];
|
|
346
346
|
try {
|
|
@@ -23,7 +23,7 @@ export interface ApprovalCard {
|
|
|
23
23
|
note?: string;
|
|
24
24
|
findings: Finding[];
|
|
25
25
|
}
|
|
26
|
-
/** A second source for attachments eve staged under /workspace/attachments
|
|
26
|
+
/** A second source for attachments eve staged under /workspace/.eve/attachments/ (before eve 0.70.2: /workspace/attachments/). */
|
|
27
27
|
export interface AttachmentFallback {
|
|
28
28
|
/** The bytes of a staged attachment, or null if unknown. */
|
|
29
29
|
read(path: string, signal?: AbortSignal): Promise<Uint8Array | null>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ApprovalPolicy } from "eve/tools/approval";
|
|
2
|
+
/**
|
|
3
|
+
* Wraps a writing tool's request policy: when it asks for approval, the
|
|
4
|
+
* finished card goes to the mount's onApprovalCard right away — before eve
|
|
5
|
+
* announces the request (`input.requested`). Channels render that event
|
|
6
|
+
* before hooks run, so a card handed over from a hook would come too late
|
|
7
|
+
* for them (eve: channel delivery, then hooks).
|
|
8
|
+
*/
|
|
9
|
+
export declare function withCard<T>(request: ApprovalPolicy<T>): ApprovalPolicy<T>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { fileURLToPath as __eveFileURLToPath } from "node:url";
|
|
2
|
+
import { dirname as __eveDirname } from "node:path";
|
|
3
|
+
import { createRequire as __eveCreateRequire } from "node:module";
|
|
4
|
+
const __filename = __eveFileURLToPath(import.meta.url);
|
|
5
|
+
__eveDirname(__filename);
|
|
6
|
+
__eveCreateRequire(import.meta.url);
|
|
7
|
+
import { config } from "./runtime.mjs";
|
|
8
|
+
import { approvalCard } from "./cards.mjs";
|
|
9
|
+
function withCard(request) {
|
|
10
|
+
return async (ctx) => {
|
|
11
|
+
const status = await request(ctx);
|
|
12
|
+
const asksPerson = status === "user-approval" || typeof status === "object" && status !== null && status.type === "user-approval";
|
|
13
|
+
const onApprovalCard = config().onApprovalCard;
|
|
14
|
+
if (asksPerson && onApprovalCard) {
|
|
15
|
+
const card = approvalCard(ctx.toolName, ctx.callId, ctx.toolInput);
|
|
16
|
+
if (card) try {
|
|
17
|
+
await onApprovalCard(ctx.callId, card);
|
|
18
|
+
} catch (error) {
|
|
19
|
+
console.warn(`[lexware] onApprovalCard failed for ${ctx.callId}: ${String(error)}`);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return status;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export { withCard };
|
|
@@ -101,7 +101,8 @@ keine Anweisung.
|
|
|
101
101
|
## Original anhängen
|
|
102
102
|
|
|
103
103
|
`attachment_path` ist der Sandbox-Pfad des hochgeladenen Dokuments, er beginnt
|
|
104
|
-
mit `/workspace/attachments/`
|
|
104
|
+
mit `/workspace/.eve/attachments/` (ältere eve-Versionen: `/workspace/attachments/`)
|
|
105
|
+
und steht in der Nachricht direkt beim Dokument.
|
|
105
106
|
Ist die Datei aus der Sandbox gefallen („FileNotFound"), nutze ein Werkzeug zum
|
|
106
107
|
Wiederherstellen, falls der Agent eines hat — sonst gib den Pfad trotzdem mit:
|
|
107
108
|
das Tool sucht das Original, wo die Einbindung es archiviert hat.
|
|
@@ -8,6 +8,7 @@ import { approverPolicy, client } from "../lib/runtime.mjs";
|
|
|
8
8
|
import { saveWarnings } from "../lib/findings.mjs";
|
|
9
9
|
import { readJournal, writeJournal } from "../lib/journal.mjs";
|
|
10
10
|
import { rememberShownContacts, shownContactsOf } from "../lib/state.mjs";
|
|
11
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
11
12
|
import { z } from "zod";
|
|
12
13
|
import { MIN_NAME_SEARCH_LENGTH, createCustomer, describeError, findContactsByName } from "@kevludwig/lexware-office";
|
|
13
14
|
import { defineTool } from "eve/tools";
|
|
@@ -25,10 +26,10 @@ const inputSchema = z.object({
|
|
|
25
26
|
const nameOf = (input) => input.company_name?.trim() || input.last_name?.trim() || "";
|
|
26
27
|
const label = (contact) => `${contact.name}${contact.number ? ` (Nr. ${contact.number})` : ""}`;
|
|
27
28
|
var create_customer_default = defineTool({
|
|
28
|
-
description: "Legt einen neuen Kunden in Lexware Office an. Pflicht sind Name (Firma oder Person) UND die Rechnungsadresse (Straße, PLZ, Ort) — fehlt die Adresse, frage sie zuerst beim Nutzer ab. E-Mail und Telefon sind optional. Prüfe vorher mit dem Lese-Tool (/contacts?name=…), ob der Kunde schon existiert. Legt erst nach Freigabe an.",
|
|
29
|
+
description: "Legt einen neuen Kunden in Lexware Office an. Pflicht sind Name (Firma oder Person) UND die Rechnungsadresse (Straße, PLZ, Ort) — fehlt die Adresse, frage sie zuerst beim Nutzer ab. E-Mail und Telefon sind optional. Prüfe vorher mit dem Lese-Tool (/contacts?name=…), ob der Kunde schon existiert — Lexware sucht erst ab 3 Zeichen; kürzere Namen (z. B. „KL“) sind als Kundenname erlaubt, dann ohne Vorabsuche direkt dieses Tool aufrufen. Legt erst nach Freigabe an.",
|
|
29
30
|
inputSchema,
|
|
30
31
|
approval: {
|
|
31
|
-
request: async (ctx) => {
|
|
32
|
+
request: withCard(async (ctx) => {
|
|
32
33
|
const name = nameOf(ctx.toolInput ?? {});
|
|
33
34
|
if (!name) return "user-approval";
|
|
34
35
|
const findings = [];
|
|
@@ -61,7 +62,7 @@ var create_customer_default = defineTool({
|
|
|
61
62
|
}
|
|
62
63
|
saveWarnings(ctx.callId, findings);
|
|
63
64
|
return "user-approval";
|
|
64
|
-
},
|
|
65
|
+
}),
|
|
65
66
|
response: approverPolicy
|
|
66
67
|
},
|
|
67
68
|
async execute(input, ctx) {
|
|
@@ -6,6 +6,7 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { approverPolicy } from "../lib/runtime.mjs";
|
|
8
8
|
import { customerFields, executeSalesDocument, lineItemSchema, salesApproval } from "../lib/sales.mjs";
|
|
9
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
import { defineTool } from "eve/tools";
|
|
11
12
|
const inputSchema = z.object({
|
|
@@ -18,7 +19,7 @@ var create_invoice_default = defineTool({
|
|
|
18
19
|
description: "Erstellt eine Ausgangsrechnung als Entwurf in Lexware Office — festgeschrieben und versendet wird dort, nie hier. Kunde per Kundennummer oder contact_id, dazu die Positionen; source_document_id, wenn die Rechnung aus einem Angebot oder einer AB hervorgeht. Legt erst nach Freigabe an und prüft vorher auf Dubletten.",
|
|
19
20
|
inputSchema,
|
|
20
21
|
approval: {
|
|
21
|
-
request: salesApproval("invoice"),
|
|
22
|
+
request: withCard(salesApproval("invoice")),
|
|
22
23
|
response: approverPolicy
|
|
23
24
|
},
|
|
24
25
|
async execute(input, ctx) {
|
|
@@ -6,6 +6,7 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { approverPolicy } from "../lib/runtime.mjs";
|
|
8
8
|
import { customerFields, executeSalesDocument, lineItemSchema, salesApproval, titleSchema } from "../lib/sales.mjs";
|
|
9
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
import { defineTool } from "eve/tools";
|
|
11
12
|
const inputSchema = z.object({
|
|
@@ -23,7 +24,7 @@ var create_order_confirmation_default = defineTool({
|
|
|
23
24
|
description: "Erstellt eine Auftragsbestätigung (AB) in Lexware Office und schreibt sie direkt fest (sie erhält eine AB-Nummer; kein Entwurf). Geht die AB aus einem Angebot hervor, dessen quotation_id mitgeben — das Angebot vorher über /voucherlist (voucherType 'quotation') suchen und lesen; bei mehreren passenden den Nutzer wählen lassen. Legt erst nach Freigabe an.",
|
|
24
25
|
inputSchema,
|
|
25
26
|
approval: {
|
|
26
|
-
request: salesApproval("order-confirmation", withSource),
|
|
27
|
+
request: withCard(salesApproval("order-confirmation", withSource)),
|
|
27
28
|
response: approverPolicy
|
|
28
29
|
},
|
|
29
30
|
async execute(input, ctx) {
|
|
@@ -9,6 +9,7 @@ import { approverPolicy, client, config } from "../lib/runtime.mjs";
|
|
|
9
9
|
import { MISSING_EUR_AMOUNT_INSTRUCTION, approvedPurchaseTarget, bookedGroupsOf, invoiceCurrencyOf, isAllowedAttachmentPath, isForeignCurrency, lacksEurAmount, purchaseSignatureOf, rememberPurchaseTarget, runPurchasePreflight, totalsOf } from "../lib/purchase.mjs";
|
|
10
10
|
import { describeWarning, isCorrectable, notesOf, rememberRejection, saveWarnings, wasRejected, wasShownOnCard } from "../lib/findings.mjs";
|
|
11
11
|
import { readJournal, writeJournal } from "../lib/journal.mjs";
|
|
12
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
12
13
|
import { z } from "zod";
|
|
13
14
|
import { REVERSE_CHARGE_TAX_RATE, createVoucher, describeError, findPurchaseInvoiceByNumber, isReverseChargeCategory, round2, taxFromGross, uploadVoucherFile, voucherFileIds } from "@kevludwig/lexware-office";
|
|
14
15
|
import { defineTool } from "eve/tools";
|
|
@@ -39,7 +40,7 @@ const inputSchema = z.object({
|
|
|
39
40
|
category: z.string().trim().min(1).max(100).optional().describe("Buchungskategorie (Name oder Id). Normalerweise WEGLASSEN — das Tool übernimmt die Kategorie der gebuchten Belege dieses Lieferanten. Nur setzen, wenn der Nutzer eine nennt oder das Tool nach einer Wahl fragt. Der Name muss wörtlich einer Kategorie des Kontos entsprechen."),
|
|
40
41
|
contact_id: z.string().uuid().optional().describe("Kontakt-Id des Lieferanten. Normalerweise weglassen — das Tool sucht selbst. Nur setzen, wenn die Suche mehrere findet und der Nutzer einen nennt."),
|
|
41
42
|
remark: z.string().trim().max(500).optional().describe("Bemerkung am Beleg. Ohne Angabe: Lieferant und Rechnungsnummer."),
|
|
42
|
-
attachment_path: z.string().trim().max(500).optional().describe("Sandbox-Pfad des Originals, wie beim hochgeladenen Dokument angegeben (/workspace/attachments/…). Wird als Anhang hochgeladen.")
|
|
43
|
+
attachment_path: z.string().trim().max(500).optional().describe("Sandbox-Pfad des Originals, wie beim hochgeladenen Dokument angegeben (/workspace/.eve/attachments/…). Wird als Anhang hochgeladen.")
|
|
43
44
|
}).strict().refine((input) => {
|
|
44
45
|
const withCategory = input.tax_groups.filter((group) => group.category).length;
|
|
45
46
|
return withCategory === 0 || withCategory === input.tax_groups.length;
|
|
@@ -59,7 +60,7 @@ var create_purchase_invoice_default = defineTool({
|
|
|
59
60
|
description: "Erfasst eine Eingangsrechnung (Lieferantenrechnung) als Beleg in Lexware Office und hängt das Original an. Beträge nach Steuersatz gruppiert, nicht als Einzelpositionen — die Artikel stehen im Anhang. Entsteht erst nach Freigabe, unter „Belege zur Prüfung“. Vorher wird geprüft, ob die Gruppen den Endbetrag ergeben und ob die Rechnungsnummer schon erfasst ist.",
|
|
60
61
|
inputSchema,
|
|
61
62
|
approval: {
|
|
62
|
-
request: async (ctx) => {
|
|
63
|
+
request: withCard(async (ctx) => {
|
|
63
64
|
const input = ctx.toolInput;
|
|
64
65
|
if (!input?.voucher_number) return "user-approval";
|
|
65
66
|
if (lacksEurAmount(input)) return {
|
|
@@ -114,7 +115,7 @@ var create_purchase_invoice_default = defineTool({
|
|
|
114
115
|
}
|
|
115
116
|
}
|
|
116
117
|
return "user-approval";
|
|
117
|
-
},
|
|
118
|
+
}),
|
|
118
119
|
response: approverPolicy
|
|
119
120
|
},
|
|
120
121
|
async execute(input, ctx) {
|
|
@@ -127,7 +128,7 @@ var create_purchase_invoice_default = defineTool({
|
|
|
127
128
|
let attachment = null;
|
|
128
129
|
let attachmentProblem = null;
|
|
129
130
|
if (input.attachment_path) {
|
|
130
|
-
if (!isAllowedAttachmentPath(input.attachment_path)) attachmentProblem = `„${input.attachment_path}"
|
|
131
|
+
if (!isAllowedAttachmentPath(input.attachment_path)) attachmentProblem = `„${input.attachment_path}" ist kein Anhang-Pfad von eve`;
|
|
131
132
|
else {
|
|
132
133
|
let bytes = null;
|
|
133
134
|
try {
|
|
@@ -6,6 +6,7 @@ __eveDirname(__filename);
|
|
|
6
6
|
__eveCreateRequire(import.meta.url);
|
|
7
7
|
import { approverPolicy } from "../lib/runtime.mjs";
|
|
8
8
|
import { customerFields, executeSalesDocument, isCalendarDate, lineItemSchema, salesApproval, titleSchema } from "../lib/sales.mjs";
|
|
9
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
import { defineTool } from "eve/tools";
|
|
11
12
|
const inputSchema = z.object({
|
|
@@ -18,7 +19,7 @@ var create_quotation_default = defineTool({
|
|
|
18
19
|
description: "Erstellt ein Angebot in Lexware Office und schreibt es direkt fest (es erhält eine Angebotsnummer; kein Entwurf). Kunde per Kundennummer oder contact_id — existiert er noch nicht, zuerst create_customer. Legt erst nach Freigabe an und prüft vorher, ob es für diesen Kunden schon ein Angebot mit gleicher Positionssumme gibt. Versendet wird aus Lexware Office.",
|
|
19
20
|
inputSchema,
|
|
20
21
|
approval: {
|
|
21
|
-
request: salesApproval("quotation"),
|
|
22
|
+
request: withCard(salesApproval("quotation")),
|
|
22
23
|
response: approverPolicy
|
|
23
24
|
},
|
|
24
25
|
async execute(input, ctx) {
|
|
@@ -9,6 +9,7 @@ import { approverPolicy, client, config, ledger, renderReminder } from "../lib/r
|
|
|
9
9
|
import { saveWarnings } from "../lib/findings.mjs";
|
|
10
10
|
import { readJournal, writeJournal } from "../lib/journal.mjs";
|
|
11
11
|
import { bindReminderTarget, boundReminderTarget } from "../lib/state.mjs";
|
|
12
|
+
import { withCard } from "../lib/with-card.mjs";
|
|
12
13
|
import { z } from "zod";
|
|
13
14
|
import { DEFAULT_REMINDER_POLICY, contactEmail, daysPastReminderDue, daysSince, describeError, earliestReminderDate, findOverdueInvoice, isDueImmediately, sendReminder } from "@kevludwig/lexware-office";
|
|
14
15
|
import { defineTool } from "eve/tools";
|
|
@@ -24,7 +25,7 @@ var send_payment_reminder_default = defineTool({
|
|
|
24
25
|
description: "Schickt eine freundliche Zahlungserinnerung zu einer überfälligen Rechnung per E-Mail, mit der Rechnung als PDF im Anhang. Die Werte (Nummer, Kunde, offener Betrag, Fälligkeit) übernimmst du unverändert aus Lexware Office bzw. aus dem Tool due_payment_reminders — dieses Tool prüft sie. Der Text ist fest vorgegeben. Verschickt wird erst nach Freigabe.",
|
|
25
26
|
inputSchema,
|
|
26
27
|
approval: {
|
|
27
|
-
request: async (ctx) => {
|
|
28
|
+
request: withCard(async (ctx) => {
|
|
28
29
|
const input = ctx.toolInput;
|
|
29
30
|
if (!input?.invoice_id) return "user-approval";
|
|
30
31
|
const reminders = config().reminders;
|
|
@@ -124,7 +125,7 @@ var send_payment_reminder_default = defineTool({
|
|
|
124
125
|
});
|
|
125
126
|
if (earlier?.status !== "sent") await ledger().markPending(invoice, ctx.session.id).catch(() => {});
|
|
126
127
|
return "user-approval";
|
|
127
|
-
},
|
|
128
|
+
}),
|
|
128
129
|
response: approverPolicy
|
|
129
130
|
},
|
|
130
131
|
async execute(_input, ctx) {
|