@frihet/mcp-server 1.17.0 → 2.0.1
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/README.md +106 -348
- package/dist/agent-onboarding.d.ts +1 -1
- package/dist/agent-onboarding.js +3 -3
- package/dist/canary/mcp-harness.d.ts +1 -1
- package/dist/canary/mcp-harness.js +4 -1
- package/dist/capability-truth.js +20 -12
- package/dist/claude-candidate-contract.d.ts +482 -0
- package/dist/claude-candidate-contract.js +87 -0
- package/dist/claude-output-privacy.d.ts +17 -0
- package/dist/claude-output-privacy.js +249 -0
- package/dist/claude-profile.d.ts +18 -0
- package/dist/claude-profile.js +260 -0
- package/dist/client-interface.d.ts +26 -20
- package/dist/client.d.ts +65 -29
- package/dist/client.js +278 -167
- package/dist/demo-client.d.ts +28 -21
- package/dist/demo-client.js +32 -14
- package/dist/deposit-contract.d.ts +29 -0
- package/dist/deposit-contract.js +76 -0
- package/dist/fetch-no-redirect.d.ts +3 -0
- package/dist/fetch-no-redirect.js +16 -0
- package/dist/fiscal-aliases.d.ts +2 -2
- package/dist/fiscal-aliases.js +2 -2
- package/dist/fiscal-period.d.ts +26 -0
- package/dist/fiscal-period.js +31 -0
- package/dist/google-profile.d.ts +19 -0
- package/dist/google-profile.js +122 -0
- package/dist/index.js +31 -7
- package/dist/logger.d.ts +1 -1
- package/dist/logger.js +2 -2
- package/dist/muse-profile.d.ts +19 -0
- package/dist/muse-profile.js +122 -0
- package/dist/observability.js +3 -2
- package/dist/openai-profile.d.ts +2 -2
- package/dist/openai-profile.js +37 -11
- package/dist/openai-review-contract.d.ts +1 -1
- package/dist/openai-review-contract.js +4 -5
- package/dist/openai-review-oauth.d.ts +107 -7
- package/dist/openai-review-oauth.js +161 -17
- package/dist/prompts/register-all.js +32 -78
- package/dist/redaction.d.ts +5 -0
- package/dist/redaction.js +9 -0
- package/dist/resources/register-all.js +150 -54
- package/dist/retry-after.d.ts +19 -0
- package/dist/retry-after.js +84 -0
- package/dist/server-composition.d.ts +6 -2
- package/dist/server-composition.js +46 -5
- package/dist/tool-exposure.d.ts +7 -5
- package/dist/tool-exposure.js +21 -13
- package/dist/tools/audit_gl.js +8 -8
- package/dist/tools/backend-availability.d.ts +8 -29
- package/dist/tools/backend-availability.js +31 -30
- package/dist/tools/bank_rules.js +66 -15
- package/dist/tools/banking.js +5 -13
- package/dist/tools/clients.js +254 -6
- package/dist/tools/deposits.js +107 -97
- package/dist/tools/einvoice.d.ts +17 -3
- package/dist/tools/einvoice.js +59 -12
- package/dist/tools/fiscal.d.ts +3 -4
- package/dist/tools/fiscal.js +93 -60
- package/dist/tools/gestoria.js +15 -7
- package/dist/tools/hr.js +41 -11
- package/dist/tools/igic.d.ts +9 -8
- package/dist/tools/igic.js +34 -64
- package/dist/tools/impuesto_sociedades.d.ts +4 -5
- package/dist/tools/impuesto_sociedades.js +16 -37
- package/dist/tools/intelligence.js +164 -10
- package/dist/tools/invoices.js +109 -42
- package/dist/tools/kitchen.js +19 -9
- package/dist/tools/quotes.js +86 -25
- package/dist/tools/recurring.js +26 -25
- package/dist/tools/register-all.d.ts +2 -2
- package/dist/tools/register-all.js +9 -6
- package/dist/tools/search.d.ts +19 -0
- package/dist/tools/search.js +84 -0
- package/dist/tools/shared.d.ts +18 -38
- package/dist/tools/shared.js +96 -30
- package/dist/tools/team.js +8 -16
- package/dist/tools/time.js +4 -12
- package/dist/tools/vendors.js +242 -6
- package/dist/types.d.ts +3 -1
- package/dist/workflows/daily-operational-brief.d.ts +163 -0
- package/dist/workflows/daily-operational-brief.js +881 -0
- package/docs/agent-onboarding.json +15 -5
- package/package.json +26 -12
- package/dist/tools/onboarding.d.ts +0 -19
- package/dist/tools/onboarding.js +0 -64
package/dist/client.js
CHANGED
|
@@ -11,17 +11,20 @@
|
|
|
11
11
|
* (`req.query.cursor`, functions/src/publicApi.ts in Frihet-ERP). `after`
|
|
12
12
|
* is never sent on the wire. See src/__tests__/pagination-cursor-param.test.ts.
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
14
|
+
import { rejectRedirectResponse } from "./fetch-no-redirect.js";
|
|
15
|
+
import { safeRequestId, sanitizeServerRemediation } from "./redaction.js";
|
|
15
16
|
import { logApiCall, logRetry } from "./logger.js";
|
|
17
|
+
import { parseRetryAfter } from "./retry-after.js";
|
|
18
|
+
import { fiscalModeloQuery } from "./fiscal-period.js";
|
|
19
|
+
import { normalizeApplyDepositBody, normalizeDepositBody } from "./deposit-contract.js";
|
|
16
20
|
const BASE_URL = "https://api.frihet.io/v1";
|
|
17
|
-
const OAUTH_CLOUD_FUNCTION_BASE_URL = "https://europe-west1-gen-lang-client-0335716041.cloudfunctions.net/publicApi/api/v1";
|
|
18
|
-
const OAUTH_SERVICE_TRUSTED_BASE_URLS = new Set([
|
|
19
|
-
BASE_URL,
|
|
20
|
-
OAUTH_CLOUD_FUNCTION_BASE_URL,
|
|
21
|
-
]);
|
|
22
21
|
const MAX_RETRIES = 3;
|
|
23
22
|
const DEFAULT_RETRY_DELAY_MS = 1000;
|
|
24
23
|
const REQUEST_TIMEOUT_MS = 30000;
|
|
24
|
+
/** Total time one logical call may spend sleeping on 429s before it is deferred to the caller. */
|
|
25
|
+
const RETRY_BUDGET_MS = 30000;
|
|
26
|
+
/** setTimeout fires immediately above this (32-bit signed); a larger wait must defer, never sleep. */
|
|
27
|
+
const MAX_TIMER_MS = 2 ** 31 - 1;
|
|
25
28
|
/**
|
|
26
29
|
* Hard size caps for document responses. Enforced TWICE: precheck on
|
|
27
30
|
* `Content-Length` (so an honest server doesn't waste bandwidth) and after
|
|
@@ -39,6 +42,52 @@ const REQUEST_TIMEOUT_MS = 30000;
|
|
|
39
42
|
export const MAX_PDF_BYTES = 25 * 1024 * 1024;
|
|
40
43
|
export const MAX_XML_BYTES = 5 * 1024 * 1024;
|
|
41
44
|
const MAX_ERROR_BYTES = 64 * 1024;
|
|
45
|
+
/** One exhaustive translation at the HTTP boundary. Never send the MCP's
|
|
46
|
+
* lowercase convenience values to the strict API. Factur-X Extended, Basic
|
|
47
|
+
* and Minimum are intentionally absent: collapsing them to EN16931 would lie
|
|
48
|
+
* about the generated profile. */
|
|
49
|
+
const E_INVOICE_EXPORT_API_FORMAT = {
|
|
50
|
+
facturae: "Facturae",
|
|
51
|
+
"xrechnung-cii": "XRechnung-CII",
|
|
52
|
+
"xrechnung-ubl": "XRechnung-UBL",
|
|
53
|
+
"facturx-en16931": "Factur-X",
|
|
54
|
+
fatturapa: "FatturaPA",
|
|
55
|
+
"peppol-bis-3": "PEPPOL-BIS-3",
|
|
56
|
+
"fa-2-ksef": "FA-2-KSeF",
|
|
57
|
+
ubl: "UBL",
|
|
58
|
+
cii: "CII",
|
|
59
|
+
};
|
|
60
|
+
// Only the existing guards below can attest that a request was never sent.
|
|
61
|
+
// Provider error codes, copied errors and public properties cannot forge this
|
|
62
|
+
// process-local provenance. Both runtimes use this fetch-only client.
|
|
63
|
+
const LOCAL_VALIDATION_ERRORS = new WeakSet();
|
|
64
|
+
export function isLocalInputValidationError(error) {
|
|
65
|
+
return typeof error === "object" && error !== null && LOCAL_VALIDATION_ERRORS.has(error);
|
|
66
|
+
}
|
|
67
|
+
export function toApiEInvoiceExportFormat(format) {
|
|
68
|
+
if (!Object.hasOwn(E_INVOICE_EXPORT_API_FORMAT, format)) {
|
|
69
|
+
const error = new FrihetApiError(400, "unsupported_einvoice_export_format", "Unsupported e-invoice export format. Factur-X export supports the EN16931 profile only.");
|
|
70
|
+
LOCAL_VALIDATION_ERRORS.add(error);
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
return E_INVOICE_EXPORT_API_FORMAT[format];
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Encode one caller-supplied path segment. `encodeURIComponent` leaves "." and
|
|
77
|
+
* ".." unchanged and URL parsing resolves them as dot-segments, so either value
|
|
78
|
+
* would change the route a request reaches; an empty value would drop the
|
|
79
|
+
* segment. Every id interpolated into a request path goes through here, and
|
|
80
|
+
* those three values are refused before any request is sent.
|
|
81
|
+
*/
|
|
82
|
+
function pathSegment(value) {
|
|
83
|
+
const segment = String(value);
|
|
84
|
+
if (segment === "" || segment === "." || segment === "..") {
|
|
85
|
+
const error = new FrihetApiError(400, "invalid_path_segment", 'Invalid identifier: an id cannot be empty, "." or "..".');
|
|
86
|
+
LOCAL_VALIDATION_ERRORS.add(error);
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
return encodeURIComponent(segment);
|
|
90
|
+
}
|
|
42
91
|
/**
|
|
43
92
|
* HTTP methods for which the backend treats the request as a mutation and
|
|
44
93
|
* therefore accepts (and for some endpoints REQUIRES) an `Idempotency-Key`
|
|
@@ -172,13 +221,17 @@ export class FrihetApiError extends Error {
|
|
|
172
221
|
statusCode;
|
|
173
222
|
errorCode;
|
|
174
223
|
detail;
|
|
175
|
-
|
|
224
|
+
requestId;
|
|
225
|
+
constructor(statusCode, errorCode, message, detail,
|
|
226
|
+
/** Backend correlation id (`meta.requestId` or `X-Request-Id`); carried for diagnostics, never echoed to agents. */
|
|
227
|
+
requestId) {
|
|
176
228
|
super(statusCode === 403
|
|
177
229
|
? sanitizeServerRemediation(message, errorCode) ?? errorCode
|
|
178
230
|
: message ?? errorCode);
|
|
179
231
|
this.statusCode = statusCode;
|
|
180
232
|
this.errorCode = errorCode;
|
|
181
233
|
this.detail = detail;
|
|
234
|
+
this.requestId = requestId;
|
|
182
235
|
this.name = "FrihetApiError";
|
|
183
236
|
}
|
|
184
237
|
}
|
|
@@ -222,6 +275,12 @@ function normalizeApiError(value, statusCode, statusText) {
|
|
|
222
275
|
const record = typeof value === "object" && value !== null
|
|
223
276
|
? value
|
|
224
277
|
: undefined;
|
|
278
|
+
// publicApi error envelopes carry `meta: { requestId }` (Frihet-ERP
|
|
279
|
+
// functions/src/publicApi.ts, e.g. the 401/404 branches).
|
|
280
|
+
const meta = typeof record?.meta === "object" && record.meta !== null
|
|
281
|
+
? record.meta
|
|
282
|
+
: undefined;
|
|
283
|
+
const requestId = safeRequestId(meta?.requestId);
|
|
225
284
|
const error = typeof record?.error === "string" && record.error.trim()
|
|
226
285
|
? record.error
|
|
227
286
|
: fallbackError;
|
|
@@ -232,13 +291,16 @@ function normalizeApiError(value, statusCode, statusText) {
|
|
|
232
291
|
error,
|
|
233
292
|
message,
|
|
234
293
|
...(typeof record?.detail === "string" ? { detail: record.detail } : {}),
|
|
294
|
+
...(requestId ? { requestId } : {}),
|
|
235
295
|
};
|
|
236
296
|
}
|
|
237
297
|
export class FrihetClient {
|
|
238
298
|
apiKey;
|
|
239
299
|
baseUrl;
|
|
240
300
|
timeoutMs;
|
|
241
|
-
|
|
301
|
+
retryBudgetMs;
|
|
302
|
+
sleepFn;
|
|
303
|
+
nowFn;
|
|
242
304
|
constructor(apiKey, baseUrl, options) {
|
|
243
305
|
if (!apiKey) {
|
|
244
306
|
throw new Error("FRIHET_API_KEY is required. Set it as an environment variable or pass it to the constructor.");
|
|
@@ -247,24 +309,18 @@ export class FrihetClient {
|
|
|
247
309
|
this.apiKey = apiKey;
|
|
248
310
|
this.baseUrl = resolvedBaseUrl;
|
|
249
311
|
this.timeoutMs = options?.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
// constructor input while leaving the approved fetch fingerprint intact.
|
|
259
|
-
if (!OAUTH_SERVICE_TRUSTED_BASE_URLS.has(resolvedBaseUrl)) {
|
|
260
|
-
throw new Error("OAuth API-key service authority is not trusted");
|
|
261
|
-
}
|
|
262
|
-
this.oauthServiceSecret = options.oauthServiceSecret;
|
|
263
|
-
}
|
|
312
|
+
const budget = options?.retryBudgetMs;
|
|
313
|
+
// NaN/negative would make every `> budget` comparison pass or fail silently.
|
|
314
|
+
this.retryBudgetMs =
|
|
315
|
+
typeof budget === "number" && Number.isFinite(budget) && budget >= 0
|
|
316
|
+
? Math.min(budget, MAX_TIMER_MS)
|
|
317
|
+
: RETRY_BUDGET_MS;
|
|
318
|
+
this.sleepFn = options?.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
319
|
+
this.nowFn = options?.now ?? Date.now;
|
|
264
320
|
}
|
|
265
321
|
// ------------------------------------------------------------------ HTTP
|
|
266
322
|
// ------------------------------------------------------------------
|
|
267
|
-
async request(method, path, body, query, retryCount = 0, idempotencyKey) {
|
|
323
|
+
async request(method, path, body, query, retryCount = 0, idempotencyKey, waitedMs = 0) {
|
|
268
324
|
const url = new URL(`${this.baseUrl}${path}`);
|
|
269
325
|
// Resolved once, at the top of the call chain: a caller-supplied key wins,
|
|
270
326
|
// otherwise mutations get a freshly minted one. The resolved value is then
|
|
@@ -287,9 +343,6 @@ export class FrihetClient {
|
|
|
287
343
|
"X-Frihet-Source": SOURCE_MARKER,
|
|
288
344
|
"User-Agent": SOURCE_USER_AGENT,
|
|
289
345
|
};
|
|
290
|
-
if (this.oauthServiceSecret) {
|
|
291
|
-
headers["x-frihet-oauth-key"] = this.oauthServiceSecret;
|
|
292
|
-
}
|
|
293
346
|
if (resolvedIdempotencyKey) {
|
|
294
347
|
headers["Idempotency-Key"] = resolvedIdempotencyKey;
|
|
295
348
|
}
|
|
@@ -300,11 +353,11 @@ export class FrihetClient {
|
|
|
300
353
|
try {
|
|
301
354
|
response = await fetch(url.toString(), {
|
|
302
355
|
method,
|
|
356
|
+
redirect: "manual",
|
|
303
357
|
headers,
|
|
304
358
|
body: body ? JSON.stringify(body) : undefined,
|
|
305
359
|
signal: controller.signal,
|
|
306
|
-
|
|
307
|
-
});
|
|
360
|
+
}).then(rejectRedirectResponse);
|
|
308
361
|
}
|
|
309
362
|
catch (error) {
|
|
310
363
|
clearTimeout(timeoutId);
|
|
@@ -324,13 +377,12 @@ export class FrihetClient {
|
|
|
324
377
|
if (retryCount >= MAX_RETRIES) {
|
|
325
378
|
throw new FrihetApiError(429, "rate_limit_exceeded", "Rate limit exceeded after multiple retries. Please try again later.");
|
|
326
379
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
return this.request(method, path, body, query, retryCount + 1, resolvedIdempotencyKey);
|
|
380
|
+
// Release the discarded 429 body before waiting so the socket is freed.
|
|
381
|
+
// (requestDocument has already drained it in readBoundedBody.)
|
|
382
|
+
await response.body?.cancel().catch(() => undefined);
|
|
383
|
+
const delayMs = this.rateLimitDelayMs(response.headers.get("Retry-After"), retryCount, waitedMs, method, path);
|
|
384
|
+
await this.sleepFn(delayMs);
|
|
385
|
+
return this.request(method, path, body, query, retryCount + 1, resolvedIdempotencyKey, waitedMs + delayMs);
|
|
334
386
|
}
|
|
335
387
|
// Error responses
|
|
336
388
|
if (!response.ok) {
|
|
@@ -343,7 +395,7 @@ export class FrihetClient {
|
|
|
343
395
|
errorBody = normalizeApiError(undefined, response.status, response.statusText);
|
|
344
396
|
}
|
|
345
397
|
clearTimeout(timeoutId);
|
|
346
|
-
throw new FrihetApiError(response.status, errorBody.error, errorBody.message ?? errorBody.error, typeof errorBody.detail === "string" ? errorBody.detail : undefined);
|
|
398
|
+
throw new FrihetApiError(response.status, errorBody.error, errorBody.message ?? errorBody.error, typeof errorBody.detail === "string" ? errorBody.detail : undefined, errorBody.requestId ?? safeRequestId(response.headers.get("x-request-id")));
|
|
347
399
|
}
|
|
348
400
|
logApiCall(method, path, response.status, durationMs);
|
|
349
401
|
// 204 No Content (e.g. DELETE)
|
|
@@ -395,19 +447,16 @@ export class FrihetClient {
|
|
|
395
447
|
"X-Frihet-Source": SOURCE_MARKER,
|
|
396
448
|
"User-Agent": SOURCE_USER_AGENT,
|
|
397
449
|
};
|
|
398
|
-
if (this.oauthServiceSecret) {
|
|
399
|
-
headers["x-frihet-oauth-key"] = this.oauthServiceSecret;
|
|
400
|
-
}
|
|
401
450
|
if (resolvedIdempotencyKey) {
|
|
402
451
|
headers["Idempotency-Key"] = resolvedIdempotencyKey;
|
|
403
452
|
}
|
|
404
453
|
return fetch(url.toString(), {
|
|
405
454
|
method,
|
|
455
|
+
redirect: "manual",
|
|
406
456
|
headers,
|
|
407
457
|
body: body ? JSON.stringify(body) : undefined,
|
|
408
458
|
signal,
|
|
409
|
-
|
|
410
|
-
});
|
|
459
|
+
}).then(rejectRedirectResponse);
|
|
411
460
|
}
|
|
412
461
|
/** Consume one response body without ever retaining more than `maxBytes`. */
|
|
413
462
|
async readBoundedBody(response, maxBytes, controller) {
|
|
@@ -476,7 +525,7 @@ export class FrihetClient {
|
|
|
476
525
|
* active until the body is complete, and a normal success performs one GET.
|
|
477
526
|
* A 429 may retry, matching the generic JSON client path.
|
|
478
527
|
*/
|
|
479
|
-
async requestDocument(method, path, body, query, idempotencyKey, maxBytesForContentType, retryCount = 0) {
|
|
528
|
+
async requestDocument(method, path, body, query, idempotencyKey, maxBytesForContentType, retryCount = 0, waitedMs = 0) {
|
|
480
529
|
const controller = new AbortController();
|
|
481
530
|
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
482
531
|
const startedAt = Date.now();
|
|
@@ -491,15 +540,10 @@ export class FrihetClient {
|
|
|
491
540
|
if (retryCount >= MAX_RETRIES) {
|
|
492
541
|
throw new FrihetApiError(429, "rate_limit_exceeded", "Rate limit exceeded after multiple retries. Please try again later.");
|
|
493
542
|
}
|
|
494
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
495
|
-
const parsedRetryAfter = retryAfter ? Number.parseInt(retryAfter, 10) : Number.NaN;
|
|
496
|
-
const delayMs = Number.isFinite(parsedRetryAfter)
|
|
497
|
-
? parsedRetryAfter * 1000
|
|
498
|
-
: DEFAULT_RETRY_DELAY_MS * Math.pow(2, retryCount);
|
|
499
|
-
logRetry(method, path, retryCount, delayMs);
|
|
500
543
|
clearTimeout(timeoutId);
|
|
501
|
-
|
|
502
|
-
|
|
544
|
+
const delayMs = this.rateLimitDelayMs(response.headers.get("Retry-After"), retryCount, waitedMs, method, path);
|
|
545
|
+
await this.sleepFn(delayMs);
|
|
546
|
+
return this.requestDocument(method, path, body, query, idempotencyKey, maxBytesForContentType, retryCount + 1, waitedMs + delayMs);
|
|
503
547
|
}
|
|
504
548
|
if (!response.ok) {
|
|
505
549
|
logApiCall(method, path, response.status, durationMs);
|
|
@@ -511,7 +555,7 @@ export class FrihetClient {
|
|
|
511
555
|
if (error instanceof FrihetApiError && error.errorCode !== "invalid_response")
|
|
512
556
|
throw error;
|
|
513
557
|
}
|
|
514
|
-
throw new FrihetApiError(response.status, errorBody.error, errorBody.message ?? errorBody.error, typeof errorBody.detail === "string" ? errorBody.detail : undefined);
|
|
558
|
+
throw new FrihetApiError(response.status, errorBody.error, errorBody.message ?? errorBody.error, typeof errorBody.detail === "string" ? errorBody.detail : undefined, errorBody.requestId ?? safeRequestId(response.headers.get("x-request-id")));
|
|
515
559
|
}
|
|
516
560
|
logApiCall(method, path, response.status, durationMs);
|
|
517
561
|
const filename = attachmentFilename(response.headers.get("content-disposition"));
|
|
@@ -538,8 +582,22 @@ export class FrihetClient {
|
|
|
538
582
|
clearTimeout(timeoutId);
|
|
539
583
|
}
|
|
540
584
|
}
|
|
541
|
-
|
|
542
|
-
|
|
585
|
+
/**
|
|
586
|
+
* Wait before the next 429 retry: the server's `Retry-After` when it parses
|
|
587
|
+
* (RFC 9110 §10.2.3), otherwise exponential backoff. Either wait that does
|
|
588
|
+
* not fit the remaining cumulative budget is deferred to the caller; a server
|
|
589
|
+
* wait is never shortened into an early retry.
|
|
590
|
+
*/
|
|
591
|
+
rateLimitDelayMs(retryAfter, retryCount, waitedMs, method, path) {
|
|
592
|
+
const serverMs = parseRetryAfter(retryAfter, this.nowFn());
|
|
593
|
+
const delayMs = serverMs ?? DEFAULT_RETRY_DELAY_MS * Math.pow(2, retryCount);
|
|
594
|
+
if (waitedMs + delayMs > this.retryBudgetMs) {
|
|
595
|
+
throw new FrihetApiError(429, "rate_limit_deferred", serverMs !== null
|
|
596
|
+
? `Rate limited: the server asks to wait ${Math.ceil(serverMs / 1000)} seconds, which exceeds this operation's retry budget. Retry after that time; nothing was retried early.`
|
|
597
|
+
: "Rate limited: the next backoff wait exceeds this operation's retry budget. Retry later.");
|
|
598
|
+
}
|
|
599
|
+
logRetry(method, path, retryCount, delayMs, serverMs === null ? "backoff" : "retry_after");
|
|
600
|
+
return delayMs;
|
|
543
601
|
}
|
|
544
602
|
/**
|
|
545
603
|
* Wrapper for endpoints whose backend wraps the payload in a `{ data, meta }`
|
|
@@ -569,7 +627,7 @@ export class FrihetClient {
|
|
|
569
627
|
* Only unwraps when the body is an object carrying a `data` property that is
|
|
570
628
|
* itself a (non-array) object — i.e. a genuine single-object envelope. It is
|
|
571
629
|
* deliberately distinct from {@link requestPaginated}, which keeps the
|
|
572
|
-
*
|
|
630
|
+
* list rows and pagination fields while omitting transport metadata. If the body is
|
|
573
631
|
* not an envelope (legacy endpoints that return the item directly), it is
|
|
574
632
|
* returned unchanged so existing callers keep working.
|
|
575
633
|
*/
|
|
@@ -589,13 +647,21 @@ export class FrihetClient {
|
|
|
589
647
|
}
|
|
590
648
|
return raw;
|
|
591
649
|
}
|
|
592
|
-
/**
|
|
650
|
+
/** Validate list rows and emit only the declared MCP pagination envelope. */
|
|
593
651
|
async requestPaginated(method, path, body, query) {
|
|
594
652
|
const result = await this.request(method, path, body, query);
|
|
595
653
|
if (!result || !Array.isArray(result.data)) {
|
|
596
654
|
throw new FrihetApiError(200, 'invalid_response', 'API returned invalid paginated response');
|
|
597
655
|
}
|
|
598
|
-
|
|
656
|
+
// API transport metadata is not part of the closed MCP page contract.
|
|
657
|
+
// Keep rows untouched and preserve every declared pagination field.
|
|
658
|
+
return {
|
|
659
|
+
data: result.data,
|
|
660
|
+
total: result.total,
|
|
661
|
+
limit: result.limit,
|
|
662
|
+
offset: result.offset,
|
|
663
|
+
...(result.nextCursor !== undefined ? { nextCursor: result.nextCursor } : {}),
|
|
664
|
+
};
|
|
599
665
|
}
|
|
600
666
|
// ---------------------------------------------------------------- Public
|
|
601
667
|
// ----------------------------------------------------------------
|
|
@@ -615,13 +681,13 @@ export class FrihetClient {
|
|
|
615
681
|
});
|
|
616
682
|
}
|
|
617
683
|
async getInvoice(id) {
|
|
618
|
-
return this.requestUnwrapped("GET", `/invoices/${
|
|
684
|
+
return this.requestUnwrapped("GET", `/invoices/${pathSegment(id)}`);
|
|
619
685
|
}
|
|
620
686
|
async createInvoice(data) {
|
|
621
687
|
return this.requestUnwrapped("POST", "/invoices", data);
|
|
622
688
|
}
|
|
623
689
|
async updateInvoice(id, data) {
|
|
624
|
-
return this.requestUnwrapped("PATCH", `/invoices/${
|
|
690
|
+
return this.requestUnwrapped("PATCH", `/invoices/${pathSegment(id)}`, data);
|
|
625
691
|
}
|
|
626
692
|
/**
|
|
627
693
|
* DELETE /invoices/{id} — NOT an unconditional destroy.
|
|
@@ -638,7 +704,7 @@ export class FrihetClient {
|
|
|
638
704
|
* Resolves to the unwrapped soft-cancel payload on 200, `undefined` on 204.
|
|
639
705
|
*/
|
|
640
706
|
async deleteInvoice(id) {
|
|
641
|
-
return this.requestUnwrapped("DELETE", `/invoices/${
|
|
707
|
+
return this.requestUnwrapped("DELETE", `/invoices/${pathSegment(id)}`);
|
|
642
708
|
}
|
|
643
709
|
async searchInvoices(query, params) {
|
|
644
710
|
return this.requestPaginated("GET", "/invoices", undefined, {
|
|
@@ -667,16 +733,16 @@ export class FrihetClient {
|
|
|
667
733
|
});
|
|
668
734
|
}
|
|
669
735
|
async getExpense(id) {
|
|
670
|
-
return this.requestUnwrapped("GET", `/expenses/${
|
|
736
|
+
return this.requestUnwrapped("GET", `/expenses/${pathSegment(id)}`);
|
|
671
737
|
}
|
|
672
738
|
async createExpense(data) {
|
|
673
739
|
return this.requestUnwrapped("POST", "/expenses", data);
|
|
674
740
|
}
|
|
675
741
|
async updateExpense(id, data) {
|
|
676
|
-
return this.requestUnwrapped("PATCH", `/expenses/${
|
|
742
|
+
return this.requestUnwrapped("PATCH", `/expenses/${pathSegment(id)}`, data);
|
|
677
743
|
}
|
|
678
744
|
async deleteExpense(id) {
|
|
679
|
-
return this.request("DELETE", `/expenses/${
|
|
745
|
+
return this.request("DELETE", `/expenses/${pathSegment(id)}`);
|
|
680
746
|
}
|
|
681
747
|
// ---------------------------------------------------------------- Clients
|
|
682
748
|
// ----------------------------------------------------------------
|
|
@@ -691,16 +757,16 @@ export class FrihetClient {
|
|
|
691
757
|
});
|
|
692
758
|
}
|
|
693
759
|
async getClient(id) {
|
|
694
|
-
return this.requestUnwrapped("GET", `/clients/${
|
|
760
|
+
return this.requestUnwrapped("GET", `/clients/${pathSegment(id)}`);
|
|
695
761
|
}
|
|
696
762
|
async createClient(data) {
|
|
697
763
|
return this.requestUnwrapped("POST", "/clients", data);
|
|
698
764
|
}
|
|
699
765
|
async updateClient(id, data) {
|
|
700
|
-
return this.requestUnwrapped("PATCH", `/clients/${
|
|
766
|
+
return this.requestUnwrapped("PATCH", `/clients/${pathSegment(id)}`, data);
|
|
701
767
|
}
|
|
702
768
|
async deleteClient(id) {
|
|
703
|
-
return this.request("DELETE", `/clients/${
|
|
769
|
+
return this.request("DELETE", `/clients/${pathSegment(id)}`);
|
|
704
770
|
}
|
|
705
771
|
// ---------------------------------------------------------------- Products
|
|
706
772
|
// ----------------------------------------------------------------
|
|
@@ -717,16 +783,16 @@ export class FrihetClient {
|
|
|
717
783
|
});
|
|
718
784
|
}
|
|
719
785
|
async getProduct(id) {
|
|
720
|
-
return this.requestUnwrapped("GET", `/products/${
|
|
786
|
+
return this.requestUnwrapped("GET", `/products/${pathSegment(id)}`);
|
|
721
787
|
}
|
|
722
788
|
async createProduct(data) {
|
|
723
789
|
return this.requestUnwrapped("POST", "/products", data);
|
|
724
790
|
}
|
|
725
791
|
async updateProduct(id, data) {
|
|
726
|
-
return this.requestUnwrapped("PATCH", `/products/${
|
|
792
|
+
return this.requestUnwrapped("PATCH", `/products/${pathSegment(id)}`, data);
|
|
727
793
|
}
|
|
728
794
|
async deleteProduct(id) {
|
|
729
|
-
return this.request("DELETE", `/products/${
|
|
795
|
+
return this.request("DELETE", `/products/${pathSegment(id)}`);
|
|
730
796
|
}
|
|
731
797
|
// ---------------------------------------------------------------- Quotes
|
|
732
798
|
// ----------------------------------------------------------------
|
|
@@ -744,13 +810,13 @@ export class FrihetClient {
|
|
|
744
810
|
});
|
|
745
811
|
}
|
|
746
812
|
async getQuote(id) {
|
|
747
|
-
return this.requestUnwrapped("GET", `/quotes/${
|
|
813
|
+
return this.requestUnwrapped("GET", `/quotes/${pathSegment(id)}`);
|
|
748
814
|
}
|
|
749
815
|
async createQuote(data) {
|
|
750
816
|
return this.requestUnwrapped("POST", "/quotes", data);
|
|
751
817
|
}
|
|
752
818
|
async updateQuote(id, data) {
|
|
753
|
-
return this.requestUnwrapped("PATCH", `/quotes/${
|
|
819
|
+
return this.requestUnwrapped("PATCH", `/quotes/${pathSegment(id)}`, data);
|
|
754
820
|
}
|
|
755
821
|
/**
|
|
756
822
|
* DELETE /quotes/{id} — same two-outcome contract as {@link deleteInvoice}:
|
|
@@ -759,7 +825,7 @@ export class FrihetClient {
|
|
|
759
825
|
* and refuses a protected draft with 409.
|
|
760
826
|
*/
|
|
761
827
|
async deleteQuote(id) {
|
|
762
|
-
return this.requestUnwrapped("DELETE", `/quotes/${
|
|
828
|
+
return this.requestUnwrapped("DELETE", `/quotes/${pathSegment(id)}`);
|
|
763
829
|
}
|
|
764
830
|
// ---------------------------------------------------------------- Vendors
|
|
765
831
|
// ----------------------------------------------------------------
|
|
@@ -773,42 +839,52 @@ export class FrihetClient {
|
|
|
773
839
|
});
|
|
774
840
|
}
|
|
775
841
|
async getVendor(id) {
|
|
776
|
-
return this.requestUnwrapped("GET", `/vendors/${
|
|
842
|
+
return this.requestUnwrapped("GET", `/vendors/${pathSegment(id)}`);
|
|
777
843
|
}
|
|
778
844
|
async createVendor(data) {
|
|
779
845
|
return this.requestUnwrapped("POST", "/vendors", data);
|
|
780
846
|
}
|
|
781
847
|
async updateVendor(id, data) {
|
|
782
|
-
return this.requestUnwrapped("PATCH", `/vendors/${
|
|
848
|
+
return this.requestUnwrapped("PATCH", `/vendors/${pathSegment(id)}`, data);
|
|
783
849
|
}
|
|
784
850
|
async deleteVendor(id) {
|
|
785
|
-
return this.request("DELETE", `/vendors/${
|
|
851
|
+
return this.request("DELETE", `/vendors/${pathSegment(id)}`);
|
|
786
852
|
}
|
|
787
853
|
// ---------------------------------------------------------------- Invoice Actions
|
|
788
854
|
// ----------------------------------------------------------------
|
|
789
855
|
async sendInvoice(id, to) {
|
|
790
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
856
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(id)}/send`, to ? { recipientEmail: to } : undefined);
|
|
791
857
|
}
|
|
792
858
|
async markInvoicePaid(id, paidDate) {
|
|
793
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
859
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(id)}/paid`, paidDate ? { paidDate } : undefined);
|
|
794
860
|
}
|
|
795
861
|
async getInvoicePdf(id) {
|
|
796
862
|
// One fetch, one Response: validate MIME and signature only after the
|
|
797
|
-
// bounded stream completes. The ERP endpoint has always returned bytes
|
|
798
|
-
// there is no pre-signed-URL JSON
|
|
799
|
-
|
|
800
|
-
const
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
863
|
+
// bounded stream completes. The ERP endpoint has always returned raw bytes
|
|
864
|
+
// (functions/src/publicApi.ts:6149-6167); there is no pre-signed-URL or JSON
|
|
865
|
+
// success contract to emulate.
|
|
866
|
+
const url = `/invoices/${pathSegment(id)}/pdf`;
|
|
867
|
+
const doc = await this.requestDocument("GET", url, undefined, undefined, undefined, (contentType) => {
|
|
868
|
+
const norm = normalizedContentType(contentType);
|
|
869
|
+
return norm === "application/pdf"
|
|
870
|
+
|| norm === "application/octet-stream"
|
|
871
|
+
|| norm === "binary/octet-stream"
|
|
872
|
+
? MAX_PDF_BYTES
|
|
873
|
+
: MAX_ERROR_BYTES;
|
|
874
|
+
});
|
|
875
|
+
const normType = normalizedContentType(doc.contentType);
|
|
876
|
+
if (normType !== "application/pdf" && normType !== "application/octet-stream" && normType !== "binary/octet-stream") {
|
|
804
877
|
throw new FrihetApiError(200, "invalid_response", `Invoice PDF endpoint returned unexpected Content-Type: ${doc.contentType}`);
|
|
805
878
|
}
|
|
806
879
|
if (!hasPdfSignature(doc.bytes)) {
|
|
807
880
|
throw new FrihetApiError(200, "invalid_response", "Invoice PDF response is malformed");
|
|
808
881
|
}
|
|
882
|
+
if (doc.sizeBytes > MAX_PDF_BYTES) {
|
|
883
|
+
throw new FrihetApiError(413, "payload_too_large", `Invoice PDF exceeds maximum allowed size of ${MAX_PDF_BYTES} bytes`);
|
|
884
|
+
}
|
|
809
885
|
return {
|
|
810
886
|
id,
|
|
811
|
-
contentType:
|
|
887
|
+
contentType: "application/pdf",
|
|
812
888
|
sizeBytes: doc.sizeBytes,
|
|
813
889
|
base64: encodeBase64(doc.bytes),
|
|
814
890
|
...(doc.filename ? { filename: doc.filename } : {}),
|
|
@@ -817,7 +893,7 @@ export class FrihetClient {
|
|
|
817
893
|
async getInvoiceEInvoice(invoiceId) {
|
|
818
894
|
// Stored e-invoices are either XML or Factur-X PDF. Select the size cap
|
|
819
895
|
// from the actual MIME before reading and preserve the request identity.
|
|
820
|
-
const url = `/invoices/${
|
|
896
|
+
const url = `/invoices/${pathSegment(invoiceId)}/xml`;
|
|
821
897
|
const doc = await this.requestDocument("GET", url, undefined, undefined, undefined, (contentType) => {
|
|
822
898
|
const normalized = normalizedContentType(contentType);
|
|
823
899
|
if (normalized === "application/pdf")
|
|
@@ -867,15 +943,15 @@ export class FrihetClient {
|
|
|
867
943
|
* which is the property that matters here.
|
|
868
944
|
*/
|
|
869
945
|
async createCreditNote(invoiceId, data, idempotencyKey) {
|
|
870
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
946
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(invoiceId)}/credit-note`, data, undefined, idempotencyKey);
|
|
871
947
|
}
|
|
872
948
|
async applyLateFee(invoiceId, data) {
|
|
873
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
949
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(invoiceId)}/late-fee`, data ?? {});
|
|
874
950
|
}
|
|
875
951
|
// ---------------------------------------------------------------- Quote Actions
|
|
876
952
|
// ----------------------------------------------------------------
|
|
877
953
|
async sendQuote(id, to) {
|
|
878
|
-
return this.requestUnwrapped("POST", `/quotes/${
|
|
954
|
+
return this.requestUnwrapped("POST", `/quotes/${pathSegment(id)}/send`, to ? { recipientEmail: to } : undefined);
|
|
879
955
|
}
|
|
880
956
|
// ---------------------------------------------------------------- Webhooks
|
|
881
957
|
// ----------------------------------------------------------------
|
|
@@ -897,7 +973,7 @@ export class FrihetClient {
|
|
|
897
973
|
};
|
|
898
974
|
}
|
|
899
975
|
async getWebhook(id) {
|
|
900
|
-
const result = await this.requestUnwrapped("GET", `/webhooks/${
|
|
976
|
+
const result = await this.requestUnwrapped("GET", `/webhooks/${pathSegment(id)}`);
|
|
901
977
|
return normalizeWebhookRecord(result);
|
|
902
978
|
}
|
|
903
979
|
async createWebhook(data) {
|
|
@@ -905,50 +981,79 @@ export class FrihetClient {
|
|
|
905
981
|
return normalizeWebhookRecord(result, typeof data.secret === "string" ? data.secret : undefined);
|
|
906
982
|
}
|
|
907
983
|
async updateWebhook(id, data) {
|
|
908
|
-
const result = await this.requestUnwrapped("PATCH", `/webhooks/${
|
|
984
|
+
const result = await this.requestUnwrapped("PATCH", `/webhooks/${pathSegment(id)}`, data);
|
|
909
985
|
return normalizeWebhookRecord(result);
|
|
910
986
|
}
|
|
911
987
|
async deleteWebhook(id) {
|
|
912
|
-
return this.request("DELETE", `/webhooks/${
|
|
988
|
+
return this.request("DELETE", `/webhooks/${pathSegment(id)}`);
|
|
989
|
+
}
|
|
990
|
+
// ---------------------------------------------------------------- Cross-resource search
|
|
991
|
+
// ----------------------------------------------------------------
|
|
992
|
+
//
|
|
993
|
+
// GET /v1/search/global?q=<term>&types=a,b,c&limit=&offset= (publicApi.ts:4476)
|
|
994
|
+
//
|
|
995
|
+
// Read-only fan-out across invoices / expenses / vendors / clients / products.
|
|
996
|
+
// All five collections are workspace-scoped via users/{userId}/..., so the
|
|
997
|
+
// tenant boundary is preserved by Firestore subcollection paths — same as
|
|
998
|
+
// every other list/search endpoint on the server. No mutation counterpart;
|
|
999
|
+
// no auth boundary different from any other read tool.
|
|
1000
|
+
async globalSearch(params) {
|
|
1001
|
+
const typesCsv = params.types && params.types.length > 0 ? params.types.join(",") : undefined;
|
|
1002
|
+
const result = await this.request("GET", "/search/global", undefined, {
|
|
1003
|
+
q: params.q,
|
|
1004
|
+
types: typesCsv,
|
|
1005
|
+
limit: params.limit,
|
|
1006
|
+
offset: params.offset,
|
|
1007
|
+
});
|
|
1008
|
+
return {
|
|
1009
|
+
data: result.data,
|
|
1010
|
+
total: result.total,
|
|
1011
|
+
limit: result.limit,
|
|
1012
|
+
offset: result.offset,
|
|
1013
|
+
hasMore: result.hasMore,
|
|
1014
|
+
query: result.query,
|
|
1015
|
+
types: result.types,
|
|
1016
|
+
...(result.truncated !== undefined ? { truncated: result.truncated } : {}),
|
|
1017
|
+
};
|
|
913
1018
|
}
|
|
914
1019
|
// ---------------------------------------------------------------- CRM: Contacts
|
|
915
1020
|
// ----------------------------------------------------------------
|
|
916
1021
|
async listClientContacts(clientId, params) {
|
|
917
|
-
return this.requestPaginated("GET", `/clients/${
|
|
1022
|
+
return this.requestPaginated("GET", `/clients/${pathSegment(clientId)}/contacts`, undefined, {
|
|
918
1023
|
limit: params?.limit,
|
|
919
1024
|
offset: params?.offset,
|
|
920
1025
|
});
|
|
921
1026
|
}
|
|
922
1027
|
async createClientContact(clientId, data) {
|
|
923
|
-
return this.requestUnwrapped("POST", `/clients/${
|
|
1028
|
+
return this.requestUnwrapped("POST", `/clients/${pathSegment(clientId)}/contacts`, data);
|
|
924
1029
|
}
|
|
925
1030
|
async deleteClientContact(clientId, contactId) {
|
|
926
|
-
return this.request("DELETE", `/clients/${
|
|
1031
|
+
return this.request("DELETE", `/clients/${pathSegment(clientId)}/contacts/${pathSegment(contactId)}`);
|
|
927
1032
|
}
|
|
928
1033
|
// ---------------------------------------------------------------- CRM: Activities
|
|
929
1034
|
// ----------------------------------------------------------------
|
|
930
1035
|
async listClientActivities(clientId, params) {
|
|
931
|
-
return this.requestPaginated("GET", `/clients/${
|
|
1036
|
+
return this.requestPaginated("GET", `/clients/${pathSegment(clientId)}/activities`, undefined, {
|
|
932
1037
|
limit: params?.limit,
|
|
933
1038
|
offset: params?.offset,
|
|
934
1039
|
});
|
|
935
1040
|
}
|
|
936
1041
|
async logClientActivity(clientId, data) {
|
|
937
|
-
return this.requestUnwrapped("POST", `/clients/${
|
|
1042
|
+
return this.requestUnwrapped("POST", `/clients/${pathSegment(clientId)}/activities`, data);
|
|
938
1043
|
}
|
|
939
1044
|
// ---------------------------------------------------------------- CRM: Notes
|
|
940
1045
|
// ----------------------------------------------------------------
|
|
941
1046
|
async listClientNotes(clientId, params) {
|
|
942
|
-
return this.requestPaginated("GET", `/clients/${
|
|
1047
|
+
return this.requestPaginated("GET", `/clients/${pathSegment(clientId)}/notes`, undefined, {
|
|
943
1048
|
limit: params?.limit,
|
|
944
1049
|
offset: params?.offset,
|
|
945
1050
|
});
|
|
946
1051
|
}
|
|
947
1052
|
async createClientNote(clientId, data) {
|
|
948
|
-
return this.requestUnwrapped("POST", `/clients/${
|
|
1053
|
+
return this.requestUnwrapped("POST", `/clients/${pathSegment(clientId)}/notes`, data);
|
|
949
1054
|
}
|
|
950
1055
|
async deleteClientNote(clientId, noteId) {
|
|
951
|
-
return this.request("DELETE", `/clients/${
|
|
1056
|
+
return this.request("DELETE", `/clients/${pathSegment(clientId)}/notes/${pathSegment(noteId)}`);
|
|
952
1057
|
}
|
|
953
1058
|
// ---------------------------------------------------------------- Deposits
|
|
954
1059
|
// ----------------------------------------------------------------
|
|
@@ -965,22 +1070,22 @@ export class FrihetClient {
|
|
|
965
1070
|
});
|
|
966
1071
|
}
|
|
967
1072
|
async getDeposit(id) {
|
|
968
|
-
return this.requestUnwrapped("GET", `/deposits/${
|
|
1073
|
+
return this.requestUnwrapped("GET", `/deposits/${pathSegment(id)}`);
|
|
969
1074
|
}
|
|
970
1075
|
async createDeposit(data) {
|
|
971
|
-
return this.requestUnwrapped("POST", "/deposits", data);
|
|
1076
|
+
return this.requestUnwrapped("POST", "/deposits", normalizeDepositBody(data, false));
|
|
972
1077
|
}
|
|
973
1078
|
async updateDeposit(id, data) {
|
|
974
|
-
return this.requestUnwrapped("PATCH", `/deposits/${
|
|
1079
|
+
return this.requestUnwrapped("PATCH", `/deposits/${pathSegment(id)}`, normalizeDepositBody(data, true));
|
|
975
1080
|
}
|
|
976
1081
|
async deleteDeposit(id) {
|
|
977
|
-
return this.request("DELETE", `/deposits/${
|
|
1082
|
+
return this.request("DELETE", `/deposits/${pathSegment(id)}`);
|
|
978
1083
|
}
|
|
979
1084
|
async applyDeposit(id, data) {
|
|
980
|
-
return this.requestUnwrapped("POST", `/deposits/${
|
|
1085
|
+
return this.requestUnwrapped("POST", `/deposits/${pathSegment(id)}/apply`, normalizeApplyDepositBody(data));
|
|
981
1086
|
}
|
|
982
1087
|
async refundDeposit(id, data) {
|
|
983
|
-
return this.requestUnwrapped("POST", `/deposits/${
|
|
1088
|
+
return this.requestUnwrapped("POST", `/deposits/${pathSegment(id)}/refund`, data ?? {});
|
|
984
1089
|
}
|
|
985
1090
|
// ---------------------------------------------------------------- E-Invoicing
|
|
986
1091
|
// ----------------------------------------------------------------
|
|
@@ -991,7 +1096,7 @@ export class FrihetClient {
|
|
|
991
1096
|
// get_einvoice_status's outputSchema (eInvoiceStatusOutput) expects the
|
|
992
1097
|
// flat status object, same single-object envelope convention as the rest
|
|
993
1098
|
// of the /v1 REST surface.
|
|
994
|
-
return this.requestUnwrapped("GET", `/einvoice/status/${
|
|
1099
|
+
return this.requestUnwrapped("GET", `/einvoice/status/${pathSegment(workflowRunId)}`);
|
|
995
1100
|
}
|
|
996
1101
|
async validateEInvoiceXml(params) {
|
|
997
1102
|
return this.requestUnwrapped("POST", "/einvoice/validate", params);
|
|
@@ -1004,22 +1109,26 @@ export class FrihetClient {
|
|
|
1004
1109
|
// New per-invoice endpoints: /v1/invoices/:id/einvoice/export, /face/*, /ticketbai/*
|
|
1005
1110
|
// 404 responses propagate to tool handlers which fall back to stub responses.
|
|
1006
1111
|
async exportEInvoice(params) {
|
|
1007
|
-
const { invoiceId,
|
|
1008
|
-
|
|
1112
|
+
const { invoiceId, format, signed } = params;
|
|
1113
|
+
const body = {
|
|
1114
|
+
format: toApiEInvoiceExportFormat(format),
|
|
1115
|
+
...(signed === undefined ? {} : { signed }),
|
|
1116
|
+
};
|
|
1117
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(invoiceId)}/einvoice/export`, body);
|
|
1009
1118
|
}
|
|
1010
1119
|
async faceSubmit(params) {
|
|
1011
1120
|
const { invoiceId, mode } = params;
|
|
1012
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
1121
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(invoiceId)}/face/submit`, { mode });
|
|
1013
1122
|
}
|
|
1014
1123
|
async faceStatus(params) {
|
|
1015
|
-
return this.request("GET", `/invoices/${
|
|
1124
|
+
return this.request("GET", `/invoices/${pathSegment(params.invoiceId)}/face/status`);
|
|
1016
1125
|
}
|
|
1017
1126
|
async ticketbaiSubmit(params) {
|
|
1018
1127
|
const { invoiceId, sandbox } = params;
|
|
1019
|
-
return this.requestUnwrapped("POST", `/invoices/${
|
|
1128
|
+
return this.requestUnwrapped("POST", `/invoices/${pathSegment(invoiceId)}/ticketbai/submit`, { sandbox });
|
|
1020
1129
|
}
|
|
1021
1130
|
async ticketbaiStatus(params) {
|
|
1022
|
-
return this.request("GET", `/invoices/${
|
|
1131
|
+
return this.request("GET", `/invoices/${pathSegment(params.invoiceId)}/ticketbai/status`);
|
|
1023
1132
|
}
|
|
1024
1133
|
// kSeFSubmit intentionally omitted — ksef_submit tool is always-stub (public KSeF endpoint not yet exposed; transport infra-ready in Frihet-ERP).
|
|
1025
1134
|
// ---------------------------------------------------------------- Stay (Vacation Rental)
|
|
@@ -1040,7 +1149,7 @@ export class FrihetClient {
|
|
|
1040
1149
|
});
|
|
1041
1150
|
}
|
|
1042
1151
|
async getReservation(id) {
|
|
1043
|
-
return this.requestUnwrapped("GET", `/stay/reservations/${
|
|
1152
|
+
return this.requestUnwrapped("GET", `/stay/reservations/${pathSegment(id)}`);
|
|
1044
1153
|
}
|
|
1045
1154
|
async createReservation(data) {
|
|
1046
1155
|
return this.requestUnwrapped("POST", "/stay/reservations", data);
|
|
@@ -1056,7 +1165,7 @@ export class FrihetClient {
|
|
|
1056
1165
|
});
|
|
1057
1166
|
}
|
|
1058
1167
|
async syncChannel(channelId, direction) {
|
|
1059
|
-
return this.requestUnwrapped("POST", `/stay/channels/${
|
|
1168
|
+
return this.requestUnwrapped("POST", `/stay/channels/${pathSegment(channelId)}/sync`, { direction });
|
|
1060
1169
|
}
|
|
1061
1170
|
// ---------------------------------------------------------------- POS (Point of Sale)
|
|
1062
1171
|
// ----------------------------------------------------------------
|
|
@@ -1069,7 +1178,7 @@ export class FrihetClient {
|
|
|
1069
1178
|
});
|
|
1070
1179
|
}
|
|
1071
1180
|
async getSale(id) {
|
|
1072
|
-
return this.requestUnwrapped("GET", `/pos/sales/${
|
|
1181
|
+
return this.requestUnwrapped("GET", `/pos/sales/${pathSegment(id)}`);
|
|
1073
1182
|
}
|
|
1074
1183
|
async listSales(params) {
|
|
1075
1184
|
return this.requestPaginated("GET", "/pos/sales", undefined, {
|
|
@@ -1083,7 +1192,7 @@ export class FrihetClient {
|
|
|
1083
1192
|
});
|
|
1084
1193
|
}
|
|
1085
1194
|
async refundSale(id, data) {
|
|
1086
|
-
return this.requestUnwrapped("POST", `/pos/sales/${
|
|
1195
|
+
return this.requestUnwrapped("POST", `/pos/sales/${pathSegment(id)}/refund`, data ?? {});
|
|
1087
1196
|
}
|
|
1088
1197
|
// ---------------------------------------------------------------- Kitchen (KDS)
|
|
1089
1198
|
// ----------------------------------------------------------------
|
|
@@ -1098,10 +1207,13 @@ export class FrihetClient {
|
|
|
1098
1207
|
});
|
|
1099
1208
|
}
|
|
1100
1209
|
async getKitchenTicket(id) {
|
|
1101
|
-
return this.requestUnwrapped("GET", `/kitchen/tickets/${
|
|
1210
|
+
return this.requestUnwrapped("GET", `/kitchen/tickets/${pathSegment(id)}`);
|
|
1102
1211
|
}
|
|
1103
1212
|
async updateKitchenTicket(id, data) {
|
|
1104
|
-
|
|
1213
|
+
// The public tool accepts one station; the strict REST contract stores an array.
|
|
1214
|
+
const { stationId, ...patch } = data;
|
|
1215
|
+
const body = stationId === undefined ? patch : { ...patch, stationIds: [stationId] };
|
|
1216
|
+
return this.requestUnwrapped("PATCH", `/kitchen/tickets/${pathSegment(id)}`, body);
|
|
1105
1217
|
}
|
|
1106
1218
|
async listKitchenStations(params) {
|
|
1107
1219
|
return this.requestPaginated("GET", "/kitchen/stations", undefined, {
|
|
@@ -1144,7 +1256,7 @@ export class FrihetClient {
|
|
|
1144
1256
|
});
|
|
1145
1257
|
}
|
|
1146
1258
|
async getBankAccount(id) {
|
|
1147
|
-
return this.requestUnwrapped("GET", `/banking/accounts/${
|
|
1259
|
+
return this.requestUnwrapped("GET", `/banking/accounts/${pathSegment(id)}`);
|
|
1148
1260
|
}
|
|
1149
1261
|
async listTransactions(params) {
|
|
1150
1262
|
return this.requestPaginated("GET", "/banking/transactions", undefined, {
|
|
@@ -1159,10 +1271,10 @@ export class FrihetClient {
|
|
|
1159
1271
|
});
|
|
1160
1272
|
}
|
|
1161
1273
|
async categorizeTransaction(id, data) {
|
|
1162
|
-
return this.requestUnwrapped("PATCH", `/banking/transactions/${
|
|
1274
|
+
return this.requestUnwrapped("PATCH", `/banking/transactions/${pathSegment(id)}/categorize`, data);
|
|
1163
1275
|
}
|
|
1164
1276
|
async matchTransactionToDocument(transactionId, data) {
|
|
1165
|
-
return this.requestUnwrapped("POST", `/banking/transactions/${
|
|
1277
|
+
return this.requestUnwrapped("POST", `/banking/transactions/${pathSegment(transactionId)}/match`, data);
|
|
1166
1278
|
}
|
|
1167
1279
|
// ---------------------------------------------------------------- Fiscal
|
|
1168
1280
|
// RESOLVED (was: "planned — 404 propagates until backend ships"). The
|
|
@@ -1174,18 +1286,19 @@ export class FrihetClient {
|
|
|
1174
1286
|
// NOTE: /v1/fiscal/verifactu/* + /ticketbai/* below remain pending and 404
|
|
1175
1287
|
// until those backends ship (they keep the raw `request` path for now).
|
|
1176
1288
|
async getFiscalModeloSummary(modeloCode, period) {
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1289
|
+
// The backend reads `?quarter=` (303/130) or `?year=` (390/347) and
|
|
1290
|
+
// silently answers the CURRENT period for any other param, so the param
|
|
1291
|
+
// name comes from the shared rule table, never a generic `?period=`.
|
|
1292
|
+
return this.requestUnwrapped("GET", `/fiscal/modelo/${pathSegment(modeloCode)}`, undefined, fiscalModeloQuery(modeloCode, period));
|
|
1180
1293
|
}
|
|
1181
1294
|
async getVerifactuStatus(invoiceId) {
|
|
1182
|
-
return this.requestUnwrapped("GET", `/fiscal/verifactu/${
|
|
1295
|
+
return this.requestUnwrapped("GET", `/fiscal/verifactu/${pathSegment(invoiceId)}/status`);
|
|
1183
1296
|
}
|
|
1184
1297
|
async resubmitVerifactu(invoiceId) {
|
|
1185
|
-
return this.requestUnwrapped("POST", `/fiscal/verifactu/${
|
|
1298
|
+
return this.requestUnwrapped("POST", `/fiscal/verifactu/${pathSegment(invoiceId)}/resubmit`, {});
|
|
1186
1299
|
}
|
|
1187
1300
|
async getTicketbaiStatus(invoiceId) {
|
|
1188
|
-
return this.request("GET", `/fiscal/ticketbai/${
|
|
1301
|
+
return this.request("GET", `/fiscal/ticketbai/${pathSegment(invoiceId)}/status`);
|
|
1189
1302
|
}
|
|
1190
1303
|
// ---------------------------------------------------------------- Time Tracking
|
|
1191
1304
|
// Backend: /v1/time/* endpoints live as of Wave 4-A (Frihet-ERP functions/src/publicApi.ts).
|
|
@@ -1202,16 +1315,16 @@ export class FrihetClient {
|
|
|
1202
1315
|
});
|
|
1203
1316
|
}
|
|
1204
1317
|
async getTimeEntry(id) {
|
|
1205
|
-
return this.requestUnwrapped("GET", `/time/entries/${
|
|
1318
|
+
return this.requestUnwrapped("GET", `/time/entries/${pathSegment(id)}`);
|
|
1206
1319
|
}
|
|
1207
1320
|
async createTimeEntry(data) {
|
|
1208
1321
|
return this.requestUnwrapped("POST", "/time/entries", data);
|
|
1209
1322
|
}
|
|
1210
1323
|
async updateTimeEntry(id, data) {
|
|
1211
|
-
return this.requestUnwrapped("PATCH", `/time/entries/${
|
|
1324
|
+
return this.requestUnwrapped("PATCH", `/time/entries/${pathSegment(id)}`, data);
|
|
1212
1325
|
}
|
|
1213
1326
|
async deleteTimeEntry(id) {
|
|
1214
|
-
return this.request("DELETE", `/time/entries/${
|
|
1327
|
+
return this.request("DELETE", `/time/entries/${pathSegment(id)}`);
|
|
1215
1328
|
}
|
|
1216
1329
|
async getTimeSummary(params) {
|
|
1217
1330
|
return this.requestUnwrapped("GET", "/time/summary", undefined, {
|
|
@@ -1232,25 +1345,25 @@ export class FrihetClient {
|
|
|
1232
1345
|
});
|
|
1233
1346
|
}
|
|
1234
1347
|
async getRecurringInvoice(id) {
|
|
1235
|
-
return this.requestUnwrapped("GET", `/recurring/invoices/${
|
|
1348
|
+
return this.requestUnwrapped("GET", `/recurring/invoices/${pathSegment(id)}`);
|
|
1236
1349
|
}
|
|
1237
1350
|
async createRecurringInvoice(data) {
|
|
1238
1351
|
return this.requestUnwrapped("POST", "/recurring/invoices", data);
|
|
1239
1352
|
}
|
|
1240
1353
|
async updateRecurringInvoice(id, data) {
|
|
1241
|
-
return this.requestUnwrapped("PATCH", `/recurring/invoices/${
|
|
1354
|
+
return this.requestUnwrapped("PATCH", `/recurring/invoices/${pathSegment(id)}`, data);
|
|
1242
1355
|
}
|
|
1243
1356
|
async pauseRecurringInvoice(id) {
|
|
1244
|
-
return this.requestUnwrapped("POST", `/recurring/invoices/${
|
|
1357
|
+
return this.requestUnwrapped("POST", `/recurring/invoices/${pathSegment(id)}/pause`, {});
|
|
1245
1358
|
}
|
|
1246
1359
|
async resumeRecurringInvoice(id) {
|
|
1247
|
-
return this.requestUnwrapped("POST", `/recurring/invoices/${
|
|
1360
|
+
return this.requestUnwrapped("POST", `/recurring/invoices/${pathSegment(id)}/resume`, {});
|
|
1248
1361
|
}
|
|
1249
1362
|
async deleteRecurringInvoice(id) {
|
|
1250
|
-
return this.request("DELETE", `/recurring/invoices/${
|
|
1363
|
+
return this.request("DELETE", `/recurring/invoices/${pathSegment(id)}`);
|
|
1251
1364
|
}
|
|
1252
1365
|
async runRecurringNow(templateId, options) {
|
|
1253
|
-
return this.requestUnwrapped("POST", `/recurring/invoices/${
|
|
1366
|
+
return this.requestUnwrapped("POST", `/recurring/invoices/${pathSegment(templateId)}/run`, {
|
|
1254
1367
|
draftOnly: options?.draftOnly ?? true,
|
|
1255
1368
|
});
|
|
1256
1369
|
}
|
|
@@ -1268,10 +1381,10 @@ export class FrihetClient {
|
|
|
1268
1381
|
return this.requestUnwrapped("POST", "/team/members/invite", data);
|
|
1269
1382
|
}
|
|
1270
1383
|
async updateTeamMemberRole(memberId, role) {
|
|
1271
|
-
return this.requestUnwrapped("PATCH", `/team/members/${
|
|
1384
|
+
return this.requestUnwrapped("PATCH", `/team/members/${pathSegment(memberId)}/role`, { role });
|
|
1272
1385
|
}
|
|
1273
1386
|
async removeTeamMember(memberId) {
|
|
1274
|
-
return this.request("DELETE", `/team/members/${
|
|
1387
|
+
return this.request("DELETE", `/team/members/${pathSegment(memberId)}`);
|
|
1275
1388
|
}
|
|
1276
1389
|
// ---------------------------------------------------------------- Gestoria (Wave Fase 1)
|
|
1277
1390
|
// Backend: /v1/gestoria/* endpoints land with Frihet-ERP Wave Fase 1 closure.
|
|
@@ -1297,20 +1410,22 @@ export class FrihetClient {
|
|
|
1297
1410
|
return this.requestUnwrapped("POST", "/gestoria/templates/bulk-send", data);
|
|
1298
1411
|
}
|
|
1299
1412
|
async getGestoriaAgingConsolidated(params) {
|
|
1300
|
-
return this.
|
|
1301
|
-
|
|
1413
|
+
return this.requestUnwrapped("POST", "/gestoria/aging", {
|
|
1414
|
+
workspaceIds: params?.workspaceIds,
|
|
1415
|
+
asOf: params?.asOf,
|
|
1416
|
+
bustCache: params?.bustCache,
|
|
1302
1417
|
});
|
|
1303
1418
|
}
|
|
1304
1419
|
// ---------------------------------------------------------------- Audit GL
|
|
1305
1420
|
// NOTE: /v1/gl/* endpoints proxy Firebase callables (PR #395). 404 until REST shell ships.
|
|
1306
1421
|
async approveGLEntry(entryId, notes) {
|
|
1307
|
-
return this.requestUnwrapped("POST", `/gl/entries/${
|
|
1422
|
+
return this.requestUnwrapped("POST", `/gl/entries/${pathSegment(entryId)}/approve`, { notes });
|
|
1308
1423
|
}
|
|
1309
|
-
async rejectGLEntry(entryId,
|
|
1310
|
-
return this.requestUnwrapped("POST", `/gl/entries/${
|
|
1424
|
+
async rejectGLEntry(entryId, note) {
|
|
1425
|
+
return this.requestUnwrapped("POST", `/gl/entries/${pathSegment(entryId)}/reject`, { note });
|
|
1311
1426
|
}
|
|
1312
1427
|
async getGLEntryAuditLog(entryId) {
|
|
1313
|
-
return this.request("GET", `/gl/entries/${
|
|
1428
|
+
return this.request("GET", `/gl/entries/${pathSegment(entryId)}/audit-log`);
|
|
1314
1429
|
}
|
|
1315
1430
|
// ---------------------------------------------------------------- White-label Portal Domain
|
|
1316
1431
|
// NOTE: /v1/portal/domain/* endpoints proxy Firebase callables (PR #397). 404 until REST shell ships.
|
|
@@ -1318,10 +1433,10 @@ export class FrihetClient {
|
|
|
1318
1433
|
return this.requestUnwrapped("POST", "/portal/domain", data);
|
|
1319
1434
|
}
|
|
1320
1435
|
async verifyCustomPortalDomain(data) {
|
|
1321
|
-
return this.requestUnwrapped("POST", `/portal/domain/${
|
|
1436
|
+
return this.requestUnwrapped("POST", `/portal/domain/${pathSegment(data.domain)}/verify`, {});
|
|
1322
1437
|
}
|
|
1323
1438
|
async removeCustomPortalDomain(data) {
|
|
1324
|
-
return this.request("DELETE", `/portal/domain/${
|
|
1439
|
+
return this.request("DELETE", `/portal/domain/${pathSegment(data.domain)}`);
|
|
1325
1440
|
}
|
|
1326
1441
|
// ---------------------------------------------------------------- Self-onboard + VIES
|
|
1327
1442
|
// NOTE: /v1/portal/onboard/* endpoints proxy Firebase callables (PR #398). 404 until REST shell ships.
|
|
@@ -1337,7 +1452,7 @@ export class FrihetClient {
|
|
|
1337
1452
|
// ---------------------------------------------------------------- IGIC (Canary Islands)
|
|
1338
1453
|
// NOTE: /v1/igic/* endpoints (PR #390). 404 until REST shell ships.
|
|
1339
1454
|
async getIgicModeloSummary(modeloCode, params) {
|
|
1340
|
-
return this.request("GET", `/igic/modelo/${
|
|
1455
|
+
return this.request("GET", `/igic/modelo/${pathSegment(modeloCode)}`, undefined, {
|
|
1341
1456
|
year: params?.year,
|
|
1342
1457
|
period: params?.period,
|
|
1343
1458
|
});
|
|
@@ -1348,7 +1463,7 @@ export class FrihetClient {
|
|
|
1348
1463
|
// ---------------------------------------------------------------- Impuesto Sociedades (IS)
|
|
1349
1464
|
// NOTE: /v1/is/* endpoints (PR #392). 404 until REST shell ships.
|
|
1350
1465
|
async getISSummary(modeloCode, params) {
|
|
1351
|
-
return this.request("GET", `/is/modelo/${
|
|
1466
|
+
return this.request("GET", `/is/modelo/${pathSegment(modeloCode)}`, undefined, {
|
|
1352
1467
|
year: params?.year,
|
|
1353
1468
|
installment: params?.installment,
|
|
1354
1469
|
});
|
|
@@ -1356,7 +1471,7 @@ export class FrihetClient {
|
|
|
1356
1471
|
// ---------------------------------------------------------------- Bank Rules
|
|
1357
1472
|
// NOTE: /v1/banking/rules Q3-flagged (PR #394). 404 until callable wrapper ships.
|
|
1358
1473
|
async listBankRules(params) {
|
|
1359
|
-
return this.
|
|
1474
|
+
return this.requestPaginated("GET", "/banking/rules", undefined, {
|
|
1360
1475
|
isActive: params?.isActive !== undefined ? String(params.isActive) : undefined,
|
|
1361
1476
|
limit: params?.limit,
|
|
1362
1477
|
offset: params?.offset,
|
|
@@ -1383,19 +1498,19 @@ export class FrihetClient {
|
|
|
1383
1498
|
return this.requestUnwrapped("POST", "/leaves", data);
|
|
1384
1499
|
}
|
|
1385
1500
|
async approveLeave(leaveId, data) {
|
|
1386
|
-
return this.requestUnwrapped("POST", `/leaves/${
|
|
1501
|
+
return this.requestUnwrapped("POST", `/leaves/${pathSegment(leaveId)}/approve`, data ?? {});
|
|
1387
1502
|
}
|
|
1388
1503
|
async rejectLeave(leaveId, data) {
|
|
1389
|
-
return this.requestUnwrapped("POST", `/leaves/${
|
|
1504
|
+
return this.requestUnwrapped("POST", `/leaves/${pathSegment(leaveId)}/reject`, data);
|
|
1390
1505
|
}
|
|
1391
1506
|
async cancelLeave(leaveId) {
|
|
1392
|
-
return this.requestUnwrapped("POST", `/leaves/${
|
|
1507
|
+
return this.requestUnwrapped("POST", `/leaves/${pathSegment(leaveId)}/cancel`, {});
|
|
1393
1508
|
}
|
|
1394
|
-
async attendanceClockIn(data) {
|
|
1395
|
-
return this.requestUnwrapped("POST", "/time-entries/clock-in", data);
|
|
1509
|
+
async attendanceClockIn(data, idempotencyKey) {
|
|
1510
|
+
return this.requestUnwrapped("POST", "/time-entries/clock-in", data, undefined, idempotencyKey);
|
|
1396
1511
|
}
|
|
1397
|
-
async attendanceClockOut(entryId) {
|
|
1398
|
-
return this.requestUnwrapped("PATCH", `/time-entries/${
|
|
1512
|
+
async attendanceClockOut(entryId, idempotencyKey) {
|
|
1513
|
+
return this.requestUnwrapped("PATCH", `/time-entries/${pathSegment(entryId)}/clock-out`, {}, undefined, idempotencyKey);
|
|
1399
1514
|
}
|
|
1400
1515
|
async getOvertimeReport(params) {
|
|
1401
1516
|
return this.requestUnwrapped("GET", "/time-entries/overtime", undefined, {
|
|
@@ -1416,7 +1531,7 @@ export class FrihetClient {
|
|
|
1416
1531
|
// ---------------------------------------------------------------- Webhook Trust-Area Extensions
|
|
1417
1532
|
// NOTE: /v1/webhooks/:id/test — D4-A parallel deploy. 404 propagates until backend ships.
|
|
1418
1533
|
async testWebhook(id, data) {
|
|
1419
|
-
return this.requestUnwrapped("POST", `/webhooks/${
|
|
1534
|
+
return this.requestUnwrapped("POST", `/webhooks/${pathSegment(id)}/test`, data ?? {});
|
|
1420
1535
|
}
|
|
1421
1536
|
// ---------------------------------------------------------------- Payroll
|
|
1422
1537
|
// These live read routes return normalized payroll-preparation records in
|
|
@@ -1432,17 +1547,13 @@ export class FrihetClient {
|
|
|
1432
1547
|
month: params.month,
|
|
1433
1548
|
});
|
|
1434
1549
|
}
|
|
1435
|
-
// ---------------------------------------------------------------- Onboarding
|
|
1436
|
-
// NOTE: /v1/onboarding/* — D4-A parallel deploy. 404 propagates until backend ships.
|
|
1437
|
-
async getOnboardingStatus() {
|
|
1438
|
-
return this.request("GET", "/onboarding/status");
|
|
1439
|
-
}
|
|
1440
|
-
async setOnboardingPersona(data) {
|
|
1441
|
-
return this.requestUnwrapped("PATCH", "/onboarding/persona", data);
|
|
1442
|
-
}
|
|
1443
1550
|
// ---------------------------------------------------------------- Permissions
|
|
1444
1551
|
// These are standard ERP family responses: unwrap their single-object
|
|
1445
1552
|
// `{ data, meta }` envelope before handing the payload to MCP output schemas.
|
|
1553
|
+
//
|
|
1554
|
+
// Note: /v1/onboarding/* methods (getOnboardingStatus, setOnboardingPersona)
|
|
1555
|
+
// were removed when the onboarding family was withdrawn via issue #124.
|
|
1556
|
+
// See src/tools/igic.ts for the rationale and the inline-stub precedent.
|
|
1446
1557
|
async getPermissionsMatrix() {
|
|
1447
1558
|
return this.requestUnwrapped("GET", "/permissions/matrix");
|
|
1448
1559
|
}
|
|
@@ -1454,7 +1565,7 @@ export class FrihetClient {
|
|
|
1454
1565
|
// honest ERP 501 responses and are intentionally unchanged here.
|
|
1455
1566
|
async getCurrentPeriod(params) {
|
|
1456
1567
|
if (params?.fiscalYear) {
|
|
1457
|
-
return this.requestUnwrapped("GET", `/periods/${
|
|
1568
|
+
return this.requestUnwrapped("GET", `/periods/${pathSegment(params.fiscalYear)}`);
|
|
1458
1569
|
}
|
|
1459
1570
|
return this.requestUnwrapped("GET", "/periods/current");
|
|
1460
1571
|
}
|
|
@@ -1462,7 +1573,7 @@ export class FrihetClient {
|
|
|
1462
1573
|
return this.requestUnwrapped("POST", "/periods/close", data);
|
|
1463
1574
|
}
|
|
1464
1575
|
async reopenPeriod(data) {
|
|
1465
|
-
return this.requestUnwrapped("POST", `/periods/${
|
|
1576
|
+
return this.requestUnwrapped("POST", `/periods/${pathSegment(data.periodId)}/reopen`, { reason: data.reason });
|
|
1466
1577
|
}
|
|
1467
1578
|
}
|
|
1468
1579
|
//# sourceMappingURL=client.js.map
|