@genz-its/sevdesk-cli 0.0.0
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/README.md +800 -0
- package/dist/index.js +1794 -0
- package/dist/index.js.map +1 -0
- package/package.json +48 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1794 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { ZliError, defineCommand, defineConfig, defineOptions, processConfig } from "@robingenz/zli";
|
|
4
|
+
import { consola } from "consola";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { SevDesk, SevDeskError } from "@genz-its/sevdesk-sdk";
|
|
7
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { basename, join, resolve } from "node:path";
|
|
10
|
+
//#region src/config.ts
|
|
11
|
+
function configDir() {
|
|
12
|
+
const xdgConfigHome = process.env["XDG_CONFIG_HOME"];
|
|
13
|
+
const base = xdgConfigHome ? xdgConfigHome : join(homedir(), ".config");
|
|
14
|
+
return join(base, "sevdesk");
|
|
15
|
+
}
|
|
16
|
+
function configPath() {
|
|
17
|
+
return join(configDir(), "config.json");
|
|
18
|
+
}
|
|
19
|
+
async function readConfig() {
|
|
20
|
+
try {
|
|
21
|
+
return JSON.parse(await readFile(configPath(), "utf8"));
|
|
22
|
+
} catch {
|
|
23
|
+
return {};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
async function writeConfig(config) {
|
|
27
|
+
await mkdir(configDir(), { recursive: true });
|
|
28
|
+
await writeFile(configPath(), `${JSON.stringify(config, null, 2)}\n`, { mode: 384 });
|
|
29
|
+
}
|
|
30
|
+
async function resolveToken() {
|
|
31
|
+
const environmentToken = process.env["SEVDESK_TOKEN"]?.trim();
|
|
32
|
+
if (environmentToken) return {
|
|
33
|
+
token: environmentToken,
|
|
34
|
+
source: "environment"
|
|
35
|
+
};
|
|
36
|
+
const configToken = (await readConfig()).token?.trim();
|
|
37
|
+
if (configToken) return {
|
|
38
|
+
token: configToken,
|
|
39
|
+
source: "config"
|
|
40
|
+
};
|
|
41
|
+
return { source: "none" };
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/package.ts
|
|
45
|
+
const pkg = createRequire(import.meta.url)("../package.json");
|
|
46
|
+
//#endregion
|
|
47
|
+
//#region src/client.ts
|
|
48
|
+
function createClient(token) {
|
|
49
|
+
return new SevDesk({
|
|
50
|
+
token,
|
|
51
|
+
userAgent: `${pkg.name}/${pkg.version}`
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Resolves the API token and returns a ready-to-use client. Exits with an
|
|
56
|
+
* explanation when no token is available.
|
|
57
|
+
*/
|
|
58
|
+
async function requireClient() {
|
|
59
|
+
const { token } = await resolveToken();
|
|
60
|
+
if (!token) {
|
|
61
|
+
consola.error("You must be logged in. Run `sevdesk login` or set the SEVDESK_TOKEN environment variable.");
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
return createClient(token);
|
|
65
|
+
}
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/interactive.ts
|
|
68
|
+
function isInteractive() {
|
|
69
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true && !process.env["CI"];
|
|
70
|
+
}
|
|
71
|
+
//#endregion
|
|
72
|
+
//#region src/prompt.ts
|
|
73
|
+
async function promptText(message) {
|
|
74
|
+
const response = await consola.prompt(message, {
|
|
75
|
+
type: "text",
|
|
76
|
+
cancel: "symbol"
|
|
77
|
+
});
|
|
78
|
+
if (typeof response === "symbol") process.exit(0);
|
|
79
|
+
return response;
|
|
80
|
+
}
|
|
81
|
+
async function promptConfirm(message, initial = true) {
|
|
82
|
+
const response = await consola.prompt(message, {
|
|
83
|
+
type: "confirm",
|
|
84
|
+
initial,
|
|
85
|
+
cancel: "symbol"
|
|
86
|
+
});
|
|
87
|
+
if (typeof response === "symbol") process.exit(0);
|
|
88
|
+
return response;
|
|
89
|
+
}
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/options.ts
|
|
92
|
+
/**
|
|
93
|
+
* Returns the option value, asking for it when it is missing. Exits when the
|
|
94
|
+
* value is missing in a non-interactive environment.
|
|
95
|
+
*/
|
|
96
|
+
async function requireStringOption(options) {
|
|
97
|
+
const value = options.value?.trim();
|
|
98
|
+
if (value) return value;
|
|
99
|
+
requireInteractive(options.requirement);
|
|
100
|
+
const answer = (await promptText(options.question)).trim();
|
|
101
|
+
if (!answer) {
|
|
102
|
+
consola.error("The value must not be empty.");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
return answer;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Returns the option value, asking for it when it is missing. Exits when the
|
|
109
|
+
* value is missing in a non-interactive environment or is not a number.
|
|
110
|
+
*/
|
|
111
|
+
async function requireNumberOption(options) {
|
|
112
|
+
if (options.value !== void 0) return options.value;
|
|
113
|
+
requireInteractive(options.requirement);
|
|
114
|
+
const answer = (await promptText(options.question)).trim();
|
|
115
|
+
const value = Number(answer);
|
|
116
|
+
if (!answer || !Number.isFinite(value)) {
|
|
117
|
+
consola.error("The value must be a number.");
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Asks for confirmation unless `yes` is set. Returns `false` when the user
|
|
124
|
+
* declines, in which case the command must stop. Exits when the confirmation
|
|
125
|
+
* is missing in a non-interactive environment.
|
|
126
|
+
*/
|
|
127
|
+
async function confirmOrAbort(options) {
|
|
128
|
+
if (options.yes) return true;
|
|
129
|
+
if (!isInteractive()) {
|
|
130
|
+
consola.error("You must pass --yes to confirm this action when running in a non-interactive environment.");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const confirmed = options.initial === void 0 ? await promptConfirm(options.message) : await promptConfirm(options.message, options.initial);
|
|
134
|
+
if (!confirmed) consola.info("Aborted.");
|
|
135
|
+
return confirmed;
|
|
136
|
+
}
|
|
137
|
+
function requireInteractive(requirement) {
|
|
138
|
+
if (!isInteractive()) {
|
|
139
|
+
consola.error(`You must provide ${requirement} when running in a non-interactive environment.`);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
//#endregion
|
|
144
|
+
//#region src/output.ts
|
|
145
|
+
function printJson(value) {
|
|
146
|
+
console.log(JSON.stringify(value, null, 2));
|
|
147
|
+
}
|
|
148
|
+
function printTable(rows) {
|
|
149
|
+
const first = rows[0];
|
|
150
|
+
if (!first) return;
|
|
151
|
+
const columns = Object.keys(first);
|
|
152
|
+
const widths = columns.map((column) => Math.max(column.length, ...rows.map((row) => (row[column] ?? "").length)));
|
|
153
|
+
const line = (values) => values.map((value, index) => value.padEnd(widths[index] ?? 0)).join(" ");
|
|
154
|
+
console.log(line(columns.map((column) => column.toUpperCase())));
|
|
155
|
+
for (const row of rows) console.log(line(columns.map((column) => row[column] ?? "")));
|
|
156
|
+
}
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/commands/accounts/balance.ts
|
|
159
|
+
var balance_default = defineCommand({
|
|
160
|
+
description: "Show the balance of a check account at a given date.",
|
|
161
|
+
options: defineOptions(z.object({
|
|
162
|
+
id: z.coerce.number().optional().describe("The check account ID. If omitted, you will be prompted."),
|
|
163
|
+
date: z.string().default(() => (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)).describe("The date to calculate the balance for (YYYY-MM-DD)."),
|
|
164
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
165
|
+
})),
|
|
166
|
+
action: async (options) => {
|
|
167
|
+
const client = await requireClient();
|
|
168
|
+
const checkAccountId = await requireNumberOption({
|
|
169
|
+
value: options.id,
|
|
170
|
+
requirement: "a check account ID via --id",
|
|
171
|
+
question: "Enter the check account ID:"
|
|
172
|
+
});
|
|
173
|
+
const balance = await client.checkAccounts.getBalanceAtDate({
|
|
174
|
+
checkAccountId,
|
|
175
|
+
date: options.date
|
|
176
|
+
});
|
|
177
|
+
if (options.json) {
|
|
178
|
+
printJson({
|
|
179
|
+
checkAccountId,
|
|
180
|
+
date: options.date,
|
|
181
|
+
balance
|
|
182
|
+
});
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
consola.info(`Balance at ${options.date}: ${balance}`);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
//#endregion
|
|
189
|
+
//#region src/commands/accounts/create-clearing.ts
|
|
190
|
+
var create_clearing_default = defineCommand({
|
|
191
|
+
description: "Create a clearing check account.",
|
|
192
|
+
options: defineOptions(z.object({
|
|
193
|
+
name: z.string().optional().describe("The name of the check account. If omitted, you will be prompted."),
|
|
194
|
+
accountingNumber: z.coerce.number().optional().describe("The booking account number of the check account."),
|
|
195
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
196
|
+
})),
|
|
197
|
+
action: async (options) => {
|
|
198
|
+
const client = await requireClient();
|
|
199
|
+
const name = await requireStringOption({
|
|
200
|
+
value: options.name,
|
|
201
|
+
requirement: "a name via --name",
|
|
202
|
+
question: "Enter the name of the check account:"
|
|
203
|
+
});
|
|
204
|
+
const checkAccount = await client.checkAccounts.createClearingAccount({
|
|
205
|
+
name,
|
|
206
|
+
accountingNumber: options.accountingNumber
|
|
207
|
+
});
|
|
208
|
+
if (options.json) {
|
|
209
|
+
printJson(checkAccount);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
consola.success(`Created clearing account ${checkAccount.id}.`);
|
|
213
|
+
}
|
|
214
|
+
});
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/commands/accounts/create-file-import.ts
|
|
217
|
+
var create_file_import_default = defineCommand({
|
|
218
|
+
description: "Create a check account that imports its transactions from files.",
|
|
219
|
+
options: defineOptions(z.object({
|
|
220
|
+
name: z.string().optional().describe("The name of the check account. If omitted, you will be prompted."),
|
|
221
|
+
importType: z.enum(["CSV", "MT940"]).default("CSV").describe("The file format used to import transactions. Supported values are CSV and MT940."),
|
|
222
|
+
accountingNumber: z.coerce.number().optional().describe("The booking account number of the check account."),
|
|
223
|
+
iban: z.string().optional().describe("The IBAN of the check account."),
|
|
224
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
225
|
+
})),
|
|
226
|
+
action: async (options) => {
|
|
227
|
+
const client = await requireClient();
|
|
228
|
+
const name = await requireStringOption({
|
|
229
|
+
value: options.name,
|
|
230
|
+
requirement: "a name via --name",
|
|
231
|
+
question: "Enter the name of the check account:"
|
|
232
|
+
});
|
|
233
|
+
const checkAccount = await client.checkAccounts.createFileImportAccount({
|
|
234
|
+
name,
|
|
235
|
+
importType: options.importType,
|
|
236
|
+
accountingNumber: options.accountingNumber,
|
|
237
|
+
iban: options.iban
|
|
238
|
+
});
|
|
239
|
+
if (options.json) {
|
|
240
|
+
printJson(checkAccount);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
consola.success(`Created file import account ${checkAccount.id}.`);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
//#endregion
|
|
247
|
+
//#region src/commands/accounts/get.ts
|
|
248
|
+
var get_default$7 = defineCommand({
|
|
249
|
+
description: "Show a single check account.",
|
|
250
|
+
options: defineOptions(z.object({
|
|
251
|
+
id: z.coerce.number().optional().describe("The check account ID. If omitted, you will be prompted."),
|
|
252
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
253
|
+
})),
|
|
254
|
+
action: async (options) => {
|
|
255
|
+
const client = await requireClient();
|
|
256
|
+
const checkAccountId = await requireNumberOption({
|
|
257
|
+
value: options.id,
|
|
258
|
+
requirement: "a check account ID via --id",
|
|
259
|
+
question: "Enter the check account ID:"
|
|
260
|
+
});
|
|
261
|
+
const checkAccount = await client.checkAccounts.get({ checkAccountId });
|
|
262
|
+
if (options.json) {
|
|
263
|
+
printJson(checkAccount);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
consola.info(`ID: ${checkAccount.id}`);
|
|
267
|
+
consola.info(`Name: ${checkAccount.name}`);
|
|
268
|
+
consola.info(`Type: ${checkAccount.type}`);
|
|
269
|
+
consola.info(`Import type: ${checkAccount.importType ?? "-"}`);
|
|
270
|
+
consola.info(`IBAN: ${checkAccount.iban ?? "-"}`);
|
|
271
|
+
consola.info(`Accounting number: ${checkAccount.accountingNumber ?? "-"}`);
|
|
272
|
+
consola.info(`Currency: ${checkAccount.currency}`);
|
|
273
|
+
consola.info(`Balance: ${checkAccount.balance ?? "-"}`);
|
|
274
|
+
consola.info(`Status: ${checkAccount.status}`);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
//#endregion
|
|
278
|
+
//#region src/commands/accounts/list.ts
|
|
279
|
+
var list_default$8 = defineCommand({
|
|
280
|
+
description: "List your check accounts.",
|
|
281
|
+
options: defineOptions(z.object({
|
|
282
|
+
limit: z.coerce.number().optional().describe("The maximum number of check accounts to return."),
|
|
283
|
+
offset: z.coerce.number().optional().describe("The number of check accounts to skip."),
|
|
284
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
285
|
+
})),
|
|
286
|
+
action: async (options) => {
|
|
287
|
+
const checkAccounts = await (await requireClient()).checkAccounts.list({
|
|
288
|
+
limit: options.limit,
|
|
289
|
+
offset: options.offset
|
|
290
|
+
});
|
|
291
|
+
if (options.json) {
|
|
292
|
+
printJson(checkAccounts);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
if (checkAccounts.length === 0) {
|
|
296
|
+
consola.info("No check accounts found.");
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
printTable(checkAccounts.map((checkAccount) => ({
|
|
300
|
+
id: checkAccount.id,
|
|
301
|
+
name: checkAccount.name,
|
|
302
|
+
type: checkAccount.type,
|
|
303
|
+
importType: checkAccount.importType ?? "",
|
|
304
|
+
accountingNumber: checkAccount.accountingNumber ?? "",
|
|
305
|
+
currency: checkAccount.currency,
|
|
306
|
+
status: checkAccount.status
|
|
307
|
+
})));
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/commands/contacts/create.ts
|
|
312
|
+
var create_default$3 = defineCommand({
|
|
313
|
+
description: "Create a contact.",
|
|
314
|
+
options: defineOptions(z.object({
|
|
315
|
+
name: z.string().optional().describe("Name of the organization. Not to be used for persons. If both --name and --familyname are omitted, you will be prompted for the organization name."),
|
|
316
|
+
surename: z.string().optional().describe("First name of the person. Not to be used for organizations."),
|
|
317
|
+
familyname: z.string().optional().describe("Last name of the person. Not to be used for organizations."),
|
|
318
|
+
category: z.coerce.number().default(3).describe("ID of the contact category: 2 supplier, 3 customer, 4 partner."),
|
|
319
|
+
customerNumber: z.string().optional().describe("Customer number of the contact."),
|
|
320
|
+
description: z.string().optional().describe("Description of the contact."),
|
|
321
|
+
vatNumber: z.string().optional().describe("VAT number of the contact."),
|
|
322
|
+
taxNumber: z.string().optional().describe("Tax number of the contact."),
|
|
323
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
324
|
+
})),
|
|
325
|
+
action: async (options) => {
|
|
326
|
+
const client = await requireClient();
|
|
327
|
+
const name = !options.name && !options.familyname ? await requireStringOption({
|
|
328
|
+
value: void 0,
|
|
329
|
+
requirement: "a name via --name or --familyname",
|
|
330
|
+
question: "Enter the organization name:"
|
|
331
|
+
}) : options.name;
|
|
332
|
+
const contact = await client.contacts.create({
|
|
333
|
+
categoryId: options.category,
|
|
334
|
+
name,
|
|
335
|
+
surename: options.surename,
|
|
336
|
+
familyname: options.familyname,
|
|
337
|
+
customerNumber: options.customerNumber,
|
|
338
|
+
description: options.description,
|
|
339
|
+
vatNumber: options.vatNumber,
|
|
340
|
+
taxNumber: options.taxNumber
|
|
341
|
+
});
|
|
342
|
+
if (options.json) {
|
|
343
|
+
printJson(contact);
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
consola.success(`Created contact ${contact.id}.`);
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
//#endregion
|
|
350
|
+
//#region src/commands/contacts/delete.ts
|
|
351
|
+
var delete_default$1 = defineCommand({
|
|
352
|
+
description: "Delete a contact.",
|
|
353
|
+
options: defineOptions(z.object({
|
|
354
|
+
id: z.coerce.number().optional().describe("The contact ID. If omitted, you will be prompted."),
|
|
355
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
356
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
357
|
+
})),
|
|
358
|
+
action: async (options) => {
|
|
359
|
+
const client = await requireClient();
|
|
360
|
+
const contactId = await requireNumberOption({
|
|
361
|
+
value: options.id,
|
|
362
|
+
requirement: "a contact ID via --id",
|
|
363
|
+
question: "Enter the contact ID:"
|
|
364
|
+
});
|
|
365
|
+
if (!await confirmOrAbort({
|
|
366
|
+
message: `Delete contact ${contactId}?`,
|
|
367
|
+
yes: options.yes,
|
|
368
|
+
initial: false
|
|
369
|
+
})) return;
|
|
370
|
+
await client.contacts.delete({ contactId });
|
|
371
|
+
if (options.json) {
|
|
372
|
+
printJson({ deleted: true });
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
consola.success("Contact deleted.");
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
//#endregion
|
|
379
|
+
//#region src/commands/contacts/get.ts
|
|
380
|
+
var get_default$6 = defineCommand({
|
|
381
|
+
description: "Show a single contact.",
|
|
382
|
+
options: defineOptions(z.object({
|
|
383
|
+
id: z.coerce.number().optional().describe("The contact ID. If omitted, you will be prompted."),
|
|
384
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
385
|
+
})),
|
|
386
|
+
action: async (options) => {
|
|
387
|
+
const client = await requireClient();
|
|
388
|
+
const contactId = await requireNumberOption({
|
|
389
|
+
value: options.id,
|
|
390
|
+
requirement: "a contact ID via --id",
|
|
391
|
+
question: "Enter the contact ID:"
|
|
392
|
+
});
|
|
393
|
+
const contact = await client.contacts.get({ contactId });
|
|
394
|
+
if (options.json) {
|
|
395
|
+
printJson(contact);
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
consola.info(`ID: ${contact.id}`);
|
|
399
|
+
consola.info(`Name: ${contact.name ?? "-"}`);
|
|
400
|
+
consola.info(`First name: ${contact.surename ?? "-"}`);
|
|
401
|
+
consola.info(`Last name: ${contact.familyname ?? "-"}`);
|
|
402
|
+
consola.info(`Customer number: ${contact.customerNumber ?? "-"}`);
|
|
403
|
+
consola.info(`Category: ${contact.category.id}`);
|
|
404
|
+
consola.info(`Status: ${contact.status ?? "-"}`);
|
|
405
|
+
consola.info(`Description: ${contact.description ?? "-"}`);
|
|
406
|
+
consola.info(`VAT number: ${contact.vatNumber ?? "-"}`);
|
|
407
|
+
consola.info(`Tax number: ${contact.taxNumber ?? "-"}`);
|
|
408
|
+
}
|
|
409
|
+
});
|
|
410
|
+
//#endregion
|
|
411
|
+
//#region src/commands/contacts/list.ts
|
|
412
|
+
var list_default$7 = defineCommand({
|
|
413
|
+
description: "List contacts.",
|
|
414
|
+
options: defineOptions(z.object({
|
|
415
|
+
name: z.string().optional().describe("Filter by organization, first or last name."),
|
|
416
|
+
customerNumber: z.string().optional().describe("Filter by customer number."),
|
|
417
|
+
limit: z.coerce.number().optional().describe("Maximum number of contacts to return."),
|
|
418
|
+
offset: z.coerce.number().optional().describe("Number of contacts to skip."),
|
|
419
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
420
|
+
})),
|
|
421
|
+
action: async (options) => {
|
|
422
|
+
const contacts = await (await requireClient()).contacts.list({
|
|
423
|
+
name: options.name,
|
|
424
|
+
customerNumber: options.customerNumber,
|
|
425
|
+
limit: options.limit,
|
|
426
|
+
offset: options.offset
|
|
427
|
+
});
|
|
428
|
+
if (options.json) {
|
|
429
|
+
printJson(contacts);
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
if (contacts.length === 0) {
|
|
433
|
+
consola.info("No contacts found.");
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
printTable(contacts.map((contact) => ({
|
|
437
|
+
id: contact.id,
|
|
438
|
+
number: contact.customerNumber ?? "",
|
|
439
|
+
name: formatContactName(contact),
|
|
440
|
+
status: contact.status ?? "",
|
|
441
|
+
category: contact.category.id
|
|
442
|
+
})));
|
|
443
|
+
}
|
|
444
|
+
});
|
|
445
|
+
function formatContactName(contact) {
|
|
446
|
+
return contact.name ?? [contact.surename, contact.familyname].filter(Boolean).join(" ");
|
|
447
|
+
}
|
|
448
|
+
//#endregion
|
|
449
|
+
//#region src/commands/contacts/update.ts
|
|
450
|
+
var update_default = defineCommand({
|
|
451
|
+
description: "Update a contact.",
|
|
452
|
+
options: defineOptions(z.object({
|
|
453
|
+
id: z.coerce.number().optional().describe("The contact ID. If omitted, you will be prompted."),
|
|
454
|
+
name: z.string().optional().describe("Name of the organization. Not to be used for persons."),
|
|
455
|
+
surename: z.string().optional().describe("First name of the person. Not to be used for organizations."),
|
|
456
|
+
familyname: z.string().optional().describe("Last name of the person. Not to be used for organizations."),
|
|
457
|
+
category: z.coerce.number().optional().describe("ID of the contact category: 2 supplier, 3 customer, 4 partner."),
|
|
458
|
+
customerNumber: z.string().optional().describe("Customer number of the contact."),
|
|
459
|
+
description: z.string().optional().describe("Description of the contact."),
|
|
460
|
+
vatNumber: z.string().optional().describe("VAT number of the contact."),
|
|
461
|
+
taxNumber: z.string().optional().describe("Tax number of the contact."),
|
|
462
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
463
|
+
})),
|
|
464
|
+
action: async (options) => {
|
|
465
|
+
const client = await requireClient();
|
|
466
|
+
const contactId = await requireNumberOption({
|
|
467
|
+
value: options.id,
|
|
468
|
+
requirement: "a contact ID via --id",
|
|
469
|
+
question: "Enter the contact ID:"
|
|
470
|
+
});
|
|
471
|
+
const contact = await client.contacts.update({
|
|
472
|
+
contactId,
|
|
473
|
+
categoryId: options.category,
|
|
474
|
+
name: options.name,
|
|
475
|
+
surename: options.surename,
|
|
476
|
+
familyname: options.familyname,
|
|
477
|
+
customerNumber: options.customerNumber,
|
|
478
|
+
description: options.description,
|
|
479
|
+
vatNumber: options.vatNumber,
|
|
480
|
+
taxNumber: options.taxNumber
|
|
481
|
+
});
|
|
482
|
+
if (options.json) {
|
|
483
|
+
printJson(contact);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
consola.success(`Updated contact ${contact.id}.`);
|
|
487
|
+
}
|
|
488
|
+
});
|
|
489
|
+
//#endregion
|
|
490
|
+
//#region src/commands/credit-notes/get.ts
|
|
491
|
+
var get_default$5 = defineCommand({
|
|
492
|
+
description: "Show a single credit note.",
|
|
493
|
+
options: defineOptions(z.object({
|
|
494
|
+
id: z.coerce.number().optional().describe("ID of the credit note. If omitted, you will be prompted."),
|
|
495
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
496
|
+
})),
|
|
497
|
+
action: async (options) => {
|
|
498
|
+
const client = await requireClient();
|
|
499
|
+
const creditNoteId = await requireNumberOption({
|
|
500
|
+
value: options.id,
|
|
501
|
+
requirement: "a credit note ID via --id",
|
|
502
|
+
question: "Enter the credit note ID:"
|
|
503
|
+
});
|
|
504
|
+
const creditNote = await client.creditNotes.get({ creditNoteId });
|
|
505
|
+
if (options.json) {
|
|
506
|
+
printJson(creditNote);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
consola.info(`ID: ${creditNote.id}`);
|
|
510
|
+
consola.info(`Number: ${creditNote.creditNoteNumber ?? "-"}`);
|
|
511
|
+
consola.info(`Date: ${creditNote.creditNoteDate}`);
|
|
512
|
+
consola.info(`Status: ${creditNote.status}`);
|
|
513
|
+
consola.info(`Contact: ${creditNote.contact?.id ?? "-"}`);
|
|
514
|
+
consola.info(`Net: ${creditNote.sumNet}`);
|
|
515
|
+
consola.info(`Tax: ${creditNote.sumTax}`);
|
|
516
|
+
consola.info(`Gross: ${creditNote.sumGross}`);
|
|
517
|
+
consola.info(`Currency: ${creditNote.currency ?? "-"}`);
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
//#endregion
|
|
521
|
+
//#region src/commands/credit-notes/list.ts
|
|
522
|
+
var list_default$6 = defineCommand({
|
|
523
|
+
description: "List credit notes.",
|
|
524
|
+
options: defineOptions(z.object({
|
|
525
|
+
status: z.coerce.number().optional().describe("Filter by status: 100 draft, 200 open, 750 partially paid, 1000 paid."),
|
|
526
|
+
creditNoteNumber: z.string().optional().describe("Filter by credit note number."),
|
|
527
|
+
startDate: z.string().optional().describe("Only credit notes on or after this date as dd.mm.yyyy or Unix timestamp."),
|
|
528
|
+
endDate: z.string().optional().describe("Only credit notes on or before this date as dd.mm.yyyy or Unix timestamp."),
|
|
529
|
+
contact: z.coerce.number().optional().describe("ID of the contact whose credit notes to list."),
|
|
530
|
+
limit: z.coerce.number().optional().describe("Maximum number of credit notes to return."),
|
|
531
|
+
offset: z.coerce.number().optional().describe("Number of credit notes to skip."),
|
|
532
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
533
|
+
})),
|
|
534
|
+
action: async (options) => {
|
|
535
|
+
const creditNotes = await (await requireClient()).creditNotes.list({
|
|
536
|
+
status: options.status,
|
|
537
|
+
creditNoteNumber: options.creditNoteNumber,
|
|
538
|
+
startDate: options.startDate,
|
|
539
|
+
endDate: options.endDate,
|
|
540
|
+
contactId: options.contact,
|
|
541
|
+
limit: options.limit,
|
|
542
|
+
offset: options.offset
|
|
543
|
+
});
|
|
544
|
+
if (options.json) {
|
|
545
|
+
printJson(creditNotes);
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
if (creditNotes.length === 0) {
|
|
549
|
+
consola.info("No credit notes found.");
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
printTable(creditNotes.map((creditNote) => ({
|
|
553
|
+
id: creditNote.id,
|
|
554
|
+
number: creditNote.creditNoteNumber ?? "",
|
|
555
|
+
date: creditNote.creditNoteDate,
|
|
556
|
+
contact: creditNote.contact?.id ?? "",
|
|
557
|
+
gross: creditNote.sumGross,
|
|
558
|
+
status: creditNote.status
|
|
559
|
+
})));
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/commands/credit-notes/pdf.ts
|
|
564
|
+
var pdf_default$2 = defineCommand({
|
|
565
|
+
description: "Download the PDF of a credit note.",
|
|
566
|
+
options: defineOptions(z.object({
|
|
567
|
+
id: z.coerce.number().optional().describe("ID of the credit note. If omitted, you will be prompted."),
|
|
568
|
+
output: z.string().optional().describe("Path to write the PDF to. Defaults to the file name reported by the API."),
|
|
569
|
+
preventSendBy: z.boolean().default(false).describe("Do not mark the credit note as sent by download."),
|
|
570
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
571
|
+
})),
|
|
572
|
+
action: async (options) => {
|
|
573
|
+
const client = await requireClient();
|
|
574
|
+
const creditNoteId = await requireNumberOption({
|
|
575
|
+
value: options.id,
|
|
576
|
+
requirement: "a credit note ID via --id",
|
|
577
|
+
question: "Enter the credit note ID:"
|
|
578
|
+
});
|
|
579
|
+
const pdf = await client.creditNotes.getPdf({
|
|
580
|
+
creditNoteId,
|
|
581
|
+
preventSendBy: options.preventSendBy
|
|
582
|
+
});
|
|
583
|
+
const path = resolve(options.output ?? pdf.filename);
|
|
584
|
+
await writeFile(path, Buffer.from(pdf.content, "base64"));
|
|
585
|
+
if (options.json) {
|
|
586
|
+
printJson({
|
|
587
|
+
filename: pdf.filename,
|
|
588
|
+
path
|
|
589
|
+
});
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
consola.success(`Saved ${path}.`);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
//#endregion
|
|
596
|
+
//#region src/errors.ts
|
|
597
|
+
function formatError(error) {
|
|
598
|
+
if (error instanceof ZliError) return error.message;
|
|
599
|
+
if (error instanceof z.ZodError) return error.issues.map((issue) => `${issue.path.join(".") || "input"}: ${issue.message}`).join("\n");
|
|
600
|
+
if (error instanceof SevDeskError) {
|
|
601
|
+
if (error.status === 401) return "Authentication failed. Check your API token or run `sevdesk login` again.";
|
|
602
|
+
return error.message;
|
|
603
|
+
}
|
|
604
|
+
return error instanceof Error ? error.message : String(error);
|
|
605
|
+
}
|
|
606
|
+
//#endregion
|
|
607
|
+
//#region src/commands/doctor.ts
|
|
608
|
+
var doctor_default = defineCommand({
|
|
609
|
+
description: "Check the CLI setup and the connection to the sevdesk API.",
|
|
610
|
+
options: defineOptions(z.object({ json: z.boolean().default(false).describe("Output in JSON format.") })),
|
|
611
|
+
action: async (options) => {
|
|
612
|
+
const { token, source } = await resolveToken();
|
|
613
|
+
let bookkeepingSystemVersion;
|
|
614
|
+
let apiError;
|
|
615
|
+
if (token) try {
|
|
616
|
+
bookkeepingSystemVersion = await createClient(token).basics.getBookkeepingSystemVersion();
|
|
617
|
+
} catch (error) {
|
|
618
|
+
apiError = formatError(error);
|
|
619
|
+
}
|
|
620
|
+
if (options.json) printJson({
|
|
621
|
+
cliVersion: pkg.version,
|
|
622
|
+
nodeVersion: process.version,
|
|
623
|
+
tokenSource: source,
|
|
624
|
+
bookkeepingSystemVersion: bookkeepingSystemVersion ?? null,
|
|
625
|
+
apiError: apiError ?? null
|
|
626
|
+
});
|
|
627
|
+
else {
|
|
628
|
+
consola.info(`CLI version: ${pkg.version}`);
|
|
629
|
+
consola.info(`Node.js version: ${process.version}`);
|
|
630
|
+
consola.info(`Token source: ${source}`);
|
|
631
|
+
if (!token) consola.warn("No API token found. Run `sevdesk login` or set the SEVDESK_TOKEN environment variable.");
|
|
632
|
+
else if (apiError) consola.error(`API connection failed: ${apiError}`);
|
|
633
|
+
else consola.success(`API connection succeeded. Bookkeeping system version: ${bookkeepingSystemVersion}`);
|
|
634
|
+
}
|
|
635
|
+
if (bookkeepingSystemVersion === "1.0") {
|
|
636
|
+
consola.warn("This sevdesk account still uses bookkeeping system version 1.0, which is not supported.");
|
|
637
|
+
process.exitCode = 1;
|
|
638
|
+
} else if (!token || apiError) process.exitCode = 1;
|
|
639
|
+
}
|
|
640
|
+
});
|
|
641
|
+
//#endregion
|
|
642
|
+
//#region src/commands/export/contacts.ts
|
|
643
|
+
var contacts_default = defineCommand({
|
|
644
|
+
description: "Export contacts as a CSV file.",
|
|
645
|
+
options: defineOptions(z.object({
|
|
646
|
+
output: z.string().optional().describe("Path to write the CSV file to. Defaults to the filename of the export."),
|
|
647
|
+
limit: z.coerce.number().optional().describe("The maximum number of contacts to export."),
|
|
648
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
649
|
+
})),
|
|
650
|
+
action: async (options) => {
|
|
651
|
+
const file = await (await requireClient()).exports.exportContactsCsv({ limit: options.limit });
|
|
652
|
+
const path = options.output ?? file.filename;
|
|
653
|
+
await writeFile(path, file.base64Encoded ? Buffer.from(file.content, "base64") : file.content);
|
|
654
|
+
if (options.json) {
|
|
655
|
+
printJson({
|
|
656
|
+
filename: file.filename,
|
|
657
|
+
path
|
|
658
|
+
});
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
consola.success(`Saved ${path}.`);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
//#endregion
|
|
665
|
+
//#region src/commands/export/datev.ts
|
|
666
|
+
const POLL_INTERVAL = 3e3;
|
|
667
|
+
var datev_default = defineCommand({
|
|
668
|
+
description: "Export accounting data in the DATEV format as a ZIP archive.",
|
|
669
|
+
options: defineOptions(z.object({
|
|
670
|
+
startDate: z.string().optional().describe("Start of the export period as dd.mm.yyyy or Unix timestamp. If omitted, you will be prompted."),
|
|
671
|
+
endDate: z.string().optional().describe("End of the export period as dd.mm.yyyy or Unix timestamp. If omitted, you will be prompted."),
|
|
672
|
+
format: z.enum(["csv", "xml"]).default("csv").describe("The DATEV export format. Supported values are csv and xml."),
|
|
673
|
+
scope: z.string().default("EXTCD").describe("The models to include as a string of letters: E (earnings), X (expenditure), T (transactions), C (cash register) and D (assets). XML exports support only E and X."),
|
|
674
|
+
output: z.string().optional().describe("Path to write the ZIP archive to. Defaults to the filename of the export."),
|
|
675
|
+
timeout: z.coerce.number().default(300).describe("Maximum number of seconds to wait for the export job."),
|
|
676
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
677
|
+
})),
|
|
678
|
+
action: async (options) => {
|
|
679
|
+
const client = await requireClient();
|
|
680
|
+
const jobOptions = {
|
|
681
|
+
startDate: await requireStringOption({
|
|
682
|
+
value: options.startDate,
|
|
683
|
+
requirement: "a start date via --start-date",
|
|
684
|
+
question: "Enter the start date (dd.mm.yyyy):"
|
|
685
|
+
}),
|
|
686
|
+
endDate: await requireStringOption({
|
|
687
|
+
value: options.endDate,
|
|
688
|
+
requirement: "an end date via --end-date",
|
|
689
|
+
question: "Enter the end date (dd.mm.yyyy):"
|
|
690
|
+
}),
|
|
691
|
+
scope: options.scope
|
|
692
|
+
};
|
|
693
|
+
const jobId = options.format === "csv" ? await client.exports.createDatevCsvZipExportJob(jobOptions) : await client.exports.createDatevXmlZipExportJob(jobOptions);
|
|
694
|
+
if (!options.json) consola.start("Waiting for the export job to finish...");
|
|
695
|
+
const info = await waitForDownloadInfo(client, jobId, options.timeout);
|
|
696
|
+
if (!info) {
|
|
697
|
+
consola.error(`The export job did not finish within ${options.timeout} seconds.`);
|
|
698
|
+
process.exit(1);
|
|
699
|
+
}
|
|
700
|
+
const response = await fetch(info.link);
|
|
701
|
+
if (!response.ok) {
|
|
702
|
+
consola.error(`The export file could not be downloaded (status ${response.status}).`);
|
|
703
|
+
process.exit(1);
|
|
704
|
+
}
|
|
705
|
+
const path = options.output ?? info.filename;
|
|
706
|
+
await writeFile(path, new Uint8Array(await response.arrayBuffer()));
|
|
707
|
+
if (options.json) {
|
|
708
|
+
printJson({
|
|
709
|
+
jobId,
|
|
710
|
+
filename: info.filename,
|
|
711
|
+
path
|
|
712
|
+
});
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
consola.success(`Saved ${path}.`);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
async function waitForDownloadInfo(client, jobId, timeoutSeconds) {
|
|
719
|
+
const deadline = Date.now() + timeoutSeconds * 1e3;
|
|
720
|
+
while (Date.now() < deadline) {
|
|
721
|
+
const info = await getDownloadInfo(client, jobId);
|
|
722
|
+
if (info) return info;
|
|
723
|
+
await delay(POLL_INTERVAL);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
async function getDownloadInfo(client, jobId) {
|
|
727
|
+
try {
|
|
728
|
+
const [info] = await client.exports.getJobDownloadInfo({ jobId });
|
|
729
|
+
return info?.link ? info : void 0;
|
|
730
|
+
} catch (error) {
|
|
731
|
+
if (error instanceof SevDeskError) return;
|
|
732
|
+
throw error;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function delay(milliseconds) {
|
|
736
|
+
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
737
|
+
}
|
|
738
|
+
//#endregion
|
|
739
|
+
//#region src/commands/export/invoices.ts
|
|
740
|
+
var invoices_default = defineCommand({
|
|
741
|
+
description: "Export invoices as a CSV file.",
|
|
742
|
+
options: defineOptions(z.object({
|
|
743
|
+
output: z.string().optional().describe("Path to write the CSV file to. Defaults to the filename of the export."),
|
|
744
|
+
limit: z.coerce.number().optional().describe("The maximum number of invoices to export."),
|
|
745
|
+
startDate: z.string().optional().describe("Only export invoices on or after this date (ISO 8601)."),
|
|
746
|
+
endDate: z.string().optional().describe("Only export invoices on or before this date (ISO 8601)."),
|
|
747
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
748
|
+
})),
|
|
749
|
+
action: async (options) => {
|
|
750
|
+
const file = await (await requireClient()).exports.exportInvoicesCsv({
|
|
751
|
+
limit: options.limit,
|
|
752
|
+
filter: {
|
|
753
|
+
startDate: options.startDate,
|
|
754
|
+
endDate: options.endDate
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
const path = options.output ?? file.filename;
|
|
758
|
+
await writeFile(path, file.base64Encoded ? Buffer.from(file.content, "base64") : file.content);
|
|
759
|
+
if (options.json) {
|
|
760
|
+
printJson({
|
|
761
|
+
filename: file.filename,
|
|
762
|
+
path
|
|
763
|
+
});
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
consola.success(`Saved ${path}.`);
|
|
767
|
+
}
|
|
768
|
+
});
|
|
769
|
+
//#endregion
|
|
770
|
+
//#region src/commands/export/transactions.ts
|
|
771
|
+
var transactions_default = defineCommand({
|
|
772
|
+
description: "Export transactions as a CSV file.",
|
|
773
|
+
options: defineOptions(z.object({
|
|
774
|
+
output: z.string().optional().describe("Path to write the CSV file to. Defaults to the filename of the export."),
|
|
775
|
+
limit: z.coerce.number().optional().describe("The maximum number of transactions to export."),
|
|
776
|
+
startDate: z.string().optional().describe("Only export transactions on or after this date (ISO 8601)."),
|
|
777
|
+
endDate: z.string().optional().describe("Only export transactions on or before this date (ISO 8601)."),
|
|
778
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
779
|
+
})),
|
|
780
|
+
action: async (options) => {
|
|
781
|
+
const file = await (await requireClient()).exports.exportTransactionsCsv({
|
|
782
|
+
limit: options.limit,
|
|
783
|
+
filter: {
|
|
784
|
+
startDate: options.startDate,
|
|
785
|
+
endDate: options.endDate
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
const path = options.output ?? file.filename;
|
|
789
|
+
await writeFile(path, file.base64Encoded ? Buffer.from(file.content, "base64") : file.content);
|
|
790
|
+
if (options.json) {
|
|
791
|
+
printJson({
|
|
792
|
+
filename: file.filename,
|
|
793
|
+
path
|
|
794
|
+
});
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
consola.success(`Saved ${path}.`);
|
|
798
|
+
}
|
|
799
|
+
});
|
|
800
|
+
//#endregion
|
|
801
|
+
//#region src/commands/export/vouchers.ts
|
|
802
|
+
var vouchers_default = defineCommand({
|
|
803
|
+
description: "Export vouchers as a CSV file.",
|
|
804
|
+
options: defineOptions(z.object({
|
|
805
|
+
output: z.string().optional().describe("Path to write the CSV file to. Defaults to the filename of the export."),
|
|
806
|
+
limit: z.coerce.number().optional().describe("The maximum number of vouchers to export."),
|
|
807
|
+
startDate: z.string().optional().describe("Only export vouchers on or after this date (ISO 8601)."),
|
|
808
|
+
endDate: z.string().optional().describe("Only export vouchers on or before this date (ISO 8601)."),
|
|
809
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
810
|
+
})),
|
|
811
|
+
action: async (options) => {
|
|
812
|
+
const file = await (await requireClient()).exports.exportVouchersCsv({
|
|
813
|
+
limit: options.limit,
|
|
814
|
+
filter: {
|
|
815
|
+
startDate: options.startDate,
|
|
816
|
+
endDate: options.endDate
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
const path = options.output ?? file.filename;
|
|
820
|
+
await writeFile(path, file.base64Encoded ? Buffer.from(file.content, "base64") : file.content);
|
|
821
|
+
if (options.json) {
|
|
822
|
+
printJson({
|
|
823
|
+
filename: file.filename,
|
|
824
|
+
path
|
|
825
|
+
});
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
consola.success(`Saved ${path}.`);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
//#endregion
|
|
832
|
+
//#region src/commands/guidance/accounts.ts
|
|
833
|
+
const MAX_TAX_RULES = 3;
|
|
834
|
+
function formatTaxRules(taxRules) {
|
|
835
|
+
const names = taxRules.map((taxRule) => taxRule.name);
|
|
836
|
+
if (names.length > MAX_TAX_RULES) return `${names.slice(0, MAX_TAX_RULES).join(", ")}, …`;
|
|
837
|
+
return names.join(", ");
|
|
838
|
+
}
|
|
839
|
+
var accounts_default = defineCommand({
|
|
840
|
+
description: "List bookable accounts and their allowed tax rules. The first matching filter wins: --account-number, --tax-rule, --revenue, --expense. Without a filter, all accounts are listed.",
|
|
841
|
+
options: defineOptions(z.object({
|
|
842
|
+
accountNumber: z.string().optional().describe("Only show the account with this datev account number."),
|
|
843
|
+
taxRule: z.string().optional().describe("Tax rule name, for example USTPFL_UMS_EINN."),
|
|
844
|
+
revenue: z.boolean().default(false).describe("Only show accounts that can be used for revenue."),
|
|
845
|
+
expense: z.boolean().default(false).describe("Only show accounts that can be used for expenses."),
|
|
846
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
847
|
+
})),
|
|
848
|
+
action: async (options) => {
|
|
849
|
+
const client = await requireClient();
|
|
850
|
+
let guides;
|
|
851
|
+
if (options.accountNumber) guides = await client.receiptGuidance.forAccountNumber({ accountNumber: options.accountNumber });
|
|
852
|
+
else if (options.taxRule) guides = await client.receiptGuidance.forTaxRule({ taxRule: options.taxRule });
|
|
853
|
+
else if (options.revenue) guides = await client.receiptGuidance.forRevenue();
|
|
854
|
+
else if (options.expense) guides = await client.receiptGuidance.forExpense();
|
|
855
|
+
else guides = await client.receiptGuidance.forAllAccounts();
|
|
856
|
+
if (options.json) {
|
|
857
|
+
printJson(guides);
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
if (guides.length === 0) {
|
|
861
|
+
consola.info("No accounts found.");
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
printTable(guides.map((guide) => ({
|
|
865
|
+
id: String(guide.accountDatevId),
|
|
866
|
+
number: guide.accountNumber,
|
|
867
|
+
name: guide.accountName,
|
|
868
|
+
taxRules: formatTaxRules(guide.allowedTaxRules)
|
|
869
|
+
})));
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
//#endregion
|
|
873
|
+
//#region src/commands/invoices/get.ts
|
|
874
|
+
var get_default$4 = defineCommand({
|
|
875
|
+
description: "Show a single invoice.",
|
|
876
|
+
options: defineOptions(z.object({
|
|
877
|
+
id: z.coerce.number().optional().describe("ID of the invoice. If omitted, you will be prompted."),
|
|
878
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
879
|
+
})),
|
|
880
|
+
action: async (options) => {
|
|
881
|
+
const client = await requireClient();
|
|
882
|
+
const invoiceId = await requireNumberOption({
|
|
883
|
+
value: options.id,
|
|
884
|
+
requirement: "an invoice ID via --id",
|
|
885
|
+
question: "Enter the invoice ID:"
|
|
886
|
+
});
|
|
887
|
+
const invoice = await client.invoices.get({ invoiceId });
|
|
888
|
+
if (options.json) {
|
|
889
|
+
printJson(invoice);
|
|
890
|
+
return;
|
|
891
|
+
}
|
|
892
|
+
consola.info(`ID: ${invoice.id}`);
|
|
893
|
+
consola.info(`Number: ${invoice.invoiceNumber ?? "-"}`);
|
|
894
|
+
consola.info(`Date: ${invoice.invoiceDate}`);
|
|
895
|
+
consola.info(`Status: ${invoice.status}`);
|
|
896
|
+
consola.info(`Contact: ${invoice.contact?.id ?? "-"}`);
|
|
897
|
+
consola.info(`Net: ${invoice.sumNet}`);
|
|
898
|
+
consola.info(`Tax: ${invoice.sumTax}`);
|
|
899
|
+
consola.info(`Gross: ${invoice.sumGross}`);
|
|
900
|
+
consola.info(`Currency: ${invoice.currency}`);
|
|
901
|
+
}
|
|
902
|
+
});
|
|
903
|
+
//#endregion
|
|
904
|
+
//#region src/commands/invoices/list.ts
|
|
905
|
+
var list_default$5 = defineCommand({
|
|
906
|
+
description: "List invoices.",
|
|
907
|
+
options: defineOptions(z.object({
|
|
908
|
+
status: z.coerce.number().optional().describe("Filter by status: 50 deactivated recurring, 100 draft, 200 open, 750 partially paid, 1000 paid."),
|
|
909
|
+
invoiceNumber: z.string().optional().describe("Filter by invoice number."),
|
|
910
|
+
startDate: z.string().optional().describe("Only invoices on or after this date as dd.mm.yyyy or Unix timestamp."),
|
|
911
|
+
endDate: z.string().optional().describe("Only invoices on or before this date as dd.mm.yyyy or Unix timestamp."),
|
|
912
|
+
contact: z.coerce.number().optional().describe("ID of the contact whose invoices to list."),
|
|
913
|
+
limit: z.coerce.number().optional().describe("Maximum number of invoices to return."),
|
|
914
|
+
offset: z.coerce.number().optional().describe("Number of invoices to skip."),
|
|
915
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
916
|
+
})),
|
|
917
|
+
action: async (options) => {
|
|
918
|
+
const invoices = await (await requireClient()).invoices.list({
|
|
919
|
+
status: options.status,
|
|
920
|
+
invoiceNumber: options.invoiceNumber,
|
|
921
|
+
startDate: options.startDate,
|
|
922
|
+
endDate: options.endDate,
|
|
923
|
+
contactId: options.contact,
|
|
924
|
+
limit: options.limit,
|
|
925
|
+
offset: options.offset
|
|
926
|
+
});
|
|
927
|
+
if (options.json) {
|
|
928
|
+
printJson(invoices);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (invoices.length === 0) {
|
|
932
|
+
consola.info("No invoices found.");
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
printTable(invoices.map((invoice) => ({
|
|
936
|
+
id: invoice.id,
|
|
937
|
+
number: invoice.invoiceNumber ?? "",
|
|
938
|
+
date: invoice.invoiceDate,
|
|
939
|
+
contact: invoice.contact?.id ?? "",
|
|
940
|
+
gross: invoice.sumGross,
|
|
941
|
+
status: invoice.status
|
|
942
|
+
})));
|
|
943
|
+
}
|
|
944
|
+
});
|
|
945
|
+
//#endregion
|
|
946
|
+
//#region src/commands/invoices/pdf.ts
|
|
947
|
+
var pdf_default$1 = defineCommand({
|
|
948
|
+
description: "Download the PDF of an invoice.",
|
|
949
|
+
options: defineOptions(z.object({
|
|
950
|
+
id: z.coerce.number().optional().describe("ID of the invoice. If omitted, you will be prompted."),
|
|
951
|
+
output: z.string().optional().describe("Path to write the PDF to. Defaults to the file name reported by the API."),
|
|
952
|
+
preventSendBy: z.boolean().default(false).describe("Do not mark the invoice as sent by download."),
|
|
953
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
954
|
+
})),
|
|
955
|
+
action: async (options) => {
|
|
956
|
+
const client = await requireClient();
|
|
957
|
+
const invoiceId = await requireNumberOption({
|
|
958
|
+
value: options.id,
|
|
959
|
+
requirement: "an invoice ID via --id",
|
|
960
|
+
question: "Enter the invoice ID:"
|
|
961
|
+
});
|
|
962
|
+
const pdf = await client.invoices.getPdf({
|
|
963
|
+
invoiceId,
|
|
964
|
+
preventSendBy: options.preventSendBy
|
|
965
|
+
});
|
|
966
|
+
const path = resolve(options.output ?? pdf.filename);
|
|
967
|
+
await writeFile(path, Buffer.from(pdf.content, "base64"));
|
|
968
|
+
if (options.json) {
|
|
969
|
+
printJson({
|
|
970
|
+
filename: pdf.filename,
|
|
971
|
+
path
|
|
972
|
+
});
|
|
973
|
+
return;
|
|
974
|
+
}
|
|
975
|
+
consola.success(`Saved ${path}.`);
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
//#endregion
|
|
979
|
+
//#region src/commands/login.ts
|
|
980
|
+
var login_default = defineCommand({
|
|
981
|
+
description: "Log in with your sevdesk API token.",
|
|
982
|
+
options: defineOptions(z.object({ token: z.string().optional().describe("The sevdesk API token. If omitted, you will be prompted.") })),
|
|
983
|
+
action: async (options) => {
|
|
984
|
+
let token = options.token?.trim();
|
|
985
|
+
if (!token) {
|
|
986
|
+
if (!isInteractive()) {
|
|
987
|
+
consola.error("You must provide a token via --token when running in a non-interactive environment.");
|
|
988
|
+
process.exit(1);
|
|
989
|
+
}
|
|
990
|
+
consola.info("You can find your API token in sevdesk under Settings > User > API token.");
|
|
991
|
+
token = (await promptText("Enter your sevdesk API token:")).trim();
|
|
992
|
+
}
|
|
993
|
+
if (await createClient(token).basics.getBookkeepingSystemVersion() === "1.0") {
|
|
994
|
+
consola.error("This sevdesk account still uses bookkeeping system version 1.0, which is not supported. Please update your account to sevdesk-Update 2.0.");
|
|
995
|
+
process.exit(1);
|
|
996
|
+
}
|
|
997
|
+
await writeConfig({
|
|
998
|
+
...await readConfig(),
|
|
999
|
+
token
|
|
1000
|
+
});
|
|
1001
|
+
consola.success(`Logged in. Token stored in ${configPath()}.`);
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
//#endregion
|
|
1005
|
+
//#region src/commands/logout.ts
|
|
1006
|
+
var logout_default = defineCommand({
|
|
1007
|
+
description: "Log out by removing the stored API token.",
|
|
1008
|
+
action: async () => {
|
|
1009
|
+
const { token, ...config } = await readConfig();
|
|
1010
|
+
if (!token) consola.info("You are not logged in.");
|
|
1011
|
+
else {
|
|
1012
|
+
await writeConfig(config);
|
|
1013
|
+
consola.success("Logged out.");
|
|
1014
|
+
}
|
|
1015
|
+
if (process.env["SEVDESK_TOKEN"]) consola.warn("The SEVDESK_TOKEN environment variable is still set and will be used for authentication.");
|
|
1016
|
+
}
|
|
1017
|
+
});
|
|
1018
|
+
//#endregion
|
|
1019
|
+
//#region src/commands/orders/get.ts
|
|
1020
|
+
var get_default$3 = defineCommand({
|
|
1021
|
+
description: "Show a single order.",
|
|
1022
|
+
options: defineOptions(z.object({
|
|
1023
|
+
id: z.coerce.number().optional().describe("ID of the order. If omitted, you will be prompted."),
|
|
1024
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1025
|
+
})),
|
|
1026
|
+
action: async (options) => {
|
|
1027
|
+
const client = await requireClient();
|
|
1028
|
+
const orderId = await requireNumberOption({
|
|
1029
|
+
value: options.id,
|
|
1030
|
+
requirement: "an order ID via --id",
|
|
1031
|
+
question: "Enter the order ID:"
|
|
1032
|
+
});
|
|
1033
|
+
const order = await client.orders.get({ orderId });
|
|
1034
|
+
if (options.json) {
|
|
1035
|
+
printJson(order);
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
consola.info(`ID: ${order.id}`);
|
|
1039
|
+
consola.info(`Number: ${order.orderNumber}`);
|
|
1040
|
+
consola.info(`Date: ${order.orderDate}`);
|
|
1041
|
+
consola.info(`Status: ${order.status}`);
|
|
1042
|
+
consola.info(`Contact: ${order.contact?.id ?? "-"}`);
|
|
1043
|
+
consola.info(`Net: ${order.sumNet}`);
|
|
1044
|
+
consola.info(`Tax: ${order.sumTax}`);
|
|
1045
|
+
consola.info(`Gross: ${order.sumGross}`);
|
|
1046
|
+
consola.info(`Currency: ${order.currency}`);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
//#endregion
|
|
1050
|
+
//#region src/commands/orders/list.ts
|
|
1051
|
+
var list_default$4 = defineCommand({
|
|
1052
|
+
description: "List orders.",
|
|
1053
|
+
options: defineOptions(z.object({
|
|
1054
|
+
status: z.coerce.number().optional().describe("Filter by status: 100 draft, 200 delivered, 300 rejected, 500 accepted, 750 partially calculated, 1000 calculated."),
|
|
1055
|
+
orderNumber: z.string().optional().describe("Filter by order number."),
|
|
1056
|
+
startDate: z.string().optional().describe("Only orders on or after this date as dd.mm.yyyy or Unix timestamp."),
|
|
1057
|
+
endDate: z.string().optional().describe("Only orders on or before this date as dd.mm.yyyy or Unix timestamp."),
|
|
1058
|
+
contact: z.coerce.number().optional().describe("ID of the contact whose orders to list."),
|
|
1059
|
+
limit: z.coerce.number().optional().describe("Maximum number of orders to return."),
|
|
1060
|
+
offset: z.coerce.number().optional().describe("Number of orders to skip."),
|
|
1061
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1062
|
+
})),
|
|
1063
|
+
action: async (options) => {
|
|
1064
|
+
const orders = await (await requireClient()).orders.list({
|
|
1065
|
+
status: options.status,
|
|
1066
|
+
orderNumber: options.orderNumber,
|
|
1067
|
+
startDate: options.startDate,
|
|
1068
|
+
endDate: options.endDate,
|
|
1069
|
+
contactId: options.contact,
|
|
1070
|
+
limit: options.limit,
|
|
1071
|
+
offset: options.offset
|
|
1072
|
+
});
|
|
1073
|
+
if (options.json) {
|
|
1074
|
+
printJson(orders);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
if (orders.length === 0) {
|
|
1078
|
+
consola.info("No orders found.");
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
printTable(orders.map((order) => ({
|
|
1082
|
+
id: order.id,
|
|
1083
|
+
number: order.orderNumber,
|
|
1084
|
+
date: order.orderDate,
|
|
1085
|
+
contact: order.contact?.id ?? "",
|
|
1086
|
+
gross: order.sumGross,
|
|
1087
|
+
status: order.status
|
|
1088
|
+
})));
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
//#endregion
|
|
1092
|
+
//#region src/commands/orders/pdf.ts
|
|
1093
|
+
var pdf_default = defineCommand({
|
|
1094
|
+
description: "Download the PDF of an order.",
|
|
1095
|
+
options: defineOptions(z.object({
|
|
1096
|
+
id: z.coerce.number().optional().describe("ID of the order. If omitted, you will be prompted."),
|
|
1097
|
+
output: z.string().optional().describe("Path to write the PDF to. Defaults to the file name reported by the API."),
|
|
1098
|
+
preventSendBy: z.boolean().default(false).describe("Do not mark the order as sent by download."),
|
|
1099
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1100
|
+
})),
|
|
1101
|
+
action: async (options) => {
|
|
1102
|
+
const client = await requireClient();
|
|
1103
|
+
const orderId = await requireNumberOption({
|
|
1104
|
+
value: options.id,
|
|
1105
|
+
requirement: "an order ID via --id",
|
|
1106
|
+
question: "Enter the order ID:"
|
|
1107
|
+
});
|
|
1108
|
+
const pdf = await client.orders.getPdf({
|
|
1109
|
+
orderId,
|
|
1110
|
+
preventSendBy: options.preventSendBy
|
|
1111
|
+
});
|
|
1112
|
+
const path = resolve(options.output ?? pdf.filename);
|
|
1113
|
+
await writeFile(path, Buffer.from(pdf.content, "base64"));
|
|
1114
|
+
if (options.json) {
|
|
1115
|
+
printJson({
|
|
1116
|
+
filename: pdf.filename,
|
|
1117
|
+
path
|
|
1118
|
+
});
|
|
1119
|
+
return;
|
|
1120
|
+
}
|
|
1121
|
+
consola.success(`Saved ${path}.`);
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
//#endregion
|
|
1125
|
+
//#region src/commands/parts/get.ts
|
|
1126
|
+
var get_default$2 = defineCommand({
|
|
1127
|
+
description: "Show a single part.",
|
|
1128
|
+
options: defineOptions(z.object({
|
|
1129
|
+
id: z.coerce.number().optional().describe("The part ID. If omitted, you will be prompted."),
|
|
1130
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1131
|
+
})),
|
|
1132
|
+
action: async (options) => {
|
|
1133
|
+
const client = await requireClient();
|
|
1134
|
+
const partId = await requireNumberOption({
|
|
1135
|
+
value: options.id,
|
|
1136
|
+
requirement: "a part ID via --id",
|
|
1137
|
+
question: "Enter the part ID:"
|
|
1138
|
+
});
|
|
1139
|
+
const part = await client.parts.get({ partId });
|
|
1140
|
+
if (options.json) {
|
|
1141
|
+
printJson(part);
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
consola.info(`ID: ${part.id}`);
|
|
1145
|
+
consola.info(`Part number: ${part.partNumber}`);
|
|
1146
|
+
consola.info(`Name: ${part.name}`);
|
|
1147
|
+
consola.info(`Text: ${part.text ?? "-"}`);
|
|
1148
|
+
consola.info(`Price: ${part.price ?? "-"}`);
|
|
1149
|
+
consola.info(`Price net: ${part.priceNet ?? "-"}`);
|
|
1150
|
+
consola.info(`Price gross: ${part.priceGross ?? "-"}`);
|
|
1151
|
+
consola.info(`Tax rate: ${part.taxRate}`);
|
|
1152
|
+
consola.info(`Stock: ${part.stock}`);
|
|
1153
|
+
consola.info(`Status: ${part.status ?? "-"}`);
|
|
1154
|
+
}
|
|
1155
|
+
});
|
|
1156
|
+
//#endregion
|
|
1157
|
+
//#region src/commands/parts/list.ts
|
|
1158
|
+
var list_default$3 = defineCommand({
|
|
1159
|
+
description: "List parts.",
|
|
1160
|
+
options: defineOptions(z.object({
|
|
1161
|
+
name: z.string().optional().describe("Filter by part name."),
|
|
1162
|
+
partNumber: z.string().optional().describe("Filter by part number."),
|
|
1163
|
+
limit: z.coerce.number().optional().describe("Maximum number of parts to return."),
|
|
1164
|
+
offset: z.coerce.number().optional().describe("Number of parts to skip."),
|
|
1165
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1166
|
+
})),
|
|
1167
|
+
action: async (options) => {
|
|
1168
|
+
const parts = await (await requireClient()).parts.list({
|
|
1169
|
+
name: options.name,
|
|
1170
|
+
partNumber: options.partNumber,
|
|
1171
|
+
limit: options.limit,
|
|
1172
|
+
offset: options.offset
|
|
1173
|
+
});
|
|
1174
|
+
if (options.json) {
|
|
1175
|
+
printJson(parts);
|
|
1176
|
+
return;
|
|
1177
|
+
}
|
|
1178
|
+
if (parts.length === 0) {
|
|
1179
|
+
consola.info("No parts found.");
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
printTable(parts.map((part) => ({
|
|
1183
|
+
id: part.id,
|
|
1184
|
+
partNumber: part.partNumber,
|
|
1185
|
+
name: part.name,
|
|
1186
|
+
price: part.price ?? "",
|
|
1187
|
+
taxRate: part.taxRate,
|
|
1188
|
+
status: part.status ?? "",
|
|
1189
|
+
stock: part.stock
|
|
1190
|
+
})));
|
|
1191
|
+
}
|
|
1192
|
+
});
|
|
1193
|
+
//#endregion
|
|
1194
|
+
//#region src/commands/tags/create.ts
|
|
1195
|
+
const OBJECT_TYPES = [
|
|
1196
|
+
"Invoice",
|
|
1197
|
+
"Voucher",
|
|
1198
|
+
"Order",
|
|
1199
|
+
"CreditNote"
|
|
1200
|
+
];
|
|
1201
|
+
var create_default$2 = defineCommand({
|
|
1202
|
+
description: "Create a tag and attach it to a document.",
|
|
1203
|
+
options: defineOptions(z.object({
|
|
1204
|
+
name: z.string().optional().describe("Name of the tag. If omitted, you will be prompted."),
|
|
1205
|
+
objectType: z.string().optional().describe("Type of the document to tag: Invoice, Voucher, Order or CreditNote. If omitted, you will be prompted."),
|
|
1206
|
+
objectId: z.coerce.number().optional().describe("ID of the document to tag. If omitted, you will be prompted."),
|
|
1207
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1208
|
+
})),
|
|
1209
|
+
action: async (options) => {
|
|
1210
|
+
const client = await requireClient();
|
|
1211
|
+
const name = await requireStringOption({
|
|
1212
|
+
value: options.name,
|
|
1213
|
+
requirement: "a tag name via --name",
|
|
1214
|
+
question: "Enter the tag name:"
|
|
1215
|
+
});
|
|
1216
|
+
const objectType = await requireStringOption({
|
|
1217
|
+
value: options.objectType,
|
|
1218
|
+
requirement: "a document type via --object-type",
|
|
1219
|
+
question: `Enter the document type (${OBJECT_TYPES.join(", ")}):`
|
|
1220
|
+
});
|
|
1221
|
+
if (!isTagObjectType(objectType)) {
|
|
1222
|
+
consola.error(`The document type must be one of: ${OBJECT_TYPES.join(", ")}.`);
|
|
1223
|
+
process.exit(1);
|
|
1224
|
+
}
|
|
1225
|
+
const objectId = await requireNumberOption({
|
|
1226
|
+
value: options.objectId,
|
|
1227
|
+
requirement: "a document ID via --object-id",
|
|
1228
|
+
question: "Enter the document ID:"
|
|
1229
|
+
});
|
|
1230
|
+
const relation = await client.tags.create({
|
|
1231
|
+
name,
|
|
1232
|
+
objectId,
|
|
1233
|
+
objectName: objectType
|
|
1234
|
+
});
|
|
1235
|
+
if (options.json) {
|
|
1236
|
+
printJson(relation);
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
consola.success(`Created tag ${relation.tag.id}.`);
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
function isTagObjectType(value) {
|
|
1243
|
+
return OBJECT_TYPES.some((objectType) => objectType === value);
|
|
1244
|
+
}
|
|
1245
|
+
//#endregion
|
|
1246
|
+
//#region src/commands/tags/delete.ts
|
|
1247
|
+
var delete_default = defineCommand({
|
|
1248
|
+
description: "Delete a tag.",
|
|
1249
|
+
options: defineOptions(z.object({
|
|
1250
|
+
id: z.coerce.number().optional().describe("The tag ID. If omitted, you will be prompted."),
|
|
1251
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
1252
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1253
|
+
})),
|
|
1254
|
+
action: async (options) => {
|
|
1255
|
+
const client = await requireClient();
|
|
1256
|
+
const tagId = await requireNumberOption({
|
|
1257
|
+
value: options.id,
|
|
1258
|
+
requirement: "a tag ID via --id",
|
|
1259
|
+
question: "Enter the tag ID:"
|
|
1260
|
+
});
|
|
1261
|
+
if (!await confirmOrAbort({
|
|
1262
|
+
message: `Delete tag ${tagId}?`,
|
|
1263
|
+
yes: options.yes,
|
|
1264
|
+
initial: false
|
|
1265
|
+
})) return;
|
|
1266
|
+
await client.tags.delete({ tagId });
|
|
1267
|
+
if (options.json) {
|
|
1268
|
+
printJson({ deleted: true });
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
consola.success("Tag deleted.");
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
//#endregion
|
|
1275
|
+
//#region src/commands/tags/list.ts
|
|
1276
|
+
var list_default$2 = defineCommand({
|
|
1277
|
+
description: "List tags.",
|
|
1278
|
+
options: defineOptions(z.object({
|
|
1279
|
+
limit: z.coerce.number().optional().describe("Maximum number of tags to return."),
|
|
1280
|
+
offset: z.coerce.number().optional().describe("Number of tags to skip."),
|
|
1281
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1282
|
+
})),
|
|
1283
|
+
action: async (options) => {
|
|
1284
|
+
const tags = await (await requireClient()).tags.list({
|
|
1285
|
+
limit: options.limit,
|
|
1286
|
+
offset: options.offset
|
|
1287
|
+
});
|
|
1288
|
+
if (options.json) {
|
|
1289
|
+
printJson(tags);
|
|
1290
|
+
return;
|
|
1291
|
+
}
|
|
1292
|
+
if (tags.length === 0) {
|
|
1293
|
+
consola.info("No tags found.");
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
printTable(tags.map((tag) => ({
|
|
1297
|
+
id: tag.id,
|
|
1298
|
+
name: tag.name
|
|
1299
|
+
})));
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
//#endregion
|
|
1303
|
+
//#region src/commands/transactions/create.ts
|
|
1304
|
+
/**
|
|
1305
|
+
* Creates a transaction manually. This should only be used on check accounts
|
|
1306
|
+
* of type online, preferably on accounts that import their transactions from
|
|
1307
|
+
* files, because transactions of connected bank accounts are synchronized by
|
|
1308
|
+
* sevdesk itself.
|
|
1309
|
+
*/
|
|
1310
|
+
var create_default$1 = defineCommand({
|
|
1311
|
+
description: "Create a transaction. Only use this on file import (online) check accounts.",
|
|
1312
|
+
options: defineOptions(z.object({
|
|
1313
|
+
checkAccount: z.coerce.number().optional().describe("The ID of the check account the transaction belongs to. If omitted, you will be prompted."),
|
|
1314
|
+
amount: z.coerce.number().optional().describe("The amount of the transaction. Negative for expenses. If omitted, you will be prompted."),
|
|
1315
|
+
payee: z.string().optional().describe("The name of the payee or payer. If omitted, you will be prompted."),
|
|
1316
|
+
valueDate: z.string().default(() => (/* @__PURE__ */ new Date()).toISOString()).describe("The date the transaction was booked (ISO 8601)."),
|
|
1317
|
+
entryDate: z.string().optional().describe("The date the transaction was imported (ISO 8601)."),
|
|
1318
|
+
purpose: z.string().optional().describe("The payment purpose of the transaction."),
|
|
1319
|
+
status: z.coerce.number().default(100).describe("100 created, 200 linked, 300 private, 400 booked."),
|
|
1320
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1321
|
+
})),
|
|
1322
|
+
action: async (options) => {
|
|
1323
|
+
const client = await requireClient();
|
|
1324
|
+
const checkAccountId = await requireNumberOption({
|
|
1325
|
+
value: options.checkAccount,
|
|
1326
|
+
requirement: "a check account ID via --check-account",
|
|
1327
|
+
question: "Enter the check account ID:"
|
|
1328
|
+
});
|
|
1329
|
+
const amount = await requireNumberOption({
|
|
1330
|
+
value: options.amount,
|
|
1331
|
+
requirement: "an amount via --amount",
|
|
1332
|
+
question: "Enter the amount:"
|
|
1333
|
+
});
|
|
1334
|
+
const payee = await requireStringOption({
|
|
1335
|
+
value: options.payee,
|
|
1336
|
+
requirement: "a payee via --payee",
|
|
1337
|
+
question: "Enter the payee or payer name:"
|
|
1338
|
+
});
|
|
1339
|
+
const transaction = await client.transactions.create({
|
|
1340
|
+
checkAccountId,
|
|
1341
|
+
amount,
|
|
1342
|
+
payeePayerName: payee,
|
|
1343
|
+
valueDate: options.valueDate,
|
|
1344
|
+
entryDate: options.entryDate,
|
|
1345
|
+
paymtPurpose: options.purpose,
|
|
1346
|
+
status: options.status
|
|
1347
|
+
});
|
|
1348
|
+
if (options.json) {
|
|
1349
|
+
printJson(transaction);
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
consola.success(`Created transaction ${transaction.id}.`);
|
|
1353
|
+
}
|
|
1354
|
+
});
|
|
1355
|
+
//#endregion
|
|
1356
|
+
//#region src/commands/transactions/get.ts
|
|
1357
|
+
var get_default$1 = defineCommand({
|
|
1358
|
+
description: "Show a single transaction.",
|
|
1359
|
+
options: defineOptions(z.object({
|
|
1360
|
+
id: z.coerce.number().optional().describe("The transaction ID. If omitted, you will be prompted."),
|
|
1361
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1362
|
+
})),
|
|
1363
|
+
action: async (options) => {
|
|
1364
|
+
const client = await requireClient();
|
|
1365
|
+
const transactionId = await requireNumberOption({
|
|
1366
|
+
value: options.id,
|
|
1367
|
+
requirement: "a transaction ID via --id",
|
|
1368
|
+
question: "Enter the transaction ID:"
|
|
1369
|
+
});
|
|
1370
|
+
const transaction = await client.transactions.get({ transactionId });
|
|
1371
|
+
if (options.json) {
|
|
1372
|
+
printJson(transaction);
|
|
1373
|
+
return;
|
|
1374
|
+
}
|
|
1375
|
+
consola.info(`ID: ${transaction.id}`);
|
|
1376
|
+
consola.info(`Value date: ${transaction.valueDate}`);
|
|
1377
|
+
consola.info(`Amount: ${transaction.amount}`);
|
|
1378
|
+
consola.info(`Payee: ${transaction.payeePayerName ?? "-"}`);
|
|
1379
|
+
consola.info(`Purpose: ${transaction.paymtPurpose ?? "-"}`);
|
|
1380
|
+
consola.info(`Status: ${transaction.status}`);
|
|
1381
|
+
consola.info(`Check account: ${transaction.checkAccount.id}`);
|
|
1382
|
+
}
|
|
1383
|
+
});
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/commands/transactions/list.ts
|
|
1386
|
+
var list_default$1 = defineCommand({
|
|
1387
|
+
description: "List transactions of your check accounts.",
|
|
1388
|
+
options: defineOptions(z.object({
|
|
1389
|
+
checkAccount: z.coerce.number().optional().describe("Only show transactions of this check account ID."),
|
|
1390
|
+
unbooked: z.boolean().default(false).describe("Only show transactions that are not yet booked."),
|
|
1391
|
+
startDate: z.string().optional().describe("Only show transactions on or after this date (ISO 8601)."),
|
|
1392
|
+
endDate: z.string().optional().describe("Only show transactions on or before this date (ISO 8601)."),
|
|
1393
|
+
payee: z.string().optional().describe("Only show transactions with this payee or payer name."),
|
|
1394
|
+
purpose: z.string().optional().describe("Only show transactions with this payment purpose."),
|
|
1395
|
+
limit: z.coerce.number().optional().describe("The maximum number of transactions to return."),
|
|
1396
|
+
offset: z.coerce.number().optional().describe("The number of transactions to skip."),
|
|
1397
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1398
|
+
})),
|
|
1399
|
+
action: async (options) => {
|
|
1400
|
+
const transactions = await (await requireClient()).transactions.list({
|
|
1401
|
+
checkAccountId: options.checkAccount,
|
|
1402
|
+
isBooked: options.unbooked ? false : void 0,
|
|
1403
|
+
startDate: options.startDate,
|
|
1404
|
+
endDate: options.endDate,
|
|
1405
|
+
payeePayerName: options.payee,
|
|
1406
|
+
paymtPurpose: options.purpose,
|
|
1407
|
+
limit: options.limit,
|
|
1408
|
+
offset: options.offset
|
|
1409
|
+
});
|
|
1410
|
+
if (options.json) {
|
|
1411
|
+
printJson(transactions);
|
|
1412
|
+
return;
|
|
1413
|
+
}
|
|
1414
|
+
if (transactions.length === 0) {
|
|
1415
|
+
consola.info("No transactions found.");
|
|
1416
|
+
return;
|
|
1417
|
+
}
|
|
1418
|
+
printTable(transactions.map((transaction) => ({
|
|
1419
|
+
id: transaction.id,
|
|
1420
|
+
valueDate: transaction.valueDate,
|
|
1421
|
+
payee: transaction.payeePayerName ?? "",
|
|
1422
|
+
purpose: transaction.paymtPurpose ?? "",
|
|
1423
|
+
amount: transaction.amount,
|
|
1424
|
+
status: transaction.status
|
|
1425
|
+
})));
|
|
1426
|
+
}
|
|
1427
|
+
});
|
|
1428
|
+
//#endregion
|
|
1429
|
+
//#region src/commands/vouchers/book.ts
|
|
1430
|
+
var book_default = defineCommand({
|
|
1431
|
+
description: "Book a payment on a voucher.",
|
|
1432
|
+
options: defineOptions(z.object({
|
|
1433
|
+
id: z.coerce.number().optional().describe("ID of the voucher."),
|
|
1434
|
+
amount: z.coerce.number().optional().describe("Amount to book. Can also be a partial amount."),
|
|
1435
|
+
date: z.string().default(() => (/* @__PURE__ */ new Date()).toISOString()).describe("Booking date as ISO 8601. Defaults to now."),
|
|
1436
|
+
type: z.enum([
|
|
1437
|
+
"FULL_PAYMENT",
|
|
1438
|
+
"N",
|
|
1439
|
+
"CB",
|
|
1440
|
+
"O",
|
|
1441
|
+
"OF",
|
|
1442
|
+
"MTC"
|
|
1443
|
+
]).default("FULL_PAYMENT").describe("Type of the booking. Supported values are FULL_PAYMENT, N (partial), CB (cash discount), O (other), OF (reminder charges) and MTC (monetary traffic costs)."),
|
|
1444
|
+
checkAccount: z.coerce.number().optional().describe("ID of the check account."),
|
|
1445
|
+
transaction: z.coerce.number().optional().describe("ID of the bank transaction to link. Required for online check accounts, omit for clearing accounts."),
|
|
1446
|
+
createFeed: z.boolean().optional().describe("Create a feed entry for the booking."),
|
|
1447
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
1448
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1449
|
+
})),
|
|
1450
|
+
action: async (options) => {
|
|
1451
|
+
const client = await requireClient();
|
|
1452
|
+
const voucherId = await requireNumberOption({
|
|
1453
|
+
value: options.id,
|
|
1454
|
+
requirement: "a voucher via --id",
|
|
1455
|
+
question: "Enter the voucher ID:"
|
|
1456
|
+
});
|
|
1457
|
+
const amount = await requireNumberOption({
|
|
1458
|
+
value: options.amount,
|
|
1459
|
+
requirement: "an amount via --amount",
|
|
1460
|
+
question: "Enter the amount to book:"
|
|
1461
|
+
});
|
|
1462
|
+
const checkAccountId = await requireNumberOption({
|
|
1463
|
+
value: options.checkAccount,
|
|
1464
|
+
requirement: "a check account via --check-account",
|
|
1465
|
+
question: "Enter the check account ID:"
|
|
1466
|
+
});
|
|
1467
|
+
if (!await confirmOrAbort({
|
|
1468
|
+
message: `Book ${amount} on voucher ${voucherId}?`,
|
|
1469
|
+
yes: options.yes
|
|
1470
|
+
})) return;
|
|
1471
|
+
const result = await client.vouchers.book({
|
|
1472
|
+
voucherId,
|
|
1473
|
+
amount,
|
|
1474
|
+
date: options.date,
|
|
1475
|
+
type: options.type,
|
|
1476
|
+
checkAccountId,
|
|
1477
|
+
checkAccountTransactionId: options.transaction,
|
|
1478
|
+
createFeed: options.createFeed
|
|
1479
|
+
});
|
|
1480
|
+
if (options.json) {
|
|
1481
|
+
printJson(result);
|
|
1482
|
+
return;
|
|
1483
|
+
}
|
|
1484
|
+
consola.success(`Booked ${amount} on voucher ${voucherId}. Status changed from ${result.fromStatus} to ${result.toStatus}.`);
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
//#endregion
|
|
1488
|
+
//#region src/commands/vouchers/create.ts
|
|
1489
|
+
var create_default = defineCommand({
|
|
1490
|
+
description: "Create a voucher from a receipt file.",
|
|
1491
|
+
options: defineOptions(z.object({
|
|
1492
|
+
file: z.string().optional().describe("Path to the receipt file to attach, for example a PDF, an image or an XML invoice."),
|
|
1493
|
+
status: z.enum(["draft", "open"]).default("open").describe("Status of the created voucher. Supported values are draft and open."),
|
|
1494
|
+
creditDebit: z.enum(["C", "D"]).default("C").describe("C for expense (credit), D for income (debit) vouchers."),
|
|
1495
|
+
taxRule: z.coerce.number().optional().describe("ID of the tax rule, for example 9 for deductible input tax expenses."),
|
|
1496
|
+
accountDatev: z.coerce.number().optional().describe("ID of the booking account (AccountDatev). Find it with guidance:accounts."),
|
|
1497
|
+
amount: z.coerce.number().optional().describe("Amount of the voucher position."),
|
|
1498
|
+
net: z.boolean().default(false).describe("Treat the amount as net instead of gross."),
|
|
1499
|
+
taxRate: z.coerce.number().default(19).describe("Tax rate of the voucher position in percent."),
|
|
1500
|
+
voucherDate: z.string().optional().describe("Date as dd.mm.yyyy or Unix timestamp."),
|
|
1501
|
+
payDate: z.string().optional().describe("Date as dd.mm.yyyy or Unix timestamp."),
|
|
1502
|
+
supplierId: z.coerce.number().optional().describe("ID of the supplier contact."),
|
|
1503
|
+
supplierName: z.string().optional().describe("Name of the supplier, used when no supplier ID is given."),
|
|
1504
|
+
description: z.string().optional().describe("The voucher number."),
|
|
1505
|
+
comment: z.string().optional().describe("Comment for the voucher position."),
|
|
1506
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1507
|
+
})),
|
|
1508
|
+
action: async (options) => {
|
|
1509
|
+
const client = await requireClient();
|
|
1510
|
+
const path = await requireStringOption({
|
|
1511
|
+
value: options.file,
|
|
1512
|
+
requirement: "a file via --file",
|
|
1513
|
+
question: "Enter the path to the receipt file:"
|
|
1514
|
+
});
|
|
1515
|
+
const taxRuleId = await requireNumberOption({
|
|
1516
|
+
value: options.taxRule,
|
|
1517
|
+
requirement: "a tax rule via --tax-rule",
|
|
1518
|
+
question: "Enter the tax rule ID:"
|
|
1519
|
+
});
|
|
1520
|
+
const accountDatevId = await requireNumberOption({
|
|
1521
|
+
value: options.accountDatev,
|
|
1522
|
+
requirement: "a booking account via --account-datev",
|
|
1523
|
+
question: "Enter the booking account ID:"
|
|
1524
|
+
});
|
|
1525
|
+
const amount = await requireNumberOption({
|
|
1526
|
+
value: options.amount,
|
|
1527
|
+
requirement: "an amount via --amount",
|
|
1528
|
+
question: "Enter the amount:"
|
|
1529
|
+
});
|
|
1530
|
+
const supplierName = options.supplierId === void 0 && !options.supplierName ? await requireStringOption({
|
|
1531
|
+
value: void 0,
|
|
1532
|
+
requirement: "a supplier via --supplier-id or --supplier-name",
|
|
1533
|
+
question: "Enter the supplier name:"
|
|
1534
|
+
}) : options.supplierName;
|
|
1535
|
+
const file = await readFile(path).catch(() => {
|
|
1536
|
+
consola.error(`The file "${path}" could not be read. Check that the path is correct.`);
|
|
1537
|
+
process.exit(1);
|
|
1538
|
+
});
|
|
1539
|
+
const result = await client.vouchers.createFromFile({
|
|
1540
|
+
file,
|
|
1541
|
+
filename: basename(path),
|
|
1542
|
+
voucher: {
|
|
1543
|
+
status: options.status === "draft" ? 50 : 100,
|
|
1544
|
+
creditDebit: options.creditDebit,
|
|
1545
|
+
taxRuleId,
|
|
1546
|
+
voucherDate: options.voucherDate,
|
|
1547
|
+
payDate: options.payDate,
|
|
1548
|
+
supplierId: options.supplierId,
|
|
1549
|
+
supplierName,
|
|
1550
|
+
description: options.description
|
|
1551
|
+
},
|
|
1552
|
+
positions: [{
|
|
1553
|
+
accountDatevId,
|
|
1554
|
+
taxRate: options.taxRate,
|
|
1555
|
+
net: options.net,
|
|
1556
|
+
...options.net ? { sumNet: amount } : { sumGross: amount },
|
|
1557
|
+
comment: options.comment
|
|
1558
|
+
}]
|
|
1559
|
+
});
|
|
1560
|
+
if (options.json) {
|
|
1561
|
+
printJson(result);
|
|
1562
|
+
return;
|
|
1563
|
+
}
|
|
1564
|
+
consola.success(`Created voucher ${result.voucher.id} with status ${result.voucher.status}.`);
|
|
1565
|
+
}
|
|
1566
|
+
});
|
|
1567
|
+
//#endregion
|
|
1568
|
+
//#region src/commands/vouchers/enshrine.ts
|
|
1569
|
+
var enshrine_default = defineCommand({
|
|
1570
|
+
description: "Enshrine a voucher so that it can no longer be changed.",
|
|
1571
|
+
options: defineOptions(z.object({
|
|
1572
|
+
id: z.coerce.number().optional().describe("ID of the voucher."),
|
|
1573
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
1574
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1575
|
+
})),
|
|
1576
|
+
action: async (options) => {
|
|
1577
|
+
const client = await requireClient();
|
|
1578
|
+
const voucherId = await requireNumberOption({
|
|
1579
|
+
value: options.id,
|
|
1580
|
+
requirement: "a voucher ID via --id",
|
|
1581
|
+
question: "Enter the voucher ID:"
|
|
1582
|
+
});
|
|
1583
|
+
if (!await confirmOrAbort({
|
|
1584
|
+
message: `Enshrine voucher ${voucherId}? This cannot be undone.`,
|
|
1585
|
+
yes: options.yes,
|
|
1586
|
+
initial: false
|
|
1587
|
+
})) return;
|
|
1588
|
+
await client.vouchers.enshrine({ voucherId });
|
|
1589
|
+
if (options.json) {
|
|
1590
|
+
printJson({ enshrined: true });
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
consola.success(`Enshrined voucher ${voucherId}.`);
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
//#endregion
|
|
1597
|
+
//#region src/commands/vouchers/get.ts
|
|
1598
|
+
var get_default = defineCommand({
|
|
1599
|
+
description: "Show a single voucher.",
|
|
1600
|
+
options: defineOptions(z.object({
|
|
1601
|
+
id: z.coerce.number().optional().describe("ID of the voucher."),
|
|
1602
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1603
|
+
})),
|
|
1604
|
+
action: async (options) => {
|
|
1605
|
+
const client = await requireClient();
|
|
1606
|
+
const voucherId = await requireNumberOption({
|
|
1607
|
+
value: options.id,
|
|
1608
|
+
requirement: "a voucher ID via --id",
|
|
1609
|
+
question: "Enter the voucher ID:"
|
|
1610
|
+
});
|
|
1611
|
+
const voucher = await client.vouchers.get({ voucherId });
|
|
1612
|
+
if (options.json) {
|
|
1613
|
+
printJson(voucher);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
consola.info(`ID: ${voucher.id}`);
|
|
1617
|
+
consola.info(`Status: ${voucher.status ?? "-"}`);
|
|
1618
|
+
consola.info(`Date: ${voucher.voucherDate ?? "-"}`);
|
|
1619
|
+
consola.info(`Supplier: ${voucher.supplierName ?? voucher.supplier?.id ?? "-"}`);
|
|
1620
|
+
consola.info(`Description: ${voucher.description ?? "-"}`);
|
|
1621
|
+
consola.info(`Net: ${voucher.sumNet}`);
|
|
1622
|
+
consola.info(`Tax: ${voucher.sumTax}`);
|
|
1623
|
+
consola.info(`Gross: ${voucher.sumGross}`);
|
|
1624
|
+
consola.info(`Currency: ${voucher.currency ?? "-"}`);
|
|
1625
|
+
consola.info(`Enshrined: ${voucher.enshrined ?? "no"}`);
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
//#endregion
|
|
1629
|
+
//#region src/commands/vouchers/list.ts
|
|
1630
|
+
var list_default = defineCommand({
|
|
1631
|
+
description: "List vouchers.",
|
|
1632
|
+
options: defineOptions(z.object({
|
|
1633
|
+
status: z.coerce.number().optional().describe("Filter by status: 50 draft, 100 open, 150 transferred, 750 partially paid, 1000 paid."),
|
|
1634
|
+
creditDebit: z.enum(["C", "D"]).optional().describe("Filter by C for expense or D for income vouchers."),
|
|
1635
|
+
descriptionLike: z.string().optional().describe("Filter by voucher number, matching partially."),
|
|
1636
|
+
startDate: z.string().optional().describe("Only vouchers on or after this date as dd.mm.yyyy or Unix timestamp."),
|
|
1637
|
+
endDate: z.string().optional().describe("Only vouchers on or before this date as dd.mm.yyyy or Unix timestamp."),
|
|
1638
|
+
contact: z.coerce.number().optional().describe("ID of the contact whose vouchers to list."),
|
|
1639
|
+
limit: z.coerce.number().optional().describe("Maximum number of vouchers to return."),
|
|
1640
|
+
offset: z.coerce.number().optional().describe("Number of vouchers to skip."),
|
|
1641
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1642
|
+
})),
|
|
1643
|
+
action: async (options) => {
|
|
1644
|
+
const vouchers = await (await requireClient()).vouchers.list({
|
|
1645
|
+
status: options.status,
|
|
1646
|
+
creditDebit: options.creditDebit,
|
|
1647
|
+
descriptionLike: options.descriptionLike,
|
|
1648
|
+
startDate: options.startDate,
|
|
1649
|
+
endDate: options.endDate,
|
|
1650
|
+
contactId: options.contact,
|
|
1651
|
+
limit: options.limit,
|
|
1652
|
+
offset: options.offset
|
|
1653
|
+
});
|
|
1654
|
+
if (options.json) {
|
|
1655
|
+
printJson(vouchers);
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
if (vouchers.length === 0) {
|
|
1659
|
+
consola.info("No vouchers found.");
|
|
1660
|
+
return;
|
|
1661
|
+
}
|
|
1662
|
+
printTable(vouchers.map((voucher) => ({
|
|
1663
|
+
id: voucher.id,
|
|
1664
|
+
date: voucher.voucherDate ?? "",
|
|
1665
|
+
supplier: voucher.supplierName ?? voucher.supplier?.id ?? "",
|
|
1666
|
+
description: voucher.description ?? "",
|
|
1667
|
+
gross: voucher.sumGross,
|
|
1668
|
+
status: voucher.status ?? ""
|
|
1669
|
+
})));
|
|
1670
|
+
}
|
|
1671
|
+
});
|
|
1672
|
+
//#endregion
|
|
1673
|
+
//#region src/commands/vouchers/reset-to-draft.ts
|
|
1674
|
+
var reset_to_draft_default = defineCommand({
|
|
1675
|
+
description: "Reset a voucher to the draft status.",
|
|
1676
|
+
options: defineOptions(z.object({
|
|
1677
|
+
id: z.coerce.number().optional().describe("ID of the voucher."),
|
|
1678
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
1679
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1680
|
+
})),
|
|
1681
|
+
action: async (options) => {
|
|
1682
|
+
const client = await requireClient();
|
|
1683
|
+
const voucherId = await requireNumberOption({
|
|
1684
|
+
value: options.id,
|
|
1685
|
+
requirement: "a voucher ID via --id",
|
|
1686
|
+
question: "Enter the voucher ID:"
|
|
1687
|
+
});
|
|
1688
|
+
if (!await confirmOrAbort({
|
|
1689
|
+
message: `Reset voucher ${voucherId} to draft? This unlinks existing payments.`,
|
|
1690
|
+
yes: options.yes,
|
|
1691
|
+
initial: false
|
|
1692
|
+
})) return;
|
|
1693
|
+
const voucher = await client.vouchers.resetToDraft({ voucherId });
|
|
1694
|
+
if (options.json) {
|
|
1695
|
+
printJson(voucher);
|
|
1696
|
+
return;
|
|
1697
|
+
}
|
|
1698
|
+
consola.success(`Reset voucher ${voucher.id}. New status: ${voucher.status ?? "-"}.`);
|
|
1699
|
+
}
|
|
1700
|
+
});
|
|
1701
|
+
//#endregion
|
|
1702
|
+
//#region src/commands/vouchers/reset-to-open.ts
|
|
1703
|
+
var reset_to_open_default = defineCommand({
|
|
1704
|
+
description: "Reset a voucher to the open status.",
|
|
1705
|
+
options: defineOptions(z.object({
|
|
1706
|
+
id: z.coerce.number().optional().describe("ID of the voucher."),
|
|
1707
|
+
yes: z.boolean().default(false).describe("Skip the confirmation prompt."),
|
|
1708
|
+
json: z.boolean().default(false).describe("Output in JSON format.")
|
|
1709
|
+
})),
|
|
1710
|
+
action: async (options) => {
|
|
1711
|
+
const client = await requireClient();
|
|
1712
|
+
const voucherId = await requireNumberOption({
|
|
1713
|
+
value: options.id,
|
|
1714
|
+
requirement: "a voucher ID via --id",
|
|
1715
|
+
question: "Enter the voucher ID:"
|
|
1716
|
+
});
|
|
1717
|
+
if (!await confirmOrAbort({
|
|
1718
|
+
message: `Reset voucher ${voucherId} to open? This unlinks existing payments.`,
|
|
1719
|
+
yes: options.yes,
|
|
1720
|
+
initial: false
|
|
1721
|
+
})) return;
|
|
1722
|
+
const voucher = await client.vouchers.resetToOpen({ voucherId });
|
|
1723
|
+
if (options.json) {
|
|
1724
|
+
printJson(voucher);
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
consola.success(`Reset voucher ${voucher.id}. New status: ${voucher.status ?? "-"}.`);
|
|
1728
|
+
}
|
|
1729
|
+
});
|
|
1730
|
+
//#endregion
|
|
1731
|
+
//#region src/index.ts
|
|
1732
|
+
const config = defineConfig({
|
|
1733
|
+
meta: {
|
|
1734
|
+
name: "sevdesk",
|
|
1735
|
+
version: pkg.version,
|
|
1736
|
+
description: pkg.description
|
|
1737
|
+
},
|
|
1738
|
+
commands: {
|
|
1739
|
+
login: login_default,
|
|
1740
|
+
logout: logout_default,
|
|
1741
|
+
doctor: doctor_default,
|
|
1742
|
+
"accounts:balance": balance_default,
|
|
1743
|
+
"accounts:create-clearing": create_clearing_default,
|
|
1744
|
+
"accounts:create-file-import": create_file_import_default,
|
|
1745
|
+
"accounts:get": get_default$7,
|
|
1746
|
+
"accounts:list": list_default$8,
|
|
1747
|
+
"contacts:create": create_default$3,
|
|
1748
|
+
"contacts:delete": delete_default$1,
|
|
1749
|
+
"contacts:get": get_default$6,
|
|
1750
|
+
"contacts:list": list_default$7,
|
|
1751
|
+
"contacts:update": update_default,
|
|
1752
|
+
"credit-notes:get": get_default$5,
|
|
1753
|
+
"credit-notes:list": list_default$6,
|
|
1754
|
+
"credit-notes:pdf": pdf_default$2,
|
|
1755
|
+
"export:contacts": contacts_default,
|
|
1756
|
+
"export:datev": datev_default,
|
|
1757
|
+
"export:invoices": invoices_default,
|
|
1758
|
+
"export:transactions": transactions_default,
|
|
1759
|
+
"export:vouchers": vouchers_default,
|
|
1760
|
+
"guidance:accounts": accounts_default,
|
|
1761
|
+
"invoices:get": get_default$4,
|
|
1762
|
+
"invoices:list": list_default$5,
|
|
1763
|
+
"invoices:pdf": pdf_default$1,
|
|
1764
|
+
"orders:get": get_default$3,
|
|
1765
|
+
"orders:list": list_default$4,
|
|
1766
|
+
"orders:pdf": pdf_default,
|
|
1767
|
+
"parts:get": get_default$2,
|
|
1768
|
+
"parts:list": list_default$3,
|
|
1769
|
+
"tags:create": create_default$2,
|
|
1770
|
+
"tags:delete": delete_default,
|
|
1771
|
+
"tags:list": list_default$2,
|
|
1772
|
+
"transactions:create": create_default$1,
|
|
1773
|
+
"transactions:get": get_default$1,
|
|
1774
|
+
"transactions:list": list_default$1,
|
|
1775
|
+
"vouchers:book": book_default,
|
|
1776
|
+
"vouchers:create": create_default,
|
|
1777
|
+
"vouchers:enshrine": enshrine_default,
|
|
1778
|
+
"vouchers:get": get_default,
|
|
1779
|
+
"vouchers:list": list_default,
|
|
1780
|
+
"vouchers:reset-to-draft": reset_to_draft_default,
|
|
1781
|
+
"vouchers:reset-to-open": reset_to_open_default
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
try {
|
|
1785
|
+
const result = processConfig(config, process.argv.slice(2));
|
|
1786
|
+
await result.command.action(result.options, result.args);
|
|
1787
|
+
} catch (error) {
|
|
1788
|
+
consola.error(formatError(error));
|
|
1789
|
+
process.exitCode = 1;
|
|
1790
|
+
}
|
|
1791
|
+
//#endregion
|
|
1792
|
+
export {};
|
|
1793
|
+
|
|
1794
|
+
//# sourceMappingURL=index.js.map
|