tiime-cli 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +427 -0
- package/dist/cli.js +2639 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.js +694 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,694 @@
|
|
|
1
|
+
// src/sdk/auth.ts
|
|
2
|
+
import { execSync } from "child_process";
|
|
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 KEYCHAIN_ACCOUNT = "tiime-cli";
|
|
13
|
+
var KEYCHAIN_SERVICE = "tiime-credentials";
|
|
14
|
+
var saveCredentialsToKeychain = (email, password) => {
|
|
15
|
+
try {
|
|
16
|
+
const payload = JSON.stringify({ email, password });
|
|
17
|
+
execSync(
|
|
18
|
+
`security add-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w '${payload.replace(/'/g, "'\\''")}' -U`,
|
|
19
|
+
{ stdio: "ignore" }
|
|
20
|
+
);
|
|
21
|
+
return true;
|
|
22
|
+
} catch {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var loadCredentialsFromKeychain = () => {
|
|
27
|
+
try {
|
|
28
|
+
const raw = execSync(
|
|
29
|
+
`security find-generic-password -a "${KEYCHAIN_ACCOUNT}" -s "${KEYCHAIN_SERVICE}" -w`,
|
|
30
|
+
{ encoding: "utf-8", stdio: ["ignore", "pipe", "ignore"] }
|
|
31
|
+
).trim();
|
|
32
|
+
const data = JSON.parse(raw);
|
|
33
|
+
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
34
|
+
return data;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var saveCredentialsToFile = (email, password) => {
|
|
42
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
43
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
44
|
+
}
|
|
45
|
+
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
46
|
+
writeFileSync(filePath, JSON.stringify({ email, password }, null, 2), {
|
|
47
|
+
mode: 384
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
var loadCredentialsFromFile = () => {
|
|
51
|
+
try {
|
|
52
|
+
const filePath = join(CONFIG_DIR, "credentials.json");
|
|
53
|
+
if (existsSync(filePath)) {
|
|
54
|
+
const data = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
55
|
+
if (typeof data === "object" && data !== null && "email" in data && "password" in data && typeof data.email === "string" && typeof data.password === "string") {
|
|
56
|
+
return data;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
};
|
|
63
|
+
var saveCredentials = (email, password) => {
|
|
64
|
+
if (!saveCredentialsToKeychain(email, password)) {
|
|
65
|
+
saveCredentialsToFile(email, password);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var loadCredentials = () => {
|
|
69
|
+
return loadCredentialsFromKeychain() ?? loadCredentialsFromFile();
|
|
70
|
+
};
|
|
71
|
+
var TokenManager = class {
|
|
72
|
+
tokens = null;
|
|
73
|
+
constructor() {
|
|
74
|
+
this.loadFromDisk();
|
|
75
|
+
}
|
|
76
|
+
async login(email, password) {
|
|
77
|
+
const response = await ofetch(`https://${AUTH0_DOMAIN}/oauth/token`, {
|
|
78
|
+
method: "POST",
|
|
79
|
+
body: {
|
|
80
|
+
grant_type: "password",
|
|
81
|
+
client_id: AUTH0_CLIENT_ID,
|
|
82
|
+
audience: AUTH0_AUDIENCE,
|
|
83
|
+
scope: "openid email",
|
|
84
|
+
username: email,
|
|
85
|
+
password
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
this.tokens = {
|
|
89
|
+
access_token: response.access_token,
|
|
90
|
+
expires_at: Date.now() + response.expires_in * 1e3
|
|
91
|
+
};
|
|
92
|
+
this.saveToDisk();
|
|
93
|
+
saveCredentials(email, password);
|
|
94
|
+
return this.tokens;
|
|
95
|
+
}
|
|
96
|
+
async getValidToken() {
|
|
97
|
+
if (!this.tokens || this.isExpired()) {
|
|
98
|
+
const creds = loadCredentials();
|
|
99
|
+
if (creds) {
|
|
100
|
+
const tokens = await this.login(creds.email, creds.password);
|
|
101
|
+
return tokens.access_token;
|
|
102
|
+
}
|
|
103
|
+
throw new Error(
|
|
104
|
+
this.tokens ? "Token expired. Run `tiime auth login` to re-authenticate." : "Not authenticated. Run `tiime auth login` first."
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
return this.tokens.access_token;
|
|
108
|
+
}
|
|
109
|
+
isAuthenticated() {
|
|
110
|
+
return this.tokens !== null && !this.isExpired();
|
|
111
|
+
}
|
|
112
|
+
logout() {
|
|
113
|
+
this.tokens = null;
|
|
114
|
+
if (existsSync(AUTH_FILE)) {
|
|
115
|
+
writeFileSync(AUTH_FILE, "{}");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
getTokenInfo() {
|
|
119
|
+
if (!this.tokens) {
|
|
120
|
+
return { email: null, expiresAt: null };
|
|
121
|
+
}
|
|
122
|
+
try {
|
|
123
|
+
const payload = JSON.parse(
|
|
124
|
+
Buffer.from(
|
|
125
|
+
this.tokens.access_token.split(".")[1],
|
|
126
|
+
"base64"
|
|
127
|
+
).toString()
|
|
128
|
+
);
|
|
129
|
+
return {
|
|
130
|
+
email: payload["tiime/userEmail"] || null,
|
|
131
|
+
expiresAt: new Date(this.tokens.expires_at)
|
|
132
|
+
};
|
|
133
|
+
} catch {
|
|
134
|
+
return { email: null, expiresAt: new Date(this.tokens.expires_at) };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
isExpired() {
|
|
138
|
+
if (!this.tokens) return true;
|
|
139
|
+
return Date.now() >= this.tokens.expires_at - 6e4;
|
|
140
|
+
}
|
|
141
|
+
loadFromDisk() {
|
|
142
|
+
try {
|
|
143
|
+
if (existsSync(AUTH_FILE)) {
|
|
144
|
+
const data = JSON.parse(readFileSync(AUTH_FILE, "utf-8"));
|
|
145
|
+
if (data.access_token && data.expires_at) {
|
|
146
|
+
this.tokens = data;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
} catch {
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
saveToDisk() {
|
|
153
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
154
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
writeFileSync(AUTH_FILE, JSON.stringify(this.tokens, null, 2));
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/sdk/client.ts
|
|
161
|
+
import { ofetch as ofetch2 } from "ofetch";
|
|
162
|
+
|
|
163
|
+
// src/sdk/errors.ts
|
|
164
|
+
var TiimeError = class extends Error {
|
|
165
|
+
constructor(message, status, endpoint, details) {
|
|
166
|
+
super(message);
|
|
167
|
+
this.status = status;
|
|
168
|
+
this.endpoint = endpoint;
|
|
169
|
+
this.details = details;
|
|
170
|
+
this.name = "TiimeError";
|
|
171
|
+
}
|
|
172
|
+
toJSON() {
|
|
173
|
+
return {
|
|
174
|
+
error: this.name,
|
|
175
|
+
message: this.message,
|
|
176
|
+
status: this.status,
|
|
177
|
+
endpoint: this.endpoint,
|
|
178
|
+
details: this.details
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/sdk/resources/bank-accounts.ts
|
|
184
|
+
var BankAccountsResource = class {
|
|
185
|
+
constructor(fetch, companyId) {
|
|
186
|
+
this.fetch = fetch;
|
|
187
|
+
this.companyId = companyId;
|
|
188
|
+
}
|
|
189
|
+
list(enabled) {
|
|
190
|
+
return this.fetch(
|
|
191
|
+
`/companies/${this.companyId}/bank_accounts`,
|
|
192
|
+
{ query: enabled !== void 0 ? { enabled } : void 0 }
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
get(bankAccountId) {
|
|
196
|
+
return this.fetch(
|
|
197
|
+
`/companies/${this.companyId}/bank_accounts/${bankAccountId}`
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
async balance() {
|
|
201
|
+
const accounts = await this.list(true);
|
|
202
|
+
return accounts.map((a) => ({
|
|
203
|
+
name: a.name,
|
|
204
|
+
balance_amount: a.balance_amount,
|
|
205
|
+
currency: a.balance_currency
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/sdk/resources/bank-transactions.ts
|
|
211
|
+
var BankTransactionsResource = class {
|
|
212
|
+
constructor(fetch, companyId) {
|
|
213
|
+
this.fetch = fetch;
|
|
214
|
+
this.companyId = companyId;
|
|
215
|
+
}
|
|
216
|
+
list(params) {
|
|
217
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 100);
|
|
218
|
+
const end = start + (params?.pageSize ?? 100);
|
|
219
|
+
const { page: _, pageSize: __, from, to, search, ...query } = params ?? {};
|
|
220
|
+
if (from) query.transaction_date_start = from;
|
|
221
|
+
if (to) query.transaction_date_end = to;
|
|
222
|
+
if (search) query.wording = search;
|
|
223
|
+
return this.fetch(
|
|
224
|
+
`/companies/${this.companyId}/bank_transactions`,
|
|
225
|
+
{
|
|
226
|
+
query: { hide_refused: false, ...query },
|
|
227
|
+
headers: {
|
|
228
|
+
Accept: "application/vnd.tiime.bank_transactions.v2+json,application/vnd.tiime.bank_transactions.without_documents+json",
|
|
229
|
+
Range: `items=${start}-${end}`
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
async listAll(params) {
|
|
235
|
+
const pageSize = params?.pageSize ?? 200;
|
|
236
|
+
const all = [];
|
|
237
|
+
let page = 1;
|
|
238
|
+
let hasMore = true;
|
|
239
|
+
while (hasMore) {
|
|
240
|
+
const response = await this.list({ ...params, page, pageSize });
|
|
241
|
+
all.push(...response.transactions);
|
|
242
|
+
hasMore = response.transactions.length === pageSize;
|
|
243
|
+
page++;
|
|
244
|
+
}
|
|
245
|
+
return all;
|
|
246
|
+
}
|
|
247
|
+
unimputed() {
|
|
248
|
+
return this.fetch(
|
|
249
|
+
`/companies/${this.companyId}/bank_transactions/unimputed`
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
// src/sdk/resources/clients.ts
|
|
255
|
+
var ClientsResource = class {
|
|
256
|
+
constructor(fetch, companyId) {
|
|
257
|
+
this.fetch = fetch;
|
|
258
|
+
this.companyId = companyId;
|
|
259
|
+
}
|
|
260
|
+
list(params) {
|
|
261
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
262
|
+
query: params,
|
|
263
|
+
headers: {
|
|
264
|
+
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
265
|
+
Range: "items=0-*"
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
get(clientId) {
|
|
270
|
+
return this.fetch(
|
|
271
|
+
`/companies/${this.companyId}/clients/${clientId}`
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
create(params) {
|
|
275
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
276
|
+
method: "POST",
|
|
277
|
+
body: params
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
search(query) {
|
|
281
|
+
return this.fetch(`/companies/${this.companyId}/clients`, {
|
|
282
|
+
query: { search: query },
|
|
283
|
+
headers: {
|
|
284
|
+
Accept: "application/vnd.tiime.timeline.v2+json",
|
|
285
|
+
Range: "items=0-*"
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
// src/sdk/resources/company.ts
|
|
292
|
+
var CompanyResource = class {
|
|
293
|
+
constructor(fetch, companyId) {
|
|
294
|
+
this.fetch = fetch;
|
|
295
|
+
this.companyId = companyId;
|
|
296
|
+
}
|
|
297
|
+
get() {
|
|
298
|
+
return this.fetch(`/companies/${this.companyId}`);
|
|
299
|
+
}
|
|
300
|
+
users() {
|
|
301
|
+
return this.fetch(`/companies/${this.companyId}/users`);
|
|
302
|
+
}
|
|
303
|
+
appConfig() {
|
|
304
|
+
return this.fetch(`/companies/${this.companyId}/app_config`);
|
|
305
|
+
}
|
|
306
|
+
accountingPeriod(rangeYear = 1) {
|
|
307
|
+
return this.fetch(
|
|
308
|
+
`/companies/${this.companyId}/accounting_period/current`,
|
|
309
|
+
{ query: { range_year: rangeYear } }
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
tiles(keys) {
|
|
313
|
+
return this.fetch(`/companies/${this.companyId}/tiles`, {
|
|
314
|
+
query: { keys: keys.join(",") }
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
dashboardBlocks(displayGroup = "monitoring") {
|
|
318
|
+
return this.fetch(`/companies/${this.companyId}/dashboard_blocks`, {
|
|
319
|
+
query: { sorts: "rank:asc", display_group: displayGroup }
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// src/sdk/resources/documents.ts
|
|
325
|
+
var DocumentsResource = class {
|
|
326
|
+
constructor(fetch, companyId) {
|
|
327
|
+
this.fetch = fetch;
|
|
328
|
+
this.companyId = companyId;
|
|
329
|
+
}
|
|
330
|
+
list(params) {
|
|
331
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
332
|
+
const end = start + (params?.pageSize ?? 25);
|
|
333
|
+
const { page: _, pageSize: __, ...query } = params ?? {};
|
|
334
|
+
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
335
|
+
query: {
|
|
336
|
+
sorts: "created_at:desc",
|
|
337
|
+
expand: "file_family,preview_available",
|
|
338
|
+
...query
|
|
339
|
+
},
|
|
340
|
+
headers: {
|
|
341
|
+
Accept: "application/vnd.tiime.documents.v2+json,application/vnd.tiime.docs.query+json,application/vnd.tiime.docs.imputation+json",
|
|
342
|
+
Range: `items=${start}-${end}`
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
categories() {
|
|
347
|
+
return this.fetch(
|
|
348
|
+
`/companies/${this.companyId}/document_categories`,
|
|
349
|
+
{
|
|
350
|
+
headers: {
|
|
351
|
+
Accept: "application/vnd.tiime.documents.v3+json"
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
preview(documentId) {
|
|
357
|
+
return this.fetch(
|
|
358
|
+
`/companies/${this.companyId}/documents/${documentId}/preview`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
upload(file, filename, type) {
|
|
362
|
+
const formData = new FormData();
|
|
363
|
+
formData.append("file", new Blob([file]), filename);
|
|
364
|
+
if (type) {
|
|
365
|
+
formData.append("type", type);
|
|
366
|
+
}
|
|
367
|
+
return this.fetch(`/companies/${this.companyId}/documents`, {
|
|
368
|
+
method: "POST",
|
|
369
|
+
body: formData
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
async download(documentId) {
|
|
373
|
+
return this.fetch(
|
|
374
|
+
`/companies/${this.companyId}/documents/${documentId}/download`,
|
|
375
|
+
{
|
|
376
|
+
headers: { Accept: "application/octet-stream" }
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
// src/sdk/resources/expense-reports.ts
|
|
383
|
+
var ExpenseReportsResource = class {
|
|
384
|
+
constructor(fetch, companyId) {
|
|
385
|
+
this.fetch = fetch;
|
|
386
|
+
this.companyId = companyId;
|
|
387
|
+
}
|
|
388
|
+
list(sorts = "metadata.date:desc") {
|
|
389
|
+
return this.fetch(
|
|
390
|
+
`/companies/${this.companyId}/expense_reports`,
|
|
391
|
+
{
|
|
392
|
+
query: { expand: "total_amount", sorts },
|
|
393
|
+
headers: { Range: "items=0-25" }
|
|
394
|
+
}
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
get(expenseReportId) {
|
|
398
|
+
return this.fetch(
|
|
399
|
+
`/companies/${this.companyId}/expense_reports/${expenseReportId}`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
create(params) {
|
|
403
|
+
return this.fetch(
|
|
404
|
+
`/companies/${this.companyId}/expense_reports`,
|
|
405
|
+
{
|
|
406
|
+
method: "POST",
|
|
407
|
+
body: params
|
|
408
|
+
}
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// src/sdk/resources/invoices.ts
|
|
414
|
+
var DEFAULT_INVOICE_TEMPLATE = {
|
|
415
|
+
template: "advanced",
|
|
416
|
+
status: "draft",
|
|
417
|
+
due_date_mode: "thirty_days",
|
|
418
|
+
title_enabled: true,
|
|
419
|
+
free_field_enabled: false,
|
|
420
|
+
free_field: "",
|
|
421
|
+
discount_enabled: false,
|
|
422
|
+
bank_detail_enabled: true,
|
|
423
|
+
payment_condition_enabled: true,
|
|
424
|
+
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.",
|
|
425
|
+
text_lines: []
|
|
426
|
+
};
|
|
427
|
+
var InvoicesResource = class {
|
|
428
|
+
constructor(fetch, companyId) {
|
|
429
|
+
this.fetch = fetch;
|
|
430
|
+
this.companyId = companyId;
|
|
431
|
+
}
|
|
432
|
+
list(params) {
|
|
433
|
+
const start = ((params?.page ?? 1) - 1) * (params?.pageSize ?? 25);
|
|
434
|
+
const end = start + (params?.pageSize ?? 25);
|
|
435
|
+
const query = {
|
|
436
|
+
sorts: params?.sorts ?? "invoice_number:desc"
|
|
437
|
+
};
|
|
438
|
+
if (params?.status) query.status = params.status;
|
|
439
|
+
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
440
|
+
query,
|
|
441
|
+
headers: { Range: `items=${start}-${end}` }
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
async listAll(params) {
|
|
445
|
+
const pageSize = params?.pageSize ?? 100;
|
|
446
|
+
const all = [];
|
|
447
|
+
let page = 1;
|
|
448
|
+
let hasMore = true;
|
|
449
|
+
while (hasMore) {
|
|
450
|
+
const batch = await this.list({
|
|
451
|
+
sorts: params?.sorts,
|
|
452
|
+
status: params?.status,
|
|
453
|
+
page,
|
|
454
|
+
pageSize
|
|
455
|
+
});
|
|
456
|
+
all.push(...batch);
|
|
457
|
+
hasMore = batch.length === pageSize;
|
|
458
|
+
page++;
|
|
459
|
+
}
|
|
460
|
+
return all;
|
|
461
|
+
}
|
|
462
|
+
get(invoiceId) {
|
|
463
|
+
return this.fetch(
|
|
464
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
create(params) {
|
|
468
|
+
const body = { ...DEFAULT_INVOICE_TEMPLATE, ...params };
|
|
469
|
+
for (const line of body.lines ?? []) {
|
|
470
|
+
line.line_amount = line.quantity * line.unit_amount;
|
|
471
|
+
line.sequence ??= 1;
|
|
472
|
+
line.invoicing_category_type ??= "benefit";
|
|
473
|
+
line.discount_description ??= "";
|
|
474
|
+
line.discount_amount ??= null;
|
|
475
|
+
line.discount_percentage ??= null;
|
|
476
|
+
}
|
|
477
|
+
return this.fetch(`/companies/${this.companyId}/invoices`, {
|
|
478
|
+
method: "POST",
|
|
479
|
+
body
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
update(invoiceId, params) {
|
|
483
|
+
return this.fetch(
|
|
484
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
485
|
+
{
|
|
486
|
+
method: "PUT",
|
|
487
|
+
body: params
|
|
488
|
+
}
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
send(invoiceId, params) {
|
|
492
|
+
return this.fetch(
|
|
493
|
+
`/companies/${this.companyId}/invoices/${invoiceId}/send`,
|
|
494
|
+
{
|
|
495
|
+
method: "POST",
|
|
496
|
+
body: params
|
|
497
|
+
}
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
async downloadPdf(invoiceId) {
|
|
501
|
+
return this.fetch(
|
|
502
|
+
`/companies/${this.companyId}/invoices/${invoiceId}/pdf`,
|
|
503
|
+
{
|
|
504
|
+
headers: { Accept: "application/pdf" }
|
|
505
|
+
}
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
delete(invoiceId) {
|
|
509
|
+
return this.fetch(
|
|
510
|
+
`/companies/${this.companyId}/invoices/${invoiceId}`,
|
|
511
|
+
{ method: "DELETE" }
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
async duplicate(invoiceId, overrides) {
|
|
515
|
+
const source = await this.get(invoiceId);
|
|
516
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
517
|
+
const lines = source.lines.map((l) => ({
|
|
518
|
+
description: l.description,
|
|
519
|
+
quantity: overrides?.quantity ?? l.quantity,
|
|
520
|
+
unit_amount: l.unit_amount,
|
|
521
|
+
vat_type: l.vat_type,
|
|
522
|
+
invoicing_unit: l.invoicing_unit,
|
|
523
|
+
invoicing_category_type: l.invoicing_category_type,
|
|
524
|
+
article: l.article
|
|
525
|
+
}));
|
|
526
|
+
return this.create({
|
|
527
|
+
client: source.client_id ? { id: source.client_id } : null,
|
|
528
|
+
emission_date: overrides?.emission_date ?? today,
|
|
529
|
+
title: source.title,
|
|
530
|
+
title_enabled: !!source.title,
|
|
531
|
+
lines,
|
|
532
|
+
status: "draft"
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
|
|
537
|
+
// src/sdk/resources/labels.ts
|
|
538
|
+
var LabelsResource = class {
|
|
539
|
+
constructor(fetch, companyId) {
|
|
540
|
+
this.fetch = fetch;
|
|
541
|
+
this.companyId = companyId;
|
|
542
|
+
}
|
|
543
|
+
list() {
|
|
544
|
+
return this.fetch(`/companies/${this.companyId}/labels`, {
|
|
545
|
+
headers: {
|
|
546
|
+
Accept: "application/vnd.tiime.labels.v2+json"
|
|
547
|
+
}
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
standard() {
|
|
551
|
+
return this.fetch(`/companies/${this.companyId}/standard_labels`);
|
|
552
|
+
}
|
|
553
|
+
tags() {
|
|
554
|
+
return this.fetch(`/companies/${this.companyId}/tags`, {
|
|
555
|
+
query: { expand: "tag_detail" }
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
};
|
|
559
|
+
|
|
560
|
+
// src/sdk/resources/quotations.ts
|
|
561
|
+
var QuotationsResource = class {
|
|
562
|
+
constructor(fetch, companyId) {
|
|
563
|
+
this.fetch = fetch;
|
|
564
|
+
this.companyId = companyId;
|
|
565
|
+
}
|
|
566
|
+
list(expand = "invoices") {
|
|
567
|
+
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
568
|
+
query: { expand },
|
|
569
|
+
headers: { Range: "items=0-25" }
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
get(quotationId) {
|
|
573
|
+
return this.fetch(
|
|
574
|
+
`/companies/${this.companyId}/quotations/${quotationId}`
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
create(params) {
|
|
578
|
+
return this.fetch(`/companies/${this.companyId}/quotations`, {
|
|
579
|
+
method: "POST",
|
|
580
|
+
body: params
|
|
581
|
+
});
|
|
582
|
+
}
|
|
583
|
+
async downloadPdf(quotationId) {
|
|
584
|
+
return this.fetch(
|
|
585
|
+
`/companies/${this.companyId}/quotations/${quotationId}/pdf`,
|
|
586
|
+
{
|
|
587
|
+
headers: { Accept: "application/pdf" }
|
|
588
|
+
}
|
|
589
|
+
);
|
|
590
|
+
}
|
|
591
|
+
send(quotationId, params) {
|
|
592
|
+
return this.fetch(
|
|
593
|
+
`/companies/${this.companyId}/quotations/${quotationId}/send`,
|
|
594
|
+
{
|
|
595
|
+
method: "POST",
|
|
596
|
+
body: params
|
|
597
|
+
}
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// src/sdk/resources/users.ts
|
|
603
|
+
var UsersResource = class {
|
|
604
|
+
constructor(fetch) {
|
|
605
|
+
this.fetch = fetch;
|
|
606
|
+
}
|
|
607
|
+
me() {
|
|
608
|
+
return this.fetch("/users/me");
|
|
609
|
+
}
|
|
610
|
+
legalInformations() {
|
|
611
|
+
return this.fetch("/users/me/legal_informations");
|
|
612
|
+
}
|
|
613
|
+
settings(companyId) {
|
|
614
|
+
return this.fetch(`/users/me/companies/${companyId}/settings`);
|
|
615
|
+
}
|
|
616
|
+
};
|
|
617
|
+
|
|
618
|
+
// src/sdk/client.ts
|
|
619
|
+
var BASE_URL = "https://chronos-api.tiime-apps.com/v1";
|
|
620
|
+
var TiimeClient = class {
|
|
621
|
+
fetch;
|
|
622
|
+
tokenManager;
|
|
623
|
+
companyId;
|
|
624
|
+
constructor(options) {
|
|
625
|
+
this.companyId = options.companyId;
|
|
626
|
+
this.tokenManager = options.tokenManager ?? new TokenManager();
|
|
627
|
+
this.fetch = ofetch2.create({
|
|
628
|
+
baseURL: BASE_URL,
|
|
629
|
+
retry: 2,
|
|
630
|
+
retryDelay: 500,
|
|
631
|
+
retryStatusCodes: [408, 429, 500, 502, 503, 504],
|
|
632
|
+
headers: {
|
|
633
|
+
"tiime-app": "tiime",
|
|
634
|
+
"tiime-app-version": "4.30.3",
|
|
635
|
+
"tiime-app-platform": "cli"
|
|
636
|
+
},
|
|
637
|
+
onRequest: async ({ options: options2 }) => {
|
|
638
|
+
const token = await this.tokenManager.getValidToken();
|
|
639
|
+
options2.headers.set("Authorization", `Bearer ${token}`);
|
|
640
|
+
},
|
|
641
|
+
onResponseError: ({ request, response }) => {
|
|
642
|
+
throw new TiimeError(
|
|
643
|
+
response.statusText || `HTTP ${response.status}`,
|
|
644
|
+
response.status,
|
|
645
|
+
String(request),
|
|
646
|
+
response._data
|
|
647
|
+
);
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
listCompanies() {
|
|
652
|
+
return this.fetch("/companies", {
|
|
653
|
+
headers: {
|
|
654
|
+
Accept: "application/vnd.tiime.companies.v2+json",
|
|
655
|
+
Range: "items=0-101"
|
|
656
|
+
}
|
|
657
|
+
});
|
|
658
|
+
}
|
|
659
|
+
get users() {
|
|
660
|
+
return new UsersResource(this.fetch);
|
|
661
|
+
}
|
|
662
|
+
get company() {
|
|
663
|
+
return new CompanyResource(this.fetch, this.companyId);
|
|
664
|
+
}
|
|
665
|
+
get clients() {
|
|
666
|
+
return new ClientsResource(this.fetch, this.companyId);
|
|
667
|
+
}
|
|
668
|
+
get invoices() {
|
|
669
|
+
return new InvoicesResource(this.fetch, this.companyId);
|
|
670
|
+
}
|
|
671
|
+
get quotations() {
|
|
672
|
+
return new QuotationsResource(this.fetch, this.companyId);
|
|
673
|
+
}
|
|
674
|
+
get bankAccounts() {
|
|
675
|
+
return new BankAccountsResource(this.fetch, this.companyId);
|
|
676
|
+
}
|
|
677
|
+
get bankTransactions() {
|
|
678
|
+
return new BankTransactionsResource(this.fetch, this.companyId);
|
|
679
|
+
}
|
|
680
|
+
get documents() {
|
|
681
|
+
return new DocumentsResource(this.fetch, this.companyId);
|
|
682
|
+
}
|
|
683
|
+
get expenseReports() {
|
|
684
|
+
return new ExpenseReportsResource(this.fetch, this.companyId);
|
|
685
|
+
}
|
|
686
|
+
get labels() {
|
|
687
|
+
return new LabelsResource(this.fetch, this.companyId);
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
export {
|
|
691
|
+
TiimeClient,
|
|
692
|
+
TiimeError,
|
|
693
|
+
TokenManager
|
|
694
|
+
};
|