tiime-sdk 2.1.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +46 -61
- 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;
|
|
@@ -9,7 +7,7 @@ interface AuthConfig {
|
|
|
9
7
|
password: string;
|
|
10
8
|
}
|
|
11
9
|
interface TiimeClientOptions {
|
|
12
|
-
companyId
|
|
10
|
+
companyId: number;
|
|
13
11
|
tokens?: AuthTokens;
|
|
14
12
|
email?: string;
|
|
15
13
|
password?: string;
|
|
@@ -438,28 +436,29 @@ interface MatchableDocument {
|
|
|
438
436
|
}[];
|
|
439
437
|
}
|
|
440
438
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
439
|
+
interface TokenStorage {
|
|
440
|
+
load(): AuthTokens | null;
|
|
441
|
+
save(tokens: AuthTokens): void;
|
|
442
|
+
clear(): void;
|
|
443
|
+
}
|
|
444
|
+
interface CredentialStorage {
|
|
445
|
+
load(): {
|
|
446
|
+
email: string;
|
|
447
|
+
password: string;
|
|
448
|
+
} | null;
|
|
449
|
+
save(email: string, password: string): void;
|
|
450
|
+
}
|
|
448
451
|
declare class TokenManager {
|
|
449
452
|
private tokens;
|
|
450
453
|
private credentials;
|
|
451
|
-
private
|
|
452
|
-
|
|
453
|
-
* @param options.tokens - Use these tokens directly (no disk I/O)
|
|
454
|
-
* @param options.email - Login with these credentials
|
|
455
|
-
* @param options.password - Login with these credentials
|
|
456
|
-
* @param options.persist - Save tokens/credentials to disk (default: true when no explicit auth)
|
|
457
|
-
*/
|
|
454
|
+
private tokenStorage;
|
|
455
|
+
private credentialStorage;
|
|
458
456
|
constructor(options?: {
|
|
459
457
|
tokens?: AuthTokens;
|
|
460
458
|
email?: string;
|
|
461
459
|
password?: string;
|
|
462
|
-
|
|
460
|
+
tokenStorage?: TokenStorage;
|
|
461
|
+
credentialStorage?: CredentialStorage;
|
|
463
462
|
});
|
|
464
463
|
login(email: string, password: string): Promise<AuthTokens>;
|
|
465
464
|
getValidToken(): Promise<string>;
|
|
@@ -470,14 +469,24 @@ declare class TokenManager {
|
|
|
470
469
|
expiresAt: Date | null;
|
|
471
470
|
};
|
|
472
471
|
private isExpired;
|
|
473
|
-
private loadFromDisk;
|
|
474
|
-
private saveToDisk;
|
|
475
472
|
}
|
|
476
473
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
474
|
+
interface FetchOptions {
|
|
475
|
+
method?: string;
|
|
476
|
+
headers?: Record<string, string>;
|
|
477
|
+
body?: unknown;
|
|
478
|
+
query?: Record<string, unknown> | object;
|
|
479
|
+
}
|
|
480
|
+
type FetchFn = <T = unknown>(url: string, options?: FetchOptions) => Promise<T>;
|
|
481
|
+
|
|
482
|
+
declare class Resource {
|
|
483
|
+
protected fetch: FetchFn;
|
|
484
|
+
protected companyId: number;
|
|
485
|
+
constructor(fetch: FetchFn, companyId: number);
|
|
486
|
+
protected url(path: string): string;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
declare class BankAccountsResource extends Resource {
|
|
481
490
|
list(enabled?: boolean): Promise<BankAccount[]>;
|
|
482
491
|
get(bankAccountId: number): Promise<BankAccount>;
|
|
483
492
|
balance(): Promise<{
|
|
@@ -499,10 +508,7 @@ interface BankTransactionsListParams {
|
|
|
499
508
|
to?: string;
|
|
500
509
|
search?: string;
|
|
501
510
|
}
|
|
502
|
-
declare class BankTransactionsResource {
|
|
503
|
-
private fetch;
|
|
504
|
-
private companyId;
|
|
505
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
511
|
+
declare class BankTransactionsResource extends Resource {
|
|
506
512
|
list(params?: BankTransactionsListParams): Promise<BankTransactionsResponse>;
|
|
507
513
|
listAll(params?: Omit<BankTransactionsListParams, "page">): Promise<BankTransaction[]>;
|
|
508
514
|
unimputed(): Promise<BankTransaction[]>;
|
|
@@ -529,20 +535,14 @@ interface ClientCreateParams {
|
|
|
529
535
|
siren_or_siret?: string;
|
|
530
536
|
professional?: boolean;
|
|
531
537
|
}
|
|
532
|
-
declare class ClientsResource {
|
|
533
|
-
private fetch;
|
|
534
|
-
private companyId;
|
|
535
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
538
|
+
declare class ClientsResource extends Resource {
|
|
536
539
|
list(params?: ClientsListParams): Promise<Client[]>;
|
|
537
540
|
get(clientId: number): Promise<Client>;
|
|
538
541
|
create(params: ClientCreateParams): Promise<Client>;
|
|
539
542
|
search(query: string): Promise<Client[]>;
|
|
540
543
|
}
|
|
541
544
|
|
|
542
|
-
declare class CompanyResource {
|
|
543
|
-
private fetch;
|
|
544
|
-
private companyId;
|
|
545
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
545
|
+
declare class CompanyResource extends Resource {
|
|
546
546
|
get(): Promise<Company>;
|
|
547
547
|
users(): Promise<unknown>;
|
|
548
548
|
appConfig(): Promise<unknown>;
|
|
@@ -559,10 +559,7 @@ interface DocumentsListParams {
|
|
|
559
559
|
page?: number;
|
|
560
560
|
pageSize?: number;
|
|
561
561
|
}
|
|
562
|
-
declare class DocumentsResource {
|
|
563
|
-
private fetch;
|
|
564
|
-
private companyId;
|
|
565
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
562
|
+
declare class DocumentsResource extends Resource {
|
|
566
563
|
list(params?: DocumentsListParams): Promise<Document[]>;
|
|
567
564
|
categories(): Promise<DocumentCategory[]>;
|
|
568
565
|
preview(documentId: number): Promise<unknown>;
|
|
@@ -571,10 +568,7 @@ declare class DocumentsResource {
|
|
|
571
568
|
download(documentId: number): Promise<ArrayBuffer>;
|
|
572
569
|
}
|
|
573
570
|
|
|
574
|
-
declare class ExpenseReportsResource {
|
|
575
|
-
private fetch;
|
|
576
|
-
private companyId;
|
|
577
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
571
|
+
declare class ExpenseReportsResource extends Resource {
|
|
578
572
|
list(sorts?: string): Promise<ExpenseReport[]>;
|
|
579
573
|
get(expenseReportId: number): Promise<ExpenseReport>;
|
|
580
574
|
create(params: ExpenseReportCreateParams): Promise<ExpenseReport>;
|
|
@@ -586,10 +580,7 @@ interface InvoicesListParams {
|
|
|
586
580
|
page?: number;
|
|
587
581
|
pageSize?: number;
|
|
588
582
|
}
|
|
589
|
-
declare class InvoicesResource {
|
|
590
|
-
private fetch;
|
|
591
|
-
private companyId;
|
|
592
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
583
|
+
declare class InvoicesResource extends Resource {
|
|
593
584
|
list(params?: InvoicesListParams): Promise<Invoice[]>;
|
|
594
585
|
listAll(params?: {
|
|
595
586
|
sorts?: string;
|
|
@@ -608,19 +599,13 @@ declare class InvoicesResource {
|
|
|
608
599
|
}): Promise<Invoice>;
|
|
609
600
|
}
|
|
610
601
|
|
|
611
|
-
declare class LabelsResource {
|
|
612
|
-
private fetch;
|
|
613
|
-
private companyId;
|
|
614
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
602
|
+
declare class LabelsResource extends Resource {
|
|
615
603
|
list(): Promise<Label[]>;
|
|
616
604
|
standard(): Promise<Label[]>;
|
|
617
605
|
tags(): Promise<Tag[]>;
|
|
618
606
|
}
|
|
619
607
|
|
|
620
|
-
declare class QuotationsResource {
|
|
621
|
-
private fetch;
|
|
622
|
-
private companyId;
|
|
623
|
-
constructor(fetch: $Fetch, companyId: number);
|
|
608
|
+
declare class QuotationsResource extends Resource {
|
|
624
609
|
list(expand?: string): Promise<Quotation[]>;
|
|
625
610
|
get(quotationId: number): Promise<Quotation>;
|
|
626
611
|
create(params: QuotationCreateParams): Promise<Quotation>;
|
|
@@ -629,18 +614,18 @@ declare class QuotationsResource {
|
|
|
629
614
|
}
|
|
630
615
|
|
|
631
616
|
declare class UsersResource {
|
|
632
|
-
|
|
633
|
-
constructor(fetch:
|
|
617
|
+
protected fetch: FetchFn;
|
|
618
|
+
constructor(fetch: FetchFn);
|
|
634
619
|
me(): Promise<User>;
|
|
635
620
|
legalInformations(): Promise<unknown>;
|
|
636
621
|
settings(companyId: number): Promise<unknown>;
|
|
637
622
|
}
|
|
638
623
|
|
|
639
624
|
declare class TiimeClient {
|
|
640
|
-
readonly fetch:
|
|
625
|
+
readonly fetch: FetchFn;
|
|
641
626
|
readonly tokenManager: TokenManager;
|
|
642
627
|
readonly companyId: number;
|
|
643
|
-
constructor(options
|
|
628
|
+
constructor(options: TiimeClientOptions & {
|
|
644
629
|
tokenManager?: TokenManager;
|
|
645
630
|
});
|
|
646
631
|
listCompanies(): Promise<Company[]>;
|
|
@@ -670,4 +655,4 @@ declare class TiimeError extends Error {
|
|
|
670
655
|
};
|
|
671
656
|
}
|
|
672
657
|
|
|
673
|
-
export { type AccountingPeriod, type Address, type ApeCode, type AuthConfig, type AuthTokens, type Bank, type BankAccount, type BankTransaction, type BankTransactionsResponse, type Client, type Company, type Country, type DashboardBlock, type Document, type DocumentCategory, type DocumentMatching, type ExpenseReport, type ExpenseReportCreateParams, type Imputation, type ImputationLabel, type ImputationParams, type Invoice, type InvoiceCreateParams, type InvoiceLine, type InvoiceSendParams, type Label, type LabelSuggestion, type MatchableDocument, type PaginatedResponse, type Quotation, type QuotationCreateParams, type QuotationSendParams, type Tag, TiimeClient, type TiimeClientOptions, TiimeError, TokenManager, type User, type VatSystem
|
|
658
|
+
export { type AccountingPeriod, type Address, type ApeCode, type AuthConfig, type AuthTokens, type Bank, type BankAccount, type BankTransaction, type BankTransactionsResponse, type Client, type Company, type Country, type CredentialStorage, type DashboardBlock, type Document, type DocumentCategory, type DocumentMatching, type ExpenseReport, type ExpenseReportCreateParams, type Imputation, type ImputationLabel, type ImputationParams, type Invoice, type InvoiceCreateParams, type InvoiceLine, type InvoiceSendParams, type Label, type LabelSuggestion, type MatchableDocument, type PaginatedResponse, type Quotation, type QuotationCreateParams, type QuotationSendParams, type Tag, TiimeClient, type TiimeClientOptions, TiimeError, TokenManager, type TokenStorage, type User, type VatSystem };
|
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
|
+
var E=(s,e,t)=>{let n=new URL(e,s.endsWith("/")?s:`${s}/`);if(t)for(let[r,i]of Object.entries(t))i!=null&&n.searchParams.set(r,String(i));return n.href},q=s=>new Promise(e=>setTimeout(e,s)),D=s=>!!s?.includes("application/json")||!!s?.includes("+json"),I=s=>{let e=s.retry??0,t=s.retryDelay??500,n=new Set(s.retryStatusCodes??[]);return async(r,i)=>{let o=E(s.baseURL,r,i?.query),h=new Headers(s.headers);if(i?.headers)for(let[u,c]of Object.entries(i.headers))h.set(u,c);let l;i?.body!==void 0&&(i.body instanceof FormData?l=i.body:(h.set("Content-Type","application/json"),l=JSON.stringify(i.body))),s.onRequest&&await s.onRequest({options:{headers:h}});let p;for(let u=0;u<=e;u++){u>0&&await q(t);let c;try{c=await fetch(o,{method:i?.method??"GET",headers:h,body:l});}catch(m){if(p=m,u<e)continue;throw m}if(!c.ok&&n.has(c.status)&&(p=c,u<e))continue;if(!c.ok&&s.onResponseError){let m;try{m=await c.clone().json();}catch{}let C=Object.assign(c,{_data:m});s.onResponseError({request:o,response:C});}let A=c.headers.get("content-type");return c.status===204||!A?void 0:D(A)?c.json():c.arrayBuffer()}throw p}},R=async(s,e)=>{let t=await fetch(s,e);if(!t.ok)throw new Error(`HTTP ${t.status}: ${t.statusText}`);return t.json()};var $="auth0.tiime.fr",B="iEbsbe3o66gcTBfGRa012kj1Rb6vjAND",M="https://chronos/",d=class{tokens=null;credentials=null;tokenStorage;credentialStorage;constructor(e={}){if(this.tokenStorage=e.tokenStorage??null,this.credentialStorage=e.credentialStorage??null,e.tokens){this.tokens=e.tokens;return}if(e.email&&e.password){this.credentials={email:e.email,password:e.password};return}let t=process.env.TIIME_ACCESS_TOKEN;if(t){this.tokens={access_token:t,expires_at:Number.MAX_SAFE_INTEGER};return}let n=process.env.TIIME_EMAIL,r=process.env.TIIME_PASSWORD;if(n&&r){this.credentials={email:n,password:r};return}this.tokenStorage&&(this.tokens=this.tokenStorage.load());}async login(e,t){let n=await R(`https://${$}/oauth/token`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({grant_type:"password",client_id:B,audience:M,scope:"openid email",username:e,password:t})});return this.tokens={access_token:n.access_token,expires_at:Date.now()+n.expires_in*1e3},this.tokenStorage?.save(this.tokens),this.credentialStorage?.save(e,t),this.tokens}async getValidToken(){if(!this.tokens||this.isExpired()){let e=this.credentials??this.credentialStorage?.load()??null;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,this.tokenStorage?.clear();}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}};var g=class extends Error{constructor(t,n,r,i){super(t);this.status=n;this.endpoint=r;this.details=i;this.name="TiimeError";}toJSON(){return {error:this.name,message:this.message,status:this.status,endpoint:this.endpoint,details:this.details}}};var a=class{constructor(e,t){this.fetch=e;this.companyId=t;}url(e){return `/companies/${this.companyId}${e}`}};var f=class extends a{list(e){return this.fetch(this.url("/bank_accounts"),{query:e!==void 0?{enabled:e}:void 0})}get(e){return this.fetch(this.url(`/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 y=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??100),n=t+(e?.pageSize??100),{page:r,pageSize:i,from:o,to:h,search:l,...p}=e??{},u={...p};return o&&(u.transaction_date_start=o),h&&(u.transaction_date_end=h),l&&(u.wording=l),this.fetch(this.url("/bank_transactions"),{query:{hide_refused:false,...u},headers:{Accept:"application/vnd.tiime.bank_transactions.v2+json",Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??200,n=[],r=1,i=true;for(;i;){let o=await this.list({...e,page:r,pageSize:t});n.push(...o.transactions),i=o.transactions.length===t,r++;}return n}unimputed(){return this.fetch(this.url("/bank_transactions/unimputed"))}get(e){return this.fetch(this.url(`/bank_transactions/${e}`))}labelSuggestions(e){return this.fetch(this.url(`/bank_transactions/${e}/label_suggestions`),{headers:{Accept:"application/vnd.tiime.bank_transactions.label_suggestions.v2+json"}})}impute(e,t){return this.fetch(this.url(`/bank_transactions/${e}`),{method:"PATCH",body:{imputations:t}})}matchDocuments(e,t){return this.fetch(this.url(`/bank_transactions/${e}/document_matchings`),{method:"PUT",body:{documents:t.map(n=>({id:n}))}})}getMatchings(e){return this.fetch(this.url(`/bank_transactions/${e}/matchings`))}};var k=class extends a{list(e){return this.fetch(this.url("/clients"),{query:e,headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}get(e){return this.fetch(this.url(`/clients/${e}`))}create(e){return this.fetch(this.url("/clients"),{method:"POST",body:e})}search(e){return this.fetch(this.url("/clients"),{query:{search:e},headers:{Accept:"application/vnd.tiime.timeline.v2+json",Range:"items=0-*"}})}};var b=class extends a{get(){return this.fetch(this.url(""))}users(){return this.fetch(this.url("/users"))}appConfig(){return this.fetch(this.url("/app_config"))}accountingPeriod(e=1){return this.fetch(this.url("/accounting_period/current"),{query:{range_year:e}})}tiles(e){return this.fetch(this.url("/tiles"),{query:{keys:e.join(",")}})}dashboardBlocks(e="monitoring"){return this.fetch(this.url("/dashboard_blocks"),{query:{sorts:"rank:asc",display_group:e}})}};var _=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),{page:r,pageSize:i,...o}=e??{};return this.fetch(this.url("/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(this.url("/document_categories"),{headers:{Accept:"application/vnd.tiime.documents.v3+json"}})}preview(e){return this.fetch(this.url(`/documents/${e}/preview`))}upload(e,t,n){let r=new FormData;return r.append("file",new Blob([e]),t),n&&r.append("type",n),this.fetch(this.url("/documents"),{method:"POST",body:r})}searchMatchable(e){return this.fetch(this.url("/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(this.url(`/documents/${e}/download`),{headers:{Accept:"application/octet-stream"}})}};var v=class extends a{list(e="metadata.date:desc"){return this.fetch(this.url("/expense_reports"),{query:{expand:"total_amount",sorts:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(this.url(`/expense_reports/${e}`))}create(e){return this.fetch(this.url("/expense_reports"),{method:"POST",body:e})}};var w=class extends a{list(e){let t=((e?.page??1)-1)*(e?.pageSize??25),n=t+(e?.pageSize??25),r={sorts:e?.sorts??"invoice_number:desc"};return e?.status&&(r.status=e.status),this.fetch(this.url("/invoices"),{query:r,headers:{Range:`items=${t}-${n}`}})}async listAll(e){let t=e?.pageSize??100,n=[],r=1,i=true;for(;i;){let o=await this.list({sorts:e?.sorts,status:e?.status,page:r,pageSize:t});n.push(...o),i=o.length===t,r++;}return n}get(e){return this.fetch(this.url(`/invoices/${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(this.url("/invoices"),{method:"POST",body:t})}update(e,t){return this.fetch(this.url(`/invoices/${e}`),{method:"PUT",body:t})}send(e,t){return this.fetch(this.url(`/invoices/${e}/send`),{method:"POST",body:t})}async downloadPdf(e){return this.fetch(this.url(`/invoices/${e}/pdf`),{headers:{Accept:"application/pdf"}})}delete(e){return this.fetch(this.url(`/invoices/${e}`),{method:"DELETE"})}async duplicate(e,t){let n=await this.get(e),r=new Date().toISOString().split("T")[0],i=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??r,title:n.title,title_enabled:!!n.title,lines:i,status:"draft"})}};var T=class extends a{list(){return this.fetch(this.url("/labels"),{headers:{Accept:"application/vnd.tiime.labels.v2+json"}})}standard(){return this.fetch(this.url("/standard_labels"))}tags(){return this.fetch(this.url("/tags"),{query:{expand:"tag_detail"}})}};var S=class extends a{list(e="invoices"){return this.fetch(this.url("/quotations"),{query:{expand:e},headers:{Range:"items=0-25"}})}get(e){return this.fetch(this.url(`/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(this.url("/quotations"),{method:"POST",body:t})}async downloadPdf(e){return this.fetch(this.url(`/quotations/${e}/pdf`),{headers:{Accept:"application/pdf"}})}send(e,t){return this.fetch(this.url(`/quotations/${e}/send`),{method:"POST",body:t})}};var P=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 O="https://chronos-api.tiime-apps.com/v1",x=class{fetch;tokenManager;companyId;constructor(e){this.companyId=e.companyId,this.tokenManager=e.tokenManager??new d({tokens:e.tokens,email:e.email,password:e.password}),this.fetch=I({baseURL:O,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 g(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 P(this.fetch)}get company(){return new b(this.fetch,this.companyId)}get clients(){return new k(this.fetch,this.companyId)}get invoices(){return new w(this.fetch,this.companyId)}get quotations(){return new S(this.fetch,this.companyId)}get bankAccounts(){return new f(this.fetch,this.companyId)}get bankTransactions(){return new y(this.fetch,this.companyId)}get documents(){return new _(this.fetch,this.companyId)}get expenseReports(){return new v(this.fetch,this.companyId)}get labels(){return new T(this.fetch,this.companyId)}};
|
|
2
|
+
export{x as TiimeClient,g as TiimeError,d as TokenManager};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tiime-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.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",
|