tiime-sdk 2.1.2 → 2.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/index.d.ts +19 -13
- package/dist/index.js +2 -820
- package/package.json +1 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { $Fetch } from 'ofetch';
|
|
2
|
-
|
|
3
1
|
interface AuthTokens {
|
|
4
2
|
access_token: string;
|
|
5
3
|
expires_at: number;
|
|
@@ -474,10 +472,18 @@ declare class TokenManager {
|
|
|
474
472
|
private saveToDisk;
|
|
475
473
|
}
|
|
476
474
|
|
|
475
|
+
interface FetchOptions {
|
|
476
|
+
method?: string;
|
|
477
|
+
headers?: Record<string, string>;
|
|
478
|
+
body?: unknown;
|
|
479
|
+
query?: Record<string, unknown> | object;
|
|
480
|
+
}
|
|
481
|
+
type FetchFn = <T = unknown>(url: string, options?: FetchOptions) => Promise<T>;
|
|
482
|
+
|
|
477
483
|
declare class BankAccountsResource {
|
|
478
484
|
private fetch;
|
|
479
485
|
private companyId;
|
|
480
|
-
constructor(fetch:
|
|
486
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
481
487
|
list(enabled?: boolean): Promise<BankAccount[]>;
|
|
482
488
|
get(bankAccountId: number): Promise<BankAccount>;
|
|
483
489
|
balance(): Promise<{
|
|
@@ -502,7 +508,7 @@ interface BankTransactionsListParams {
|
|
|
502
508
|
declare class BankTransactionsResource {
|
|
503
509
|
private fetch;
|
|
504
510
|
private companyId;
|
|
505
|
-
constructor(fetch:
|
|
511
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
506
512
|
list(params?: BankTransactionsListParams): Promise<BankTransactionsResponse>;
|
|
507
513
|
listAll(params?: Omit<BankTransactionsListParams, "page">): Promise<BankTransaction[]>;
|
|
508
514
|
unimputed(): Promise<BankTransaction[]>;
|
|
@@ -532,7 +538,7 @@ interface ClientCreateParams {
|
|
|
532
538
|
declare class ClientsResource {
|
|
533
539
|
private fetch;
|
|
534
540
|
private companyId;
|
|
535
|
-
constructor(fetch:
|
|
541
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
536
542
|
list(params?: ClientsListParams): Promise<Client[]>;
|
|
537
543
|
get(clientId: number): Promise<Client>;
|
|
538
544
|
create(params: ClientCreateParams): Promise<Client>;
|
|
@@ -542,7 +548,7 @@ declare class ClientsResource {
|
|
|
542
548
|
declare class CompanyResource {
|
|
543
549
|
private fetch;
|
|
544
550
|
private companyId;
|
|
545
|
-
constructor(fetch:
|
|
551
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
546
552
|
get(): Promise<Company>;
|
|
547
553
|
users(): Promise<unknown>;
|
|
548
554
|
appConfig(): Promise<unknown>;
|
|
@@ -562,7 +568,7 @@ interface DocumentsListParams {
|
|
|
562
568
|
declare class DocumentsResource {
|
|
563
569
|
private fetch;
|
|
564
570
|
private companyId;
|
|
565
|
-
constructor(fetch:
|
|
571
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
566
572
|
list(params?: DocumentsListParams): Promise<Document[]>;
|
|
567
573
|
categories(): Promise<DocumentCategory[]>;
|
|
568
574
|
preview(documentId: number): Promise<unknown>;
|
|
@@ -574,7 +580,7 @@ declare class DocumentsResource {
|
|
|
574
580
|
declare class ExpenseReportsResource {
|
|
575
581
|
private fetch;
|
|
576
582
|
private companyId;
|
|
577
|
-
constructor(fetch:
|
|
583
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
578
584
|
list(sorts?: string): Promise<ExpenseReport[]>;
|
|
579
585
|
get(expenseReportId: number): Promise<ExpenseReport>;
|
|
580
586
|
create(params: ExpenseReportCreateParams): Promise<ExpenseReport>;
|
|
@@ -589,7 +595,7 @@ interface InvoicesListParams {
|
|
|
589
595
|
declare class InvoicesResource {
|
|
590
596
|
private fetch;
|
|
591
597
|
private companyId;
|
|
592
|
-
constructor(fetch:
|
|
598
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
593
599
|
list(params?: InvoicesListParams): Promise<Invoice[]>;
|
|
594
600
|
listAll(params?: {
|
|
595
601
|
sorts?: string;
|
|
@@ -611,7 +617,7 @@ declare class InvoicesResource {
|
|
|
611
617
|
declare class LabelsResource {
|
|
612
618
|
private fetch;
|
|
613
619
|
private companyId;
|
|
614
|
-
constructor(fetch:
|
|
620
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
615
621
|
list(): Promise<Label[]>;
|
|
616
622
|
standard(): Promise<Label[]>;
|
|
617
623
|
tags(): Promise<Tag[]>;
|
|
@@ -620,7 +626,7 @@ declare class LabelsResource {
|
|
|
620
626
|
declare class QuotationsResource {
|
|
621
627
|
private fetch;
|
|
622
628
|
private companyId;
|
|
623
|
-
constructor(fetch:
|
|
629
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
624
630
|
list(expand?: string): Promise<Quotation[]>;
|
|
625
631
|
get(quotationId: number): Promise<Quotation>;
|
|
626
632
|
create(params: QuotationCreateParams): Promise<Quotation>;
|
|
@@ -630,14 +636,14 @@ declare class QuotationsResource {
|
|
|
630
636
|
|
|
631
637
|
declare class UsersResource {
|
|
632
638
|
private fetch;
|
|
633
|
-
constructor(fetch:
|
|
639
|
+
constructor(fetch: FetchFn);
|
|
634
640
|
me(): Promise<User>;
|
|
635
641
|
legalInformations(): Promise<unknown>;
|
|
636
642
|
settings(companyId: number): Promise<unknown>;
|
|
637
643
|
}
|
|
638
644
|
|
|
639
645
|
declare class TiimeClient {
|
|
640
|
-
readonly fetch:
|
|
646
|
+
readonly fetch: FetchFn;
|
|
641
647
|
readonly tokenManager: TokenManager;
|
|
642
648
|
readonly companyId: number;
|
|
643
649
|
constructor(options?: TiimeClientOptions & {
|
package/dist/index.js
CHANGED
|
@@ -1,820 +1,2 @@
|
|
|
1
|
-
// src/auth.ts
|
|
2
|
-
|
|
3
|
-
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
4
|
-
import { homedir } from "os";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
import { ofetch } from "ofetch";
|
|
7
|
-
var AUTH0_DOMAIN = "auth0.tiime.fr";
|
|
8
|
-
var AUTH0_CLIENT_ID = "iEbsbe3o66gcTBfGRa012kj1Rb6vjAND";
|
|
9
|
-
var AUTH0_AUDIENCE = "https://chronos/";
|
|
10
|
-
var CONFIG_DIR = join(homedir(), ".config", "tiime");
|
|
11
|
-
var AUTH_FILE = join(CONFIG_DIR, "auth.json");
|
|
12
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
13
|
-
var KEYCHAIN_ACCOUNT = "tiime-cli";
|
|
14
|
-
var KEYCHAIN_SERVICE = "tiime-credentials";
|
|
15
|
-
var saveCredentialsToKeychain = (email, password) => {
|
|
16
|
-
try {
|
|
17
|
-
const payload = JSON.stringify({ email, password });
|
|
18
|
-
execSync(
|
|
19
|
-
`security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w '${payload.replace(/'/g, "'\\''")}' -U`,
|
|
20
|
-
{ stdio: "ignore" }
|
|
21
|
-
);
|
|
22
|
-
return true;
|
|
23
|
-
} catch {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
var loadCredentialsFromKeychain = () => {
|
|
28
|
-
try {
|
|
29
|
-
const raw = execSync(
|
|
30
|
-
`security find-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w`,
|
|
31
|
-
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
32
|
-
).trim();
|
|
33
|
-
const data = JSON.parse(raw);
|
|
34
|
-
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
35
|
-
return data;
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
} catch {
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
var saveCredentialsToFile = (email, password) => {
|
|
43
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
44
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
45
|
-
}
|
|
46
|
-
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
47
|
-
writeFileSync(filePath, JSON.stringify({ email, password }, null, 2), {
|
|
48
|
-
mode: 384
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
var loadCredentialsFromFile = () => {
|
|
52
|
-
try {
|
|
53
|
-
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
54
|
-
if (existsSync(filePath)) {
|
|
55
|
-
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
56
|
-
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
57
|
-
return data;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
} catch {
|
|
61
|
-
}
|
|
62
|
-
return null;
|
|
63
|
-
};
|
|
64
|
-
var saveCredentials = (email, password) => {
|
|
65
|
-
if (!saveCredentialsToKeychain(email, password)) {
|
|
66
|
-
saveCredentialsToFile(email, password);
|
|
67
|
-
}
|
|
68
|
-
};
|
|
69
|
-
var loadCredentials = () => {
|
|
70
|
-
return loadCredentialsFromKeychain() ?? loadCredentialsFromFile();
|
|
71
|
-
};
|
|
72
|
-
var resolveCompanyId = (explicit) => {
|
|
73
|
-
if (explicit) return explicit;
|
|
74
|
-
const envId = process.env.TIIME_COMPANY_ID;
|
|
75
|
-
if (envId) {
|
|
76
|
-
const parsed = Number.parseInt(envId, 10);
|
|
77
|
-
if (!Number.isNaN(parsed)) return parsed;
|
|
78
|
-
}
|
|
79
|
-
try {
|
|
80
|
-
if (existsSync(CONFIG_FILE)) {
|
|
81
|
-
const config = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
82
|
-
if (config.companyId) return config.companyId;
|
|
83
|
-
}
|
|
84
|
-
} catch {
|
|
85
|
-
}
|
|
86
|
-
throw new Error(
|
|
87
|
-
"No company ID configured. Set TIIME_COMPANY_ID env var, pass companyId option, or run `tiime company use --id <ID>`."
|
|
88
|
-
);
|
|
89
|
-
};
|
|
90
|
-
var TokenManager = class {
|
|
91
|
-
tokens = null;
|
|
92
|
-
credentials = null;
|
|
93
|
-
persist;
|
|
94
|
-
/**
|
|
95
|
-
* @param options.tokens - Use these tokens directly (no disk I/O)
|
|
96
|
-
* @param options.email - Login with these credentials
|
|
97
|
-
* @param options.password - Login with these credentials
|
|
98
|
-
* @param options.persist - Save tokens/credentials to disk (default: true when no explicit auth)
|
|
99
|
-
*/
|
|
100
|
-
constructor(options = {}) {
|
|
101
|
-
const hasExplicitAuth = options.tokens || options.email && options.password;
|
|
102
|
-
this.persist = options.persist ?? !hasExplicitAuth;
|
|
103
|
-
if (options.tokens) {
|
|
104
|
-
this.tokens = options.tokens;
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
if (options.email && options.password) {
|
|
108
|
-
this.credentials = { email: options.email, password: options.password };
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
const envToken = process.env.TIIME_ACCESS_TOKEN;
|
|
112
|
-
if (envToken) {
|
|
113
|
-
this.tokens = {
|
|
114
|
-
access_token: envToken,
|
|
115
|
-
// No expiry info from env — assume valid, never auto-refresh
|
|
116
|
-
expires_at: Number.MAX_SAFE_INTEGER
|
|
117
|
-
};
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
const envEmail = process.env.TIIME_EMAIL;
|
|
121
|
-
const envPassword = process.env.TIIME_PASSWORD;
|
|
122
|
-
if (envEmail && envPassword) {
|
|
123
|
-
this.credentials = { email: envEmail, password: envPassword };
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
this.loadFromDisk();
|
|
127
|
-
}
|
|
128
|
-
async login(email, password) {
|
|
129
|
-
const response = await ofetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
130
|
-
method: "POST",
|
|
131
|
-
body: {
|
|
132
|
-
grant_type: "password",
|
|
133
|
-
client_id: AUTH0_CLIENT_ID,
|
|
134
|
-
audience: AUTH0_AUDIENCE,
|
|
135
|
-
scope: "openid email",
|
|
136
|
-
username: email,
|
|
137
|
-
password
|
|
138
|
-
}
|
|
139
|
-
});
|
|
140
|
-
this.tokens = {
|
|
141
|
-
access_token: response.access_token,
|
|
142
|
-
expires_at: Date.now() + response.expires_in * 1e3
|
|
143
|
-
};
|
|
144
|
-
if (this.persist) {
|
|
145
|
-
this.saveToDisk();
|
|
146
|
-
saveCredentials(email, password);
|
|
147
|
-
}
|
|
148
|
-
return this.tokens;
|
|
149
|
-
}
|
|
150
|
-
async getValidToken() {
|
|
151
|
-
if (!this.tokens || this.isExpired()) {
|
|
152
|
-
const creds = this.credentials ?? loadCredentials();
|
|
153
|
-
if (creds) {
|
|
154
|
-
const tokens = await this.login(creds.email, creds.password);
|
|
155
|
-
return tokens.access_token;
|
|
156
|
-
}
|
|
157
|
-
throw new Error(
|
|
158
|
-
this.tokens ? "Token expired. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`." : "Not authenticated. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`."
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
return this.tokens.access_token;
|
|
162
|
-
}
|
|
163
|
-
isAuthenticated() {
|
|
164
|
-
return this.tokens !== null && !this.isExpired();
|
|
165
|
-
}
|
|
166
|
-
logout() {
|
|
167
|
-
this.tokens = null;
|
|
168
|
-
if (existsSync(AUTH_FILE)) {
|
|
169
|
-
writeFileSync(AUTH_FILE, "{}");
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
getTokenInfo() {
|
|
173
|
-
if (!this.tokens) {
|
|
174
|
-
return { email: null, expiresAt: null };
|
|
175
|
-
}
|
|
176
|
-
try {
|
|
177
|
-
const payload = JSON.parse(
|
|
178
|
-
Buffer.from(
|
|
179
|
-
this.tokens.access_token.split(".")[1],
|
|
180
|
-
"base64"
|
|
181
|
-
).toString()
|
|
182
|
-
);
|
|
183
|
-
return {
|
|
184
|
-
email: payload["tiime/userEmail"] || null,
|
|
185
|
-
expiresAt: new Date(this.tokens.expires_at)
|
|
186
|
-
};
|
|
187
|
-
} catch {
|
|
188
|
-
return { email: null, expiresAt: new Date(this.tokens.expires_at) };
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
isExpired() {
|
|
192
|
-
if (!this.tokens) return true;
|
|
193
|
-
return Date.now() >= this.tokens.expires_at - 6e4;
|
|
194
|
-
}
|
|
195
|
-
loadFromDisk() {
|
|
196
|
-
try {
|
|
197
|
-
if (existsSync(AUTH_FILE)) {
|
|
198
|
-
const data = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
199
|
-
if (data.access_token && data.expires_at) {
|
|
200
|
-
this.tokens = data;
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
} catch {
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
saveToDisk() {
|
|
207
|
-
if (!existsSync(CONFIG_DIR)) {
|
|
208
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
209
|
-
}
|
|
210
|
-
writeFileSync(AUTH_FILE, JSON.stringify(this.tokens, null, 2));
|
|
211
|
-
}
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
// src/client.ts
|
|
215
|
-
import { ofetch as ofetch2 } from "ofetch";
|
|
216
|
-
|
|
217
|
-
// src/errors.ts
|
|
218
|
-
var TiimeError = class extends Error {
|
|
219
|
-
constructor(message, status, endpoint, details) {
|
|
220
|
-
super(message);
|
|
221
|
-
this.status = status;
|
|
222
|
-
this.endpoint = endpoint;
|
|
223
|
-
this.details = details;
|
|
224
|
-
this.name = "TiimeError";
|
|
225
|
-
}
|
|
226
|
-
toJSON() {
|
|
227
|
-
return {
|
|
228
|
-
error: this.name,
|
|
229
|
-
message: this.message,
|
|
230
|
-
status: this.status,
|
|
231
|
-
endpoint: this.endpoint,
|
|
232
|
-
details: this.details
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
|
|
237
|
-
// src/resources/bank-accounts.ts
|
|
238
|
-
var BankAccountsResource = class {
|
|
239
|
-
constructor(fetch, companyId) {
|
|
240
|
-
this.fetch = fetch;
|
|
241
|
-
this.companyId = companyId;
|
|
242
|
-
}
|
|
243
|
-
list(enabled) {
|
|
244
|
-
return this.fetch(
|
|
245
|
-
`/companies/${this.companyId}/bank_accounts`,
|
|
246
|
-
{ query: enabled !== void 0 ? { enabled } : void 0 }
|
|
247
|
-
);
|
|
248
|
-
}
|
|
249
|
-
get(bankAccountId) {
|
|
250
|
-
return this.fetch(
|
|
251
|
-
`/companies/${this.companyId}/bank_accounts/${bankAccountId}`
|
|
252
|
-
);
|
|
253
|
-
}
|
|
254
|
-
async balance() {
|
|
255
|
-
const accounts = await this.list(true);
|
|
256
|
-
return accounts.map((a) => ({
|
|
257
|
-
name: a.name,
|
|
258
|
-
balance_amount: a.balance_amount,
|
|
259
|
-
currency: a.balance_currency
|
|
260
|
-
}));
|
|
261
|
-
}
|
|
262
|
-
};
|
|
263
|
-
|
|
264
|
-
// src/resources/bank-transactions.ts
|
|
265
|
-
var BankTransactionsResource = class {
|
|
266
|
-
constructor(fetch, companyId) {
|
|
267
|
-
this.fetch = fetch;
|
|
268
|
-
this.companyId = companyId;
|
|
269
|
-
}
|
|
270
|
-
list(params) {
|
|
271
|
-
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 100);
|
|
272
|
-
const end = start + (params?.pageSize ?? 100);
|
|
273
|
-
const { page: _, pageSize: __, from, to, search, ...rest } = params ?? {};
|
|
274
|
-
const query = { ...rest };
|
|
275
|
-
if (from) query.transaction_date_start = from;
|
|
276
|
-
if (to) query.transaction_date_end = to;
|
|
277
|
-
if (search) query.wording = search;
|
|
278
|
-
return this.fetch(
|
|
279
|
-
`/companies/${this.companyId}/bank_transactions`,
|
|
280
|
-
{
|
|
281
|
-
query: { hide_refused: false, ...query },
|
|
282
|
-
headers: {
|
|
283
|
-
Accept: "application/vnd.tiime.bank_transactions.v2+json",
|
|
284
|
-
Range: `items=${start}-${end}`
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
async listAll(params) {
|
|
290
|
-
const pageSize = params?.pageSize ?? 200;
|
|
291
|
-
const all = [];
|
|
292
|
-
let page = 1;
|
|
293
|
-
let hasMore = true;
|
|
294
|
-
while (hasMore) {
|
|
295
|
-
const response = await this.list({ ...params, page, pageSize });
|
|
296
|
-
all.push(...response.transactions);
|
|
297
|
-
hasMore = response.transactions.length === pageSize;
|
|
298
|
-
page++;
|
|
299
|
-
}
|
|
300
|
-
return all;
|
|
301
|
-
}
|
|
302
|
-
unimputed() {
|
|
303
|
-
return this.fetch(
|
|
304
|
-
`/companies/${this.companyId}/bank_transactions/unimputed`
|
|
305
|
-
);
|
|
306
|
-
}
|
|
307
|
-
get(transactionId) {
|
|
308
|
-
return this.fetch(
|
|
309
|
-
`/companies/${this.companyId}/bank_transactions/${transactionId}`
|
|
310
|
-
);
|
|
311
|
-
}
|
|
312
|
-
labelSuggestions(transactionId) {
|
|
313
|
-
return this.fetch(
|
|
314
|
-
`/companies/${this.companyId}/bank_transactions/${transactionId}/label_suggestions`,
|
|
315
|
-
{
|
|
316
|
-
headers: {
|
|
317
|
-
Accept: "application/vnd.tiime.bank_transactions.label_suggestions.v2+json"
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
);
|
|
321
|
-
}
|
|
322
|
-
impute(transactionId, imputations) {
|
|
323
|
-
return this.fetch(
|
|
324
|
-
`/companies/${this.companyId}/bank_transactions/${transactionId}`,
|
|
325
|
-
{
|
|
326
|
-
method: "PATCH",
|
|
327
|
-
body: { imputations }
|
|
328
|
-
}
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
matchDocuments(transactionId, documentIds) {
|
|
332
|
-
return this.fetch(
|
|
333
|
-
`/companies/${this.companyId}/bank_transactions/${transactionId}/document_matchings`,
|
|
334
|
-
{
|
|
335
|
-
method: "PUT",
|
|
336
|
-
body: { documents: documentIds.map((id) => ({ id })) }
|
|
337
|
-
}
|
|
338
|
-
);
|
|
339
|
-
}
|
|
340
|
-
getMatchings(transactionId) {
|
|
341
|
-
return this.fetch(
|
|
342
|
-
`/companies/${this.companyId}/bank_transactions/${transactionId}/matchings`
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
};
|
|
346
|
-
|
|
347
|
-
// src/resources/clients.ts
|
|
348
|
-
var ClientsResource = class {
|
|
349
|
-
constructor(fetch, companyId) {
|
|
350
|
-
this.fetch = fetch;
|
|
351
|
-
this.companyId = companyId;
|
|
352
|
-
}
|
|
353
|
-
list(params) {
|
|
354
|
-
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
355
|
-
query: params,
|
|
356
|
-
headers: {
|
|
357
|
-
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
358
|
-
Range: "items=0-*"
|
|
359
|
-
}
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
get(clientId) {
|
|
363
|
-
return this.fetch(
|
|
364
|
-
`/companies/${this.companyId}/clients/${clientId}`
|
|
365
|
-
);
|
|
366
|
-
}
|
|
367
|
-
create(params) {
|
|
368
|
-
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
369
|
-
method: "POST",
|
|
370
|
-
body: params
|
|
371
|
-
});
|
|
372
|
-
}
|
|
373
|
-
search(query) {
|
|
374
|
-
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
375
|
-
query: { search: query },
|
|
376
|
-
headers: {
|
|
377
|
-
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
378
|
-
Range: "items=0-*"
|
|
379
|
-
}
|
|
380
|
-
});
|
|
381
|
-
}
|
|
382
|
-
};
|
|
383
|
-
|
|
384
|
-
// src/resources/company.ts
|
|
385
|
-
var CompanyResource = class {
|
|
386
|
-
constructor(fetch, companyId) {
|
|
387
|
-
this.fetch = fetch;
|
|
388
|
-
this.companyId = companyId;
|
|
389
|
-
}
|
|
390
|
-
get() {
|
|
391
|
-
return this.fetch(`/companies/${this.companyId}`);
|
|
392
|
-
}
|
|
393
|
-
users() {
|
|
394
|
-
return this.fetch(`/companies/${this.companyId}/users`);
|
|
395
|
-
}
|
|
396
|
-
appConfig() {
|
|
397
|
-
return this.fetch(`/companies/${this.companyId}/app_config`);
|
|
398
|
-
}
|
|
399
|
-
accountingPeriod(rangeYear = 1) {
|
|
400
|
-
return this.fetch(
|
|
401
|
-
`/companies/${this.companyId}/accounting_period/current`,
|
|
402
|
-
{ query: { range_year: rangeYear } }
|
|
403
|
-
);
|
|
404
|
-
}
|
|
405
|
-
tiles(keys) {
|
|
406
|
-
return this.fetch(`/companies/${this.companyId}/tiles`, {
|
|
407
|
-
query: { keys: keys.join(",") }
|
|
408
|
-
});
|
|
409
|
-
}
|
|
410
|
-
dashboardBlocks(displayGroup = "monitoring") {
|
|
411
|
-
return this.fetch(`/companies/${this.companyId}/dashboard_blocks`, {
|
|
412
|
-
query: { sorts: "rank:asc", display_group: displayGroup }
|
|
413
|
-
});
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
// src/resources/documents.ts
|
|
418
|
-
var DocumentsResource = class {
|
|
419
|
-
constructor(fetch, companyId) {
|
|
420
|
-
this.fetch = fetch;
|
|
421
|
-
this.companyId = companyId;
|
|
422
|
-
}
|
|
423
|
-
list(params) {
|
|
424
|
-
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
425
|
-
const end = start + (params?.pageSize ?? 25);
|
|
426
|
-
const { page: _, pageSize: __, ...query } = params ?? {};
|
|
427
|
-
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
428
|
-
query: {
|
|
429
|
-
sorts: "created_at:desc",
|
|
430
|
-
expand: "file_family,preview_available",
|
|
431
|
-
...query
|
|
432
|
-
},
|
|
433
|
-
headers: {
|
|
434
|
-
Accept: "application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",
|
|
435
|
-
Range: `items=${start}-${end}`
|
|
436
|
-
}
|
|
437
|
-
});
|
|
438
|
-
}
|
|
439
|
-
categories() {
|
|
440
|
-
return this.fetch(
|
|
441
|
-
`/companies/${this.companyId}/document_categories`,
|
|
442
|
-
{
|
|
443
|
-
headers: {
|
|
444
|
-
Accept: "application/vnd.tiime.documents.v3+json"
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
);
|
|
448
|
-
}
|
|
449
|
-
preview(documentId) {
|
|
450
|
-
return this.fetch(
|
|
451
|
-
`/companies/${this.companyId}/documents/${documentId}/preview`
|
|
452
|
-
);
|
|
453
|
-
}
|
|
454
|
-
upload(file, filename, type) {
|
|
455
|
-
const formData = new FormData();
|
|
456
|
-
formData.append("file", new Blob([file]), filename);
|
|
457
|
-
if (type) {
|
|
458
|
-
formData.append("type", type);
|
|
459
|
-
}
|
|
460
|
-
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
461
|
-
method: "POST",
|
|
462
|
-
body: formData
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
searchMatchable(query) {
|
|
466
|
-
return this.fetch(
|
|
467
|
-
`/companies/${this.companyId}/documents`,
|
|
468
|
-
{
|
|
469
|
-
query: { matchable: true, q: query },
|
|
470
|
-
headers: {
|
|
471
|
-
Accept: "application/vnd.tiime.documents.v3+json,application/vnd.tiime.docs.imputation+json",
|
|
472
|
-
Range: "items=0-25"
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
);
|
|
476
|
-
}
|
|
477
|
-
async download(documentId) {
|
|
478
|
-
return this.fetch(
|
|
479
|
-
`/companies/${this.companyId}/documents/${documentId}/download`,
|
|
480
|
-
{
|
|
481
|
-
headers: { Accept: "application/octet-stream" }
|
|
482
|
-
}
|
|
483
|
-
);
|
|
484
|
-
}
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
// src/resources/expense-reports.ts
|
|
488
|
-
var ExpenseReportsResource = class {
|
|
489
|
-
constructor(fetch, companyId) {
|
|
490
|
-
this.fetch = fetch;
|
|
491
|
-
this.companyId = companyId;
|
|
492
|
-
}
|
|
493
|
-
list(sorts = "metadata.date:desc") {
|
|
494
|
-
return this.fetch(
|
|
495
|
-
`/companies/${this.companyId}/expense_reports`,
|
|
496
|
-
{
|
|
497
|
-
query: { expand: "total_amount", sorts },
|
|
498
|
-
headers: { Range: "items=0-25" }
|
|
499
|
-
}
|
|
500
|
-
);
|
|
501
|
-
}
|
|
502
|
-
get(expenseReportId) {
|
|
503
|
-
return this.fetch(
|
|
504
|
-
`/companies/${this.companyId}/expense_reports/${expenseReportId}`
|
|
505
|
-
);
|
|
506
|
-
}
|
|
507
|
-
create(params) {
|
|
508
|
-
return this.fetch(
|
|
509
|
-
`/companies/${this.companyId}/expense_reports`,
|
|
510
|
-
{
|
|
511
|
-
method: "POST",
|
|
512
|
-
body: params
|
|
513
|
-
}
|
|
514
|
-
);
|
|
515
|
-
}
|
|
516
|
-
};
|
|
517
|
-
|
|
518
|
-
// src/resources/invoices.ts
|
|
519
|
-
var DEFAULT_INVOICE_TEMPLATE = {
|
|
520
|
-
template: "advanced",
|
|
521
|
-
status: "draft",
|
|
522
|
-
due_date_mode: "thirty_days",
|
|
523
|
-
title_enabled: true,
|
|
524
|
-
free_field_enabled: false,
|
|
525
|
-
free_field: "",
|
|
526
|
-
discount_enabled: false,
|
|
527
|
-
bank_detail_enabled: true,
|
|
528
|
-
payment_condition_enabled: true,
|
|
529
|
-
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.",
|
|
530
|
-
text_lines: []
|
|
531
|
-
};
|
|
532
|
-
var InvoicesResource = class {
|
|
533
|
-
constructor(fetch, companyId) {
|
|
534
|
-
this.fetch = fetch;
|
|
535
|
-
this.companyId = companyId;
|
|
536
|
-
}
|
|
537
|
-
list(params) {
|
|
538
|
-
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
539
|
-
const end = start + (params?.pageSize ?? 25);
|
|
540
|
-
const query = {
|
|
541
|
-
sorts: params?.sorts ?? "invoice_number:desc"
|
|
542
|
-
};
|
|
543
|
-
if (params?.status) query.status = params.status;
|
|
544
|
-
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
545
|
-
query,
|
|
546
|
-
headers: { Range: `items=${start}-${end}` }
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
async listAll(params) {
|
|
550
|
-
const pageSize = params?.pageSize ?? 100;
|
|
551
|
-
const all = [];
|
|
552
|
-
let page = 1;
|
|
553
|
-
let hasMore = true;
|
|
554
|
-
while (hasMore) {
|
|
555
|
-
const batch = await this.list({
|
|
556
|
-
sorts: params?.sorts,
|
|
557
|
-
status: params?.status,
|
|
558
|
-
page,
|
|
559
|
-
pageSize
|
|
560
|
-
});
|
|
561
|
-
all.push(...batch);
|
|
562
|
-
hasMore = batch.length === pageSize;
|
|
563
|
-
page++;
|
|
564
|
-
}
|
|
565
|
-
return all;
|
|
566
|
-
}
|
|
567
|
-
get(invoiceId) {
|
|
568
|
-
return this.fetch(
|
|
569
|
-
`/companies/${this.companyId}/invoices/${invoiceId}`
|
|
570
|
-
);
|
|
571
|
-
}
|
|
572
|
-
create(params) {
|
|
573
|
-
const body = {
|
|
574
|
-
...DEFAULT_INVOICE_TEMPLATE,
|
|
575
|
-
...params,
|
|
576
|
-
lines: params.lines?.map((line) => ({ ...line }))
|
|
577
|
-
};
|
|
578
|
-
for (const line of body.lines ?? []) {
|
|
579
|
-
line.line_amount = line.quantity * line.unit_amount;
|
|
580
|
-
line.sequence ??= 1;
|
|
581
|
-
line.invoicing_category_type ??= "benefit";
|
|
582
|
-
line.discount_description ??= "";
|
|
583
|
-
line.discount_amount ??= null;
|
|
584
|
-
line.discount_percentage ??= null;
|
|
585
|
-
}
|
|
586
|
-
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
587
|
-
method: "POST",
|
|
588
|
-
body
|
|
589
|
-
});
|
|
590
|
-
}
|
|
591
|
-
update(invoiceId, params) {
|
|
592
|
-
return this.fetch(
|
|
593
|
-
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
594
|
-
{
|
|
595
|
-
method: "PUT",
|
|
596
|
-
body: params
|
|
597
|
-
}
|
|
598
|
-
);
|
|
599
|
-
}
|
|
600
|
-
send(invoiceId, params) {
|
|
601
|
-
return this.fetch(
|
|
602
|
-
`/companies/${this.companyId}/invoices/${invoiceId}/send`,
|
|
603
|
-
{
|
|
604
|
-
method: "POST",
|
|
605
|
-
body: params
|
|
606
|
-
}
|
|
607
|
-
);
|
|
608
|
-
}
|
|
609
|
-
async downloadPdf(invoiceId) {
|
|
610
|
-
return this.fetch(
|
|
611
|
-
`/companies/${this.companyId}/invoices/${invoiceId}/pdf`,
|
|
612
|
-
{
|
|
613
|
-
headers: { Accept: "application/pdf" }
|
|
614
|
-
}
|
|
615
|
-
);
|
|
616
|
-
}
|
|
617
|
-
delete(invoiceId) {
|
|
618
|
-
return this.fetch(
|
|
619
|
-
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
620
|
-
{ method: "DELETE" }
|
|
621
|
-
);
|
|
622
|
-
}
|
|
623
|
-
async duplicate(invoiceId, overrides) {
|
|
624
|
-
const source = await this.get(invoiceId);
|
|
625
|
-
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
626
|
-
const lines = source.lines.map((l) => ({
|
|
627
|
-
description: l.description,
|
|
628
|
-
quantity: overrides?.quantity ?? l.quantity,
|
|
629
|
-
unit_amount: l.unit_amount,
|
|
630
|
-
vat_type: l.vat_type,
|
|
631
|
-
invoicing_unit: l.invoicing_unit,
|
|
632
|
-
invoicing_category_type: l.invoicing_category_type,
|
|
633
|
-
article: l.article
|
|
634
|
-
}));
|
|
635
|
-
return this.create({
|
|
636
|
-
client: source.client_id ? { id: source.client_id } : null,
|
|
637
|
-
emission_date: overrides?.emission_date ?? today,
|
|
638
|
-
title: source.title,
|
|
639
|
-
title_enabled: !!source.title,
|
|
640
|
-
lines,
|
|
641
|
-
status: "draft"
|
|
642
|
-
});
|
|
643
|
-
}
|
|
644
|
-
};
|
|
645
|
-
|
|
646
|
-
// src/resources/labels.ts
|
|
647
|
-
var LabelsResource = class {
|
|
648
|
-
constructor(fetch, companyId) {
|
|
649
|
-
this.fetch = fetch;
|
|
650
|
-
this.companyId = companyId;
|
|
651
|
-
}
|
|
652
|
-
list() {
|
|
653
|
-
return this.fetch(`/companies/${this.companyId}/labels`, {
|
|
654
|
-
headers: {
|
|
655
|
-
Accept: "application/vnd.tiime.labels.v2+json"
|
|
656
|
-
}
|
|
657
|
-
});
|
|
658
|
-
}
|
|
659
|
-
standard() {
|
|
660
|
-
return this.fetch(`/companies/${this.companyId}/standard_labels`);
|
|
661
|
-
}
|
|
662
|
-
tags() {
|
|
663
|
-
return this.fetch(`/companies/${this.companyId}/tags`, {
|
|
664
|
-
query: { expand: "tag_detail" }
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
// src/resources/quotations.ts
|
|
670
|
-
var QuotationsResource = class {
|
|
671
|
-
constructor(fetch, companyId) {
|
|
672
|
-
this.fetch = fetch;
|
|
673
|
-
this.companyId = companyId;
|
|
674
|
-
}
|
|
675
|
-
list(expand = "invoices") {
|
|
676
|
-
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
677
|
-
query: { expand },
|
|
678
|
-
headers: { Range: "items=0-25" }
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
get(quotationId) {
|
|
682
|
-
return this.fetch(
|
|
683
|
-
`/companies/${this.companyId}/quotations/${quotationId}`
|
|
684
|
-
);
|
|
685
|
-
}
|
|
686
|
-
create(params) {
|
|
687
|
-
const body = {
|
|
688
|
-
...params,
|
|
689
|
-
lines: params.lines?.map((line) => ({ ...line }))
|
|
690
|
-
};
|
|
691
|
-
for (const line of body.lines ?? []) {
|
|
692
|
-
line.line_amount = line.quantity * line.unit_amount;
|
|
693
|
-
line.sequence ??= 1;
|
|
694
|
-
line.invoicing_category_type ??= "benefit";
|
|
695
|
-
line.discount_description ??= "";
|
|
696
|
-
line.discount_amount ??= null;
|
|
697
|
-
line.discount_percentage ??= null;
|
|
698
|
-
}
|
|
699
|
-
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
700
|
-
method: "POST",
|
|
701
|
-
body
|
|
702
|
-
});
|
|
703
|
-
}
|
|
704
|
-
async downloadPdf(quotationId) {
|
|
705
|
-
return this.fetch(
|
|
706
|
-
`/companies/${this.companyId}/quotations/${quotationId}/pdf`,
|
|
707
|
-
{
|
|
708
|
-
headers: { Accept: "application/pdf" }
|
|
709
|
-
}
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
send(quotationId, params) {
|
|
713
|
-
return this.fetch(
|
|
714
|
-
`/companies/${this.companyId}/quotations/${quotationId}/send`,
|
|
715
|
-
{
|
|
716
|
-
method: "POST",
|
|
717
|
-
body: params
|
|
718
|
-
}
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
|
|
723
|
-
// src/resources/users.ts
|
|
724
|
-
var UsersResource = class {
|
|
725
|
-
constructor(fetch) {
|
|
726
|
-
this.fetch = fetch;
|
|
727
|
-
}
|
|
728
|
-
me() {
|
|
729
|
-
return this.fetch("/users/me");
|
|
730
|
-
}
|
|
731
|
-
legalInformations() {
|
|
732
|
-
return this.fetch("/users/me/legal_informations");
|
|
733
|
-
}
|
|
734
|
-
settings(companyId) {
|
|
735
|
-
return this.fetch(`/users/me/companies/${companyId}/settings`);
|
|
736
|
-
}
|
|
737
|
-
};
|
|
738
|
-
|
|
739
|
-
// src/client.ts
|
|
740
|
-
var BASE_URL = "https://chronos-api.tiime-apps.com/v1";
|
|
741
|
-
var TiimeClient = class {
|
|
742
|
-
fetch;
|
|
743
|
-
tokenManager;
|
|
744
|
-
companyId;
|
|
745
|
-
constructor(options = {}) {
|
|
746
|
-
this.companyId = resolveCompanyId(options.companyId);
|
|
747
|
-
this.tokenManager = options.tokenManager ?? new TokenManager({
|
|
748
|
-
tokens: options.tokens,
|
|
749
|
-
email: options.email,
|
|
750
|
-
password: options.password
|
|
751
|
-
});
|
|
752
|
-
this.fetch = ofetch2.create({
|
|
753
|
-
baseURL: BASE_URL,
|
|
754
|
-
retry: 2,
|
|
755
|
-
retryDelay: 500,
|
|
756
|
-
retryStatusCodes: [408, 429, 500, 502, 503, 504],
|
|
757
|
-
headers: {
|
|
758
|
-
"tiime-app": "tiime",
|
|
759
|
-
"tiime-app-version": "4.30.3",
|
|
760
|
-
"tiime-app-platform": "cli"
|
|
761
|
-
},
|
|
762
|
-
onRequest: async ({ options: options2 }) => {
|
|
763
|
-
const token = await this.tokenManager.getValidToken();
|
|
764
|
-
options2.headers.set("Authorization", `Bearer ${token}`);
|
|
765
|
-
},
|
|
766
|
-
onResponseError: ({ request, response }) => {
|
|
767
|
-
throw new TiimeError(
|
|
768
|
-
response.statusText || `HTTP ${response.status}`,
|
|
769
|
-
response.status,
|
|
770
|
-
String(request),
|
|
771
|
-
response._data
|
|
772
|
-
);
|
|
773
|
-
}
|
|
774
|
-
});
|
|
775
|
-
}
|
|
776
|
-
listCompanies() {
|
|
777
|
-
return this.fetch("/companies", {
|
|
778
|
-
headers: {
|
|
779
|
-
Accept: "application/vnd.tiime.companies.v2+json",
|
|
780
|
-
Range: "items=0-101"
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
get users() {
|
|
785
|
-
return new UsersResource(this.fetch);
|
|
786
|
-
}
|
|
787
|
-
get company() {
|
|
788
|
-
return new CompanyResource(this.fetch, this.companyId);
|
|
789
|
-
}
|
|
790
|
-
get clients() {
|
|
791
|
-
return new ClientsResource(this.fetch, this.companyId);
|
|
792
|
-
}
|
|
793
|
-
get invoices() {
|
|
794
|
-
return new InvoicesResource(this.fetch, this.companyId);
|
|
795
|
-
}
|
|
796
|
-
get quotations() {
|
|
797
|
-
return new QuotationsResource(this.fetch, this.companyId);
|
|
798
|
-
}
|
|
799
|
-
get bankAccounts() {
|
|
800
|
-
return new BankAccountsResource(this.fetch, this.companyId);
|
|
801
|
-
}
|
|
802
|
-
get bankTransactions() {
|
|
803
|
-
return new BankTransactionsResource(this.fetch, this.companyId);
|
|
804
|
-
}
|
|
805
|
-
get documents() {
|
|
806
|
-
return new DocumentsResource(this.fetch, this.companyId);
|
|
807
|
-
}
|
|
808
|
-
get expenseReports() {
|
|
809
|
-
return new ExpenseReportsResource(this.fetch, this.companyId);
|
|
810
|
-
}
|
|
811
|
-
get labels() {
|
|
812
|
-
return new LabelsResource(this.fetch, this.companyId);
|
|
813
|
-
}
|
|
814
|
-
};
|
|
815
|
-
export {
|
|
816
|
-
TiimeClient,
|
|
817
|
-
TiimeError,
|
|
818
|
-
TokenManager,
|
|
819
|
-
resolveCompanyId
|
|
820
|
-
};
|
|
1
|
+
import {execSync}from'child_process';import {existsSync,readFileSync,writeFileSync,mkdirSync}from'fs';import {homedir}from'os';import {join}from'path';var z=(s,e,t)=>{let n=new URL(e,s.endsWith("/")?s:`${s}/`);if(t)for(let[i,r]of Object.entries(t))r!=null&&n.searchParams.set(i,String(r));return n.href},U=s=>new Promise(e=>setTimeout(e,s)),J=s=>!!s?.includes("application/json")||!!s?.includes("+json"),D=s=>{let e=s.retry??0,t=s.retryDelay??500,n=new Set(s.retryStatusCodes??[]);return async(i,r)=>{let o=z(s.baseURL,i,r?.query),m=new Headers(s.headers);if(r?.headers)for(let[c,a]of Object.entries(r.headers))m.set(c,a);let u;r?.body!==void 0&&(r.body instanceof FormData?u=r.body:(m.set("Content-Type","application/json"),u=JSON.stringify(r.body))),s.onRequest&&await s.onRequest({options:{headers:m}});let h;for(let c=0;c<=e;c++){c>0&&await U(t);let a;try{a=await fetch(o,{method:r?.method??"GET",headers:m,body:u});}catch(l){if(h=l,c<e)continue;throw l}if(!a.ok&&n.has(a.status)&&(h=a,c<e))continue;if(!a.ok&&s.onResponseError){let l;try{l=await a.clone().json();}catch{}let L=Object.assign(a,{_data:l});s.onResponseError({request:o,response:L});}let R=a.headers.get("content-type");return a.status===204||!R?void 0:J(R)?a.json():a.arrayBuffer()}throw h}},q=async(s,e)=>{let t=await fetch(s,e);if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);return t.json()};var Q="auth0.tiime.fr",G="iEbsbe3o66gcTBfGRa012kj1Rb6vjAND",K="https://chronos/",p=join(homedir(),".config","tiime"),f=join(p,"auth.json"),O=join(p,"config.json"),N="tiime-cli",B="tiime-credentials",V=(s,e)=>{try{let t=JSON.stringify({email:s,password:e});return execSync(`security add-generic-password -a "${N}" -s "${B}" -w '${t.replace(/'/g,"'\\''")}' -U`,{stdio:"ignore"}),!0}catch{return false}},W=()=>{try{let s=execSync(`security find-generic-password -a "${N}" -s "${B}" -w`,{encoding:"utf-8",stdio:["ignore","pipe","ignore"]}).trim(),e=JSON.parse(s);return typeof e=="object"&&e!==null&&"email"in e&&"password"in e&&typeof e.email=="string"&&typeof e.password=="string"?e:null}catch{return null}},Y=(s,e)=>{existsSync(p)||mkdirSync(p,{recursive:true});let t=join(p,"credentials.json");writeFileSync(t,JSON.stringify({email:s,password:e},null,2),{mode:384});},X=()=>{try{let s=join(p,"credentials.json");if(existsSync(s)){let e=JSON.parse(readFileSync(s,"utf-8"));if(typeof e=="object"&&e!==null&&"email"in e&&"password"in e&&typeof e.email=="string"&&typeof e.password=="string")return e}}catch{}return null},Z=(s,e)=>{V(s,e)||Y(s,e);},ee=()=>W()??X(),A=s=>{if(s)return s;let e=process.env.TIIME_COMPANY_ID;if(e){let t=Number.parseInt(e,10);if(!Number.isNaN(t))return t}try{if(existsSync(O)){let t=JSON.parse(readFileSync(O,"utf-8"));if(t.companyId)return t.companyId}}catch{}throw new Error("No company ID configured. Set TIIME_COMPANY_ID env var, pass companyId option, or run `tiime company use --id <ID>`.")},y=class{tokens=null;credentials=null;persist;constructor(e={}){let t=e.tokens||e.email&&e.password;if(this.persist=e.persist??!t,e.tokens){this.tokens=e.tokens;return}if(e.email&&e.password){this.credentials={email:e.email,password:e.password};return}let n=process.env.TIIME_ACCESS_TOKEN;if(n){this.tokens={access_token:n,expires_at:Number.MAX_SAFE_INTEGER};return}let i=process.env.TIIME_EMAIL,r=process.env.TIIME_PASSWORD;if(i&&r){this.credentials={email:i,password:r};return}this.loadFromDisk();}async login(e,t){let n=await q(`https://${Q}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"password",client_id:G,audience:K,scope:"openid email",username:e,password:t})});return this.tokens={access_token:n.access_token,expires_at:Date.now()+n.expires_in*1e3},this.persist&&(this.saveToDisk(),Z(e,t)),this.tokens}async getValidToken(){if(!this.tokens||this.isExpired()){let e=this.credentials??ee();if(e)return (await this.login(e.email,e.password)).access_token;throw new Error(this.tokens?"Token expired. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.":"Not authenticated. Provide credentials via options, TIIME_EMAIL/TIIME_PASSWORD env vars, or run `tiime auth login`.")}return this.tokens.access_token}isAuthenticated(){return this.tokens!==null&&!this.isExpired()}logout(){this.tokens=null,existsSync(f)&&writeFileSync(f,"{}");}getTokenInfo(){if(!this.tokens)return {email:null,expiresAt:null};try{return {email:JSON.parse(Buffer.from(this.tokens.access_token.split(".")[1],"base64").toString())["tiime/userEmail"]||null,expiresAt:new Date(this.tokens.expires_at)}}catch{return {email:null,expiresAt:new Date(this.tokens.expires_at)}}}isExpired(){return this.tokens?Date.now()>=this.tokens.expires_at-6e4:true}loadFromDisk(){try{if(existsSync(f)){let e=JSON.parse(readFileSync(f,"utf-8"));e.access_token&&e.expires_at&&(this.tokens=e);}}catch{}}saveToDisk(){existsSync(p)||mkdirSync(p,{recursive:true}),writeFileSync(f,JSON.stringify(this.tokens,null,2));}};var b=class extends Error{constructor(t,n,i,r){super(t);this.status=n;this.endpoint=i;this.details=r;this.name="TiimeError";}toJSON(){return {error:this.name,message:this.message,status:this.status,endpoint:this.endpoint,details:this.details}}};var _=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){return this.fetch(`/companies/${this.companyId}/bank_accounts`,{query:e!==void 0?{enabled:e}:void 0})}get(e){return this.fetch(`/companies/${this.companyId}/bank_accounts/${e}`)}async balance(){return (await this.list(true)).map(t=>({name:t.name,balance_amount:t.balance_amount,currency:t.balance_currency}))}};var I=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??100),n=t+(e?.pageSize??100),{page:i,pageSize:r,from:o,to:m,search:u,...h}=e??{},c={...h};return o&&(c.transaction_date_start=o),m&&(c.transaction_date_end=m),u&&(c.wording=u),this.fetch(`/companies/${this.companyId}/bank_transactions`,{query:{hide_refused:false,...c},headers:{Accept:"application/vnd.tiime.bank_transactions.v2+json",Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??200,n=[],i=1,r=true;for(;r;){let o=await this.list({...e,page:i,pageSize:t});n.push(...o.transactions),r=o.transactions.length===t,i++;}return n}unimputed(){return this.fetch(`/companies/${this.companyId}/bank_transactions/unimputed`)}get(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}`)}labelSuggestions(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/label_suggestions`,{headers:{Accept:"application/vnd.tiime.bank_transactions.label_suggestions.v2+json"}})}impute(e,t){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}`,{method:"PATCH",body:{imputations:t}})}matchDocuments(e,t){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/document_matchings`,{method:"PUT",body:{documents:t.map(n=>({id:n}))}})}getMatchings(e){return this.fetch(`/companies/${this.companyId}/bank_transactions/${e}/matchings`)}};var v=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){return this.fetch(`/companies/${this.companyId}/clients`,{query:e,headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}get(e){return this.fetch(`/companies/${this.companyId}/clients/${e}`)}create(e){return this.fetch(`/companies/${this.companyId}/clients`,{method:"POST",body:e})}search(e){return this.fetch(`/companies/${this.companyId}/clients`,{query:{search:e},headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}};var k=class{constructor(e,t){this.fetch=e;this.companyId=t;}get(){return this.fetch(`/companies/${this.companyId}`)}users(){return this.fetch(`/companies/${this.companyId}/users`)}appConfig(){return this.fetch(`/companies/${this.companyId}/app_config`)}accountingPeriod(e=1){return this.fetch(`/companies/${this.companyId}/accounting_period/current`,{query:{range_year:e}})}tiles(e){return this.fetch(`/companies/${this.companyId}/tiles`,{query:{keys:e.join(",")}})}dashboardBlocks(e="monitoring"){return this.fetch(`/companies/${this.companyId}/dashboard_blocks`,{query:{sorts:"rank:asc",display_group:e}})}};var w=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),{page:i,pageSize:r,...o}=e??{};return this.fetch(`/companies/${this.companyId}/documents`,{query:{sorts:"created_at:desc",expand:"file_family,preview_available",...o},headers:{Accept:"application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",Range:`items=${t}-${n}`}})}categories(){return this.fetch(`/companies/${this.companyId}/document_categories`,{headers:{Accept:"application/vnd.tiime.documents.v3+json"}})}preview(e){return this.fetch(`/companies/${this.companyId}/documents/${e}/preview`)}upload(e,t,n){let i=new FormData;return i.append("file",new Blob([e]),t),n&&i.append("type",n),this.fetch(`/companies/${this.companyId}/documents`,{method:"POST",body:i})}searchMatchable(e){return this.fetch(`/companies/${this.companyId}/documents`,{query:{matchable:true,q:e},headers:{Accept:"application/vnd.tiime.documents.v3+json,application/vnd.tiime.docs.imputation+json",Range:"items=0-25"}})}async download(e){return this.fetch(`/companies/${this.companyId}/documents/${e}/download`,{headers:{Accept:"application/octet-stream"}})}};var T=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e="metadata.date:desc"){return this.fetch(`/companies/${this.companyId}/expense_reports`,{query:{expand:"total_amount",sorts:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(`/companies/${this.companyId}/expense_reports/${e}`)}create(e){return this.fetch(`/companies/${this.companyId}/expense_reports`,{method:"POST",body:e})}};var te={template:"advanced",status:"draft",due_date_mode:"thirty_days",title_enabled:true,free_field_enabled:false,free_field:"",discount_enabled:false,bank_detail_enabled:true,payment_condition_enabled:true,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.",text_lines:[]},P=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),i={sorts:e?.sorts??"invoice_number:desc"};return e?.status&&(i.status=e.status),this.fetch(`/companies/${this.companyId}/invoices`,{query:i,headers:{Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??100,n=[],i=1,r=true;for(;r;){let o=await this.list({sorts:e?.sorts,status:e?.status,page:i,pageSize:t});n.push(...o),r=o.length===t,i++;}return n}get(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}`)}create(e){let t={...te,...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(`/companies/${this.companyId}/invoices`,{method:"POST",body:t})}update(e,t){return this.fetch(`/companies/${this.companyId}/invoices/${e}`,{method:"PUT",body:t})}send(e,t){return this.fetch(`/companies/${this.companyId}/invoices/${e}/send`,{method:"POST",body:t})}async downloadPdf(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}/pdf`,{headers:{Accept:"application/pdf"}})}delete(e){return this.fetch(`/companies/${this.companyId}/invoices/${e}`,{method:"DELETE"})}async duplicate(e,t){let n=await this.get(e),i=new Date().toISOString().split("T")[0],r=n.lines.map(o=>({description:o.description,quantity:t?.quantity??o.quantity,unit_amount:o.unit_amount,vat_type:o.vat_type,invoicing_unit:o.invoicing_unit,invoicing_category_type:o.invoicing_category_type,article:o.article}));return this.create({client:n.client_id?{id:n.client_id}:null,emission_date:t?.emission_date??i,title:n.title,title_enabled:!!n.title,lines:r,status:"draft"})}};var $=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(){return this.fetch(`/companies/${this.companyId}/labels`,{headers:{Accept:"application/vnd.tiime.labels.v2+json"}})}standard(){return this.fetch(`/companies/${this.companyId}/standard_labels`)}tags(){return this.fetch(`/companies/${this.companyId}/tags`,{query:{expand:"tag_detail"}})}};var C=class{constructor(e,t){this.fetch=e;this.companyId=t;}list(e="invoices"){return this.fetch(`/companies/${this.companyId}/quotations`,{query:{expand:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(`/companies/${this.companyId}/quotations/${e}`)}create(e){let t={...e,lines:e.lines?.map(n=>({...n}))};for(let n of t.lines??[])n.line_amount=n.quantity*n.unit_amount,n.sequence??=1,n.invoicing_category_type??="benefit",n.discount_description??="",n.discount_amount??=null,n.discount_percentage??=null;return this.fetch(`/companies/${this.companyId}/quotations`,{method:"POST",body:t})}async downloadPdf(e){return this.fetch(`/companies/${this.companyId}/quotations/${e}/pdf`,{headers:{Accept:"application/pdf"}})}send(e,t){return this.fetch(`/companies/${this.companyId}/quotations/${e}/send`,{method:"POST",body:t})}};var F=class{constructor(e){this.fetch=e;}me(){return this.fetch("/users/me")}legalInformations(){return this.fetch("/users/me/legal_informations")}settings(e){return this.fetch(`/users/me/companies/${e}/settings`)}};var ne="https://chronos-api.tiime-apps.com/v1",E=class{fetch;tokenManager;companyId;constructor(e={}){this.companyId=A(e.companyId),this.tokenManager=e.tokenManager??new y({tokens:e.tokens,email:e.email,password:e.password}),this.fetch=D({baseURL:ne,retry:2,retryDelay:500,retryStatusCodes:[408,429,500,502,503,504],headers:{"tiime-app":"tiime","tiime-app-version":"4.30.3","tiime-app-platform":"cli"},onRequest:async({options:t})=>{let n=await this.tokenManager.getValidToken();t.headers.set("Authorization",`Bearer ${n}`);},onResponseError:({request:t,response:n})=>{throw new b(n.statusText||`HTTP ${n.status}`,n.status,String(t),n._data)}});}listCompanies(){return this.fetch("/companies",{headers:{Accept:"application/vnd.tiime.companies.v2+json",Range:"items=0-101"}})}get users(){return new F(this.fetch)}get company(){return new k(this.fetch,this.companyId)}get clients(){return new v(this.fetch,this.companyId)}get invoices(){return new P(this.fetch,this.companyId)}get quotations(){return new C(this.fetch,this.companyId)}get bankAccounts(){return new _(this.fetch,this.companyId)}get bankTransactions(){return new I(this.fetch,this.companyId)}get documents(){return new w(this.fetch,this.companyId)}get expenseReports(){return new T(this.fetch,this.companyId)}get labels(){return new $(this.fetch,this.companyId)}};
|
|
2
|
+
export{E as TiimeClient,b as TiimeError,y as TokenManager,A as resolveCompanyId};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiime-sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "TypeScript SDK for Tiime accounting API",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -44,9 +44,6 @@
|
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=20"
|
|
46
46
|
},
|
|
47
|
-
"dependencies": {
|
|
48
|
-
"ofetch": "^1.5.1"
|
|
49
|
-
},
|
|
50
47
|
"devDependencies": {
|
|
51
48
|
"@biomejs/biome": "^2.4.6",
|
|
52
49
|
"@types/node": "^25.3.5",
|