tiime-cli 1.1.0 → 1.2.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/dist/cli.js CHANGED
@@ -1,11 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli/index.ts
4
- import { defineCommand as defineCommand14, renderUsage, runMain } from "citty";
4
+ import { defineCommand as defineCommand15, renderUsage, runMain } from "citty";
5
5
 
6
- // src/cli/commands/auth.ts
7
- import * as p from "@clack/prompts";
6
+ // src/cli/commands/audit.ts
8
7
  import { defineCommand } from "citty";
8
+ import { consola as consola2 } from "consola";
9
+
10
+ // src/sdk/client.ts
11
+ import { ofetch as ofetch2 } from "ofetch";
9
12
 
10
13
  // src/sdk/auth.ts
11
14
  import { execSync } from "child_process";
@@ -166,10 +169,6 @@ var TokenManager = class {
166
169
  }
167
170
  };
168
171
 
169
- // src/cli/output.ts
170
- import Table from "cli-table3";
171
- import { consola } from "consola";
172
-
173
172
  // src/sdk/errors.ts
174
173
  var TiimeError = class extends Error {
175
174
  constructor(message, status, endpoint, details) {
@@ -190,239 +189,6 @@ var TiimeError = class extends Error {
190
189
  }
191
190
  };
192
191
 
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
192
  // src/sdk/resources/bank-accounts.ts
427
193
  var BankAccountsResource = class {
428
194
  constructor(fetch, companyId) {
@@ -492,6 +258,30 @@ var BankTransactionsResource = class {
492
258
  `/companies/${this.companyId}/bank_transactions/unimputed`
493
259
  );
494
260
  }
261
+ get(transactionId) {
262
+ return this.fetch(
263
+ `/companies/${this.companyId}/bank_transactions/${transactionId}`
264
+ );
265
+ }
266
+ labelSuggestions(transactionId) {
267
+ return this.fetch(
268
+ `/companies/${this.companyId}/bank_transactions/${transactionId}/label_suggestions`,
269
+ {
270
+ headers: {
271
+ Accept: "application/vnd.tiime.bank_transactions.label_suggestions.v2+json"
272
+ }
273
+ }
274
+ );
275
+ }
276
+ impute(transactionId, imputations) {
277
+ return this.fetch(
278
+ `/companies/${this.companyId}/bank_transactions/${transactionId}`,
279
+ {
280
+ method: "PATCH",
281
+ body: { imputations }
282
+ }
283
+ );
284
+ }
495
285
  };
496
286
 
497
287
  // src/sdk/resources/clients.ts
@@ -818,6 +608,14 @@ var QuotationsResource = class {
818
608
  );
819
609
  }
820
610
  create(params) {
611
+ for (const line of params.lines ?? []) {
612
+ line.line_amount = line.quantity * line.unit_amount;
613
+ line.sequence ??= 1;
614
+ line.invoicing_category_type ??= "benefit";
615
+ line.discount_description ??= "";
616
+ line.discount_amount ??= null;
617
+ line.discount_percentage ??= null;
618
+ }
821
619
  return this.fetch(`/companies/${this.companyId}/quotations`, {
822
620
  method: "POST",
823
621
  body: params
@@ -931,42 +729,594 @@ var TiimeClient = class {
931
729
  }
932
730
  };
933
731
 
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"));
732
+ // src/cli/audit.ts
733
+ var log = (...args) => console.error(...args);
734
+ var batchAsync = async (items, batchSize, fn) => {
735
+ const results = [];
736
+ for (let i = 0; i < items.length; i += batchSize) {
737
+ const batch = items.slice(i, i + batchSize);
738
+ const batchResults = await Promise.all(batch.map(fn));
739
+ results.push(...batchResults);
740
+ }
741
+ return results;
742
+ };
743
+ async function auditForCompany(client, companyId, companyName, options) {
744
+ const unimputedFindings = [];
745
+ const appliedImputations = [];
746
+ log(`[${companyName}] R\xE9cup\xE9ration des transactions non imput\xE9es...`);
747
+ const unimputedTxs = await client.bankTransactions.unimputed();
748
+ log(
749
+ `[${companyName}] ${unimputedTxs.length} non imput\xE9es, r\xE9cup\xE9ration des suggestions...`
750
+ );
751
+ const suggestions = await batchAsync(unimputedTxs, 5, async (tx) => {
752
+ try {
753
+ const fullTx = tx.wording !== void 0 ? tx : await client.bankTransactions.get(tx.id);
754
+ const sug = await client.bankTransactions.labelSuggestions(tx.id);
755
+ return { tx: fullTx, suggestion: sug[0] ?? null };
756
+ } catch (e) {
757
+ log(
758
+ `[${companyName}] Erreur pour tx #${tx.id}: ${e instanceof Error ? e.message : e}`
759
+ );
760
+ return { tx, suggestion: null };
761
+ }
762
+ });
763
+ for (const { tx, suggestion } of suggestions) {
764
+ unimputedFindings.push({
765
+ transaction_id: tx.id,
766
+ wording: tx.wording,
767
+ amount: tx.amount,
768
+ currency: tx.currency,
769
+ transaction_date: tx.transaction_date,
770
+ suggested_label_id: suggestion?.id ?? null,
771
+ suggested_label_name: suggestion ? suggestion.name ?? suggestion.label : null
772
+ });
773
+ if (options.apply && suggestion) {
774
+ const imputationLabel = {
775
+ ...suggestion,
776
+ disabled: false
777
+ };
778
+ const imputationParams = [
779
+ {
780
+ label: imputationLabel,
781
+ amount: tx.amount,
782
+ documents: [],
783
+ accountant_detail_requests: []
784
+ }
785
+ ];
786
+ try {
787
+ await client.bankTransactions.impute(tx.id, imputationParams);
788
+ appliedImputations.push({
789
+ transaction_id: tx.id,
790
+ wording: tx.wording,
791
+ amount: tx.amount,
792
+ label_name: suggestion.name ?? suggestion.label,
793
+ status: "applied"
794
+ });
795
+ } catch {
796
+ appliedImputations.push({
797
+ transaction_id: tx.id,
798
+ wording: tx.wording,
799
+ amount: tx.amount,
800
+ label_name: suggestion.name ?? suggestion.label,
801
+ status: "error"
802
+ });
803
+ }
804
+ }
805
+ }
806
+ let from;
807
+ let to;
808
+ try {
809
+ const period = await client.company.accountingPeriod();
810
+ from = period.start_date;
811
+ to = period.end_date;
812
+ } catch {
813
+ const now = /* @__PURE__ */ new Date();
814
+ from = `${now.getFullYear()}-01-01`;
815
+ to = now.toISOString().slice(0, 10);
816
+ }
817
+ const missingDocuments = [];
818
+ const appliedIds = new Set(
819
+ appliedImputations.filter((a) => a.status === "applied").map((a) => a.transaction_id)
820
+ );
821
+ log(
822
+ `[${companyName}] V\xE9rification des documents manquants (${from} \u2192 ${to})...`
823
+ );
824
+ const allTransactions = await client.bankTransactions.listAll({ from, to });
825
+ for (const tx of allTransactions) {
826
+ if (tx.imputations.length === 0) continue;
827
+ if (appliedIds.has(tx.id)) continue;
828
+ const allEmpty = tx.imputations.every(
829
+ (imp) => imp.count_documents === 0 && imp.count_invoices === 0
830
+ );
831
+ if (allEmpty) {
832
+ missingDocuments.push({
833
+ transaction_id: tx.id,
834
+ wording: tx.wording,
835
+ amount: tx.amount,
836
+ currency: tx.currency,
837
+ transaction_date: tx.transaction_date,
838
+ operation_type: tx.operation_type,
839
+ label_name: tx.imputations[0].label.name ?? tx.imputations[0].label.label
840
+ });
841
+ }
842
+ }
843
+ const withSuggestions = unimputedFindings.filter(
844
+ (f) => f.suggested_label_id !== null
845
+ ).length;
846
+ return {
847
+ company_id: companyId,
848
+ company_name: companyName,
849
+ error: null,
850
+ unimputed_transactions: unimputedFindings,
851
+ missing_documents: missingDocuments,
852
+ applied_imputations: appliedImputations,
853
+ summary: {
854
+ total_unimputed: unimputedFindings.length,
855
+ total_unimputed_amount: unimputedFindings.reduce(
856
+ (sum, f) => sum + Math.abs(f.amount),
857
+ 0
858
+ ),
859
+ with_suggestions: withSuggestions,
860
+ without_suggestions: unimputedFindings.length - withSuggestions,
861
+ total_missing_documents: missingDocuments.length,
862
+ total_missing_documents_amount: missingDocuments.reduce(
863
+ (sum, f) => sum + Math.abs(f.amount),
864
+ 0
865
+ ),
866
+ applied_count: appliedImputations.filter((a) => a.status === "applied").length
867
+ }
868
+ };
869
+ }
870
+ function emptyReport(companyId, companyName, error) {
871
+ return {
872
+ company_id: companyId,
873
+ company_name: companyName,
874
+ error,
875
+ unimputed_transactions: [],
876
+ missing_documents: [],
877
+ applied_imputations: [],
878
+ summary: {
879
+ total_unimputed: 0,
880
+ total_unimputed_amount: 0,
881
+ with_suggestions: 0,
882
+ without_suggestions: 0,
883
+ total_missing_documents: 0,
884
+ total_missing_documents_amount: 0,
885
+ applied_count: 0
886
+ }
887
+ };
888
+ }
889
+
890
+ // src/cli/auto-impute.ts
891
+ async function autoImputeForCompany(client, companyId, companyName, options) {
892
+ const proposals = [];
893
+ const transactions = await client.bankTransactions.unimputed();
894
+ for (const tx of transactions) {
895
+ const suggestions = await client.bankTransactions.labelSuggestions(tx.id);
896
+ const firstSuggestion = suggestions[0];
897
+ if (!firstSuggestion) {
898
+ proposals.push({
899
+ company_id: companyId,
900
+ company_name: companyName,
901
+ transaction_id: tx.id,
902
+ wording: tx.wording,
903
+ amount: tx.amount,
904
+ currency: tx.currency,
905
+ suggested_label_id: 0,
906
+ suggested_label_name: "(aucune suggestion)",
907
+ status: "skipped"
908
+ });
909
+ continue;
910
+ }
911
+ if (!options.apply) {
912
+ proposals.push({
913
+ company_id: companyId,
914
+ company_name: companyName,
915
+ transaction_id: tx.id,
916
+ wording: tx.wording,
917
+ amount: tx.amount,
918
+ currency: tx.currency,
919
+ suggested_label_id: firstSuggestion.id,
920
+ suggested_label_name: firstSuggestion.name ?? firstSuggestion.label,
921
+ status: "proposed"
922
+ });
923
+ } else {
924
+ const imputationLabel = {
925
+ ...firstSuggestion,
926
+ disabled: false
927
+ };
928
+ const imputationParams = [
929
+ {
930
+ label: imputationLabel,
931
+ amount: tx.amount,
932
+ documents: [],
933
+ accountant_detail_requests: []
934
+ }
935
+ ];
936
+ try {
937
+ await client.bankTransactions.impute(tx.id, imputationParams);
938
+ proposals.push({
939
+ company_id: companyId,
940
+ company_name: companyName,
941
+ transaction_id: tx.id,
942
+ wording: tx.wording,
943
+ amount: tx.amount,
944
+ currency: tx.currency,
945
+ suggested_label_id: firstSuggestion.id,
946
+ suggested_label_name: firstSuggestion.name ?? firstSuggestion.label,
947
+ status: "applied"
948
+ });
949
+ } catch {
950
+ proposals.push({
951
+ company_id: companyId,
952
+ company_name: companyName,
953
+ transaction_id: tx.id,
954
+ wording: tx.wording,
955
+ amount: tx.amount,
956
+ currency: tx.currency,
957
+ suggested_label_id: firstSuggestion.id,
958
+ suggested_label_name: firstSuggestion.name ?? firstSuggestion.label,
959
+ status: "error"
960
+ });
961
+ }
962
+ }
963
+ }
964
+ return proposals;
965
+ }
966
+ function resolveCompanyIds(parts, companies) {
967
+ return parts.map((p2) => {
968
+ if (/^\d+$/.test(p2)) return Number(p2);
969
+ const match = companies.find(
970
+ (c) => c.name.toLowerCase() === p2.toLowerCase()
971
+ );
972
+ if (!match) throw new Error(`Entreprise introuvable : "${p2}"`);
973
+ return match.id;
974
+ });
975
+ }
976
+
977
+ // src/cli/config.ts
978
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
979
+ import { homedir as homedir2 } from "os";
980
+ import { join as join2 } from "path";
981
+ var CONFIG_DIR2 = join2(homedir2(), ".config", "tiime");
982
+ var CONFIG_FILE = join2(CONFIG_DIR2, "config.json");
983
+ var loadConfig = () => {
984
+ try {
985
+ if (existsSync2(CONFIG_FILE)) {
986
+ return JSON.parse(readFileSync2(CONFIG_FILE, "utf-8"));
987
+ }
988
+ } catch {
989
+ }
990
+ return {};
991
+ };
992
+ var saveConfig = (config) => {
993
+ if (!existsSync2(CONFIG_DIR2)) {
994
+ mkdirSync2(CONFIG_DIR2, { recursive: true });
995
+ }
996
+ writeFileSync2(CONFIG_FILE, JSON.stringify(config, null, 2));
997
+ };
998
+ var getCompanyId = () => {
999
+ const config = loadConfig();
1000
+ if (!config.companyId) {
1001
+ throw new Error(
1002
+ "Aucune entreprise configur\xE9e. Ex\xE9cutez `tiime company use <id>` d'abord."
1003
+ );
1004
+ }
1005
+ return config.companyId;
1006
+ };
1007
+
1008
+ // src/cli/output.ts
1009
+ import Table from "cli-table3";
1010
+ import { consola } from "consola";
1011
+ var formatArg = {
1012
+ format: {
1013
+ type: "string",
1014
+ description: "Format de sortie (json, table, csv)",
1015
+ default: "json"
1016
+ }
1017
+ };
1018
+ var stringifyValue = (value) => {
1019
+ if (value === null || value === void 0) return "";
1020
+ if (typeof value === "object") return JSON.stringify(value);
1021
+ return String(value);
1022
+ };
1023
+ var outputJson = (data) => {
1024
+ process.stdout.write(`${JSON.stringify(data, null, 2)}
1025
+ `);
1026
+ };
1027
+ var outputTable = (data) => {
1028
+ if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object" && data[0] !== null) {
1029
+ const keys = Object.keys(data[0]);
1030
+ const table = new Table({ head: keys });
1031
+ for (const row of data) {
1032
+ const record = row;
1033
+ table.push(keys.map((key) => stringifyValue(record[key])));
1034
+ }
1035
+ process.stdout.write(`${table.toString()}
1036
+ `);
1037
+ } else if (typeof data === "object" && data !== null && !Array.isArray(data)) {
1038
+ const table = new Table();
1039
+ for (const [key, value] of Object.entries(
1040
+ data
1041
+ )) {
1042
+ table.push({ [key]: stringifyValue(value) });
1043
+ }
1044
+ process.stdout.write(`${table.toString()}
1045
+ `);
1046
+ } else {
1047
+ outputJson(data);
1048
+ }
1049
+ };
1050
+ var escapeCsvField = (value) => {
1051
+ if (value.includes(",") || value.includes('"') || value.includes("\n")) {
1052
+ return `"${value.replace(/"/g, '""')}"`;
1053
+ }
1054
+ return value;
1055
+ };
1056
+ var outputCsv = (data) => {
1057
+ if (Array.isArray(data) && data.length > 0 && typeof data[0] === "object" && data[0] !== null) {
1058
+ const keys = Object.keys(data[0]);
1059
+ const header = keys.map(escapeCsvField).join(",");
1060
+ const lines = data.map((row) => {
1061
+ const record = row;
1062
+ return keys.map((key) => escapeCsvField(stringifyValue(record[key]))).join(",");
1063
+ });
1064
+ process.stdout.write(`${header}
1065
+ ${lines.join("\n")}
1066
+ `);
1067
+ } else if (typeof data === "object" && data !== null && !Array.isArray(data)) {
1068
+ const entries = Object.entries(data);
1069
+ const header = "key,value";
1070
+ const lines = entries.map(
1071
+ ([key, value]) => `${escapeCsvField(key)},${escapeCsvField(stringifyValue(value))}`
1072
+ );
1073
+ process.stdout.write(`${header}
1074
+ ${lines.join("\n")}
1075
+ `);
1076
+ } else {
1077
+ outputJson(data);
1078
+ }
1079
+ };
1080
+ var output = (data, options) => {
1081
+ const format = options?.format ?? "json";
1082
+ if (!["json", "table", "csv"].includes(format)) {
1083
+ process.stderr.write(
1084
+ `${JSON.stringify({ error: `Format invalide : "${format}". Utilisez json, table ou csv.` })}
1085
+ `
1086
+ );
1087
+ process.exit(1);
1088
+ }
1089
+ switch (format) {
1090
+ case "table":
1091
+ outputTable(data);
1092
+ break;
1093
+ case "csv":
1094
+ outputCsv(data);
1095
+ break;
1096
+ default:
1097
+ outputJson(data);
1098
+ break;
1099
+ }
1100
+ };
1101
+ var outputColoredStatus = (data) => {
1102
+ const {
1103
+ company_id,
1104
+ bank_accounts,
1105
+ invoices,
1106
+ pending_quotations,
1107
+ total_clients,
1108
+ unimputed_transactions
1109
+ } = data;
1110
+ console.error("");
1111
+ console.error(` \u{1F4CA} R\xE9sum\xE9 \u2014 Entreprise #${company_id}`);
1112
+ for (const a of bank_accounts) {
1113
+ console.error(
1114
+ ` \u{1F4B0} Soldes : ${a.name} ${a.balance.toFixed(2)}${a.currency === "EUR" ? "\u20AC" : a.currency}`
1115
+ );
1116
+ }
1117
+ console.error(
1118
+ ` \u{1F4C4} Factures : ${invoices.drafts} brouillon(s), ${invoices.unpaid} impay\xE9e(s)`
1119
+ );
1120
+ console.error(` \u{1F4CB} Devis en cours : ${pending_quotations}`);
1121
+ console.error(` \u{1F465} Clients : ${total_clients}`);
1122
+ if (unimputed_transactions > 0) {
1123
+ console.error(` \u26A0\uFE0F Transactions non imput\xE9es : ${unimputed_transactions}`);
1124
+ } else {
1125
+ console.error(` \u2705 Toutes les transactions sont imput\xE9es`);
1126
+ }
1127
+ console.error("");
1128
+ };
1129
+ var outputError = (error) => {
1130
+ if (error instanceof TiimeError) {
1131
+ process.stderr.write(`${JSON.stringify(error.toJSON())}
1132
+ `);
1133
+ } else {
1134
+ const message = error instanceof Error ? error.message : String(error);
1135
+ process.stderr.write(`${JSON.stringify({ error: message })}
1136
+ `);
1137
+ }
1138
+ process.exit(1);
1139
+ };
1140
+
1141
+ // src/cli/commands/audit.ts
1142
+ var auditCommand = defineCommand({
1143
+ meta: {
1144
+ name: "audit",
1145
+ description: "Audit comptable multi-entreprises"
1146
+ },
1147
+ args: {
1148
+ "all-companies": {
1149
+ type: "boolean",
1150
+ description: "Traiter toutes les entreprises",
1151
+ default: false
1152
+ },
1153
+ company: {
1154
+ type: "string",
1155
+ description: "Entreprise(s) cible(s) (ID ou nom, s\xE9par\xE9s par des virgules)"
1156
+ },
1157
+ apply: {
1158
+ type: "boolean",
1159
+ description: "Appliquer les corrections automatiques (imputation auto)",
1160
+ default: false
1161
+ },
1162
+ ...formatArg
1163
+ },
1164
+ async run({ args }) {
1165
+ try {
1166
+ let companyIds;
1167
+ if (args["all-companies"]) {
1168
+ const rootClient = new TiimeClient({ companyId: 0 });
1169
+ const companies = await rootClient.listCompanies();
1170
+ companyIds = companies.map((c) => c.id);
1171
+ } else if (args.company) {
1172
+ const parts = args.company.split(",").map((s) => s.trim());
1173
+ const allNumeric = parts.every((p2) => /^\d+$/.test(p2));
1174
+ if (allNumeric) {
1175
+ companyIds = parts.map(Number);
1176
+ } else {
1177
+ const rootClient = new TiimeClient({ companyId: 0 });
1178
+ const companies = await rootClient.listCompanies();
1179
+ companyIds = resolveCompanyIds(parts, companies);
1180
+ }
1181
+ } else {
1182
+ companyIds = [getCompanyId()];
1183
+ }
1184
+ const companyReports = [];
1185
+ for (const companyId of companyIds) {
1186
+ const client = new TiimeClient({ companyId });
1187
+ let companyName = String(companyId);
1188
+ try {
1189
+ const info = await client.company.get();
1190
+ companyName = info.name ?? String(companyId);
1191
+ } catch {
1192
+ }
1193
+ try {
1194
+ const report2 = await auditForCompany(client, companyId, companyName, {
1195
+ apply: args.apply
1196
+ });
1197
+ companyReports.push(report2);
1198
+ } catch (e) {
1199
+ const message = e instanceof Error ? e.message : String(e);
1200
+ consola2.error(`Erreur audit entreprise ${companyName}: ${message}`);
1201
+ companyReports.push(emptyReport(companyId, companyName, message));
1202
+ }
1203
+ }
1204
+ const report = {
1205
+ date: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
1206
+ companies: companyReports,
1207
+ apply_mode: args.apply
1208
+ };
1209
+ output(report, { format: args.format });
1210
+ } catch (e) {
1211
+ outputError(e);
944
1212
  }
945
- } catch {
946
- }
947
- return {};
948
- };
949
- var saveConfig = (config) => {
950
- if (!existsSync2(CONFIG_DIR2)) {
951
- mkdirSync2(CONFIG_DIR2, { recursive: true });
952
1213
  }
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
- );
1214
+ });
1215
+
1216
+ // src/cli/commands/auth.ts
1217
+ import * as p from "@clack/prompts";
1218
+ import { defineCommand as defineCommand2 } from "citty";
1219
+ var authCommand = defineCommand2({
1220
+ meta: { name: "auth", description: "Gestion de l'authentification" },
1221
+ subCommands: {
1222
+ login: defineCommand2({
1223
+ meta: { name: "login", description: "Se connecter \xE0 Tiime" },
1224
+ args: {
1225
+ email: {
1226
+ type: "string",
1227
+ description: "Adresse email"
1228
+ },
1229
+ password: {
1230
+ type: "string",
1231
+ description: "Mot de passe"
1232
+ }
1233
+ },
1234
+ async run({ args }) {
1235
+ const hasArgs = args.email && args.password;
1236
+ if (hasArgs) {
1237
+ try {
1238
+ const tm = new TokenManager();
1239
+ await tm.login(args.email, args.password);
1240
+ const info = tm.getTokenInfo();
1241
+ output({
1242
+ status: "authenticated",
1243
+ email: info.email,
1244
+ expires_at: info.expiresAt?.toISOString()
1245
+ });
1246
+ } catch (e) {
1247
+ outputError(e);
1248
+ }
1249
+ return;
1250
+ }
1251
+ p.intro("Connexion \xE0 Tiime");
1252
+ const email = await p.text({
1253
+ message: "Adresse email",
1254
+ placeholder: "vous@example.com",
1255
+ validate: (value) => {
1256
+ if (!value || !value.includes("@")) return "Adresse email invalide";
1257
+ }
1258
+ });
1259
+ if (p.isCancel(email)) {
1260
+ p.cancel("Connexion annul\xE9e.");
1261
+ return;
1262
+ }
1263
+ const password2 = await p.password({
1264
+ message: "Mot de passe",
1265
+ validate: (value) => {
1266
+ if (!value) return "Le mot de passe est requis";
1267
+ }
1268
+ });
1269
+ if (p.isCancel(password2)) {
1270
+ p.cancel("Connexion annul\xE9e.");
1271
+ return;
1272
+ }
1273
+ const s = p.spinner();
1274
+ s.start("Authentification en cours...");
1275
+ try {
1276
+ const tm = new TokenManager();
1277
+ await tm.login(email, password2);
1278
+ const info = tm.getTokenInfo();
1279
+ s.stop("Authentification r\xE9ussie");
1280
+ p.outro(`Connect\xE9 en tant que ${info.email ?? email}`);
1281
+ } catch (e) {
1282
+ s.stop("Authentification \xE9chou\xE9e");
1283
+ const message = e instanceof Error ? e.message : "Erreur inconnue";
1284
+ p.cancel(message);
1285
+ }
1286
+ }
1287
+ }),
1288
+ logout: defineCommand2({
1289
+ meta: { name: "logout", description: "Se d\xE9connecter de Tiime" },
1290
+ run() {
1291
+ const tm = new TokenManager();
1292
+ tm.logout();
1293
+ output({ status: "logged_out" });
1294
+ }
1295
+ }),
1296
+ status: defineCommand2({
1297
+ meta: {
1298
+ name: "status",
1299
+ description: "Afficher le statut d'authentification"
1300
+ },
1301
+ run() {
1302
+ const tm = new TokenManager();
1303
+ const info = tm.getTokenInfo();
1304
+ output({
1305
+ authenticated: tm.isAuthenticated(),
1306
+ email: info.email,
1307
+ expires_at: info.expiresAt?.toISOString() ?? null
1308
+ });
1309
+ }
1310
+ })
961
1311
  }
962
- return config.companyId;
963
- };
1312
+ });
964
1313
 
965
1314
  // src/cli/commands/bank.ts
966
- var bankCommand = defineCommand2({
1315
+ import { defineCommand as defineCommand3 } from "citty";
1316
+ var bankCommand = defineCommand3({
967
1317
  meta: { name: "bank", description: "Comptes bancaires et transactions" },
968
1318
  subCommands: {
969
- balance: defineCommand2({
1319
+ balance: defineCommand3({
970
1320
  meta: {
971
1321
  name: "balance",
972
1322
  description: "Afficher les soldes des comptes"
@@ -982,7 +1332,7 @@ var bankCommand = defineCommand2({
982
1332
  }
983
1333
  }
984
1334
  }),
985
- accounts: defineCommand2({
1335
+ accounts: defineCommand3({
986
1336
  meta: { name: "accounts", description: "Lister les comptes bancaires" },
987
1337
  args: {
988
1338
  ...formatArg,
@@ -1002,7 +1352,7 @@ var bankCommand = defineCommand2({
1002
1352
  }
1003
1353
  }
1004
1354
  }),
1005
- transactions: defineCommand2({
1355
+ transactions: defineCommand3({
1006
1356
  meta: {
1007
1357
  name: "transactions",
1008
1358
  description: "Lister les transactions bancaires"
@@ -1074,7 +1424,7 @@ var bankCommand = defineCommand2({
1074
1424
  }
1075
1425
  }
1076
1426
  }),
1077
- unimputed: defineCommand2({
1427
+ unimputed: defineCommand3({
1078
1428
  meta: {
1079
1429
  name: "unimputed",
1080
1430
  description: "Transactions non imput\xE9es"
@@ -1089,16 +1439,189 @@ var bankCommand = defineCommand2({
1089
1439
  outputError(e);
1090
1440
  }
1091
1441
  }
1442
+ }),
1443
+ impute: defineCommand3({
1444
+ meta: {
1445
+ name: "impute",
1446
+ description: "Imputer manuellement une transaction"
1447
+ },
1448
+ args: {
1449
+ id: {
1450
+ type: "string",
1451
+ description: "ID de la transaction (requis)",
1452
+ required: true
1453
+ },
1454
+ "label-id": {
1455
+ type: "string",
1456
+ description: "ID du label \xE0 assigner (requis)",
1457
+ required: true
1458
+ },
1459
+ "label-name": {
1460
+ type: "string",
1461
+ description: "Nom du label (optionnel, pour affichage)"
1462
+ },
1463
+ "dry-run": {
1464
+ type: "boolean",
1465
+ description: "Pr\xE9visualiser sans appliquer",
1466
+ default: false
1467
+ },
1468
+ ...formatArg
1469
+ },
1470
+ async run({ args }) {
1471
+ try {
1472
+ const client = new TiimeClient({ companyId: getCompanyId() });
1473
+ const transactionId = Number(args.id);
1474
+ const labelId = Number(args["label-id"]);
1475
+ const [transaction, suggestions] = await Promise.all([
1476
+ client.bankTransactions.get(transactionId),
1477
+ client.bankTransactions.labelSuggestions(transactionId)
1478
+ ]);
1479
+ let matchedLabel = suggestions.find(
1480
+ (s) => s.id === labelId
1481
+ );
1482
+ if (!matchedLabel) {
1483
+ const [labels, standardLabels] = await Promise.all([
1484
+ client.labels.list(),
1485
+ client.labels.standard()
1486
+ ]);
1487
+ const allLabels = [...labels, ...standardLabels];
1488
+ const found = allLabels.find((l) => l.id === labelId);
1489
+ if (found) {
1490
+ matchedLabel = {
1491
+ id: found.id,
1492
+ label: found.name,
1493
+ name: found.name,
1494
+ acronym: "",
1495
+ color: found.color,
1496
+ client: null
1497
+ };
1498
+ }
1499
+ }
1500
+ if (!matchedLabel) {
1501
+ outputError(
1502
+ new Error(
1503
+ `Label #${labelId} introuvable dans les suggestions ni dans les labels disponibles`
1504
+ )
1505
+ );
1506
+ return;
1507
+ }
1508
+ const imputationLabel = {
1509
+ ...matchedLabel,
1510
+ disabled: false
1511
+ };
1512
+ const imputationParams = [
1513
+ {
1514
+ label: imputationLabel,
1515
+ amount: transaction.amount,
1516
+ documents: [],
1517
+ accountant_detail_requests: []
1518
+ }
1519
+ ];
1520
+ const displayName = args["label-name"] ?? matchedLabel.name ?? matchedLabel.label;
1521
+ if (args["dry-run"]) {
1522
+ output(
1523
+ {
1524
+ dry_run: true,
1525
+ transaction_id: transactionId,
1526
+ wording: transaction.wording,
1527
+ amount: transaction.amount,
1528
+ currency: transaction.currency,
1529
+ label_id: labelId,
1530
+ label_name: displayName
1531
+ },
1532
+ { format: args.format }
1533
+ );
1534
+ return;
1535
+ }
1536
+ const result = await client.bankTransactions.impute(
1537
+ transactionId,
1538
+ imputationParams
1539
+ );
1540
+ output(result, { format: args.format });
1541
+ } catch (e) {
1542
+ outputError(e);
1543
+ }
1544
+ }
1545
+ }),
1546
+ "auto-impute": defineCommand3({
1547
+ meta: {
1548
+ name: "auto-impute",
1549
+ description: "Auto-imputer les transactions non imput\xE9es via les suggestions"
1550
+ },
1551
+ args: {
1552
+ "dry-run": {
1553
+ type: "boolean",
1554
+ description: "Mode pr\xE9visualisation (par d\xE9faut)",
1555
+ default: true
1556
+ },
1557
+ apply: {
1558
+ type: "boolean",
1559
+ description: "Appliquer les imputations (d\xE9sactive dry-run)",
1560
+ default: false
1561
+ },
1562
+ "all-companies": {
1563
+ type: "boolean",
1564
+ description: "Traiter toutes les entreprises du compte (sinon entreprise active)",
1565
+ default: false
1566
+ },
1567
+ company: {
1568
+ type: "string",
1569
+ description: "ID ou nom de l'entreprise cible (peut \xEAtre r\xE9p\xE9t\xE9 avec virgule : 50824,117954)"
1570
+ },
1571
+ ...formatArg
1572
+ },
1573
+ async run({ args }) {
1574
+ try {
1575
+ let companyIds;
1576
+ if (args["all-companies"]) {
1577
+ const rootClient = new TiimeClient({ companyId: 0 });
1578
+ const companies = await rootClient.listCompanies();
1579
+ companyIds = companies.map((c) => c.id);
1580
+ } else if (args.company) {
1581
+ const parts = args.company.split(",").map((s) => s.trim());
1582
+ const allNumeric = parts.every((p2) => /^\d+$/.test(p2));
1583
+ if (allNumeric) {
1584
+ companyIds = parts.map(Number);
1585
+ } else {
1586
+ const rootClient = new TiimeClient({ companyId: 0 });
1587
+ const companies = await rootClient.listCompanies();
1588
+ companyIds = resolveCompanyIds(parts, companies);
1589
+ }
1590
+ } else {
1591
+ companyIds = [getCompanyId()];
1592
+ }
1593
+ const allProposals = [];
1594
+ for (const companyId of companyIds) {
1595
+ const client = new TiimeClient({ companyId });
1596
+ let companyName = String(companyId);
1597
+ try {
1598
+ const info = await client.company.get();
1599
+ companyName = info.name ?? String(companyId);
1600
+ } catch {
1601
+ }
1602
+ const proposals = await autoImputeForCompany(
1603
+ client,
1604
+ companyId,
1605
+ companyName,
1606
+ { apply: args.apply }
1607
+ );
1608
+ allProposals.push(...proposals);
1609
+ }
1610
+ output(allProposals, { format: args.format });
1611
+ } catch (e) {
1612
+ outputError(e);
1613
+ }
1614
+ }
1092
1615
  })
1093
1616
  }
1094
1617
  });
1095
1618
 
1096
1619
  // src/cli/commands/clients.ts
1097
- import { defineCommand as defineCommand3 } from "citty";
1098
- var clientsCommand = defineCommand3({
1620
+ import { defineCommand as defineCommand4 } from "citty";
1621
+ var clientsCommand = defineCommand4({
1099
1622
  meta: { name: "clients", description: "Gestion des clients" },
1100
1623
  subCommands: {
1101
- list: defineCommand3({
1624
+ list: defineCommand4({
1102
1625
  meta: { name: "list", description: "Lister les clients" },
1103
1626
  args: {
1104
1627
  ...formatArg,
@@ -1120,7 +1643,7 @@ var clientsCommand = defineCommand3({
1120
1643
  }
1121
1644
  }
1122
1645
  }),
1123
- get: defineCommand3({
1646
+ get: defineCommand4({
1124
1647
  meta: { name: "get", description: "D\xE9tails d'un client" },
1125
1648
  args: {
1126
1649
  id: { type: "string", description: "ID du client", required: true }
@@ -1135,7 +1658,7 @@ var clientsCommand = defineCommand3({
1135
1658
  }
1136
1659
  }
1137
1660
  }),
1138
- create: defineCommand3({
1661
+ create: defineCommand4({
1139
1662
  meta: { name: "create", description: "Cr\xE9er un client" },
1140
1663
  args: {
1141
1664
  name: {
@@ -1192,7 +1715,7 @@ var clientsCommand = defineCommand3({
1192
1715
  }
1193
1716
  }
1194
1717
  }),
1195
- search: defineCommand3({
1718
+ search: defineCommand4({
1196
1719
  meta: { name: "search", description: "Rechercher un client" },
1197
1720
  args: {
1198
1721
  ...formatArg,
@@ -1216,11 +1739,11 @@ var clientsCommand = defineCommand3({
1216
1739
  });
1217
1740
 
1218
1741
  // src/cli/commands/company.ts
1219
- import { defineCommand as defineCommand4 } from "citty";
1220
- var companyCommand = defineCommand4({
1742
+ import { defineCommand as defineCommand5 } from "citty";
1743
+ var companyCommand = defineCommand5({
1221
1744
  meta: { name: "company", description: "Gestion de l'entreprise" },
1222
1745
  subCommands: {
1223
- list: defineCommand4({
1746
+ list: defineCommand5({
1224
1747
  meta: { name: "list", description: "Lister toutes les entreprises" },
1225
1748
  args: { ...formatArg },
1226
1749
  async run({ args }) {
@@ -1242,7 +1765,7 @@ var companyCommand = defineCommand4({
1242
1765
  }
1243
1766
  }
1244
1767
  }),
1245
- get: defineCommand4({
1768
+ get: defineCommand5({
1246
1769
  meta: { name: "get", description: "D\xE9tails de l'entreprise active" },
1247
1770
  async run() {
1248
1771
  try {
@@ -1256,7 +1779,7 @@ var companyCommand = defineCommand4({
1256
1779
  }
1257
1780
  }
1258
1781
  }),
1259
- use: defineCommand4({
1782
+ use: defineCommand5({
1260
1783
  meta: { name: "use", description: "D\xE9finir l'entreprise active" },
1261
1784
  args: {
1262
1785
  id: {
@@ -1272,7 +1795,7 @@ var companyCommand = defineCommand4({
1272
1795
  output({ status: "ok", companyId: config.companyId });
1273
1796
  }
1274
1797
  }),
1275
- me: defineCommand4({
1798
+ me: defineCommand5({
1276
1799
  meta: {
1277
1800
  name: "me",
1278
1801
  description: "Info utilisateur courant (inclut active_company)"
@@ -1291,7 +1814,7 @@ var companyCommand = defineCommand4({
1291
1814
  });
1292
1815
 
1293
1816
  // src/cli/commands/completion.ts
1294
- import { defineCommand as defineCommand5 } from "citty";
1817
+ import { defineCommand as defineCommand6 } from "citty";
1295
1818
  var commands = {
1296
1819
  auth: {
1297
1820
  description: "Gestion de l'authentification",
@@ -1516,7 +2039,7 @@ var generateFish = () => {
1516
2039
  return `${lines.join("\n")}
1517
2040
  `;
1518
2041
  };
1519
- var completionCommand = defineCommand5({
2042
+ var completionCommand = defineCommand6({
1520
2043
  meta: {
1521
2044
  name: "completion",
1522
2045
  description: "G\xE9n\xE9rer le script d'autocompl\xE9tion shell"
@@ -1553,11 +2076,11 @@ var completionCommand = defineCommand5({
1553
2076
  // src/cli/commands/documents.ts
1554
2077
  import { readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
1555
2078
  import { basename } from "path";
1556
- import { defineCommand as defineCommand6 } from "citty";
1557
- var documentsCommand = defineCommand6({
2079
+ import { defineCommand as defineCommand7 } from "citty";
2080
+ var documentsCommand = defineCommand7({
1558
2081
  meta: { name: "documents", description: "Gestion des documents" },
1559
2082
  subCommands: {
1560
- list: defineCommand6({
2083
+ list: defineCommand7({
1561
2084
  meta: { name: "list", description: "Lister les documents" },
1562
2085
  args: {
1563
2086
  ...formatArg,
@@ -1585,7 +2108,7 @@ var documentsCommand = defineCommand6({
1585
2108
  }
1586
2109
  }
1587
2110
  }),
1588
- upload: defineCommand6({
2111
+ upload: defineCommand7({
1589
2112
  meta: { name: "upload", description: "Uploader un justificatif" },
1590
2113
  args: {
1591
2114
  file: {
@@ -1614,7 +2137,7 @@ var documentsCommand = defineCommand6({
1614
2137
  }
1615
2138
  }
1616
2139
  }),
1617
- download: defineCommand6({
2140
+ download: defineCommand7({
1618
2141
  meta: { name: "download", description: "T\xE9l\xE9charger un document" },
1619
2142
  args: {
1620
2143
  id: {
@@ -1640,7 +2163,7 @@ var documentsCommand = defineCommand6({
1640
2163
  }
1641
2164
  }
1642
2165
  }),
1643
- categories: defineCommand6({
2166
+ categories: defineCommand7({
1644
2167
  meta: {
1645
2168
  name: "categories",
1646
2169
  description: "Lister les cat\xE9gories de documents"
@@ -1660,11 +2183,11 @@ var documentsCommand = defineCommand6({
1660
2183
  });
1661
2184
 
1662
2185
  // src/cli/commands/expenses.ts
1663
- import { defineCommand as defineCommand7 } from "citty";
1664
- var expensesCommand = defineCommand7({
2186
+ import { defineCommand as defineCommand8 } from "citty";
2187
+ var expensesCommand = defineCommand8({
1665
2188
  meta: { name: "expenses", description: "Gestion des notes de frais" },
1666
2189
  subCommands: {
1667
- list: defineCommand7({
2190
+ list: defineCommand8({
1668
2191
  meta: { name: "list", description: "Lister les notes de frais" },
1669
2192
  args: {
1670
2193
  ...formatArg,
@@ -1684,7 +2207,7 @@ var expensesCommand = defineCommand7({
1684
2207
  }
1685
2208
  }
1686
2209
  }),
1687
- get: defineCommand7({
2210
+ get: defineCommand8({
1688
2211
  meta: { name: "get", description: "D\xE9tails d'une note de frais" },
1689
2212
  args: {
1690
2213
  id: {
@@ -1703,7 +2226,7 @@ var expensesCommand = defineCommand7({
1703
2226
  }
1704
2227
  }
1705
2228
  }),
1706
- create: defineCommand7({
2229
+ create: defineCommand8({
1707
2230
  meta: { name: "create", description: "Cr\xE9er une note de frais" },
1708
2231
  args: {
1709
2232
  name: {
@@ -1734,11 +2257,11 @@ var expensesCommand = defineCommand7({
1734
2257
 
1735
2258
  // src/cli/commands/invoices.ts
1736
2259
  import { writeFileSync as writeFileSync4 } from "fs";
1737
- import { defineCommand as defineCommand8 } from "citty";
1738
- var invoicesCommand = defineCommand8({
2260
+ import { defineCommand as defineCommand9 } from "citty";
2261
+ var invoicesCommand = defineCommand9({
1739
2262
  meta: { name: "invoices", description: "Gestion des factures" },
1740
2263
  subCommands: {
1741
- list: defineCommand8({
2264
+ list: defineCommand9({
1742
2265
  meta: { name: "list", description: "Lister les factures" },
1743
2266
  args: {
1744
2267
  ...formatArg,
@@ -1787,7 +2310,7 @@ var invoicesCommand = defineCommand8({
1787
2310
  }
1788
2311
  }
1789
2312
  }),
1790
- get: defineCommand8({
2313
+ get: defineCommand9({
1791
2314
  meta: { name: "get", description: "D\xE9tails d'une facture" },
1792
2315
  args: {
1793
2316
  id: { type: "string", description: "ID de la facture", required: true }
@@ -1802,7 +2325,7 @@ var invoicesCommand = defineCommand8({
1802
2325
  }
1803
2326
  }
1804
2327
  }),
1805
- create: defineCommand8({
2328
+ create: defineCommand9({
1806
2329
  meta: {
1807
2330
  name: "create",
1808
2331
  description: "Cr\xE9er une facture (brouillon par d\xE9faut)"
@@ -1931,7 +2454,7 @@ var invoicesCommand = defineCommand8({
1931
2454
  }
1932
2455
  }
1933
2456
  }),
1934
- duplicate: defineCommand8({
2457
+ duplicate: defineCommand9({
1935
2458
  meta: {
1936
2459
  name: "duplicate",
1937
2460
  description: "Dupliquer une facture existante en brouillon"
@@ -1964,7 +2487,7 @@ var invoicesCommand = defineCommand8({
1964
2487
  }
1965
2488
  }
1966
2489
  }),
1967
- update: defineCommand8({
2490
+ update: defineCommand9({
1968
2491
  meta: { name: "update", description: "Mettre \xE0 jour une facture" },
1969
2492
  args: {
1970
2493
  id: {
@@ -2010,7 +2533,7 @@ var invoicesCommand = defineCommand8({
2010
2533
  }
2011
2534
  }
2012
2535
  }),
2013
- send: defineCommand8({
2536
+ send: defineCommand9({
2014
2537
  meta: { name: "send", description: "Envoyer une facture par email" },
2015
2538
  args: {
2016
2539
  id: {
@@ -2050,7 +2573,7 @@ var invoicesCommand = defineCommand8({
2050
2573
  }
2051
2574
  }
2052
2575
  }),
2053
- pdf: defineCommand8({
2576
+ pdf: defineCommand9({
2054
2577
  meta: {
2055
2578
  name: "pdf",
2056
2579
  description: "T\xE9l\xE9charger le PDF d'une facture"
@@ -2078,7 +2601,7 @@ var invoicesCommand = defineCommand8({
2078
2601
  }
2079
2602
  }
2080
2603
  }),
2081
- delete: defineCommand8({
2604
+ delete: defineCommand9({
2082
2605
  meta: { name: "delete", description: "Supprimer une facture brouillon" },
2083
2606
  args: {
2084
2607
  id: {
@@ -2101,11 +2624,11 @@ var invoicesCommand = defineCommand8({
2101
2624
  });
2102
2625
 
2103
2626
  // src/cli/commands/labels.ts
2104
- import { defineCommand as defineCommand9 } from "citty";
2105
- var labelsCommand = defineCommand9({
2627
+ import { defineCommand as defineCommand10 } from "citty";
2628
+ var labelsCommand = defineCommand10({
2106
2629
  meta: { name: "labels", description: "Gestion des labels et tags" },
2107
2630
  subCommands: {
2108
- list: defineCommand9({
2631
+ list: defineCommand10({
2109
2632
  meta: { name: "list", description: "Lister les labels personnalis\xE9s" },
2110
2633
  args: { ...formatArg },
2111
2634
  async run({ args }) {
@@ -2118,7 +2641,7 @@ var labelsCommand = defineCommand9({
2118
2641
  }
2119
2642
  }
2120
2643
  }),
2121
- standard: defineCommand9({
2644
+ standard: defineCommand10({
2122
2645
  meta: { name: "standard", description: "Lister les labels standards" },
2123
2646
  args: { ...formatArg },
2124
2647
  async run({ args }) {
@@ -2131,7 +2654,7 @@ var labelsCommand = defineCommand9({
2131
2654
  }
2132
2655
  }
2133
2656
  }),
2134
- tags: defineCommand9({
2657
+ tags: defineCommand10({
2135
2658
  meta: { name: "tags", description: "Lister les tags" },
2136
2659
  args: { ...formatArg },
2137
2660
  async run({ args }) {
@@ -2149,7 +2672,7 @@ var labelsCommand = defineCommand9({
2149
2672
 
2150
2673
  // src/cli/commands/open.ts
2151
2674
  import { exec } from "child_process";
2152
- import { defineCommand as defineCommand10 } from "citty";
2675
+ import { defineCommand as defineCommand11 } from "citty";
2153
2676
  var APP_BASE_URL = "https://apps.tiime.fr";
2154
2677
  var sections = {
2155
2678
  invoices: "/invoicing/invoices",
@@ -2172,7 +2695,7 @@ var buildUrl = (section) => {
2172
2695
  const companyId = getCompanyId();
2173
2696
  return `${APP_BASE_URL}/companies/${companyId}${path}`;
2174
2697
  };
2175
- var openCommand = defineCommand10({
2698
+ var openCommand = defineCommand11({
2176
2699
  meta: {
2177
2700
  name: "open",
2178
2701
  description: "Ouvrir Tiime dans le navigateur"
@@ -2197,11 +2720,11 @@ var openCommand = defineCommand10({
2197
2720
 
2198
2721
  // src/cli/commands/quotations.ts
2199
2722
  import { writeFileSync as writeFileSync5 } from "fs";
2200
- import { defineCommand as defineCommand11 } from "citty";
2201
- var quotationsCommand = defineCommand11({
2723
+ import { defineCommand as defineCommand12 } from "citty";
2724
+ var quotationsCommand = defineCommand12({
2202
2725
  meta: { name: "quotations", description: "Gestion des devis" },
2203
2726
  subCommands: {
2204
- list: defineCommand11({
2727
+ list: defineCommand12({
2205
2728
  meta: { name: "list", description: "Lister les devis" },
2206
2729
  args: { ...formatArg },
2207
2730
  async run({ args }) {
@@ -2214,7 +2737,7 @@ var quotationsCommand = defineCommand11({
2214
2737
  }
2215
2738
  }
2216
2739
  }),
2217
- get: defineCommand11({
2740
+ get: defineCommand12({
2218
2741
  meta: { name: "get", description: "D\xE9tails d'un devis" },
2219
2742
  args: {
2220
2743
  id: { type: "string", description: "ID du devis", required: true }
@@ -2229,7 +2752,7 @@ var quotationsCommand = defineCommand11({
2229
2752
  }
2230
2753
  }
2231
2754
  }),
2232
- create: defineCommand11({
2755
+ create: defineCommand12({
2233
2756
  meta: {
2234
2757
  name: "create",
2235
2758
  description: "Cr\xE9er un devis (brouillon par d\xE9faut)"
@@ -2305,7 +2828,7 @@ var quotationsCommand = defineCommand11({
2305
2828
  }
2306
2829
  }
2307
2830
  }),
2308
- pdf: defineCommand11({
2831
+ pdf: defineCommand12({
2309
2832
  meta: {
2310
2833
  name: "pdf",
2311
2834
  description: "T\xE9l\xE9charger le PDF d'un devis"
@@ -2333,7 +2856,7 @@ var quotationsCommand = defineCommand11({
2333
2856
  }
2334
2857
  }
2335
2858
  }),
2336
- send: defineCommand11({
2859
+ send: defineCommand12({
2337
2860
  meta: { name: "send", description: "Envoyer un devis par email" },
2338
2861
  args: {
2339
2862
  id: {
@@ -2377,8 +2900,8 @@ var quotationsCommand = defineCommand11({
2377
2900
  });
2378
2901
 
2379
2902
  // src/cli/commands/status.ts
2380
- import { defineCommand as defineCommand12 } from "citty";
2381
- var statusCommand = defineCommand12({
2903
+ import { defineCommand as defineCommand13 } from "citty";
2904
+ var statusCommand = defineCommand13({
2382
2905
  meta: { name: "status", description: "R\xE9sum\xE9 rapide de la situation" },
2383
2906
  args: { ...formatArg },
2384
2907
  async run({ args }) {
@@ -2429,11 +2952,11 @@ var statusCommand = defineCommand12({
2429
2952
  });
2430
2953
 
2431
2954
  // src/cli/commands/version.ts
2432
- import { defineCommand as defineCommand13 } from "citty";
2433
- var versionCommand = defineCommand13({
2955
+ import { defineCommand as defineCommand14 } from "citty";
2956
+ var versionCommand = defineCommand14({
2434
2957
  meta: { name: "version", description: "Afficher la version" },
2435
2958
  run() {
2436
- output({ version: "1.0.0", node: process.version });
2959
+ output({ version: "1.2.0", node: process.version });
2437
2960
  }
2438
2961
  });
2439
2962
 
@@ -2451,6 +2974,7 @@ var descriptionTranslations = {
2451
2974
  "Gestion des notes de frais": "Expense report management",
2452
2975
  "Gestion des documents": "Document management",
2453
2976
  "Gestion des labels et tags": "Label and tag management",
2977
+ "Audit comptable multi-entreprises": "Multi-company accounting audit",
2454
2978
  "R\xE9sum\xE9 rapide de la situation": "Quick status summary",
2455
2979
  "Ouvrir Tiime dans le navigateur": "Open Tiime in browser",
2456
2980
  "Afficher la version": "Show version",
@@ -2562,7 +3086,10 @@ var descriptionTranslations = {
2562
3086
  "Section \xE0 ouvrir (invoices, quotations, clients, bank, documents, expenses)": "Section to open (invoices, quotations, clients, bank, documents, expenses)",
2563
3087
  "Nom de la note de frais": "Expense report name",
2564
3088
  "Date (YYYY-MM-DD)": "Date (YYYY-MM-DD)",
2565
- "Titre du devis": "Quotation title"
3089
+ "Titre du devis": "Quotation title",
3090
+ "Traiter toutes les entreprises": "Process all companies",
3091
+ "Entreprise(s) cible(s) (ID ou nom, s\xE9par\xE9s par des virgules)": "Target company(ies) (ID or name, comma-separated)",
3092
+ "Appliquer les corrections automatiques (imputation auto)": "Apply automatic fixes (auto-imputation)"
2566
3093
  };
2567
3094
  var frameworkTranslations = {
2568
3095
  fr: {
@@ -2609,13 +3136,14 @@ var translateHelp = (text2) => {
2609
3136
  };
2610
3137
 
2611
3138
  // src/cli/index.ts
2612
- var main = defineCommand14({
3139
+ var main = defineCommand15({
2613
3140
  meta: {
2614
3141
  name: "tiime",
2615
3142
  version: "1.0.0",
2616
3143
  description: "CLI pour la comptabilit\xE9 Tiime \u2014 sortie JSON pour agents IA"
2617
3144
  },
2618
3145
  subCommands: {
3146
+ audit: auditCommand,
2619
3147
  auth: authCommand,
2620
3148
  company: companyCommand,
2621
3149
  invoices: invoicesCommand,