tiime-cli 1.1.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/LICENSE +21 -0
- package/README.md +427 -0
- package/dist/cli.js +2639 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.js +694 -0
- package/package.json +74 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,2639 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli/index.ts
|
|
4
|
+
import { defineCommand as defineCommand14, renderUsage, runMain } from "citty";
|
|
5
|
+
|
|
6
|
+
// src/cli/commands/auth.ts
|
|
7
|
+
import * as p from "@clack/prompts";
|
|
8
|
+
import { defineCommand } from "citty";
|
|
9
|
+
|
|
10
|
+
// src/sdk/auth.ts
|
|
11
|
+
import { execSync } from "child_process";
|
|
12
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
13
|
+
import { homedir } from "os";
|
|
14
|
+
import { join } from "path";
|
|
15
|
+
import { ofetch } from "ofetch";
|
|
16
|
+
var AUTH0_DOMAIN = "auth0.tiime.fr";
|
|
17
|
+
var AUTH0_CLIENT_ID = "iEbsbe3o66gcTBfGRa012kj1Rb6vjAND";
|
|
18
|
+
var AUTH0_AUDIENCE = "https://chronos/";
|
|
19
|
+
var CONFIG_DIR = join(homedir(), ".config", "tiime");
|
|
20
|
+
var AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
21
|
+
var KEYCHAIN_ACCOUNT = "tiime-cli";
|
|
22
|
+
var KEYCHAIN_SERVICE = "tiime-credentials";
|
|
23
|
+
var saveCredentialsToKeychain = (email, password2) => {
|
|
24
|
+
try {
|
|
25
|
+
const payload = JSON.stringify({ email, password: password2 });
|
|
26
|
+
execSync(
|
|
27
|
+
`security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w '${payload.replace(/'/g, "'\\''")}' -U`,
|
|
28
|
+
{ stdio: "ignore" }
|
|
29
|
+
);
|
|
30
|
+
return true;
|
|
31
|
+
} catch {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
var loadCredentialsFromKeychain = () => {
|
|
36
|
+
try {
|
|
37
|
+
const raw = execSync(
|
|
38
|
+
`security find-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w`,
|
|
39
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
40
|
+
).trim();
|
|
41
|
+
const data = JSON.parse(raw);
|
|
42
|
+
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
43
|
+
return data;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
} catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
var saveCredentialsToFile = (email, password2) => {
|
|
51
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
52
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
53
|
+
}
|
|
54
|
+
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
55
|
+
writeFileSync(filePath, JSON.stringify({ email, password: password2 }, null, 2), {
|
|
56
|
+
mode: 384
|
|
57
|
+
});
|
|
58
|
+
};
|
|
59
|
+
var loadCredentialsFromFile = () => {
|
|
60
|
+
try {
|
|
61
|
+
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
62
|
+
if (existsSync(filePath)) {
|
|
63
|
+
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
64
|
+
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
65
|
+
return data;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
};
|
|
72
|
+
var saveCredentials = (email, password2) => {
|
|
73
|
+
if (!saveCredentialsToKeychain(email, password2)) {
|
|
74
|
+
saveCredentialsToFile(email, password2);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var loadCredentials = () => {
|
|
78
|
+
return loadCredentialsFromKeychain() ?? loadCredentialsFromFile();
|
|
79
|
+
};
|
|
80
|
+
var TokenManager = class {
|
|
81
|
+
tokens = null;
|
|
82
|
+
constructor() {
|
|
83
|
+
this.loadFromDisk();
|
|
84
|
+
}
|
|
85
|
+
async login(email, password2) {
|
|
86
|
+
const response = await ofetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: {
|
|
89
|
+
grant_type: "password",
|
|
90
|
+
client_id: AUTH0_CLIENT_ID,
|
|
91
|
+
audience: AUTH0_AUDIENCE,
|
|
92
|
+
scope: "openid email",
|
|
93
|
+
username: email,
|
|
94
|
+
password: password2
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
this.tokens = {
|
|
98
|
+
access_token: response.access_token,
|
|
99
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
100
|
+
};
|
|
101
|
+
this.saveToDisk();
|
|
102
|
+
saveCredentials(email, password2);
|
|
103
|
+
return this.tokens;
|
|
104
|
+
}
|
|
105
|
+
async getValidToken() {
|
|
106
|
+
if (!this.tokens || this.isExpired()) {
|
|
107
|
+
const creds = loadCredentials();
|
|
108
|
+
if (creds) {
|
|
109
|
+
const tokens = await this.login(creds.email, creds.password);
|
|
110
|
+
return tokens.access_token;
|
|
111
|
+
}
|
|
112
|
+
throw new Error(
|
|
113
|
+
this.tokens ? "Token expired. Run `tiime auth login` to re-authenticate." : "Not authenticated. Run `tiime auth login` first."
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
return this.tokens.access_token;
|
|
117
|
+
}
|
|
118
|
+
isAuthenticated() {
|
|
119
|
+
return this.tokens !== null && !this.isExpired();
|
|
120
|
+
}
|
|
121
|
+
logout() {
|
|
122
|
+
this.tokens = null;
|
|
123
|
+
if (existsSync(AUTH_FILE)) {
|
|
124
|
+
writeFileSync(AUTH_FILE, "{}");
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
getTokenInfo() {
|
|
128
|
+
if (!this.tokens) {
|
|
129
|
+
return { email: null, expiresAt: null };
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const payload = JSON.parse(
|
|
133
|
+
Buffer.from(
|
|
134
|
+
this.tokens.access_token.split(".")[1],
|
|
135
|
+
"base64"
|
|
136
|
+
).toString()
|
|
137
|
+
);
|
|
138
|
+
return {
|
|
139
|
+
email: payload["tiime/userEmail"] || null,
|
|
140
|
+
expiresAt: new Date(this.tokens.expires_at)
|
|
141
|
+
};
|
|
142
|
+
} catch {
|
|
143
|
+
return { email: null, expiresAt: new Date(this.tokens.expires_at) };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
isExpired() {
|
|
147
|
+
if (!this.tokens) return true;
|
|
148
|
+
return Date.now() >= this.tokens.expires_at - 6e4;
|
|
149
|
+
}
|
|
150
|
+
loadFromDisk() {
|
|
151
|
+
try {
|
|
152
|
+
if (existsSync(AUTH_FILE)) {
|
|
153
|
+
const data = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
154
|
+
if (data.access_token && data.expires_at) {
|
|
155
|
+
this.tokens = data;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
} catch {
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
saveToDisk() {
|
|
162
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
163
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
164
|
+
}
|
|
165
|
+
writeFileSync(AUTH_FILE, JSON.stringify(this.tokens, null, 2));
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// src/cli/output.ts
|
|
170
|
+
import Table from "cli-table3";
|
|
171
|
+
import { consola } from "consola";
|
|
172
|
+
|
|
173
|
+
// src/sdk/errors.ts
|
|
174
|
+
var TiimeError = class extends Error {
|
|
175
|
+
constructor(message, status, endpoint, details) {
|
|
176
|
+
super(message);
|
|
177
|
+
this.status = status;
|
|
178
|
+
this.endpoint = endpoint;
|
|
179
|
+
this.details = details;
|
|
180
|
+
this.name = "TiimeError";
|
|
181
|
+
}
|
|
182
|
+
toJSON() {
|
|
183
|
+
return {
|
|
184
|
+
error: this.name,
|
|
185
|
+
message: this.message,
|
|
186
|
+
status: this.status,
|
|
187
|
+
endpoint: this.endpoint,
|
|
188
|
+
details: this.details
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
// src/cli/output.ts
|
|
194
|
+
var formatArg = {
|
|
195
|
+
format: {
|
|
196
|
+
type: "string",
|
|
197
|
+
description: "Format de sortie (json, table, csv)",
|
|
198
|
+
default: "json"
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
var stringifyValue = (value) => {
|
|
202
|
+
if (value === null || value === void 0) return "";
|
|
203
|
+
if (typeof value === "object") return JSON.stringify(value);
|
|
204
|
+
return String(value);
|
|
205
|
+
};
|
|
206
|
+
var outputJson = (data) => {
|
|
207
|
+
process.stdout.write(`${JSON.stringify(data, null, 2)}
|
|
208
|
+
`);
|
|
209
|
+
};
|
|
210
|
+
var outputTable = (data) => {
|
|
211
|
+
if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object" && data[0] !== null) {
|
|
212
|
+
const keys = Object.keys(data[0]);
|
|
213
|
+
const table = new Table({ head: keys });
|
|
214
|
+
for (const row of data) {
|
|
215
|
+
const record = row;
|
|
216
|
+
table.push(keys.map((key) => stringifyValue(record[key])));
|
|
217
|
+
}
|
|
218
|
+
process.stdout.write(`${table.toString()}
|
|
219
|
+
`);
|
|
220
|
+
} else if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
221
|
+
const table = new Table();
|
|
222
|
+
for (const [key, value] of Object.entries(
|
|
223
|
+
data
|
|
224
|
+
)) {
|
|
225
|
+
table.push({ [key]: stringifyValue(value) });
|
|
226
|
+
}
|
|
227
|
+
process.stdout.write(`${table.toString()}
|
|
228
|
+
`);
|
|
229
|
+
} else {
|
|
230
|
+
outputJson(data);
|
|
231
|
+
}
|
|
232
|
+
};
|
|
233
|
+
var escapeCsvField = (value) => {
|
|
234
|
+
if (value.includes(",") || value.includes('"') || value.includes("\n")) {
|
|
235
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
236
|
+
}
|
|
237
|
+
return value;
|
|
238
|
+
};
|
|
239
|
+
var outputCsv = (data) => {
|
|
240
|
+
if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object" && data[0] !== null) {
|
|
241
|
+
const keys = Object.keys(data[0]);
|
|
242
|
+
const header = keys.map(escapeCsvField).join(",");
|
|
243
|
+
const lines = data.map((row) => {
|
|
244
|
+
const record = row;
|
|
245
|
+
return keys.map((key) => escapeCsvField(stringifyValue(record[key]))).join(",");
|
|
246
|
+
});
|
|
247
|
+
process.stdout.write(`${header}
|
|
248
|
+
${lines.join("\n")}
|
|
249
|
+
`);
|
|
250
|
+
} else if (typeof data === "object" && data !== null && !Array.isArray(data)) {
|
|
251
|
+
const entries = Object.entries(data);
|
|
252
|
+
const header = "key,value";
|
|
253
|
+
const lines = entries.map(
|
|
254
|
+
([key, value]) => `${escapeCsvField(key)},${escapeCsvField(stringifyValue(value))}`
|
|
255
|
+
);
|
|
256
|
+
process.stdout.write(`${header}
|
|
257
|
+
${lines.join("\n")}
|
|
258
|
+
`);
|
|
259
|
+
} else {
|
|
260
|
+
outputJson(data);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
var output = (data, options) => {
|
|
264
|
+
const format = options?.format ?? "json";
|
|
265
|
+
if (!["json", "table", "csv"].includes(format)) {
|
|
266
|
+
process.stderr.write(
|
|
267
|
+
`${JSON.stringify({ error: `Format invalide : "${format}". Utilisez json, table ou csv.` })}
|
|
268
|
+
`
|
|
269
|
+
);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
switch (format) {
|
|
273
|
+
case "table":
|
|
274
|
+
outputTable(data);
|
|
275
|
+
break;
|
|
276
|
+
case "csv":
|
|
277
|
+
outputCsv(data);
|
|
278
|
+
break;
|
|
279
|
+
default:
|
|
280
|
+
outputJson(data);
|
|
281
|
+
break;
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
var outputColoredStatus = (data) => {
|
|
285
|
+
const {
|
|
286
|
+
company_id,
|
|
287
|
+
bank_accounts,
|
|
288
|
+
invoices,
|
|
289
|
+
pending_quotations,
|
|
290
|
+
total_clients,
|
|
291
|
+
unimputed_transactions
|
|
292
|
+
} = data;
|
|
293
|
+
console.error("");
|
|
294
|
+
console.error(` \u{1F4CA} R\xE9sum\xE9 \u2014 Entreprise #${company_id}`);
|
|
295
|
+
for (const a of bank_accounts) {
|
|
296
|
+
console.error(
|
|
297
|
+
` \u{1F4B0} Soldes : ${a.name} ${a.balance.toFixed(2)}${a.currency === "EUR" ? "\u20AC" : a.currency}`
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
console.error(
|
|
301
|
+
` \u{1F4C4} Factures : ${invoices.drafts} brouillon(s), ${invoices.unpaid} impay\xE9e(s)`
|
|
302
|
+
);
|
|
303
|
+
console.error(` \u{1F4CB} Devis en cours : ${pending_quotations}`);
|
|
304
|
+
console.error(` \u{1F465} Clients : ${total_clients}`);
|
|
305
|
+
if (unimputed_transactions > 0) {
|
|
306
|
+
console.error(` \u26A0\uFE0F Transactions non imput\xE9es : ${unimputed_transactions}`);
|
|
307
|
+
} else {
|
|
308
|
+
console.error(` \u2705 Toutes les transactions sont imput\xE9es`);
|
|
309
|
+
}
|
|
310
|
+
console.error("");
|
|
311
|
+
};
|
|
312
|
+
var outputError = (error) => {
|
|
313
|
+
if (error instanceof TiimeError) {
|
|
314
|
+
process.stderr.write(`${JSON.stringify(error.toJSON())}
|
|
315
|
+
`);
|
|
316
|
+
} else {
|
|
317
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
318
|
+
process.stderr.write(`${JSON.stringify({ error: message })}
|
|
319
|
+
`);
|
|
320
|
+
}
|
|
321
|
+
process.exit(1);
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/cli/commands/auth.ts
|
|
325
|
+
var authCommand = defineCommand({
|
|
326
|
+
meta: { name: "auth", description: "Gestion de l'authentification" },
|
|
327
|
+
subCommands: {
|
|
328
|
+
login: defineCommand({
|
|
329
|
+
meta: { name: "login", description: "Se connecter \xE0 Tiime" },
|
|
330
|
+
args: {
|
|
331
|
+
email: {
|
|
332
|
+
type: "string",
|
|
333
|
+
description: "Adresse email"
|
|
334
|
+
},
|
|
335
|
+
password: {
|
|
336
|
+
type: "string",
|
|
337
|
+
description: "Mot de passe"
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
async run({ args }) {
|
|
341
|
+
const hasArgs = args.email && args.password;
|
|
342
|
+
if (hasArgs) {
|
|
343
|
+
try {
|
|
344
|
+
const tm = new TokenManager();
|
|
345
|
+
await tm.login(args.email, args.password);
|
|
346
|
+
const info = tm.getTokenInfo();
|
|
347
|
+
output({
|
|
348
|
+
status: "authenticated",
|
|
349
|
+
email: info.email,
|
|
350
|
+
expires_at: info.expiresAt?.toISOString()
|
|
351
|
+
});
|
|
352
|
+
} catch (e) {
|
|
353
|
+
outputError(e);
|
|
354
|
+
}
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
p.intro("Connexion \xE0 Tiime");
|
|
358
|
+
const email = await p.text({
|
|
359
|
+
message: "Adresse email",
|
|
360
|
+
placeholder: "vous@example.com",
|
|
361
|
+
validate: (value) => {
|
|
362
|
+
if (!value || !value.includes("@")) return "Adresse email invalide";
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
if (p.isCancel(email)) {
|
|
366
|
+
p.cancel("Connexion annul\xE9e.");
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const password2 = await p.password({
|
|
370
|
+
message: "Mot de passe",
|
|
371
|
+
validate: (value) => {
|
|
372
|
+
if (!value) return "Le mot de passe est requis";
|
|
373
|
+
}
|
|
374
|
+
});
|
|
375
|
+
if (p.isCancel(password2)) {
|
|
376
|
+
p.cancel("Connexion annul\xE9e.");
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
const s = p.spinner();
|
|
380
|
+
s.start("Authentification en cours...");
|
|
381
|
+
try {
|
|
382
|
+
const tm = new TokenManager();
|
|
383
|
+
await tm.login(email, password2);
|
|
384
|
+
const info = tm.getTokenInfo();
|
|
385
|
+
s.stop("Authentification r\xE9ussie");
|
|
386
|
+
p.outro(`Connect\xE9 en tant que ${info.email ?? email}`);
|
|
387
|
+
} catch (e) {
|
|
388
|
+
s.stop("Authentification \xE9chou\xE9e");
|
|
389
|
+
const message = e instanceof Error ? e.message : "Erreur inconnue";
|
|
390
|
+
p.cancel(message);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}),
|
|
394
|
+
logout: defineCommand({
|
|
395
|
+
meta: { name: "logout", description: "Se d\xE9connecter de Tiime" },
|
|
396
|
+
run() {
|
|
397
|
+
const tm = new TokenManager();
|
|
398
|
+
tm.logout();
|
|
399
|
+
output({ status: "logged_out" });
|
|
400
|
+
}
|
|
401
|
+
}),
|
|
402
|
+
status: defineCommand({
|
|
403
|
+
meta: {
|
|
404
|
+
name: "status",
|
|
405
|
+
description: "Afficher le statut d'authentification"
|
|
406
|
+
},
|
|
407
|
+
run() {
|
|
408
|
+
const tm = new TokenManager();
|
|
409
|
+
const info = tm.getTokenInfo();
|
|
410
|
+
output({
|
|
411
|
+
authenticated: tm.isAuthenticated(),
|
|
412
|
+
email: info.email,
|
|
413
|
+
expires_at: info.expiresAt?.toISOString() ?? null
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
})
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// src/cli/commands/bank.ts
|
|
421
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
422
|
+
|
|
423
|
+
// src/sdk/client.ts
|
|
424
|
+
import { ofetch as ofetch2 } from "ofetch";
|
|
425
|
+
|
|
426
|
+
// src/sdk/resources/bank-accounts.ts
|
|
427
|
+
var BankAccountsResource = class {
|
|
428
|
+
constructor(fetch, companyId) {
|
|
429
|
+
this.fetch = fetch;
|
|
430
|
+
this.companyId = companyId;
|
|
431
|
+
}
|
|
432
|
+
list(enabled) {
|
|
433
|
+
return this.fetch(
|
|
434
|
+
`/companies/${this.companyId}/bank_accounts`,
|
|
435
|
+
{ query: enabled !== void 0 ? { enabled } : void 0 }
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
get(bankAccountId) {
|
|
439
|
+
return this.fetch(
|
|
440
|
+
`/companies/${this.companyId}/bank_accounts/${bankAccountId}`
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
async balance() {
|
|
444
|
+
const accounts = await this.list(true);
|
|
445
|
+
return accounts.map((a) => ({
|
|
446
|
+
name: a.name,
|
|
447
|
+
balance_amount: a.balance_amount,
|
|
448
|
+
currency: a.balance_currency
|
|
449
|
+
}));
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
// src/sdk/resources/bank-transactions.ts
|
|
454
|
+
var BankTransactionsResource = class {
|
|
455
|
+
constructor(fetch, companyId) {
|
|
456
|
+
this.fetch = fetch;
|
|
457
|
+
this.companyId = companyId;
|
|
458
|
+
}
|
|
459
|
+
list(params) {
|
|
460
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 100);
|
|
461
|
+
const end = start + (params?.pageSize ?? 100);
|
|
462
|
+
const { page: _, pageSize: __, from, to, search, ...query } = params ?? {};
|
|
463
|
+
if (from) query.transaction_date_start = from;
|
|
464
|
+
if (to) query.transaction_date_end = to;
|
|
465
|
+
if (search) query.wording = search;
|
|
466
|
+
return this.fetch(
|
|
467
|
+
`/companies/${this.companyId}/bank_transactions`,
|
|
468
|
+
{
|
|
469
|
+
query: { hide_refused: false, ...query },
|
|
470
|
+
headers: {
|
|
471
|
+
Accept: "application/vnd.tiime.bank_transactions.v2+json,application/vnd.tiime.bank_transactions.without_documents+json",
|
|
472
|
+
Range: `items=${start}-${end}`
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
}
|
|
477
|
+
async listAll(params) {
|
|
478
|
+
const pageSize = params?.pageSize ?? 200;
|
|
479
|
+
const all = [];
|
|
480
|
+
let page = 1;
|
|
481
|
+
let hasMore = true;
|
|
482
|
+
while (hasMore) {
|
|
483
|
+
const response = await this.list({ ...params, page, pageSize });
|
|
484
|
+
all.push(...response.transactions);
|
|
485
|
+
hasMore = response.transactions.length === pageSize;
|
|
486
|
+
page++;
|
|
487
|
+
}
|
|
488
|
+
return all;
|
|
489
|
+
}
|
|
490
|
+
unimputed() {
|
|
491
|
+
return this.fetch(
|
|
492
|
+
`/companies/${this.companyId}/bank_transactions/unimputed`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
// src/sdk/resources/clients.ts
|
|
498
|
+
var ClientsResource = class {
|
|
499
|
+
constructor(fetch, companyId) {
|
|
500
|
+
this.fetch = fetch;
|
|
501
|
+
this.companyId = companyId;
|
|
502
|
+
}
|
|
503
|
+
list(params) {
|
|
504
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
505
|
+
query: params,
|
|
506
|
+
headers: {
|
|
507
|
+
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
508
|
+
Range: "items=0-*"
|
|
509
|
+
}
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
get(clientId) {
|
|
513
|
+
return this.fetch(
|
|
514
|
+
`/companies/${this.companyId}/clients/${clientId}`
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
create(params) {
|
|
518
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
519
|
+
method: "POST",
|
|
520
|
+
body: params
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
search(query) {
|
|
524
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
525
|
+
query: { search: query },
|
|
526
|
+
headers: {
|
|
527
|
+
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
528
|
+
Range: "items=0-*"
|
|
529
|
+
}
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// src/sdk/resources/company.ts
|
|
535
|
+
var CompanyResource = class {
|
|
536
|
+
constructor(fetch, companyId) {
|
|
537
|
+
this.fetch = fetch;
|
|
538
|
+
this.companyId = companyId;
|
|
539
|
+
}
|
|
540
|
+
get() {
|
|
541
|
+
return this.fetch(`/companies/${this.companyId}`);
|
|
542
|
+
}
|
|
543
|
+
users() {
|
|
544
|
+
return this.fetch(`/companies/${this.companyId}/users`);
|
|
545
|
+
}
|
|
546
|
+
appConfig() {
|
|
547
|
+
return this.fetch(`/companies/${this.companyId}/app_config`);
|
|
548
|
+
}
|
|
549
|
+
accountingPeriod(rangeYear = 1) {
|
|
550
|
+
return this.fetch(
|
|
551
|
+
`/companies/${this.companyId}/accounting_period/current`,
|
|
552
|
+
{ query: { range_year: rangeYear } }
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
tiles(keys) {
|
|
556
|
+
return this.fetch(`/companies/${this.companyId}/tiles`, {
|
|
557
|
+
query: { keys: keys.join(",") }
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
dashboardBlocks(displayGroup = "monitoring") {
|
|
561
|
+
return this.fetch(`/companies/${this.companyId}/dashboard_blocks`, {
|
|
562
|
+
query: { sorts: "rank:asc", display_group: displayGroup }
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/sdk/resources/documents.ts
|
|
568
|
+
var DocumentsResource = class {
|
|
569
|
+
constructor(fetch, companyId) {
|
|
570
|
+
this.fetch = fetch;
|
|
571
|
+
this.companyId = companyId;
|
|
572
|
+
}
|
|
573
|
+
list(params) {
|
|
574
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
575
|
+
const end = start + (params?.pageSize ?? 25);
|
|
576
|
+
const { page: _, pageSize: __, ...query } = params ?? {};
|
|
577
|
+
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
578
|
+
query: {
|
|
579
|
+
sorts: "created_at:desc",
|
|
580
|
+
expand: "file_family,preview_available",
|
|
581
|
+
...query
|
|
582
|
+
},
|
|
583
|
+
headers: {
|
|
584
|
+
Accept: "application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",
|
|
585
|
+
Range: `items=${start}-${end}`
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
categories() {
|
|
590
|
+
return this.fetch(
|
|
591
|
+
`/companies/${this.companyId}/document_categories`,
|
|
592
|
+
{
|
|
593
|
+
headers: {
|
|
594
|
+
Accept: "application/vnd.tiime.documents.v3+json"
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
);
|
|
598
|
+
}
|
|
599
|
+
preview(documentId) {
|
|
600
|
+
return this.fetch(
|
|
601
|
+
`/companies/${this.companyId}/documents/${documentId}/preview`
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
upload(file, filename, type) {
|
|
605
|
+
const formData = new FormData();
|
|
606
|
+
formData.append("file", new Blob([file]), filename);
|
|
607
|
+
if (type) {
|
|
608
|
+
formData.append("type", type);
|
|
609
|
+
}
|
|
610
|
+
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
611
|
+
method: "POST",
|
|
612
|
+
body: formData
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
async download(documentId) {
|
|
616
|
+
return this.fetch(
|
|
617
|
+
`/companies/${this.companyId}/documents/${documentId}/download`,
|
|
618
|
+
{
|
|
619
|
+
headers: { Accept: "application/octet-stream" }
|
|
620
|
+
}
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
// src/sdk/resources/expense-reports.ts
|
|
626
|
+
var ExpenseReportsResource = class {
|
|
627
|
+
constructor(fetch, companyId) {
|
|
628
|
+
this.fetch = fetch;
|
|
629
|
+
this.companyId = companyId;
|
|
630
|
+
}
|
|
631
|
+
list(sorts = "metadata.date:desc") {
|
|
632
|
+
return this.fetch(
|
|
633
|
+
`/companies/${this.companyId}/expense_reports`,
|
|
634
|
+
{
|
|
635
|
+
query: { expand: "total_amount", sorts },
|
|
636
|
+
headers: { Range: "items=0-25" }
|
|
637
|
+
}
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
get(expenseReportId) {
|
|
641
|
+
return this.fetch(
|
|
642
|
+
`/companies/${this.companyId}/expense_reports/${expenseReportId}`
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
create(params) {
|
|
646
|
+
return this.fetch(
|
|
647
|
+
`/companies/${this.companyId}/expense_reports`,
|
|
648
|
+
{
|
|
649
|
+
method: "POST",
|
|
650
|
+
body: params
|
|
651
|
+
}
|
|
652
|
+
);
|
|
653
|
+
}
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// src/sdk/resources/invoices.ts
|
|
657
|
+
var DEFAULT_INVOICE_TEMPLATE = {
|
|
658
|
+
template: "advanced",
|
|
659
|
+
status: "draft",
|
|
660
|
+
due_date_mode: "thirty_days",
|
|
661
|
+
title_enabled: true,
|
|
662
|
+
free_field_enabled: false,
|
|
663
|
+
free_field: "",
|
|
664
|
+
discount_enabled: false,
|
|
665
|
+
bank_detail_enabled: true,
|
|
666
|
+
payment_condition_enabled: true,
|
|
667
|
+
payment_condition: "En cas de retard de paiement, une p\xE9nalit\xE9 de 3 fois le taux d'int\xE9r\xEAt l\xE9gal sera appliqu\xE9e, \xE0 laquelle s'ajoutera une indemnit\xE9 forfaitaire pour frais de recouvrement de 40\u20AC.",
|
|
668
|
+
text_lines: []
|
|
669
|
+
};
|
|
670
|
+
var InvoicesResource = class {
|
|
671
|
+
constructor(fetch, companyId) {
|
|
672
|
+
this.fetch = fetch;
|
|
673
|
+
this.companyId = companyId;
|
|
674
|
+
}
|
|
675
|
+
list(params) {
|
|
676
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
677
|
+
const end = start + (params?.pageSize ?? 25);
|
|
678
|
+
const query = {
|
|
679
|
+
sorts: params?.sorts ?? "invoice_number:desc"
|
|
680
|
+
};
|
|
681
|
+
if (params?.status) query.status = params.status;
|
|
682
|
+
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
683
|
+
query,
|
|
684
|
+
headers: { Range: `items=${start}-${end}` }
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
async listAll(params) {
|
|
688
|
+
const pageSize = params?.pageSize ?? 100;
|
|
689
|
+
const all = [];
|
|
690
|
+
let page = 1;
|
|
691
|
+
let hasMore = true;
|
|
692
|
+
while (hasMore) {
|
|
693
|
+
const batch = await this.list({
|
|
694
|
+
sorts: params?.sorts,
|
|
695
|
+
status: params?.status,
|
|
696
|
+
page,
|
|
697
|
+
pageSize
|
|
698
|
+
});
|
|
699
|
+
all.push(...batch);
|
|
700
|
+
hasMore = batch.length === pageSize;
|
|
701
|
+
page++;
|
|
702
|
+
}
|
|
703
|
+
return all;
|
|
704
|
+
}
|
|
705
|
+
get(invoiceId) {
|
|
706
|
+
return this.fetch(
|
|
707
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
create(params) {
|
|
711
|
+
const body = { ...DEFAULT_INVOICE_TEMPLATE, ...params };
|
|
712
|
+
for (const line of body.lines ?? []) {
|
|
713
|
+
line.line_amount = line.quantity * line.unit_amount;
|
|
714
|
+
line.sequence ??= 1;
|
|
715
|
+
line.invoicing_category_type ??= "benefit";
|
|
716
|
+
line.discount_description ??= "";
|
|
717
|
+
line.discount_amount ??= null;
|
|
718
|
+
line.discount_percentage ??= null;
|
|
719
|
+
}
|
|
720
|
+
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
721
|
+
method: "POST",
|
|
722
|
+
body
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
update(invoiceId, params) {
|
|
726
|
+
return this.fetch(
|
|
727
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
728
|
+
{
|
|
729
|
+
method: "PUT",
|
|
730
|
+
body: params
|
|
731
|
+
}
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
send(invoiceId, params) {
|
|
735
|
+
return this.fetch(
|
|
736
|
+
`/companies/${this.companyId}/invoices/${invoiceId}/send`,
|
|
737
|
+
{
|
|
738
|
+
method: "POST",
|
|
739
|
+
body: params
|
|
740
|
+
}
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
async downloadPdf(invoiceId) {
|
|
744
|
+
return this.fetch(
|
|
745
|
+
`/companies/${this.companyId}/invoices/${invoiceId}/pdf`,
|
|
746
|
+
{
|
|
747
|
+
headers: { Accept: "application/pdf" }
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
delete(invoiceId) {
|
|
752
|
+
return this.fetch(
|
|
753
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
754
|
+
{ method: "DELETE" }
|
|
755
|
+
);
|
|
756
|
+
}
|
|
757
|
+
async duplicate(invoiceId, overrides) {
|
|
758
|
+
const source = await this.get(invoiceId);
|
|
759
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
760
|
+
const lines = source.lines.map((l) => ({
|
|
761
|
+
description: l.description,
|
|
762
|
+
quantity: overrides?.quantity ?? l.quantity,
|
|
763
|
+
unit_amount: l.unit_amount,
|
|
764
|
+
vat_type: l.vat_type,
|
|
765
|
+
invoicing_unit: l.invoicing_unit,
|
|
766
|
+
invoicing_category_type: l.invoicing_category_type,
|
|
767
|
+
article: l.article
|
|
768
|
+
}));
|
|
769
|
+
return this.create({
|
|
770
|
+
client: source.client_id ? { id: source.client_id } : null,
|
|
771
|
+
emission_date: overrides?.emission_date ?? today,
|
|
772
|
+
title: source.title,
|
|
773
|
+
title_enabled: !!source.title,
|
|
774
|
+
lines,
|
|
775
|
+
status: "draft"
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
};
|
|
779
|
+
|
|
780
|
+
// src/sdk/resources/labels.ts
|
|
781
|
+
var LabelsResource = class {
|
|
782
|
+
constructor(fetch, companyId) {
|
|
783
|
+
this.fetch = fetch;
|
|
784
|
+
this.companyId = companyId;
|
|
785
|
+
}
|
|
786
|
+
list() {
|
|
787
|
+
return this.fetch(`/companies/${this.companyId}/labels`, {
|
|
788
|
+
headers: {
|
|
789
|
+
Accept: "application/vnd.tiime.labels.v2+json"
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
standard() {
|
|
794
|
+
return this.fetch(`/companies/${this.companyId}/standard_labels`);
|
|
795
|
+
}
|
|
796
|
+
tags() {
|
|
797
|
+
return this.fetch(`/companies/${this.companyId}/tags`, {
|
|
798
|
+
query: { expand: "tag_detail" }
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
// src/sdk/resources/quotations.ts
|
|
804
|
+
var QuotationsResource = class {
|
|
805
|
+
constructor(fetch, companyId) {
|
|
806
|
+
this.fetch = fetch;
|
|
807
|
+
this.companyId = companyId;
|
|
808
|
+
}
|
|
809
|
+
list(expand = "invoices") {
|
|
810
|
+
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
811
|
+
query: { expand },
|
|
812
|
+
headers: { Range: "items=0-25" }
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
get(quotationId) {
|
|
816
|
+
return this.fetch(
|
|
817
|
+
`/companies/${this.companyId}/quotations/${quotationId}`
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
create(params) {
|
|
821
|
+
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
822
|
+
method: "POST",
|
|
823
|
+
body: params
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
async downloadPdf(quotationId) {
|
|
827
|
+
return this.fetch(
|
|
828
|
+
`/companies/${this.companyId}/quotations/${quotationId}/pdf`,
|
|
829
|
+
{
|
|
830
|
+
headers: { Accept: "application/pdf" }
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
}
|
|
834
|
+
send(quotationId, params) {
|
|
835
|
+
return this.fetch(
|
|
836
|
+
`/companies/${this.companyId}/quotations/${quotationId}/send`,
|
|
837
|
+
{
|
|
838
|
+
method: "POST",
|
|
839
|
+
body: params
|
|
840
|
+
}
|
|
841
|
+
);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
// src/sdk/resources/users.ts
|
|
846
|
+
var UsersResource = class {
|
|
847
|
+
constructor(fetch) {
|
|
848
|
+
this.fetch = fetch;
|
|
849
|
+
}
|
|
850
|
+
me() {
|
|
851
|
+
return this.fetch("/users/me");
|
|
852
|
+
}
|
|
853
|
+
legalInformations() {
|
|
854
|
+
return this.fetch("/users/me/legal_informations");
|
|
855
|
+
}
|
|
856
|
+
settings(companyId) {
|
|
857
|
+
return this.fetch(`/users/me/companies/${companyId}/settings`);
|
|
858
|
+
}
|
|
859
|
+
};
|
|
860
|
+
|
|
861
|
+
// src/sdk/client.ts
|
|
862
|
+
var BASE_URL = "https://chronos-api.tiime-apps.com/v1";
|
|
863
|
+
var TiimeClient = class {
|
|
864
|
+
fetch;
|
|
865
|
+
tokenManager;
|
|
866
|
+
companyId;
|
|
867
|
+
constructor(options) {
|
|
868
|
+
this.companyId = options.companyId;
|
|
869
|
+
this.tokenManager = options.tokenManager ?? new TokenManager();
|
|
870
|
+
this.fetch = ofetch2.create({
|
|
871
|
+
baseURL: BASE_URL,
|
|
872
|
+
retry: 2,
|
|
873
|
+
retryDelay: 500,
|
|
874
|
+
retryStatusCodes: [408, 429, 500, 502, 503, 504],
|
|
875
|
+
headers: {
|
|
876
|
+
"tiime-app": "tiime",
|
|
877
|
+
"tiime-app-version": "4.30.3",
|
|
878
|
+
"tiime-app-platform": "cli"
|
|
879
|
+
},
|
|
880
|
+
onRequest: async ({ options: options2 }) => {
|
|
881
|
+
const token = await this.tokenManager.getValidToken();
|
|
882
|
+
options2.headers.set("Authorization", `Bearer ${token}`);
|
|
883
|
+
},
|
|
884
|
+
onResponseError: ({ request, response }) => {
|
|
885
|
+
throw new TiimeError(
|
|
886
|
+
response.statusText || `HTTP ${response.status}`,
|
|
887
|
+
response.status,
|
|
888
|
+
String(request),
|
|
889
|
+
response._data
|
|
890
|
+
);
|
|
891
|
+
}
|
|
892
|
+
});
|
|
893
|
+
}
|
|
894
|
+
listCompanies() {
|
|
895
|
+
return this.fetch("/companies", {
|
|
896
|
+
headers: {
|
|
897
|
+
Accept: "application/vnd.tiime.companies.v2+json",
|
|
898
|
+
Range: "items=0-101"
|
|
899
|
+
}
|
|
900
|
+
});
|
|
901
|
+
}
|
|
902
|
+
get users() {
|
|
903
|
+
return new UsersResource(this.fetch);
|
|
904
|
+
}
|
|
905
|
+
get company() {
|
|
906
|
+
return new CompanyResource(this.fetch, this.companyId);
|
|
907
|
+
}
|
|
908
|
+
get clients() {
|
|
909
|
+
return new ClientsResource(this.fetch, this.companyId);
|
|
910
|
+
}
|
|
911
|
+
get invoices() {
|
|
912
|
+
return new InvoicesResource(this.fetch, this.companyId);
|
|
913
|
+
}
|
|
914
|
+
get quotations() {
|
|
915
|
+
return new QuotationsResource(this.fetch, this.companyId);
|
|
916
|
+
}
|
|
917
|
+
get bankAccounts() {
|
|
918
|
+
return new BankAccountsResource(this.fetch, this.companyId);
|
|
919
|
+
}
|
|
920
|
+
get bankTransactions() {
|
|
921
|
+
return new BankTransactionsResource(this.fetch, this.companyId);
|
|
922
|
+
}
|
|
923
|
+
get documents() {
|
|
924
|
+
return new DocumentsResource(this.fetch, this.companyId);
|
|
925
|
+
}
|
|
926
|
+
get expenseReports() {
|
|
927
|
+
return new ExpenseReportsResource(this.fetch, this.companyId);
|
|
928
|
+
}
|
|
929
|
+
get labels() {
|
|
930
|
+
return new LabelsResource(this.fetch, this.companyId);
|
|
931
|
+
}
|
|
932
|
+
};
|
|
933
|
+
|
|
934
|
+
// src/cli/config.ts
|
|
935
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
936
|
+
import { homedir as homedir2 } from "os";
|
|
937
|
+
import { join as join2 } from "path";
|
|
938
|
+
var CONFIG_DIR2 = join2(homedir2(), ".config", "tiime");
|
|
939
|
+
var CONFIG_FILE = join2(CONFIG_DIR2, "config.json");
|
|
940
|
+
var loadConfig = () => {
|
|
941
|
+
try {
|
|
942
|
+
if (existsSync2(CONFIG_FILE)) {
|
|
943
|
+
return JSON.parse(readFileSync2(CONFIG_FILE, "utf-8"));
|
|
944
|
+
}
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
return {};
|
|
948
|
+
};
|
|
949
|
+
var saveConfig = (config) => {
|
|
950
|
+
if (!existsSync2(CONFIG_DIR2)) {
|
|
951
|
+
mkdirSync2(CONFIG_DIR2, { recursive: true });
|
|
952
|
+
}
|
|
953
|
+
writeFileSync2(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
954
|
+
};
|
|
955
|
+
var getCompanyId = () => {
|
|
956
|
+
const config = loadConfig();
|
|
957
|
+
if (!config.companyId) {
|
|
958
|
+
throw new Error(
|
|
959
|
+
"Aucune entreprise configur\xE9e. Ex\xE9cutez `tiime company use <id>` d'abord."
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
return config.companyId;
|
|
963
|
+
};
|
|
964
|
+
|
|
965
|
+
// src/cli/commands/bank.ts
|
|
966
|
+
var bankCommand = defineCommand2({
|
|
967
|
+
meta: { name: "bank", description: "Comptes bancaires et transactions" },
|
|
968
|
+
subCommands: {
|
|
969
|
+
balance: defineCommand2({
|
|
970
|
+
meta: {
|
|
971
|
+
name: "balance",
|
|
972
|
+
description: "Afficher les soldes des comptes"
|
|
973
|
+
},
|
|
974
|
+
args: { ...formatArg },
|
|
975
|
+
async run({ args }) {
|
|
976
|
+
try {
|
|
977
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
978
|
+
const balances = await client.bankAccounts.balance();
|
|
979
|
+
output(balances, { format: args.format });
|
|
980
|
+
} catch (e) {
|
|
981
|
+
outputError(e);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}),
|
|
985
|
+
accounts: defineCommand2({
|
|
986
|
+
meta: { name: "accounts", description: "Lister les comptes bancaires" },
|
|
987
|
+
args: {
|
|
988
|
+
...formatArg,
|
|
989
|
+
enabled: {
|
|
990
|
+
type: "boolean",
|
|
991
|
+
description: "Uniquement les comptes actifs",
|
|
992
|
+
default: true
|
|
993
|
+
}
|
|
994
|
+
},
|
|
995
|
+
async run({ args }) {
|
|
996
|
+
try {
|
|
997
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
998
|
+
const accounts = await client.bankAccounts.list(args.enabled);
|
|
999
|
+
output(accounts, { format: args.format });
|
|
1000
|
+
} catch (e) {
|
|
1001
|
+
outputError(e);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
}),
|
|
1005
|
+
transactions: defineCommand2({
|
|
1006
|
+
meta: {
|
|
1007
|
+
name: "transactions",
|
|
1008
|
+
description: "Lister les transactions bancaires"
|
|
1009
|
+
},
|
|
1010
|
+
args: {
|
|
1011
|
+
"bank-account": {
|
|
1012
|
+
type: "string",
|
|
1013
|
+
description: "Filtrer par ID de compte bancaire"
|
|
1014
|
+
},
|
|
1015
|
+
"hide-refused": {
|
|
1016
|
+
type: "boolean",
|
|
1017
|
+
description: "Masquer les transactions refus\xE9es",
|
|
1018
|
+
default: false
|
|
1019
|
+
},
|
|
1020
|
+
sort: {
|
|
1021
|
+
type: "string",
|
|
1022
|
+
description: "Tri champ:direction (ex: date:desc)"
|
|
1023
|
+
},
|
|
1024
|
+
page: { type: "string", description: "Num\xE9ro de page", default: "1" },
|
|
1025
|
+
"page-size": {
|
|
1026
|
+
type: "string",
|
|
1027
|
+
description: "\xC9l\xE9ments par page",
|
|
1028
|
+
default: "100"
|
|
1029
|
+
},
|
|
1030
|
+
from: {
|
|
1031
|
+
type: "string",
|
|
1032
|
+
description: "Date de d\xE9but (YYYY-MM-DD)"
|
|
1033
|
+
},
|
|
1034
|
+
to: {
|
|
1035
|
+
type: "string",
|
|
1036
|
+
description: "Date de fin (YYYY-MM-DD)"
|
|
1037
|
+
},
|
|
1038
|
+
search: {
|
|
1039
|
+
type: "string",
|
|
1040
|
+
description: "Rechercher par libell\xE9"
|
|
1041
|
+
},
|
|
1042
|
+
all: {
|
|
1043
|
+
type: "boolean",
|
|
1044
|
+
description: "R\xE9cup\xE9rer toutes les pages",
|
|
1045
|
+
default: false
|
|
1046
|
+
},
|
|
1047
|
+
...formatArg
|
|
1048
|
+
},
|
|
1049
|
+
async run({ args }) {
|
|
1050
|
+
try {
|
|
1051
|
+
const fmt = { format: args.format };
|
|
1052
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1053
|
+
const params = {
|
|
1054
|
+
bank_account: args["bank-account"] ? Number(args["bank-account"]) : void 0,
|
|
1055
|
+
hide_refused: args["hide-refused"],
|
|
1056
|
+
sorts: args.sort,
|
|
1057
|
+
from: args.from,
|
|
1058
|
+
to: args.to,
|
|
1059
|
+
search: args.search
|
|
1060
|
+
};
|
|
1061
|
+
if (args.all) {
|
|
1062
|
+
const transactions = await client.bankTransactions.listAll(params);
|
|
1063
|
+
output(transactions, fmt);
|
|
1064
|
+
} else {
|
|
1065
|
+
const transactions = await client.bankTransactions.list({
|
|
1066
|
+
...params,
|
|
1067
|
+
page: Number(args.page),
|
|
1068
|
+
pageSize: Number(args["page-size"])
|
|
1069
|
+
});
|
|
1070
|
+
output(transactions, fmt);
|
|
1071
|
+
}
|
|
1072
|
+
} catch (e) {
|
|
1073
|
+
outputError(e);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
}),
|
|
1077
|
+
unimputed: defineCommand2({
|
|
1078
|
+
meta: {
|
|
1079
|
+
name: "unimputed",
|
|
1080
|
+
description: "Transactions non imput\xE9es"
|
|
1081
|
+
},
|
|
1082
|
+
args: { ...formatArg },
|
|
1083
|
+
async run({ args }) {
|
|
1084
|
+
try {
|
|
1085
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1086
|
+
const transactions = await client.bankTransactions.unimputed();
|
|
1087
|
+
output(transactions, { format: args.format });
|
|
1088
|
+
} catch (e) {
|
|
1089
|
+
outputError(e);
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
})
|
|
1093
|
+
}
|
|
1094
|
+
});
|
|
1095
|
+
|
|
1096
|
+
// src/cli/commands/clients.ts
|
|
1097
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
1098
|
+
var clientsCommand = defineCommand3({
|
|
1099
|
+
meta: { name: "clients", description: "Gestion des clients" },
|
|
1100
|
+
subCommands: {
|
|
1101
|
+
list: defineCommand3({
|
|
1102
|
+
meta: { name: "list", description: "Lister les clients" },
|
|
1103
|
+
args: {
|
|
1104
|
+
...formatArg,
|
|
1105
|
+
archived: {
|
|
1106
|
+
type: "boolean",
|
|
1107
|
+
description: "Inclure les clients archiv\xE9s",
|
|
1108
|
+
default: false
|
|
1109
|
+
}
|
|
1110
|
+
},
|
|
1111
|
+
async run({ args }) {
|
|
1112
|
+
try {
|
|
1113
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1114
|
+
const clients = await client.clients.list({
|
|
1115
|
+
archived: args.archived
|
|
1116
|
+
});
|
|
1117
|
+
output(clients, { format: args.format });
|
|
1118
|
+
} catch (e) {
|
|
1119
|
+
outputError(e);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
}),
|
|
1123
|
+
get: defineCommand3({
|
|
1124
|
+
meta: { name: "get", description: "D\xE9tails d'un client" },
|
|
1125
|
+
args: {
|
|
1126
|
+
id: { type: "string", description: "ID du client", required: true }
|
|
1127
|
+
},
|
|
1128
|
+
async run({ args }) {
|
|
1129
|
+
try {
|
|
1130
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1131
|
+
const result = await client.clients.get(Number(args.id));
|
|
1132
|
+
output(result);
|
|
1133
|
+
} catch (e) {
|
|
1134
|
+
outputError(e);
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
}),
|
|
1138
|
+
create: defineCommand3({
|
|
1139
|
+
meta: { name: "create", description: "Cr\xE9er un client" },
|
|
1140
|
+
args: {
|
|
1141
|
+
name: {
|
|
1142
|
+
type: "string",
|
|
1143
|
+
description: "Nom du client",
|
|
1144
|
+
required: true
|
|
1145
|
+
},
|
|
1146
|
+
address: {
|
|
1147
|
+
type: "string",
|
|
1148
|
+
description: "Adresse du client"
|
|
1149
|
+
},
|
|
1150
|
+
"postal-code": {
|
|
1151
|
+
type: "string",
|
|
1152
|
+
description: "Code postal"
|
|
1153
|
+
},
|
|
1154
|
+
city: {
|
|
1155
|
+
type: "string",
|
|
1156
|
+
description: "Ville"
|
|
1157
|
+
},
|
|
1158
|
+
email: {
|
|
1159
|
+
type: "string",
|
|
1160
|
+
description: "Adresse email"
|
|
1161
|
+
},
|
|
1162
|
+
phone: {
|
|
1163
|
+
type: "string",
|
|
1164
|
+
description: "Num\xE9ro de t\xE9l\xE9phone"
|
|
1165
|
+
},
|
|
1166
|
+
siret: {
|
|
1167
|
+
type: "string",
|
|
1168
|
+
description: "SIREN ou SIRET"
|
|
1169
|
+
},
|
|
1170
|
+
professional: {
|
|
1171
|
+
type: "boolean",
|
|
1172
|
+
description: "Client professionnel",
|
|
1173
|
+
default: true
|
|
1174
|
+
}
|
|
1175
|
+
},
|
|
1176
|
+
async run({ args }) {
|
|
1177
|
+
try {
|
|
1178
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1179
|
+
const result = await client.clients.create({
|
|
1180
|
+
name: args.name,
|
|
1181
|
+
address: args.address,
|
|
1182
|
+
postal_code: args["postal-code"],
|
|
1183
|
+
city: args.city,
|
|
1184
|
+
email: args.email,
|
|
1185
|
+
phone: args.phone,
|
|
1186
|
+
siren_or_siret: args.siret,
|
|
1187
|
+
professional: args.professional
|
|
1188
|
+
});
|
|
1189
|
+
output(result);
|
|
1190
|
+
} catch (e) {
|
|
1191
|
+
outputError(e);
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
}),
|
|
1195
|
+
search: defineCommand3({
|
|
1196
|
+
meta: { name: "search", description: "Rechercher un client" },
|
|
1197
|
+
args: {
|
|
1198
|
+
...formatArg,
|
|
1199
|
+
query: {
|
|
1200
|
+
type: "string",
|
|
1201
|
+
description: "Terme de recherche",
|
|
1202
|
+
required: true
|
|
1203
|
+
}
|
|
1204
|
+
},
|
|
1205
|
+
async run({ args }) {
|
|
1206
|
+
try {
|
|
1207
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1208
|
+
const results = await client.clients.search(args.query);
|
|
1209
|
+
output(results, { format: args.format });
|
|
1210
|
+
} catch (e) {
|
|
1211
|
+
outputError(e);
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
})
|
|
1215
|
+
}
|
|
1216
|
+
});
|
|
1217
|
+
|
|
1218
|
+
// src/cli/commands/company.ts
|
|
1219
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
1220
|
+
var companyCommand = defineCommand4({
|
|
1221
|
+
meta: { name: "company", description: "Gestion de l'entreprise" },
|
|
1222
|
+
subCommands: {
|
|
1223
|
+
list: defineCommand4({
|
|
1224
|
+
meta: { name: "list", description: "Lister toutes les entreprises" },
|
|
1225
|
+
args: { ...formatArg },
|
|
1226
|
+
async run({ args }) {
|
|
1227
|
+
try {
|
|
1228
|
+
const client = new TiimeClient({ companyId: 0 });
|
|
1229
|
+
const companies = await client.listCompanies();
|
|
1230
|
+
output(
|
|
1231
|
+
companies.map((c) => ({
|
|
1232
|
+
id: c.id,
|
|
1233
|
+
name: c.name,
|
|
1234
|
+
legal_form: c.legal_form,
|
|
1235
|
+
siret: c.siret,
|
|
1236
|
+
city: c.city
|
|
1237
|
+
})),
|
|
1238
|
+
{ format: args.format }
|
|
1239
|
+
);
|
|
1240
|
+
} catch (e) {
|
|
1241
|
+
outputError(e);
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
}),
|
|
1245
|
+
get: defineCommand4({
|
|
1246
|
+
meta: { name: "get", description: "D\xE9tails de l'entreprise active" },
|
|
1247
|
+
async run() {
|
|
1248
|
+
try {
|
|
1249
|
+
const client = new TiimeClient({
|
|
1250
|
+
companyId: getCompanyId()
|
|
1251
|
+
});
|
|
1252
|
+
const company = await client.company.get();
|
|
1253
|
+
output(company);
|
|
1254
|
+
} catch (e) {
|
|
1255
|
+
outputError(e);
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
}),
|
|
1259
|
+
use: defineCommand4({
|
|
1260
|
+
meta: { name: "use", description: "D\xE9finir l'entreprise active" },
|
|
1261
|
+
args: {
|
|
1262
|
+
id: {
|
|
1263
|
+
type: "string",
|
|
1264
|
+
description: "ID de l'entreprise",
|
|
1265
|
+
required: true
|
|
1266
|
+
}
|
|
1267
|
+
},
|
|
1268
|
+
run({ args }) {
|
|
1269
|
+
const config = loadConfig();
|
|
1270
|
+
config.companyId = Number(args.id);
|
|
1271
|
+
saveConfig(config);
|
|
1272
|
+
output({ status: "ok", companyId: config.companyId });
|
|
1273
|
+
}
|
|
1274
|
+
}),
|
|
1275
|
+
me: defineCommand4({
|
|
1276
|
+
meta: {
|
|
1277
|
+
name: "me",
|
|
1278
|
+
description: "Info utilisateur courant (inclut active_company)"
|
|
1279
|
+
},
|
|
1280
|
+
async run() {
|
|
1281
|
+
try {
|
|
1282
|
+
const client = new TiimeClient({ companyId: 0 });
|
|
1283
|
+
const user = await client.users.me();
|
|
1284
|
+
output(user);
|
|
1285
|
+
} catch (e) {
|
|
1286
|
+
outputError(e);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
})
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
|
|
1293
|
+
// src/cli/commands/completion.ts
|
|
1294
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
1295
|
+
var commands = {
|
|
1296
|
+
auth: {
|
|
1297
|
+
description: "Gestion de l'authentification",
|
|
1298
|
+
subs: {
|
|
1299
|
+
login: "Se connecter",
|
|
1300
|
+
logout: "Se d\xE9connecter",
|
|
1301
|
+
status: "Statut de connexion"
|
|
1302
|
+
}
|
|
1303
|
+
},
|
|
1304
|
+
company: {
|
|
1305
|
+
description: "Gestion de l'entreprise",
|
|
1306
|
+
subs: {
|
|
1307
|
+
list: "Lister les entreprises",
|
|
1308
|
+
get: "D\xE9tails de l'entreprise active",
|
|
1309
|
+
use: "D\xE9finir l'entreprise active",
|
|
1310
|
+
me: "Info utilisateur"
|
|
1311
|
+
}
|
|
1312
|
+
},
|
|
1313
|
+
invoices: {
|
|
1314
|
+
description: "Gestion des factures",
|
|
1315
|
+
subs: {
|
|
1316
|
+
list: "Lister les factures",
|
|
1317
|
+
get: "D\xE9tails d'une facture",
|
|
1318
|
+
create: "Cr\xE9er une facture",
|
|
1319
|
+
duplicate: "Dupliquer une facture",
|
|
1320
|
+
update: "Modifier une facture",
|
|
1321
|
+
send: "Envoyer par email",
|
|
1322
|
+
pdf: "T\xE9l\xE9charger le PDF",
|
|
1323
|
+
delete: "Supprimer un brouillon"
|
|
1324
|
+
}
|
|
1325
|
+
},
|
|
1326
|
+
clients: {
|
|
1327
|
+
description: "Gestion des clients",
|
|
1328
|
+
subs: {
|
|
1329
|
+
list: "Lister les clients",
|
|
1330
|
+
get: "D\xE9tails d'un client",
|
|
1331
|
+
create: "Cr\xE9er un client",
|
|
1332
|
+
search: "Rechercher un client"
|
|
1333
|
+
}
|
|
1334
|
+
},
|
|
1335
|
+
bank: {
|
|
1336
|
+
description: "Comptes et transactions",
|
|
1337
|
+
subs: {
|
|
1338
|
+
accounts: "Comptes bancaires",
|
|
1339
|
+
balance: "Soldes des comptes",
|
|
1340
|
+
transactions: "Transactions bancaires",
|
|
1341
|
+
unimputed: "Transactions non imput\xE9es"
|
|
1342
|
+
}
|
|
1343
|
+
},
|
|
1344
|
+
quotations: {
|
|
1345
|
+
description: "Gestion des devis",
|
|
1346
|
+
subs: {
|
|
1347
|
+
list: "Lister les devis",
|
|
1348
|
+
get: "D\xE9tails d'un devis",
|
|
1349
|
+
create: "Cr\xE9er un devis",
|
|
1350
|
+
pdf: "T\xE9l\xE9charger le PDF",
|
|
1351
|
+
send: "Envoyer par email"
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
expenses: {
|
|
1355
|
+
description: "Notes de frais",
|
|
1356
|
+
subs: {
|
|
1357
|
+
list: "Lister les notes de frais",
|
|
1358
|
+
get: "D\xE9tails d'une note de frais",
|
|
1359
|
+
create: "Cr\xE9er une note de frais"
|
|
1360
|
+
}
|
|
1361
|
+
},
|
|
1362
|
+
documents: {
|
|
1363
|
+
description: "Gestion des documents",
|
|
1364
|
+
subs: {
|
|
1365
|
+
list: "Lister les documents",
|
|
1366
|
+
categories: "Cat\xE9gories de documents",
|
|
1367
|
+
upload: "Uploader un justificatif",
|
|
1368
|
+
download: "T\xE9l\xE9charger un document"
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
labels: {
|
|
1372
|
+
description: "Labels et tags",
|
|
1373
|
+
subs: {
|
|
1374
|
+
list: "Labels personnalis\xE9s",
|
|
1375
|
+
standard: "Labels standards",
|
|
1376
|
+
tags: "Tags"
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
status: {
|
|
1380
|
+
description: "R\xE9sum\xE9 rapide de la situation",
|
|
1381
|
+
subs: {}
|
|
1382
|
+
},
|
|
1383
|
+
open: {
|
|
1384
|
+
description: "Ouvrir Tiime dans le navigateur",
|
|
1385
|
+
subs: {}
|
|
1386
|
+
},
|
|
1387
|
+
version: {
|
|
1388
|
+
description: "Afficher la version",
|
|
1389
|
+
subs: {}
|
|
1390
|
+
},
|
|
1391
|
+
completion: {
|
|
1392
|
+
description: "Script d'autocompl\xE9tion",
|
|
1393
|
+
subs: {}
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
var topLevelNames = Object.keys(commands);
|
|
1397
|
+
var commonFlags = ["--format", "--id", "--all"];
|
|
1398
|
+
var generateZsh = () => {
|
|
1399
|
+
const esc = (s) => s.replace(/'/g, "'\\''");
|
|
1400
|
+
const subcases = topLevelNames.filter((cmd) => Object.keys(commands[cmd].subs).length > 0).map((cmd) => {
|
|
1401
|
+
const subs = Object.entries(commands[cmd].subs).map(([name, desc]) => `'${name}:${esc(desc)}'`).join(" ");
|
|
1402
|
+
return ` ${cmd})
|
|
1403
|
+
local -a subcmds
|
|
1404
|
+
subcmds=(${subs})
|
|
1405
|
+
_describe 'sous-commande' subcmds
|
|
1406
|
+
;;`;
|
|
1407
|
+
}).join("\n");
|
|
1408
|
+
return `#compdef tiime
|
|
1409
|
+
|
|
1410
|
+
_tiime() {
|
|
1411
|
+
local -a top_commands
|
|
1412
|
+
top_commands=(
|
|
1413
|
+
${topLevelNames.map((c) => ` '${c}:${esc(commands[c].description)}'`).join("\n")}
|
|
1414
|
+
)
|
|
1415
|
+
|
|
1416
|
+
if (( CURRENT == 2 )); then
|
|
1417
|
+
_describe 'commande' top_commands
|
|
1418
|
+
return
|
|
1419
|
+
fi
|
|
1420
|
+
|
|
1421
|
+
local cmd="\${words[2]}"
|
|
1422
|
+
|
|
1423
|
+
if (( CURRENT == 3 )); then
|
|
1424
|
+
case "$cmd" in
|
|
1425
|
+
${subcases}
|
|
1426
|
+
esac
|
|
1427
|
+
return
|
|
1428
|
+
fi
|
|
1429
|
+
|
|
1430
|
+
# Position 4+ : proposer les flags courants
|
|
1431
|
+
_arguments \\
|
|
1432
|
+
'--format[Format de sortie (json, table, csv)]' \\
|
|
1433
|
+
'--id[Identifiant de la ressource]' \\
|
|
1434
|
+
'--all[R\xE9cup\xE9rer tous les r\xE9sultats]'
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
compdef _tiime tiime
|
|
1438
|
+
`;
|
|
1439
|
+
};
|
|
1440
|
+
var generateBash = () => {
|
|
1441
|
+
const subcases = topLevelNames.filter((cmd) => Object.keys(commands[cmd].subs).length > 0).map((cmd) => {
|
|
1442
|
+
const subs = Object.keys(commands[cmd].subs).join(" ");
|
|
1443
|
+
return ` ${cmd})
|
|
1444
|
+
COMPREPLY=( $(compgen -W "${subs}" -- "$cur") )
|
|
1445
|
+
return
|
|
1446
|
+
;;`;
|
|
1447
|
+
}).join("\n");
|
|
1448
|
+
return `_tiime() {
|
|
1449
|
+
local cur prev cmd
|
|
1450
|
+
cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
1451
|
+
prev="\${COMP_WORDS[COMP_CWORD-1]}"
|
|
1452
|
+
|
|
1453
|
+
if [[ \${COMP_CWORD} -eq 1 ]]; then
|
|
1454
|
+
COMPREPLY=( $(compgen -W "${topLevelNames.join(" ")}" -- "$cur") )
|
|
1455
|
+
return
|
|
1456
|
+
fi
|
|
1457
|
+
|
|
1458
|
+
cmd="\${COMP_WORDS[1]}"
|
|
1459
|
+
|
|
1460
|
+
if [[ \${COMP_CWORD} -eq 2 ]]; then
|
|
1461
|
+
case "$cmd" in
|
|
1462
|
+
${subcases}
|
|
1463
|
+
esac
|
|
1464
|
+
return
|
|
1465
|
+
fi
|
|
1466
|
+
|
|
1467
|
+
# Position 3+ : proposer les flags courants
|
|
1468
|
+
COMPREPLY=( $(compgen -W "${commonFlags.join(" ")}" -- "$cur") )
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
complete -F _tiime tiime
|
|
1472
|
+
`;
|
|
1473
|
+
};
|
|
1474
|
+
var generateFish = () => {
|
|
1475
|
+
const esc = (s) => s.replace(/'/g, "\\'");
|
|
1476
|
+
const lines = [
|
|
1477
|
+
"# Disable file completions by default",
|
|
1478
|
+
"complete -c tiime -f",
|
|
1479
|
+
"",
|
|
1480
|
+
"# Top-level commands",
|
|
1481
|
+
...topLevelNames.map(
|
|
1482
|
+
(cmd) => `complete -c tiime -n '__fish_use_subcommand' -a '${cmd}' -d '${esc(commands[cmd].description)}'`
|
|
1483
|
+
),
|
|
1484
|
+
"",
|
|
1485
|
+
"# Subcommands"
|
|
1486
|
+
];
|
|
1487
|
+
for (const cmd of topLevelNames) {
|
|
1488
|
+
const subs = commands[cmd].subs;
|
|
1489
|
+
const subNames = Object.keys(subs);
|
|
1490
|
+
if (subNames.length > 0) {
|
|
1491
|
+
const condition = `__fish_seen_subcommand_from ${cmd}; and not __fish_seen_subcommand_from ${subNames.join(" ")}`;
|
|
1492
|
+
for (const [sub, desc] of Object.entries(subs)) {
|
|
1493
|
+
lines.push(
|
|
1494
|
+
`complete -c tiime -n '${condition}' -a '${sub}' -d '${esc(desc)}'`
|
|
1495
|
+
);
|
|
1496
|
+
}
|
|
1497
|
+
lines.push("");
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
lines.push("# Common flags");
|
|
1501
|
+
for (const cmd of topLevelNames) {
|
|
1502
|
+
const subNames = Object.keys(commands[cmd].subs);
|
|
1503
|
+
if (subNames.length > 0) {
|
|
1504
|
+
const condition = `__fish_seen_subcommand_from ${subNames.join(" ")}`;
|
|
1505
|
+
lines.push(
|
|
1506
|
+
`complete -c tiime -n '${condition}' -l format -d 'Format de sortie'`
|
|
1507
|
+
);
|
|
1508
|
+
lines.push(
|
|
1509
|
+
`complete -c tiime -n '${condition}' -l id -d 'Identifiant de la ressource'`
|
|
1510
|
+
);
|
|
1511
|
+
lines.push(
|
|
1512
|
+
`complete -c tiime -n '${condition}' -l all -d 'R\xE9cup\xE9rer tous les r\xE9sultats'`
|
|
1513
|
+
);
|
|
1514
|
+
}
|
|
1515
|
+
}
|
|
1516
|
+
return `${lines.join("\n")}
|
|
1517
|
+
`;
|
|
1518
|
+
};
|
|
1519
|
+
var completionCommand = defineCommand5({
|
|
1520
|
+
meta: {
|
|
1521
|
+
name: "completion",
|
|
1522
|
+
description: "G\xE9n\xE9rer le script d'autocompl\xE9tion shell"
|
|
1523
|
+
},
|
|
1524
|
+
args: {
|
|
1525
|
+
shell: {
|
|
1526
|
+
type: "string",
|
|
1527
|
+
description: "Shell cible (zsh, bash, fish)",
|
|
1528
|
+
default: "zsh"
|
|
1529
|
+
}
|
|
1530
|
+
},
|
|
1531
|
+
run({ args }) {
|
|
1532
|
+
const shell = args.shell;
|
|
1533
|
+
switch (shell) {
|
|
1534
|
+
case "zsh":
|
|
1535
|
+
process.stdout.write(generateZsh());
|
|
1536
|
+
break;
|
|
1537
|
+
case "bash":
|
|
1538
|
+
process.stdout.write(generateBash());
|
|
1539
|
+
break;
|
|
1540
|
+
case "fish":
|
|
1541
|
+
process.stdout.write(generateFish());
|
|
1542
|
+
break;
|
|
1543
|
+
default:
|
|
1544
|
+
process.stderr.write(
|
|
1545
|
+
`Shell non support\xE9 : ${shell}. Utilisez zsh, bash ou fish.
|
|
1546
|
+
`
|
|
1547
|
+
);
|
|
1548
|
+
process.exit(1);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
// src/cli/commands/documents.ts
|
|
1554
|
+
import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
1555
|
+
import { basename } from "path";
|
|
1556
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
1557
|
+
var documentsCommand = defineCommand6({
|
|
1558
|
+
meta: { name: "documents", description: "Gestion des documents" },
|
|
1559
|
+
subCommands: {
|
|
1560
|
+
list: defineCommand6({
|
|
1561
|
+
meta: { name: "list", description: "Lister les documents" },
|
|
1562
|
+
args: {
|
|
1563
|
+
...formatArg,
|
|
1564
|
+
type: {
|
|
1565
|
+
type: "string",
|
|
1566
|
+
description: "Type de document (ex: receipt)"
|
|
1567
|
+
},
|
|
1568
|
+
source: {
|
|
1569
|
+
type: "string",
|
|
1570
|
+
description: "Source du document (ex: accountant)"
|
|
1571
|
+
},
|
|
1572
|
+
page: { type: "string", description: "Num\xE9ro de page", default: "1" }
|
|
1573
|
+
},
|
|
1574
|
+
async run({ args }) {
|
|
1575
|
+
try {
|
|
1576
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1577
|
+
const docs = await client.documents.list({
|
|
1578
|
+
types: args.type,
|
|
1579
|
+
source: args.source,
|
|
1580
|
+
page: Number(args.page)
|
|
1581
|
+
});
|
|
1582
|
+
output(docs, { format: args.format });
|
|
1583
|
+
} catch (e) {
|
|
1584
|
+
outputError(e);
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}),
|
|
1588
|
+
upload: defineCommand6({
|
|
1589
|
+
meta: { name: "upload", description: "Uploader un justificatif" },
|
|
1590
|
+
args: {
|
|
1591
|
+
file: {
|
|
1592
|
+
type: "string",
|
|
1593
|
+
description: "Chemin du fichier \xE0 uploader",
|
|
1594
|
+
required: true
|
|
1595
|
+
},
|
|
1596
|
+
type: {
|
|
1597
|
+
type: "string",
|
|
1598
|
+
description: "Type de document"
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
async run({ args }) {
|
|
1602
|
+
try {
|
|
1603
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1604
|
+
const fileBuffer = readFileSync3(args.file);
|
|
1605
|
+
const filename = basename(args.file);
|
|
1606
|
+
const result = await client.documents.upload(
|
|
1607
|
+
fileBuffer,
|
|
1608
|
+
filename,
|
|
1609
|
+
args.type
|
|
1610
|
+
);
|
|
1611
|
+
output(result);
|
|
1612
|
+
} catch (e) {
|
|
1613
|
+
outputError(e);
|
|
1614
|
+
}
|
|
1615
|
+
}
|
|
1616
|
+
}),
|
|
1617
|
+
download: defineCommand6({
|
|
1618
|
+
meta: { name: "download", description: "T\xE9l\xE9charger un document" },
|
|
1619
|
+
args: {
|
|
1620
|
+
id: {
|
|
1621
|
+
type: "string",
|
|
1622
|
+
description: "ID du document",
|
|
1623
|
+
required: true
|
|
1624
|
+
},
|
|
1625
|
+
output: {
|
|
1626
|
+
type: "string",
|
|
1627
|
+
description: "Chemin de sortie du fichier"
|
|
1628
|
+
}
|
|
1629
|
+
},
|
|
1630
|
+
async run({ args }) {
|
|
1631
|
+
try {
|
|
1632
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1633
|
+
const documentId = Number(args.id);
|
|
1634
|
+
const data = await client.documents.download(documentId);
|
|
1635
|
+
const outputPath = args.output ?? `document-${documentId}`;
|
|
1636
|
+
writeFileSync3(outputPath, Buffer.from(data));
|
|
1637
|
+
output({ status: "downloaded", path: outputPath });
|
|
1638
|
+
} catch (e) {
|
|
1639
|
+
outputError(e);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
}),
|
|
1643
|
+
categories: defineCommand6({
|
|
1644
|
+
meta: {
|
|
1645
|
+
name: "categories",
|
|
1646
|
+
description: "Lister les cat\xE9gories de documents"
|
|
1647
|
+
},
|
|
1648
|
+
args: { ...formatArg },
|
|
1649
|
+
async run({ args }) {
|
|
1650
|
+
try {
|
|
1651
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1652
|
+
const categories = await client.documents.categories();
|
|
1653
|
+
output(categories, { format: args.format });
|
|
1654
|
+
} catch (e) {
|
|
1655
|
+
outputError(e);
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
})
|
|
1659
|
+
}
|
|
1660
|
+
});
|
|
1661
|
+
|
|
1662
|
+
// src/cli/commands/expenses.ts
|
|
1663
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
1664
|
+
var expensesCommand = defineCommand7({
|
|
1665
|
+
meta: { name: "expenses", description: "Gestion des notes de frais" },
|
|
1666
|
+
subCommands: {
|
|
1667
|
+
list: defineCommand7({
|
|
1668
|
+
meta: { name: "list", description: "Lister les notes de frais" },
|
|
1669
|
+
args: {
|
|
1670
|
+
...formatArg,
|
|
1671
|
+
sort: {
|
|
1672
|
+
type: "string",
|
|
1673
|
+
description: "Tri champ:direction",
|
|
1674
|
+
default: "metadata.date:desc"
|
|
1675
|
+
}
|
|
1676
|
+
},
|
|
1677
|
+
async run({ args }) {
|
|
1678
|
+
try {
|
|
1679
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1680
|
+
const expenses = await client.expenseReports.list(args.sort);
|
|
1681
|
+
output(expenses, { format: args.format });
|
|
1682
|
+
} catch (e) {
|
|
1683
|
+
outputError(e);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
}),
|
|
1687
|
+
get: defineCommand7({
|
|
1688
|
+
meta: { name: "get", description: "D\xE9tails d'une note de frais" },
|
|
1689
|
+
args: {
|
|
1690
|
+
id: {
|
|
1691
|
+
type: "string",
|
|
1692
|
+
description: "ID de la note de frais",
|
|
1693
|
+
required: true
|
|
1694
|
+
}
|
|
1695
|
+
},
|
|
1696
|
+
async run({ args }) {
|
|
1697
|
+
try {
|
|
1698
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1699
|
+
const expense = await client.expenseReports.get(Number(args.id));
|
|
1700
|
+
output(expense);
|
|
1701
|
+
} catch (e) {
|
|
1702
|
+
outputError(e);
|
|
1703
|
+
}
|
|
1704
|
+
}
|
|
1705
|
+
}),
|
|
1706
|
+
create: defineCommand7({
|
|
1707
|
+
meta: { name: "create", description: "Cr\xE9er une note de frais" },
|
|
1708
|
+
args: {
|
|
1709
|
+
name: {
|
|
1710
|
+
type: "string",
|
|
1711
|
+
description: "Nom de la note de frais",
|
|
1712
|
+
required: true
|
|
1713
|
+
},
|
|
1714
|
+
date: {
|
|
1715
|
+
type: "string",
|
|
1716
|
+
description: "Date (YYYY-MM-DD)"
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1719
|
+
async run({ args }) {
|
|
1720
|
+
try {
|
|
1721
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1722
|
+
const expense = await client.expenseReports.create({
|
|
1723
|
+
name: args.name,
|
|
1724
|
+
metadata: args.date ? { date: args.date } : void 0
|
|
1725
|
+
});
|
|
1726
|
+
output(expense);
|
|
1727
|
+
} catch (e) {
|
|
1728
|
+
outputError(e);
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
})
|
|
1732
|
+
}
|
|
1733
|
+
});
|
|
1734
|
+
|
|
1735
|
+
// src/cli/commands/invoices.ts
|
|
1736
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
1737
|
+
import { defineCommand as defineCommand8 } from "citty";
|
|
1738
|
+
var invoicesCommand = defineCommand8({
|
|
1739
|
+
meta: { name: "invoices", description: "Gestion des factures" },
|
|
1740
|
+
subCommands: {
|
|
1741
|
+
list: defineCommand8({
|
|
1742
|
+
meta: { name: "list", description: "Lister les factures" },
|
|
1743
|
+
args: {
|
|
1744
|
+
...formatArg,
|
|
1745
|
+
sort: {
|
|
1746
|
+
type: "string",
|
|
1747
|
+
description: "Tri champ:direction (ex: invoice_number:desc)",
|
|
1748
|
+
default: "invoice_number:desc"
|
|
1749
|
+
},
|
|
1750
|
+
status: {
|
|
1751
|
+
type: "string",
|
|
1752
|
+
description: "Filtrer par statut (draft, saved, sent, paid)"
|
|
1753
|
+
},
|
|
1754
|
+
page: { type: "string", description: "Num\xE9ro de page", default: "1" },
|
|
1755
|
+
"page-size": {
|
|
1756
|
+
type: "string",
|
|
1757
|
+
description: "\xC9l\xE9ments par page",
|
|
1758
|
+
default: "25"
|
|
1759
|
+
},
|
|
1760
|
+
all: {
|
|
1761
|
+
type: "boolean",
|
|
1762
|
+
description: "R\xE9cup\xE9rer toutes les pages",
|
|
1763
|
+
default: false
|
|
1764
|
+
}
|
|
1765
|
+
},
|
|
1766
|
+
async run({ args }) {
|
|
1767
|
+
try {
|
|
1768
|
+
const fmt = { format: args.format };
|
|
1769
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1770
|
+
if (args.all) {
|
|
1771
|
+
const invoices = await client.invoices.listAll({
|
|
1772
|
+
sorts: args.sort,
|
|
1773
|
+
status: args.status
|
|
1774
|
+
});
|
|
1775
|
+
output(invoices, fmt);
|
|
1776
|
+
} else {
|
|
1777
|
+
const invoices = await client.invoices.list({
|
|
1778
|
+
sorts: args.sort,
|
|
1779
|
+
status: args.status,
|
|
1780
|
+
page: Number(args.page),
|
|
1781
|
+
pageSize: Number(args["page-size"])
|
|
1782
|
+
});
|
|
1783
|
+
output(invoices, fmt);
|
|
1784
|
+
}
|
|
1785
|
+
} catch (e) {
|
|
1786
|
+
outputError(e);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}),
|
|
1790
|
+
get: defineCommand8({
|
|
1791
|
+
meta: { name: "get", description: "D\xE9tails d'une facture" },
|
|
1792
|
+
args: {
|
|
1793
|
+
id: { type: "string", description: "ID de la facture", required: true }
|
|
1794
|
+
},
|
|
1795
|
+
async run({ args }) {
|
|
1796
|
+
try {
|
|
1797
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1798
|
+
const invoice = await client.invoices.get(Number(args.id));
|
|
1799
|
+
output(invoice);
|
|
1800
|
+
} catch (e) {
|
|
1801
|
+
outputError(e);
|
|
1802
|
+
}
|
|
1803
|
+
}
|
|
1804
|
+
}),
|
|
1805
|
+
create: defineCommand8({
|
|
1806
|
+
meta: {
|
|
1807
|
+
name: "create",
|
|
1808
|
+
description: "Cr\xE9er une facture (brouillon par d\xE9faut)"
|
|
1809
|
+
},
|
|
1810
|
+
args: {
|
|
1811
|
+
"client-id": {
|
|
1812
|
+
type: "string",
|
|
1813
|
+
description: "ID du client"
|
|
1814
|
+
},
|
|
1815
|
+
"client-name": {
|
|
1816
|
+
type: "string",
|
|
1817
|
+
description: "Nom du client (si pas de client-id)"
|
|
1818
|
+
},
|
|
1819
|
+
date: {
|
|
1820
|
+
type: "string",
|
|
1821
|
+
description: "Date d'\xE9mission (YYYY-MM-DD, d\xE9faut : aujourd'hui)"
|
|
1822
|
+
},
|
|
1823
|
+
title: {
|
|
1824
|
+
type: "string",
|
|
1825
|
+
description: "Titre de la facture"
|
|
1826
|
+
},
|
|
1827
|
+
description: {
|
|
1828
|
+
type: "string",
|
|
1829
|
+
description: "Description de la ligne (ligne simple)"
|
|
1830
|
+
},
|
|
1831
|
+
quantity: {
|
|
1832
|
+
type: "string",
|
|
1833
|
+
description: "Quantit\xE9 (ligne simple)",
|
|
1834
|
+
default: "1"
|
|
1835
|
+
},
|
|
1836
|
+
"unit-price": {
|
|
1837
|
+
type: "string",
|
|
1838
|
+
description: "Prix unitaire HT (ligne simple)"
|
|
1839
|
+
},
|
|
1840
|
+
unit: {
|
|
1841
|
+
type: "string",
|
|
1842
|
+
description: "Code unit\xE9 (day, hour, unit, etc.)"
|
|
1843
|
+
},
|
|
1844
|
+
vat: {
|
|
1845
|
+
type: "string",
|
|
1846
|
+
description: "Code TVA (normal=20%, reduced=10%, super_reduced=5.5%, none=0%)",
|
|
1847
|
+
default: "normal"
|
|
1848
|
+
},
|
|
1849
|
+
lines: {
|
|
1850
|
+
type: "string",
|
|
1851
|
+
description: `Multi-lignes en JSON : '[{"description":"Dev","quantity":20,"unit_price":540,"unit":"day"}]'`
|
|
1852
|
+
},
|
|
1853
|
+
"free-field": {
|
|
1854
|
+
type: "string",
|
|
1855
|
+
description: "Champ libre (ex : r\xE9f\xE9rence contrat)"
|
|
1856
|
+
},
|
|
1857
|
+
status: {
|
|
1858
|
+
type: "string",
|
|
1859
|
+
description: "Statut : draft (d\xE9faut) ou saved (num\xE9rot\xE9e)",
|
|
1860
|
+
default: "draft"
|
|
1861
|
+
},
|
|
1862
|
+
"dry-run": {
|
|
1863
|
+
type: "boolean",
|
|
1864
|
+
description: "Pr\xE9visualiser le payload sans cr\xE9er la facture",
|
|
1865
|
+
default: false
|
|
1866
|
+
}
|
|
1867
|
+
},
|
|
1868
|
+
async run({ args }) {
|
|
1869
|
+
try {
|
|
1870
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
1871
|
+
const unitMap = {
|
|
1872
|
+
day: 3,
|
|
1873
|
+
hour: 2,
|
|
1874
|
+
unit: 1,
|
|
1875
|
+
package: 4,
|
|
1876
|
+
word: 5,
|
|
1877
|
+
character: 6,
|
|
1878
|
+
page: 7
|
|
1879
|
+
};
|
|
1880
|
+
let invoiceLines;
|
|
1881
|
+
if (args.lines) {
|
|
1882
|
+
const parsed = JSON.parse(args.lines);
|
|
1883
|
+
invoiceLines = parsed.map((l) => ({
|
|
1884
|
+
description: l.description,
|
|
1885
|
+
quantity: l.quantity,
|
|
1886
|
+
unit_amount: l.unit_price,
|
|
1887
|
+
vat_type: { code: l.vat ?? args.vat },
|
|
1888
|
+
invoicing_unit: l.unit ? { id: unitMap[l.unit] ?? 1, code: l.unit } : null
|
|
1889
|
+
}));
|
|
1890
|
+
} else {
|
|
1891
|
+
if (!args.description || !args["unit-price"]) {
|
|
1892
|
+
outputError(
|
|
1893
|
+
"--description et --unit-price sont requis pour une ligne simple (ou utilisez --lines pour du multi-lignes)"
|
|
1894
|
+
);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
invoiceLines = [
|
|
1898
|
+
{
|
|
1899
|
+
description: args.description,
|
|
1900
|
+
quantity: Number(args.quantity),
|
|
1901
|
+
unit_amount: Number(args["unit-price"]),
|
|
1902
|
+
vat_type: { code: args.vat },
|
|
1903
|
+
invoicing_unit: args.unit ? { id: unitMap[args.unit] ?? 1, code: args.unit } : null
|
|
1904
|
+
}
|
|
1905
|
+
];
|
|
1906
|
+
}
|
|
1907
|
+
const params = {
|
|
1908
|
+
emission_date: args.date ?? today,
|
|
1909
|
+
title: args.title ?? null,
|
|
1910
|
+
lines: invoiceLines,
|
|
1911
|
+
status: args.status
|
|
1912
|
+
};
|
|
1913
|
+
if (args["client-id"]) {
|
|
1914
|
+
params.client = { id: Number(args["client-id"]) };
|
|
1915
|
+
} else if (args["client-name"]) {
|
|
1916
|
+
params.client_name = args["client-name"];
|
|
1917
|
+
}
|
|
1918
|
+
if (args["free-field"]) {
|
|
1919
|
+
params.free_field = args["free-field"];
|
|
1920
|
+
params.free_field_enabled = true;
|
|
1921
|
+
}
|
|
1922
|
+
if (args["dry-run"]) {
|
|
1923
|
+
output({ dry_run: true, payload: params });
|
|
1924
|
+
return;
|
|
1925
|
+
}
|
|
1926
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1927
|
+
const invoice = await client.invoices.create(params);
|
|
1928
|
+
output(invoice);
|
|
1929
|
+
} catch (e) {
|
|
1930
|
+
outputError(e);
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
}),
|
|
1934
|
+
duplicate: defineCommand8({
|
|
1935
|
+
meta: {
|
|
1936
|
+
name: "duplicate",
|
|
1937
|
+
description: "Dupliquer une facture existante en brouillon"
|
|
1938
|
+
},
|
|
1939
|
+
args: {
|
|
1940
|
+
id: {
|
|
1941
|
+
type: "string",
|
|
1942
|
+
description: "ID de la facture source",
|
|
1943
|
+
required: true
|
|
1944
|
+
},
|
|
1945
|
+
date: {
|
|
1946
|
+
type: "string",
|
|
1947
|
+
description: "Date d'\xE9mission de la copie (YYYY-MM-DD, d\xE9faut : aujourd'hui)"
|
|
1948
|
+
},
|
|
1949
|
+
quantity: {
|
|
1950
|
+
type: "string",
|
|
1951
|
+
description: "Remplacer la quantit\xE9 pour toutes les lignes"
|
|
1952
|
+
}
|
|
1953
|
+
},
|
|
1954
|
+
async run({ args }) {
|
|
1955
|
+
try {
|
|
1956
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
1957
|
+
const invoice = await client.invoices.duplicate(Number(args.id), {
|
|
1958
|
+
emission_date: args.date,
|
|
1959
|
+
quantity: args.quantity ? Number(args.quantity) : void 0
|
|
1960
|
+
});
|
|
1961
|
+
output(invoice);
|
|
1962
|
+
} catch (e) {
|
|
1963
|
+
outputError(e);
|
|
1964
|
+
}
|
|
1965
|
+
}
|
|
1966
|
+
}),
|
|
1967
|
+
update: defineCommand8({
|
|
1968
|
+
meta: { name: "update", description: "Mettre \xE0 jour une facture" },
|
|
1969
|
+
args: {
|
|
1970
|
+
id: {
|
|
1971
|
+
type: "string",
|
|
1972
|
+
description: "ID de la facture",
|
|
1973
|
+
required: true
|
|
1974
|
+
},
|
|
1975
|
+
title: {
|
|
1976
|
+
type: "string",
|
|
1977
|
+
description: "Nouveau titre de la facture"
|
|
1978
|
+
},
|
|
1979
|
+
status: {
|
|
1980
|
+
type: "string",
|
|
1981
|
+
description: "Nouveau statut (draft, saved)"
|
|
1982
|
+
},
|
|
1983
|
+
date: {
|
|
1984
|
+
type: "string",
|
|
1985
|
+
description: "Nouvelle date d'\xE9mission (YYYY-MM-DD)"
|
|
1986
|
+
},
|
|
1987
|
+
"free-field": {
|
|
1988
|
+
type: "string",
|
|
1989
|
+
description: "Nouveau champ libre"
|
|
1990
|
+
}
|
|
1991
|
+
},
|
|
1992
|
+
async run({ args }) {
|
|
1993
|
+
try {
|
|
1994
|
+
const updates = {};
|
|
1995
|
+
if (args.title !== void 0) updates.title = args.title;
|
|
1996
|
+
if (args.status !== void 0) updates.status = args.status;
|
|
1997
|
+
if (args.date !== void 0) updates.emission_date = args.date;
|
|
1998
|
+
if (args["free-field"] !== void 0) {
|
|
1999
|
+
updates.free_field = args["free-field"];
|
|
2000
|
+
updates.free_field_enabled = true;
|
|
2001
|
+
}
|
|
2002
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2003
|
+
const invoice = await client.invoices.update(
|
|
2004
|
+
Number(args.id),
|
|
2005
|
+
updates
|
|
2006
|
+
);
|
|
2007
|
+
output(invoice);
|
|
2008
|
+
} catch (e) {
|
|
2009
|
+
outputError(e);
|
|
2010
|
+
}
|
|
2011
|
+
}
|
|
2012
|
+
}),
|
|
2013
|
+
send: defineCommand8({
|
|
2014
|
+
meta: { name: "send", description: "Envoyer une facture par email" },
|
|
2015
|
+
args: {
|
|
2016
|
+
id: {
|
|
2017
|
+
type: "string",
|
|
2018
|
+
description: "ID de la facture",
|
|
2019
|
+
required: true
|
|
2020
|
+
},
|
|
2021
|
+
email: {
|
|
2022
|
+
type: "string",
|
|
2023
|
+
description: "Adresse email du destinataire",
|
|
2024
|
+
required: true
|
|
2025
|
+
},
|
|
2026
|
+
subject: {
|
|
2027
|
+
type: "string",
|
|
2028
|
+
description: "Objet de l'email"
|
|
2029
|
+
},
|
|
2030
|
+
message: {
|
|
2031
|
+
type: "string",
|
|
2032
|
+
description: "Corps du message"
|
|
2033
|
+
}
|
|
2034
|
+
},
|
|
2035
|
+
async run({ args }) {
|
|
2036
|
+
try {
|
|
2037
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2038
|
+
await client.invoices.send(Number(args.id), {
|
|
2039
|
+
recipients: [{ email: args.email }],
|
|
2040
|
+
subject: args.subject,
|
|
2041
|
+
message: args.message
|
|
2042
|
+
});
|
|
2043
|
+
output({
|
|
2044
|
+
status: "sent",
|
|
2045
|
+
id: Number(args.id),
|
|
2046
|
+
email: args.email
|
|
2047
|
+
});
|
|
2048
|
+
} catch (e) {
|
|
2049
|
+
outputError(e);
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
}),
|
|
2053
|
+
pdf: defineCommand8({
|
|
2054
|
+
meta: {
|
|
2055
|
+
name: "pdf",
|
|
2056
|
+
description: "T\xE9l\xE9charger le PDF d'une facture"
|
|
2057
|
+
},
|
|
2058
|
+
args: {
|
|
2059
|
+
id: {
|
|
2060
|
+
type: "string",
|
|
2061
|
+
description: "ID de la facture",
|
|
2062
|
+
required: true
|
|
2063
|
+
},
|
|
2064
|
+
output: {
|
|
2065
|
+
type: "string",
|
|
2066
|
+
description: "Chemin de sortie du fichier (d\xE9faut : facture-{id}.pdf)"
|
|
2067
|
+
}
|
|
2068
|
+
},
|
|
2069
|
+
async run({ args }) {
|
|
2070
|
+
try {
|
|
2071
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2072
|
+
const buffer = await client.invoices.downloadPdf(Number(args.id));
|
|
2073
|
+
const filePath = args.output ?? `facture-${args.id}.pdf`;
|
|
2074
|
+
writeFileSync4(filePath, Buffer.from(buffer));
|
|
2075
|
+
output({ status: "downloaded", path: filePath });
|
|
2076
|
+
} catch (e) {
|
|
2077
|
+
outputError(e);
|
|
2078
|
+
}
|
|
2079
|
+
}
|
|
2080
|
+
}),
|
|
2081
|
+
delete: defineCommand8({
|
|
2082
|
+
meta: { name: "delete", description: "Supprimer une facture brouillon" },
|
|
2083
|
+
args: {
|
|
2084
|
+
id: {
|
|
2085
|
+
type: "string",
|
|
2086
|
+
description: "ID de la facture \xE0 supprimer",
|
|
2087
|
+
required: true
|
|
2088
|
+
}
|
|
2089
|
+
},
|
|
2090
|
+
async run({ args }) {
|
|
2091
|
+
try {
|
|
2092
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2093
|
+
await client.invoices.delete(Number(args.id));
|
|
2094
|
+
output({ status: "deleted", id: Number(args.id) });
|
|
2095
|
+
} catch (e) {
|
|
2096
|
+
outputError(e);
|
|
2097
|
+
}
|
|
2098
|
+
}
|
|
2099
|
+
})
|
|
2100
|
+
}
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
// src/cli/commands/labels.ts
|
|
2104
|
+
import { defineCommand as defineCommand9 } from "citty";
|
|
2105
|
+
var labelsCommand = defineCommand9({
|
|
2106
|
+
meta: { name: "labels", description: "Gestion des labels et tags" },
|
|
2107
|
+
subCommands: {
|
|
2108
|
+
list: defineCommand9({
|
|
2109
|
+
meta: { name: "list", description: "Lister les labels personnalis\xE9s" },
|
|
2110
|
+
args: { ...formatArg },
|
|
2111
|
+
async run({ args }) {
|
|
2112
|
+
try {
|
|
2113
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2114
|
+
const labels = await client.labels.list();
|
|
2115
|
+
output(labels, { format: args.format });
|
|
2116
|
+
} catch (e) {
|
|
2117
|
+
outputError(e);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
}),
|
|
2121
|
+
standard: defineCommand9({
|
|
2122
|
+
meta: { name: "standard", description: "Lister les labels standards" },
|
|
2123
|
+
args: { ...formatArg },
|
|
2124
|
+
async run({ args }) {
|
|
2125
|
+
try {
|
|
2126
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2127
|
+
const labels = await client.labels.standard();
|
|
2128
|
+
output(labels, { format: args.format });
|
|
2129
|
+
} catch (e) {
|
|
2130
|
+
outputError(e);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
}),
|
|
2134
|
+
tags: defineCommand9({
|
|
2135
|
+
meta: { name: "tags", description: "Lister les tags" },
|
|
2136
|
+
args: { ...formatArg },
|
|
2137
|
+
async run({ args }) {
|
|
2138
|
+
try {
|
|
2139
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2140
|
+
const tags = await client.labels.tags();
|
|
2141
|
+
output(tags, { format: args.format });
|
|
2142
|
+
} catch (e) {
|
|
2143
|
+
outputError(e);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
})
|
|
2147
|
+
}
|
|
2148
|
+
});
|
|
2149
|
+
|
|
2150
|
+
// src/cli/commands/open.ts
|
|
2151
|
+
import { exec } from "child_process";
|
|
2152
|
+
import { defineCommand as defineCommand10 } from "citty";
|
|
2153
|
+
var APP_BASE_URL = "https://apps.tiime.fr";
|
|
2154
|
+
var sections = {
|
|
2155
|
+
invoices: "/invoicing/invoices",
|
|
2156
|
+
quotations: "/invoicing/quotations",
|
|
2157
|
+
clients: "/invoicing/clients",
|
|
2158
|
+
bank: "/bank",
|
|
2159
|
+
documents: "/documents",
|
|
2160
|
+
expenses: "/expense-reports"
|
|
2161
|
+
};
|
|
2162
|
+
var buildUrl = (section) => {
|
|
2163
|
+
if (!section) {
|
|
2164
|
+
return APP_BASE_URL;
|
|
2165
|
+
}
|
|
2166
|
+
const path = sections[section];
|
|
2167
|
+
if (!path) {
|
|
2168
|
+
throw new Error(
|
|
2169
|
+
`Section inconnue : ${section}. Sections disponibles : ${Object.keys(sections).join(", ")}`
|
|
2170
|
+
);
|
|
2171
|
+
}
|
|
2172
|
+
const companyId = getCompanyId();
|
|
2173
|
+
return `${APP_BASE_URL}/companies/${companyId}${path}`;
|
|
2174
|
+
};
|
|
2175
|
+
var openCommand = defineCommand10({
|
|
2176
|
+
meta: {
|
|
2177
|
+
name: "open",
|
|
2178
|
+
description: "Ouvrir Tiime dans le navigateur"
|
|
2179
|
+
},
|
|
2180
|
+
args: {
|
|
2181
|
+
section: {
|
|
2182
|
+
type: "positional",
|
|
2183
|
+
description: `Section \xE0 ouvrir (${Object.keys(sections).join(", ")})`,
|
|
2184
|
+
required: false
|
|
2185
|
+
}
|
|
2186
|
+
},
|
|
2187
|
+
run({ args }) {
|
|
2188
|
+
try {
|
|
2189
|
+
const url = buildUrl(args.section);
|
|
2190
|
+
exec(`open "${url}"`);
|
|
2191
|
+
output({ opened: url });
|
|
2192
|
+
} catch (e) {
|
|
2193
|
+
outputError(e);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
// src/cli/commands/quotations.ts
|
|
2199
|
+
import { writeFileSync as writeFileSync5 } from "fs";
|
|
2200
|
+
import { defineCommand as defineCommand11 } from "citty";
|
|
2201
|
+
var quotationsCommand = defineCommand11({
|
|
2202
|
+
meta: { name: "quotations", description: "Gestion des devis" },
|
|
2203
|
+
subCommands: {
|
|
2204
|
+
list: defineCommand11({
|
|
2205
|
+
meta: { name: "list", description: "Lister les devis" },
|
|
2206
|
+
args: { ...formatArg },
|
|
2207
|
+
async run({ args }) {
|
|
2208
|
+
try {
|
|
2209
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2210
|
+
const quotations = await client.quotations.list();
|
|
2211
|
+
output(quotations, { format: args.format });
|
|
2212
|
+
} catch (e) {
|
|
2213
|
+
outputError(e);
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}),
|
|
2217
|
+
get: defineCommand11({
|
|
2218
|
+
meta: { name: "get", description: "D\xE9tails d'un devis" },
|
|
2219
|
+
args: {
|
|
2220
|
+
id: { type: "string", description: "ID du devis", required: true }
|
|
2221
|
+
},
|
|
2222
|
+
async run({ args }) {
|
|
2223
|
+
try {
|
|
2224
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2225
|
+
const quotation = await client.quotations.get(Number(args.id));
|
|
2226
|
+
output(quotation);
|
|
2227
|
+
} catch (e) {
|
|
2228
|
+
outputError(e);
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
}),
|
|
2232
|
+
create: defineCommand11({
|
|
2233
|
+
meta: {
|
|
2234
|
+
name: "create",
|
|
2235
|
+
description: "Cr\xE9er un devis (brouillon par d\xE9faut)"
|
|
2236
|
+
},
|
|
2237
|
+
args: {
|
|
2238
|
+
"client-id": {
|
|
2239
|
+
type: "string",
|
|
2240
|
+
description: "ID du client"
|
|
2241
|
+
},
|
|
2242
|
+
date: {
|
|
2243
|
+
type: "string",
|
|
2244
|
+
description: "Date du devis (YYYY-MM-DD, d\xE9faut : aujourd'hui)"
|
|
2245
|
+
},
|
|
2246
|
+
title: {
|
|
2247
|
+
type: "string",
|
|
2248
|
+
description: "Titre du devis"
|
|
2249
|
+
},
|
|
2250
|
+
description: {
|
|
2251
|
+
type: "string",
|
|
2252
|
+
description: "Description de la ligne (ligne simple)"
|
|
2253
|
+
},
|
|
2254
|
+
quantity: {
|
|
2255
|
+
type: "string",
|
|
2256
|
+
description: "Quantit\xE9 (ligne simple)",
|
|
2257
|
+
default: "1"
|
|
2258
|
+
},
|
|
2259
|
+
"unit-price": {
|
|
2260
|
+
type: "string",
|
|
2261
|
+
description: "Prix unitaire HT (ligne simple)"
|
|
2262
|
+
},
|
|
2263
|
+
vat: {
|
|
2264
|
+
type: "string",
|
|
2265
|
+
description: "Code TVA (normal=20%, reduced=10%, super_reduced=5.5%, none=0%)",
|
|
2266
|
+
default: "normal"
|
|
2267
|
+
},
|
|
2268
|
+
status: {
|
|
2269
|
+
type: "string",
|
|
2270
|
+
description: "Statut : draft (d\xE9faut) ou saved",
|
|
2271
|
+
default: "draft"
|
|
2272
|
+
}
|
|
2273
|
+
},
|
|
2274
|
+
async run({ args }) {
|
|
2275
|
+
try {
|
|
2276
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
2277
|
+
if (!args.description || !args["unit-price"]) {
|
|
2278
|
+
outputError(
|
|
2279
|
+
"--description et --unit-price sont requis pour cr\xE9er un devis"
|
|
2280
|
+
);
|
|
2281
|
+
return;
|
|
2282
|
+
}
|
|
2283
|
+
const lines = [
|
|
2284
|
+
{
|
|
2285
|
+
description: args.description,
|
|
2286
|
+
quantity: Number(args.quantity),
|
|
2287
|
+
unit_amount: Number(args["unit-price"]),
|
|
2288
|
+
vat_type: { code: args.vat }
|
|
2289
|
+
}
|
|
2290
|
+
];
|
|
2291
|
+
const params = {
|
|
2292
|
+
date: args.date ?? today,
|
|
2293
|
+
title: args.title ?? null,
|
|
2294
|
+
lines,
|
|
2295
|
+
status: args.status
|
|
2296
|
+
};
|
|
2297
|
+
if (args["client-id"]) {
|
|
2298
|
+
params.client = { id: Number(args["client-id"]) };
|
|
2299
|
+
}
|
|
2300
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2301
|
+
const quotation = await client.quotations.create(params);
|
|
2302
|
+
output(quotation);
|
|
2303
|
+
} catch (e) {
|
|
2304
|
+
outputError(e);
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
}),
|
|
2308
|
+
pdf: defineCommand11({
|
|
2309
|
+
meta: {
|
|
2310
|
+
name: "pdf",
|
|
2311
|
+
description: "T\xE9l\xE9charger le PDF d'un devis"
|
|
2312
|
+
},
|
|
2313
|
+
args: {
|
|
2314
|
+
id: {
|
|
2315
|
+
type: "string",
|
|
2316
|
+
description: "ID du devis",
|
|
2317
|
+
required: true
|
|
2318
|
+
},
|
|
2319
|
+
output: {
|
|
2320
|
+
type: "string",
|
|
2321
|
+
description: "Chemin de sortie du fichier (d\xE9faut : devis-{id}.pdf)"
|
|
2322
|
+
}
|
|
2323
|
+
},
|
|
2324
|
+
async run({ args }) {
|
|
2325
|
+
try {
|
|
2326
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2327
|
+
const buffer = await client.quotations.downloadPdf(Number(args.id));
|
|
2328
|
+
const filePath = args.output ?? `devis-${args.id}.pdf`;
|
|
2329
|
+
writeFileSync5(filePath, Buffer.from(buffer));
|
|
2330
|
+
output({ status: "downloaded", path: filePath });
|
|
2331
|
+
} catch (e) {
|
|
2332
|
+
outputError(e);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
}),
|
|
2336
|
+
send: defineCommand11({
|
|
2337
|
+
meta: { name: "send", description: "Envoyer un devis par email" },
|
|
2338
|
+
args: {
|
|
2339
|
+
id: {
|
|
2340
|
+
type: "string",
|
|
2341
|
+
description: "ID du devis",
|
|
2342
|
+
required: true
|
|
2343
|
+
},
|
|
2344
|
+
email: {
|
|
2345
|
+
type: "string",
|
|
2346
|
+
description: "Adresse email du destinataire",
|
|
2347
|
+
required: true
|
|
2348
|
+
},
|
|
2349
|
+
subject: {
|
|
2350
|
+
type: "string",
|
|
2351
|
+
description: "Objet de l'email"
|
|
2352
|
+
},
|
|
2353
|
+
message: {
|
|
2354
|
+
type: "string",
|
|
2355
|
+
description: "Corps du message"
|
|
2356
|
+
}
|
|
2357
|
+
},
|
|
2358
|
+
async run({ args }) {
|
|
2359
|
+
try {
|
|
2360
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2361
|
+
await client.quotations.send(Number(args.id), {
|
|
2362
|
+
recipients: [{ email: args.email }],
|
|
2363
|
+
subject: args.subject,
|
|
2364
|
+
message: args.message
|
|
2365
|
+
});
|
|
2366
|
+
output({
|
|
2367
|
+
status: "sent",
|
|
2368
|
+
id: Number(args.id),
|
|
2369
|
+
email: args.email
|
|
2370
|
+
});
|
|
2371
|
+
} catch (e) {
|
|
2372
|
+
outputError(e);
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
})
|
|
2376
|
+
}
|
|
2377
|
+
});
|
|
2378
|
+
|
|
2379
|
+
// src/cli/commands/status.ts
|
|
2380
|
+
import { defineCommand as defineCommand12 } from "citty";
|
|
2381
|
+
var statusCommand = defineCommand12({
|
|
2382
|
+
meta: { name: "status", description: "R\xE9sum\xE9 rapide de la situation" },
|
|
2383
|
+
args: { ...formatArg },
|
|
2384
|
+
async run({ args }) {
|
|
2385
|
+
try {
|
|
2386
|
+
const client = new TiimeClient({ companyId: getCompanyId() });
|
|
2387
|
+
const [
|
|
2388
|
+
accounts,
|
|
2389
|
+
draftInvoices,
|
|
2390
|
+
unpaidInvoices,
|
|
2391
|
+
unimputed,
|
|
2392
|
+
clients,
|
|
2393
|
+
quotations
|
|
2394
|
+
] = await Promise.all([
|
|
2395
|
+
client.bankAccounts.list(true),
|
|
2396
|
+
client.invoices.list({ status: "draft" }),
|
|
2397
|
+
client.invoices.list({ status: "sent" }),
|
|
2398
|
+
client.bankTransactions.unimputed(),
|
|
2399
|
+
client.clients.list(),
|
|
2400
|
+
client.quotations.list()
|
|
2401
|
+
]);
|
|
2402
|
+
const pendingQuotations = quotations.filter(
|
|
2403
|
+
(q) => q.status !== "accepted" && q.status !== "declined"
|
|
2404
|
+
);
|
|
2405
|
+
const data = {
|
|
2406
|
+
company_id: client.companyId,
|
|
2407
|
+
bank_accounts: accounts.map((a) => ({
|
|
2408
|
+
name: a.name,
|
|
2409
|
+
balance: a.balance_amount,
|
|
2410
|
+
currency: a.balance_currency
|
|
2411
|
+
})),
|
|
2412
|
+
invoices: {
|
|
2413
|
+
drafts: draftInvoices.length,
|
|
2414
|
+
unpaid: unpaidInvoices.length
|
|
2415
|
+
},
|
|
2416
|
+
pending_quotations: pendingQuotations.length,
|
|
2417
|
+
total_clients: clients.length,
|
|
2418
|
+
unimputed_transactions: unimputed.length
|
|
2419
|
+
};
|
|
2420
|
+
const format = args.format;
|
|
2421
|
+
output(data, { format });
|
|
2422
|
+
if (format === "json") {
|
|
2423
|
+
outputColoredStatus(data);
|
|
2424
|
+
}
|
|
2425
|
+
} catch (e) {
|
|
2426
|
+
outputError(e);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
|
|
2431
|
+
// src/cli/commands/version.ts
|
|
2432
|
+
import { defineCommand as defineCommand13 } from "citty";
|
|
2433
|
+
var versionCommand = defineCommand13({
|
|
2434
|
+
meta: { name: "version", description: "Afficher la version" },
|
|
2435
|
+
run() {
|
|
2436
|
+
output({ version: "1.0.0", node: process.version });
|
|
2437
|
+
}
|
|
2438
|
+
});
|
|
2439
|
+
|
|
2440
|
+
// src/cli/i18n.ts
|
|
2441
|
+
var descriptionTranslations = {
|
|
2442
|
+
// Main
|
|
2443
|
+
"CLI pour la comptabilit\xE9 Tiime \u2014 sortie JSON pour agents IA": "CLI for Tiime accounting \u2014 JSON output for AI agents",
|
|
2444
|
+
// Top-level commands
|
|
2445
|
+
"Gestion de l'authentification": "Authentication management",
|
|
2446
|
+
"Gestion de l'entreprise": "Company management",
|
|
2447
|
+
"Gestion des factures": "Invoice management",
|
|
2448
|
+
"Gestion des clients": "Client management",
|
|
2449
|
+
"Comptes bancaires et transactions": "Bank accounts and transactions",
|
|
2450
|
+
"Gestion des devis": "Quotation management",
|
|
2451
|
+
"Gestion des notes de frais": "Expense report management",
|
|
2452
|
+
"Gestion des documents": "Document management",
|
|
2453
|
+
"Gestion des labels et tags": "Label and tag management",
|
|
2454
|
+
"R\xE9sum\xE9 rapide de la situation": "Quick status summary",
|
|
2455
|
+
"Ouvrir Tiime dans le navigateur": "Open Tiime in browser",
|
|
2456
|
+
"Afficher la version": "Show version",
|
|
2457
|
+
"G\xE9n\xE9rer le script d'autocompl\xE9tion shell": "Generate shell completion script",
|
|
2458
|
+
// Common subcommands
|
|
2459
|
+
"Se connecter \xE0 Tiime": "Log in to Tiime",
|
|
2460
|
+
"Se d\xE9connecter de Tiime": "Log out from Tiime",
|
|
2461
|
+
"Afficher le statut d'authentification": "Show authentication status",
|
|
2462
|
+
"Lister les entreprises": "List companies",
|
|
2463
|
+
"Lister toutes les entreprises": "List all companies",
|
|
2464
|
+
"D\xE9tails de l'entreprise active": "Active company details",
|
|
2465
|
+
"D\xE9finir l'entreprise active": "Set active company",
|
|
2466
|
+
"Info utilisateur": "User info",
|
|
2467
|
+
"Info utilisateur courant (inclut active_company)": "Current user info (includes active_company)",
|
|
2468
|
+
"Lister les factures": "List invoices",
|
|
2469
|
+
"D\xE9tails d'une facture": "Invoice details",
|
|
2470
|
+
"Cr\xE9er une facture (brouillon par d\xE9faut)": "Create an invoice (draft by default)",
|
|
2471
|
+
"Dupliquer une facture existante en brouillon": "Duplicate an invoice as draft",
|
|
2472
|
+
"Mettre \xE0 jour une facture": "Update an invoice",
|
|
2473
|
+
"Envoyer une facture par email": "Send an invoice by email",
|
|
2474
|
+
"T\xE9l\xE9charger le PDF d'une facture": "Download invoice PDF",
|
|
2475
|
+
"Supprimer une facture brouillon": "Delete a draft invoice",
|
|
2476
|
+
"Lister les clients": "List clients",
|
|
2477
|
+
"D\xE9tails d'un client": "Client details",
|
|
2478
|
+
"Cr\xE9er un client": "Create a client",
|
|
2479
|
+
"Rechercher un client": "Search for a client",
|
|
2480
|
+
"Afficher les soldes des comptes": "Show account balances",
|
|
2481
|
+
"Lister les comptes bancaires": "List bank accounts",
|
|
2482
|
+
"Lister les transactions bancaires": "List bank transactions",
|
|
2483
|
+
"Transactions non imput\xE9es": "Unmatched transactions",
|
|
2484
|
+
"Lister les devis": "List quotations",
|
|
2485
|
+
"D\xE9tails d'un devis": "Quotation details",
|
|
2486
|
+
"Cr\xE9er un devis": "Create a quotation",
|
|
2487
|
+
"T\xE9l\xE9charger le PDF d'un devis": "Download quotation PDF",
|
|
2488
|
+
"Envoyer un devis par email": "Send a quotation by email",
|
|
2489
|
+
"Lister les notes de frais": "List expense reports",
|
|
2490
|
+
"D\xE9tails d'une note de frais": "Expense report details",
|
|
2491
|
+
"Cr\xE9er une note de frais": "Create an expense report",
|
|
2492
|
+
"Lister les documents": "List documents",
|
|
2493
|
+
"Lister les cat\xE9gories de documents": "List document categories",
|
|
2494
|
+
"Uploader un justificatif": "Upload a receipt",
|
|
2495
|
+
"T\xE9l\xE9charger un document": "Download a document",
|
|
2496
|
+
"Labels personnalis\xE9s": "Custom labels",
|
|
2497
|
+
"Lister les labels personnalis\xE9s": "List custom labels",
|
|
2498
|
+
"Labels standards": "Standard labels",
|
|
2499
|
+
"Lister les labels standards": "List standard labels",
|
|
2500
|
+
Tags: "Tags",
|
|
2501
|
+
"Lister les tags": "List tags",
|
|
2502
|
+
// Arg descriptions
|
|
2503
|
+
"Format de sortie (json, table, csv)": "Output format (json, table, csv)",
|
|
2504
|
+
"ID de la facture": "Invoice ID",
|
|
2505
|
+
"ID du client": "Client ID",
|
|
2506
|
+
"ID du devis": "Quotation ID",
|
|
2507
|
+
"ID de la facture source": "Source invoice ID",
|
|
2508
|
+
"ID de la facture \xE0 supprimer": "Invoice ID to delete",
|
|
2509
|
+
"ID du document": "Document ID",
|
|
2510
|
+
"Nom du client": "Client name",
|
|
2511
|
+
"Nom du client (si pas de client-id)": "Client name (if no client-id)",
|
|
2512
|
+
"Date d'\xE9mission (YYYY-MM-DD, d\xE9faut : aujourd'hui)": "Issue date (YYYY-MM-DD, default: today)",
|
|
2513
|
+
"Titre de la facture": "Invoice title",
|
|
2514
|
+
"Description de la ligne (ligne simple)": "Line description (single line)",
|
|
2515
|
+
"Quantit\xE9 (ligne simple)": "Quantity (single line)",
|
|
2516
|
+
"Prix unitaire HT (ligne simple)": "Unit price excl. tax (single line)",
|
|
2517
|
+
"Code unit\xE9 (day, hour, unit, etc.)": "Unit code (day, hour, unit, etc.)",
|
|
2518
|
+
"Code TVA (normal=20%, reduced=10%, super_reduced=5.5%, none=0%)": "VAT code (normal=20%, reduced=10%, super_reduced=5.5%, none=0%)",
|
|
2519
|
+
'Multi-lignes en JSON : \'[{"description":"Dev","quantity":20,"unit_price":540,"unit":"day"}]\'': `Multi-line JSON: '[{"description":"Dev","quantity":20,"unit_price":540,"unit":"day"}]'`,
|
|
2520
|
+
"Champ libre (ex : r\xE9f\xE9rence contrat)": "Free field (e.g.: contract reference)",
|
|
2521
|
+
"Statut : draft (d\xE9faut) ou saved (num\xE9rot\xE9e)": "Status: draft (default) or saved (numbered)",
|
|
2522
|
+
"Pr\xE9visualiser le payload sans cr\xE9er la facture": "Preview payload without creating invoice",
|
|
2523
|
+
"Adresse email du destinataire": "Recipient email address",
|
|
2524
|
+
"Objet de l'email": "Email subject",
|
|
2525
|
+
"Corps du message": "Message body",
|
|
2526
|
+
"Chemin de sortie du fichier": "Output file path",
|
|
2527
|
+
"Adresse email": "Email address",
|
|
2528
|
+
"Mot de passe": "Password",
|
|
2529
|
+
"ID de l'entreprise": "Company ID",
|
|
2530
|
+
"Tri champ:direction (ex: invoice_number:desc)": "Sort field:direction (e.g.: invoice_number:desc)",
|
|
2531
|
+
"Filtrer par statut (draft, saved, sent, paid)": "Filter by status (draft, saved, sent, paid)",
|
|
2532
|
+
"Num\xE9ro de page": "Page number",
|
|
2533
|
+
"\xC9l\xE9ments par page": "Items per page",
|
|
2534
|
+
"R\xE9cup\xE9rer toutes les pages": "Fetch all pages",
|
|
2535
|
+
"Inclure les clients archiv\xE9s": "Include archived clients",
|
|
2536
|
+
"Adresse du client": "Client address",
|
|
2537
|
+
"Code postal": "Postal code",
|
|
2538
|
+
Ville: "City",
|
|
2539
|
+
"Num\xE9ro de t\xE9l\xE9phone": "Phone number",
|
|
2540
|
+
"SIREN ou SIRET": "SIREN or SIRET",
|
|
2541
|
+
"Client professionnel": "Professional client",
|
|
2542
|
+
"Terme de recherche": "Search term",
|
|
2543
|
+
"Uniquement les comptes actifs": "Active accounts only",
|
|
2544
|
+
"Filtrer par ID de compte bancaire": "Filter by bank account ID",
|
|
2545
|
+
"Masquer les transactions refus\xE9es": "Hide refused transactions",
|
|
2546
|
+
"Tri champ:direction (ex: date:desc)": "Sort field:direction (e.g.: date:desc)",
|
|
2547
|
+
"Date de d\xE9but (YYYY-MM-DD)": "Start date (YYYY-MM-DD)",
|
|
2548
|
+
"Date de fin (YYYY-MM-DD)": "End date (YYYY-MM-DD)",
|
|
2549
|
+
"Rechercher par libell\xE9": "Search by label",
|
|
2550
|
+
"Tri champ:direction": "Sort field:direction",
|
|
2551
|
+
"Type de document (ex: receipt)": "Document type (e.g.: receipt)",
|
|
2552
|
+
"Source du document (ex: accountant)": "Document source (e.g.: accountant)",
|
|
2553
|
+
"Chemin du fichier \xE0 uploader": "File path to upload",
|
|
2554
|
+
"Type de document": "Document type",
|
|
2555
|
+
"Shell cible (zsh, bash, fish)": "Target shell (zsh, bash, fish)",
|
|
2556
|
+
"Nouveau titre de la facture": "New invoice title",
|
|
2557
|
+
"Nouveau statut (draft, saved)": "New status (draft, saved)",
|
|
2558
|
+
"Nouvelle date d'\xE9mission (YYYY-MM-DD)": "New issue date (YYYY-MM-DD)",
|
|
2559
|
+
"Nouveau champ libre": "New free field",
|
|
2560
|
+
"Date d'\xE9mission de la copie (YYYY-MM-DD, d\xE9faut : aujourd'hui)": "Copy issue date (YYYY-MM-DD, default: today)",
|
|
2561
|
+
"Remplacer la quantit\xE9 pour toutes les lignes": "Replace quantity for all lines",
|
|
2562
|
+
"Section \xE0 ouvrir (invoices, quotations, clients, bank, documents, expenses)": "Section to open (invoices, quotations, clients, bank, documents, expenses)",
|
|
2563
|
+
"Nom de la note de frais": "Expense report name",
|
|
2564
|
+
"Date (YYYY-MM-DD)": "Date (YYYY-MM-DD)",
|
|
2565
|
+
"Titre du devis": "Quotation title"
|
|
2566
|
+
};
|
|
2567
|
+
var frameworkTranslations = {
|
|
2568
|
+
fr: {
|
|
2569
|
+
USAGE: "UTILISATION",
|
|
2570
|
+
COMMANDS: "COMMANDES",
|
|
2571
|
+
OPTIONS: "OPTIONS",
|
|
2572
|
+
ARGUMENTS: "ARGUMENTS",
|
|
2573
|
+
"(required)": "(requis)",
|
|
2574
|
+
"Use %cmd% for more information about a command.": "Utilisez %cmd% pour plus d'informations sur une commande."
|
|
2575
|
+
},
|
|
2576
|
+
en: {}
|
|
2577
|
+
};
|
|
2578
|
+
var getLang = () => {
|
|
2579
|
+
if (process.env.TIIME_LANG) return process.env.TIIME_LANG;
|
|
2580
|
+
const sysLang = process.env.LC_ALL || process.env.LC_MESSAGES || process.env.LANG || "";
|
|
2581
|
+
if (sysLang.startsWith("fr")) return "fr";
|
|
2582
|
+
if (sysLang.startsWith("en")) return "en";
|
|
2583
|
+
return "fr";
|
|
2584
|
+
};
|
|
2585
|
+
var translateHelp = (text2) => {
|
|
2586
|
+
const lang = getLang();
|
|
2587
|
+
let result = text2;
|
|
2588
|
+
if (lang === "fr") {
|
|
2589
|
+
const dict = frameworkTranslations.fr;
|
|
2590
|
+
for (const [en, fr] of Object.entries(dict)) {
|
|
2591
|
+
if (en.includes("%cmd%")) continue;
|
|
2592
|
+
result = result.replaceAll(en, fr);
|
|
2593
|
+
}
|
|
2594
|
+
const usePattern = /Use (.*) for more information about a command\./g;
|
|
2595
|
+
const useReplacement = dict["Use %cmd% for more information about a command."];
|
|
2596
|
+
if (useReplacement) {
|
|
2597
|
+
result = result.replace(
|
|
2598
|
+
usePattern,
|
|
2599
|
+
(_match, cmd) => useReplacement.replace("%cmd%", cmd)
|
|
2600
|
+
);
|
|
2601
|
+
}
|
|
2602
|
+
}
|
|
2603
|
+
if (lang === "en") {
|
|
2604
|
+
for (const [fr, en] of Object.entries(descriptionTranslations)) {
|
|
2605
|
+
result = result.replaceAll(fr, en);
|
|
2606
|
+
}
|
|
2607
|
+
}
|
|
2608
|
+
return result;
|
|
2609
|
+
};
|
|
2610
|
+
|
|
2611
|
+
// src/cli/index.ts
|
|
2612
|
+
var main = defineCommand14({
|
|
2613
|
+
meta: {
|
|
2614
|
+
name: "tiime",
|
|
2615
|
+
version: "1.0.0",
|
|
2616
|
+
description: "CLI pour la comptabilit\xE9 Tiime \u2014 sortie JSON pour agents IA"
|
|
2617
|
+
},
|
|
2618
|
+
subCommands: {
|
|
2619
|
+
auth: authCommand,
|
|
2620
|
+
company: companyCommand,
|
|
2621
|
+
invoices: invoicesCommand,
|
|
2622
|
+
clients: clientsCommand,
|
|
2623
|
+
bank: bankCommand,
|
|
2624
|
+
quotations: quotationsCommand,
|
|
2625
|
+
expenses: expensesCommand,
|
|
2626
|
+
documents: documentsCommand,
|
|
2627
|
+
labels: labelsCommand,
|
|
2628
|
+
status: statusCommand,
|
|
2629
|
+
open: openCommand,
|
|
2630
|
+
version: versionCommand,
|
|
2631
|
+
completion: completionCommand
|
|
2632
|
+
}
|
|
2633
|
+
});
|
|
2634
|
+
var showTranslatedUsage = async (cmd, parent) => {
|
|
2635
|
+
const usage = await renderUsage(cmd, parent);
|
|
2636
|
+
console.log(`${translateHelp(usage)}
|
|
2637
|
+
`);
|
|
2638
|
+
};
|
|
2639
|
+
runMain(main, { showUsage: showTranslatedUsage });
|