@frihet/mcp-server 1.16.6 → 2.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/README.md +168 -82
- package/dist/agent-onboarding.d.ts +98 -0
- package/dist/agent-onboarding.js +275 -0
- package/dist/api-origin.d.ts +9 -0
- package/dist/api-origin.js +41 -0
- package/dist/canary/mcp-harness.d.ts +77 -0
- package/dist/canary/mcp-harness.js +552 -0
- package/dist/capability-truth.d.ts +29 -0
- package/dist/capability-truth.js +262 -0
- 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 +240 -0
- package/dist/claude-profile.d.ts +18 -0
- package/dist/claude-profile.js +260 -0
- package/dist/client-interface.d.ts +47 -33
- package/dist/client.d.ts +172 -40
- package/dist/client.js +708 -165
- package/dist/demo-client.d.ts +41 -32
- package/dist/demo-client.js +202 -31
- package/dist/demo-fixtures.d.ts +3 -3
- package/dist/demo-fixtures.js +7 -7
- 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 +44 -0
- package/dist/fiscal-aliases.js +62 -0
- 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 +68 -59
- package/dist/logger.d.ts +2 -2
- package/dist/logger.js +140 -8
- package/dist/muse-profile.d.ts +19 -0
- package/dist/muse-profile.js +122 -0
- package/dist/observability.d.ts +18 -29
- package/dist/observability.js +194 -106
- package/dist/openai-profile.d.ts +26 -22
- package/dist/openai-profile.js +1193 -157
- package/dist/openai-review-contract.d.ts +15 -6
- package/dist/openai-review-contract.js +714 -41
- package/dist/openai-review-oauth.d.ts +149 -2
- package/dist/openai-review-oauth.js +220 -6
- package/dist/prompts/register-all.d.ts +2 -0
- package/dist/prompts/register-all.js +34 -78
- package/dist/public-capability-contract.d.ts +39 -0
- package/dist/public-capability-contract.js +186 -0
- package/dist/redaction.d.ts +15 -0
- package/dist/redaction.js +83 -0
- package/dist/resources/register-all.js +154 -64
- package/dist/retry-after.d.ts +19 -0
- package/dist/retry-after.js +84 -0
- package/dist/server-composition.d.ts +19 -0
- package/dist/server-composition.js +74 -0
- package/dist/tool-exposure.d.ts +31 -27
- package/dist/tool-exposure.js +211 -87
- package/dist/tools/accountingClose.d.ts +14 -5
- package/dist/tools/accountingClose.js +41 -23
- 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 +261 -15
- package/dist/tools/crm.js +22 -21
- package/dist/tools/deposits.js +114 -85
- package/dist/tools/einvoice.d.ts +16 -2
- package/dist/tools/einvoice.js +59 -12
- package/dist/tools/expenses.d.ts +16 -0
- package/dist/tools/expenses.js +47 -27
- 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.d.ts +2 -3
- package/dist/tools/hr.js +49 -20
- 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 +166 -12
- package/dist/tools/invoices.js +224 -54
- package/dist/tools/kitchen.js +19 -9
- package/dist/tools/payroll.d.ts +5 -8
- package/dist/tools/payroll.js +19 -32
- package/dist/tools/permissions.d.ts +4 -7
- package/dist/tools/permissions.js +11 -14
- package/dist/tools/products.js +6 -6
- package/dist/tools/quotes.js +172 -26
- 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 +1445 -109
- package/dist/tools/shared.js +478 -102
- package/dist/tools/team.js +24 -28
- package/dist/tools/time.js +4 -12
- package/dist/tools/vendors.js +249 -15
- package/dist/tools/webhooks.js +35 -30
- package/dist/types.d.ts +31 -5
- package/dist/workflows/daily-operational-brief.d.ts +163 -0
- package/dist/workflows/daily-operational-brief.js +881 -0
- package/docs/agent-onboarding.json +391 -0
- package/package.json +49 -12
- package/dist/tools/onboarding.d.ts +0 -19
- package/dist/tools/onboarding.js +0 -64
|
@@ -5,63 +5,42 @@
|
|
|
5
5
|
* 1. frihet_modelo_200_summary — Modelo 200 IS anual (annual corporate tax return)
|
|
6
6
|
* 2. frihet_modelo_202_summary — Modelo 202 IS pagos fraccionados (3 installment payments)
|
|
7
7
|
*
|
|
8
|
-
* REST surface: /v1/is/*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* REST surface: Frihet-ERP functions/src/publicApi.ts has NO /v1/is/* route,
|
|
9
|
+
* so both tools return NOT_DEPLOYED without calling (capability truth:
|
|
10
|
+
* `unavailable`, src/capability-truth.ts).
|
|
11
11
|
*
|
|
12
12
|
* Scope: Spanish SLs (Sociedad Limitada) and SAs filing corporate tax with AEAT.
|
|
13
|
-
* Canary Islands variant: same models apply; ATC collects IS with territorial adjustments.
|
|
14
13
|
*/
|
|
15
14
|
import { z } from "zod/v4";
|
|
16
|
-
import { withToolLogging,
|
|
17
|
-
import {
|
|
18
|
-
export function registerImpuestoSociedadesTools(server,
|
|
15
|
+
import { withToolLogging, openObjectOutput, READ_ONLY_ANNOTATIONS, } from "./shared.js";
|
|
16
|
+
import { notDeployedError } from "./backend-availability.js";
|
|
17
|
+
export function registerImpuestoSociedadesTools(server, _client) {
|
|
19
18
|
// -- frihet_modelo_200_summary -------------------------------------------
|
|
20
19
|
server.registerTool("frihet_modelo_200_summary", {
|
|
21
20
|
title: "Get Modelo 200 Summary (Corporate Tax — Annual IS)",
|
|
22
|
-
description: "
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"Example: year='2025'. " +
|
|
27
|
-
"/ Resumen Modelo 200 — declaracion anual del Impuesto sobre Sociedades para SL/SA. " +
|
|
28
|
-
"Devuelve base imponible, deducciones, tipo, retenciones, pagos fraccionados y cuota.",
|
|
21
|
+
description: "NOT DEPLOYED: Modelo 200 (annual corporate income tax return, Impuesto sobre Sociedades) has no Frihet backend yet; " +
|
|
22
|
+
"calling this tool returns a NOT_DEPLOYED error and never data. " +
|
|
23
|
+
"/ NO DESPLEGADO: el Modelo 200 (declaracion anual del Impuesto sobre Sociedades) aun no tiene backend en Frihet; " +
|
|
24
|
+
"devuelve un error NOT_DEPLOYED y nunca datos.",
|
|
29
25
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
30
26
|
inputSchema: {
|
|
31
27
|
year: z.string().optional().describe("Fiscal year (e.g. '2025', defaults to last closed year) / Ejercicio fiscal (ej. '2025', por defecto ultimo ejercicio cerrado)"),
|
|
32
28
|
},
|
|
33
29
|
outputSchema: openObjectOutput("Modelo 200 annual corporate tax summary: taxable base, deductions, rate, net payable / Resumen anual IS: base, deducciones, tipo, cuota"),
|
|
34
|
-
}, async (
|
|
35
|
-
const result = await client.getISSummary("200", { year });
|
|
36
|
-
return {
|
|
37
|
-
content: [getContent(formatRecord("Modelo 200 Summary (IS Anual)", result))],
|
|
38
|
-
structuredContent: result,
|
|
39
|
-
};
|
|
40
|
-
})));
|
|
30
|
+
}, async () => withToolLogging("frihet_modelo_200_summary", async () => notDeployedError("frihet_modelo_200_summary", "/v1/is/modelo/200")));
|
|
41
31
|
// -- frihet_modelo_202_summary -------------------------------------------
|
|
42
32
|
server.registerTool("frihet_modelo_202_summary", {
|
|
43
33
|
title: "Get Modelo 202 Summary (Corporate Tax — Installment Payments)",
|
|
44
|
-
description: "
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"/ Resumen Modelo 202 — pagos fraccionados del Impuesto sobre Sociedades. " +
|
|
49
|
-
"Tres plazos: abril (1P), octubre (2P), diciembre (3P). Devuelve importes, plazos y estado de pago.",
|
|
34
|
+
description: "NOT DEPLOYED: Modelo 202 (Impuesto sobre Sociedades installment payments, 1P/2P/3P) has no Frihet backend yet; " +
|
|
35
|
+
"calling this tool returns a NOT_DEPLOYED error and never data. " +
|
|
36
|
+
"/ NO DESPLEGADO: el Modelo 202 (pagos fraccionados del Impuesto sobre Sociedades) aun no tiene backend en Frihet; " +
|
|
37
|
+
"devuelve un error NOT_DEPLOYED y nunca datos.",
|
|
50
38
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
51
39
|
inputSchema: {
|
|
52
40
|
year: z.string().optional().describe("Fiscal year of the installments (e.g. '2026') / Ejercicio de los pagos fraccionados (ej. '2026')"),
|
|
53
41
|
installment: z.enum(["1P", "2P", "3P"]).optional().describe("Specific installment (1P=April, 2P=October, 3P=December) or omit for all three / Plazo especifico (1P=abril, 2P=octubre, 3P=diciembre) u omitir para los tres"),
|
|
54
42
|
},
|
|
55
43
|
outputSchema: openObjectOutput("Modelo 202 installment payments: amounts, due dates, payment status / Pagos fraccionados Modelo 202: importes, plazos, estado"),
|
|
56
|
-
}, async (
|
|
57
|
-
const result = await client.getISSummary("202", { year, installment });
|
|
58
|
-
const label = installment
|
|
59
|
-
? `Modelo 202 Summary (${installment} ${year ?? ""})`
|
|
60
|
-
: `Modelo 202 Summary (all installments ${year ?? ""})`;
|
|
61
|
-
return {
|
|
62
|
-
content: [getContent(formatRecord(label.trim(), result))],
|
|
63
|
-
structuredContent: result,
|
|
64
|
-
};
|
|
65
|
-
})));
|
|
44
|
+
}, async () => withToolLogging("frihet_modelo_202_summary", async () => notDeployedError("frihet_modelo_202_summary", "/v1/is/modelo/202")));
|
|
66
45
|
}
|
|
67
46
|
//# sourceMappingURL=impuesto_sociedades.js.map
|
|
@@ -66,7 +66,7 @@ const businessContextOutput = z.object({
|
|
|
66
66
|
invoiceCount: z.number(),
|
|
67
67
|
expenseCount: z.number(),
|
|
68
68
|
}).passthrough(),
|
|
69
|
-
}).passthrough().describe("Business context snapshot: workspace profile,
|
|
69
|
+
}).passthrough().describe("Business context snapshot: workspace profile, defaults, plan usage, recent activity and current-month performance");
|
|
70
70
|
const monthlySummaryOutput = z.object({
|
|
71
71
|
period: z.string(),
|
|
72
72
|
revenue: z.object({
|
|
@@ -101,7 +101,7 @@ const monthlySummaryOutput = z.object({
|
|
|
101
101
|
irpfRetained: z.number(),
|
|
102
102
|
estimatedModel303: z.number(),
|
|
103
103
|
}).passthrough(),
|
|
104
|
-
}).passthrough().describe("Monthly
|
|
104
|
+
}).passthrough().describe("Monthly business summary with revenue, expenses, profit, invoice status, top clients and expense categories");
|
|
105
105
|
export function registerIntelligenceTools(server, client) {
|
|
106
106
|
// -- get_business_context --
|
|
107
107
|
server.registerTool("get_business_context", {
|
|
@@ -194,14 +194,40 @@ export function registerIntelligenceTools(server, client) {
|
|
|
194
194
|
},
|
|
195
195
|
outputSchema: invoiceItemOutput.describe("The newly created draft invoice, cloned from the original / La nueva factura borrador, clonada de la original"),
|
|
196
196
|
}, async ({ id, newIssueDate, newDueDate }) => withToolLogging("duplicate_invoice", async () => {
|
|
197
|
-
// 1.
|
|
198
|
-
|
|
197
|
+
// 1. Validate caller-supplied newIssueDate before touching the API.
|
|
198
|
+
if (newIssueDate !== undefined) {
|
|
199
|
+
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(newIssueDate);
|
|
200
|
+
const validCalendar = (() => {
|
|
201
|
+
if (!match)
|
|
202
|
+
return false;
|
|
203
|
+
const y = Number(match[1]);
|
|
204
|
+
const m = Number(match[2]);
|
|
205
|
+
const d = Number(match[3]);
|
|
206
|
+
if (m < 1 || m > 12 || d < 1 || d > 31)
|
|
207
|
+
return false;
|
|
208
|
+
const dt = new Date(Date.UTC(y, m - 1, d));
|
|
209
|
+
return dt.getUTCFullYear() === y && dt.getUTCMonth() === m - 1 && dt.getUTCDate() === d;
|
|
210
|
+
})();
|
|
211
|
+
if (!validCalendar) {
|
|
212
|
+
return {
|
|
213
|
+
content: [
|
|
214
|
+
{
|
|
215
|
+
type: "text",
|
|
216
|
+
text: `Error: invalid newIssueDate "${newIssueDate}". Use a real calendar date in YYYY-MM-DD format.`,
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
isError: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
// 2. Fetch the original invoice (returns the FULL raw stored document —
|
|
224
|
+
// payments, verifactu, eInvoice, createdBy, etc.)
|
|
199
225
|
const original = await client.getInvoice(id);
|
|
200
|
-
//
|
|
226
|
+
// 3. Allowlist-PICK only the fields the create endpoint actually accepts.
|
|
201
227
|
// The create POST validates against a Zod `.strict()` schema that
|
|
202
228
|
// REJECTS unknown keys (HTTP 400). Spreading the raw GET doc and only
|
|
203
229
|
// blacklisting a handful of fields left stored-only fields (payments,
|
|
204
|
-
// amountPaid, verifactu, eInvoice,
|
|
230
|
+
// amountPaid, verifactu, eInvoice, createdBy,
|
|
205
231
|
// sentTo/sentAt, cancelled*, attachments, …) in the body, so any paid /
|
|
206
232
|
// sent / cancelled / e-invoiced invoice failed to duplicate. Picking
|
|
207
233
|
// the writable subset mirrors the create schema and never 400s.
|
|
@@ -220,23 +246,151 @@ export function registerIntelligenceTools(server, client) {
|
|
|
220
246
|
"irpfRate",
|
|
221
247
|
"equivalenceSurchargeRate",
|
|
222
248
|
"clientLocation",
|
|
249
|
+
"operationType",
|
|
250
|
+
"fiscalTreatment",
|
|
223
251
|
"prepayment",
|
|
224
252
|
"seriesId",
|
|
225
253
|
];
|
|
226
|
-
|
|
254
|
+
const ALLOWED_ADDRESS_KEYS = [
|
|
255
|
+
"street",
|
|
256
|
+
"city",
|
|
257
|
+
"zip",
|
|
258
|
+
"province",
|
|
259
|
+
"country",
|
|
260
|
+
"countryCode",
|
|
261
|
+
"state",
|
|
262
|
+
"postalCode",
|
|
263
|
+
];
|
|
264
|
+
// 4. Build the duplicate from the writable subset + new values.
|
|
265
|
+
// Note: Firestore documents frequently store `null` for unset optional
|
|
266
|
+
// fields (e.g. `notes: null`, `seriesId: null`, `clientTaxId: null`), and
|
|
267
|
+
// stored `items` often carry computed/UI keys (`total`, `taxRate`,
|
|
268
|
+
// `discount`, `productId`) that `invoiceItemSchema.strict()` rejects.
|
|
269
|
+
// Economic integrity invariant: NEVER fabricate `0` when `quantity` or
|
|
270
|
+
// `unitPrice` is missing, null, non-finite, or invalid. Preserve `0` only
|
|
271
|
+
// when it is an explicit finite number (or explicit numeric string).
|
|
272
|
+
const parseExplicitFiniteNumber = (raw) => {
|
|
273
|
+
if (typeof raw === "number") {
|
|
274
|
+
return Number.isFinite(raw) ? raw : undefined;
|
|
275
|
+
}
|
|
276
|
+
if (typeof raw === "string") {
|
|
277
|
+
const trimmed = raw.trim();
|
|
278
|
+
if (trimmed === "")
|
|
279
|
+
return undefined;
|
|
280
|
+
const parsed = Number(trimmed);
|
|
281
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
282
|
+
}
|
|
283
|
+
return undefined;
|
|
284
|
+
};
|
|
285
|
+
if (typeof original.clientName !== "string" || !original.clientName.trim()) {
|
|
286
|
+
return {
|
|
287
|
+
content: [{
|
|
288
|
+
type: "text",
|
|
289
|
+
text: "Error: cannot duplicate invoice because it is missing a valid clientName. / Error: no se puede duplicar la factura porque falta el nombre del cliente.",
|
|
290
|
+
}],
|
|
291
|
+
isError: true,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
if (!Array.isArray(original.items) || original.items.length === 0) {
|
|
295
|
+
return {
|
|
296
|
+
content: [{
|
|
297
|
+
type: "text",
|
|
298
|
+
text: "Error: cannot duplicate invoice because it has no valid line items. / Error: no se puede duplicar la factura porque no tiene lineas validas.",
|
|
299
|
+
}],
|
|
300
|
+
isError: true,
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
const sanitizedItems = [];
|
|
304
|
+
for (let i = 0; i < original.items.length; i++) {
|
|
305
|
+
const rawItem = original.items[i];
|
|
306
|
+
if (!rawItem || typeof rawItem !== "object" || Array.isArray(rawItem)) {
|
|
307
|
+
return {
|
|
308
|
+
content: [{
|
|
309
|
+
type: "text",
|
|
310
|
+
text: `Error: cannot duplicate invoice: line item ${i + 1} is not a valid object. / Error: no se puede duplicar la factura: la linea ${i + 1} es invalida.`,
|
|
311
|
+
}],
|
|
312
|
+
isError: true,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const itemObj = rawItem;
|
|
316
|
+
const description = typeof itemObj.description === "string" ? itemObj.description.trim() : "";
|
|
317
|
+
if (!description) {
|
|
318
|
+
return {
|
|
319
|
+
content: [{
|
|
320
|
+
type: "text",
|
|
321
|
+
text: `Error: cannot duplicate invoice: line item ${i + 1} is missing a description. / Error: no se puede duplicar la factura: falta la descripcion en la linea ${i + 1}.`,
|
|
322
|
+
}],
|
|
323
|
+
isError: true,
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
const quantity = parseExplicitFiniteNumber(itemObj.quantity);
|
|
327
|
+
if (quantity === undefined) {
|
|
328
|
+
return {
|
|
329
|
+
content: [{
|
|
330
|
+
type: "text",
|
|
331
|
+
text: `Error: cannot duplicate invoice: line item ${i + 1} has a missing or invalid quantity. Refusing to fabricate economic values. / Error: no se puede duplicar la factura: la linea ${i + 1} carece de cantidad valida.`,
|
|
332
|
+
}],
|
|
333
|
+
isError: true,
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const unitPrice = parseExplicitFiniteNumber(itemObj.unitPrice);
|
|
337
|
+
if (unitPrice === undefined) {
|
|
338
|
+
return {
|
|
339
|
+
content: [{
|
|
340
|
+
type: "text",
|
|
341
|
+
text: `Error: cannot duplicate invoice: line item ${i + 1} has a missing or invalid unitPrice. Refusing to fabricate economic values. / Error: no se puede duplicar la factura: la linea ${i + 1} carece de precio unitario valido.`,
|
|
342
|
+
}],
|
|
343
|
+
isError: true,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
sanitizedItems.push({
|
|
347
|
+
...(typeof itemObj.id === "string" && itemObj.id.trim() ? { id: itemObj.id.trim() } : {}),
|
|
348
|
+
description: itemObj.description,
|
|
349
|
+
quantity,
|
|
350
|
+
unitPrice,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
227
353
|
const today = new Date().toISOString().split("T")[0];
|
|
228
|
-
const invoiceData = {
|
|
354
|
+
const invoiceData = {
|
|
355
|
+
clientName: original.clientName,
|
|
356
|
+
items: sanitizedItems,
|
|
357
|
+
};
|
|
229
358
|
for (const field of COPYABLE_INVOICE_FIELDS) {
|
|
230
|
-
if (
|
|
231
|
-
|
|
359
|
+
if (field === "items")
|
|
360
|
+
continue;
|
|
361
|
+
const val = original[field];
|
|
362
|
+
if (val === undefined || val === null || val === "")
|
|
363
|
+
continue;
|
|
364
|
+
if (field === "clientAddress" && typeof val === "object" && !Array.isArray(val)) {
|
|
365
|
+
const addrObj = val;
|
|
366
|
+
const cleanAddr = {};
|
|
367
|
+
for (const k of ALLOWED_ADDRESS_KEYS) {
|
|
368
|
+
if (typeof addrObj[k] === "string" && addrObj[k].trim()) {
|
|
369
|
+
cleanAddr[k] = addrObj[k].trim();
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (Object.keys(cleanAddr).length > 0) {
|
|
373
|
+
invoiceData.clientAddress = cleanAddr;
|
|
374
|
+
}
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
invoiceData[field] = val;
|
|
232
378
|
}
|
|
233
379
|
invoiceData.status = "draft";
|
|
234
380
|
invoiceData.issueDate = newIssueDate || today;
|
|
235
381
|
if (newDueDate) {
|
|
236
382
|
invoiceData.dueDate = newDueDate;
|
|
237
383
|
}
|
|
238
|
-
//
|
|
239
|
-
const
|
|
384
|
+
// 5. Create the duplicate (always as draft, never issued or sent)
|
|
385
|
+
const rawResult = await client.createInvoice(invoiceData);
|
|
386
|
+
const result = {
|
|
387
|
+
...(rawResult && typeof rawResult === "object" ? rawResult : {}),
|
|
388
|
+
};
|
|
389
|
+
for (const nullableField of ["clientId", "issueDate", "dueDate", "status", "notes", "createdAt", "updatedAt"]) {
|
|
390
|
+
if (result[nullableField] === null) {
|
|
391
|
+
delete result[nullableField];
|
|
392
|
+
}
|
|
393
|
+
}
|
|
240
394
|
return {
|
|
241
395
|
content: [
|
|
242
396
|
mutateContent(formatRecord("Invoice duplicated", result) +
|