@casys/mcp-einvoice 0.1.1 → 0.1.3
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 +129 -170
- package/mcp-einvoice.mjs +3291 -668
- package/package.json +1 -1
- package/ui-dist/action-result/index.html +128 -0
- package/ui-dist/directory-card/index.html +28 -28
- package/ui-dist/directory-list/index.html +128 -0
- package/ui-dist/doclist-viewer/index.html +48 -48
- package/ui-dist/invoice-viewer/index.html +44 -44
- package/ui-dist/status-timeline/index.html +27 -27
package/mcp-einvoice.mjs
CHANGED
|
@@ -8205,6 +8205,7 @@ var require_stringify = __commonJS({
|
|
|
8205
8205
|
nullStr: "null",
|
|
8206
8206
|
simpleKeys: false,
|
|
8207
8207
|
singleQuote: null,
|
|
8208
|
+
trailingComma: false,
|
|
8208
8209
|
trueStr: "true",
|
|
8209
8210
|
verifyAliasOrder: true
|
|
8210
8211
|
}, doc.schema.toStringOptions, options);
|
|
@@ -8722,12 +8723,19 @@ ${indent}${line}` : "\n";
|
|
|
8722
8723
|
if (comment)
|
|
8723
8724
|
reqNewline = true;
|
|
8724
8725
|
let str = stringify.stringify(item, itemCtx, () => comment = null);
|
|
8725
|
-
|
|
8726
|
+
reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
|
|
8727
|
+
if (i < items.length - 1) {
|
|
8726
8728
|
str += ",";
|
|
8729
|
+
} else if (ctx.options.trailingComma) {
|
|
8730
|
+
if (ctx.options.lineWidth > 0) {
|
|
8731
|
+
reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
|
|
8732
|
+
}
|
|
8733
|
+
if (reqNewline) {
|
|
8734
|
+
str += ",";
|
|
8735
|
+
}
|
|
8736
|
+
}
|
|
8727
8737
|
if (comment)
|
|
8728
8738
|
str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
|
|
8729
|
-
if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
|
|
8730
|
-
reqNewline = true;
|
|
8731
8739
|
lines.push(str);
|
|
8732
8740
|
linesAtValue = lines.length;
|
|
8733
8741
|
}
|
|
@@ -11741,17 +11749,22 @@ var require_compose_node = __commonJS({
|
|
|
11741
11749
|
case "block-map":
|
|
11742
11750
|
case "block-seq":
|
|
11743
11751
|
case "flow-collection":
|
|
11744
|
-
|
|
11745
|
-
|
|
11746
|
-
|
|
11752
|
+
try {
|
|
11753
|
+
node = composeCollection.composeCollection(CN, ctx, token, props, onError);
|
|
11754
|
+
if (anchor)
|
|
11755
|
+
node.anchor = anchor.source.substring(1);
|
|
11756
|
+
} catch (error2) {
|
|
11757
|
+
const message2 = error2 instanceof Error ? error2.message : String(error2);
|
|
11758
|
+
onError(token, "RESOURCE_EXHAUSTION", message2);
|
|
11759
|
+
}
|
|
11747
11760
|
break;
|
|
11748
11761
|
default: {
|
|
11749
11762
|
const message2 = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
|
|
11750
11763
|
onError(token, "UNEXPECTED_TOKEN", message2);
|
|
11751
|
-
node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
|
|
11752
11764
|
isSrcToken = false;
|
|
11753
11765
|
}
|
|
11754
11766
|
}
|
|
11767
|
+
node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
|
|
11755
11768
|
if (anchor && node.anchor === "")
|
|
11756
11769
|
onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
|
|
11757
11770
|
if (atKey && ctx.options.stringKeys && (!identity.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
|
|
@@ -30898,7 +30911,7 @@ var HonoRequest = class {
|
|
|
30898
30911
|
return headerData;
|
|
30899
30912
|
}
|
|
30900
30913
|
async parseBody(options) {
|
|
30901
|
-
return
|
|
30914
|
+
return parseBody(this, options);
|
|
30902
30915
|
}
|
|
30903
30916
|
#cachedBody = (key) => {
|
|
30904
30917
|
const { bodyCache, raw: raw2 } = this;
|
|
@@ -32562,6 +32575,9 @@ var cors = (options) => {
|
|
|
32562
32575
|
const findAllowOrigin = ((optsOrigin) => {
|
|
32563
32576
|
if (typeof optsOrigin === "string") {
|
|
32564
32577
|
if (optsOrigin === "*") {
|
|
32578
|
+
if (opts.credentials) {
|
|
32579
|
+
return (origin) => origin || null;
|
|
32580
|
+
}
|
|
32565
32581
|
return () => optsOrigin;
|
|
32566
32582
|
} else {
|
|
32567
32583
|
return (origin) => optsOrigin === origin ? origin : null;
|
|
@@ -32596,7 +32612,7 @@ var cors = (options) => {
|
|
|
32596
32612
|
set2("Access-Control-Expose-Headers", opts.exposeHeaders.join(","));
|
|
32597
32613
|
}
|
|
32598
32614
|
if (c.req.method === "OPTIONS") {
|
|
32599
|
-
if (opts.origin !== "*") {
|
|
32615
|
+
if (opts.origin !== "*" || opts.credentials) {
|
|
32600
32616
|
set2("Vary", "Origin");
|
|
32601
32617
|
}
|
|
32602
32618
|
if (opts.maxAge != null) {
|
|
@@ -32626,7 +32642,7 @@ var cors = (options) => {
|
|
|
32626
32642
|
});
|
|
32627
32643
|
}
|
|
32628
32644
|
await next();
|
|
32629
|
-
if (opts.origin !== "*") {
|
|
32645
|
+
if (opts.origin !== "*" || opts.credentials) {
|
|
32630
32646
|
c.header("Vary", "Origin", { append: true });
|
|
32631
32647
|
}
|
|
32632
32648
|
};
|
|
@@ -35496,7 +35512,7 @@ function isCloudflareWorkers() {
|
|
|
35496
35512
|
var USER_AGENT;
|
|
35497
35513
|
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
|
|
35498
35514
|
const NAME = "jose";
|
|
35499
|
-
const VERSION2 = "v6.2.
|
|
35515
|
+
const VERSION2 = "v6.2.2";
|
|
35500
35516
|
USER_AGENT = `${NAME}/${VERSION2}`;
|
|
35501
35517
|
}
|
|
35502
35518
|
var customFetch = Symbol();
|
|
@@ -37701,6 +37717,104 @@ function defaultHumanName(name) {
|
|
|
37701
37717
|
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
37702
37718
|
}
|
|
37703
37719
|
|
|
37720
|
+
// node_modules/@casys/mcp-server/src/inspector/launcher.ts
|
|
37721
|
+
async function launchInspector(serverCommand, serverArgs, options) {
|
|
37722
|
+
const port = options?.port ?? 6274;
|
|
37723
|
+
const shouldOpen = options?.open ?? true;
|
|
37724
|
+
const filteredArgs = serverArgs.filter((a) => a !== "--inspect");
|
|
37725
|
+
const inspectorEnv = {
|
|
37726
|
+
...options?.env,
|
|
37727
|
+
CLIENT_PORT: String(port)
|
|
37728
|
+
};
|
|
37729
|
+
console.error(`[mcp-inspector] Starting inspector on http://localhost:${port}`);
|
|
37730
|
+
console.error(`[mcp-inspector] Server: ${serverCommand} ${filteredArgs.join(" ")}`);
|
|
37731
|
+
const command = new Deno.Command("npx", {
|
|
37732
|
+
args: [
|
|
37733
|
+
"-y",
|
|
37734
|
+
"@modelcontextprotocol/inspector",
|
|
37735
|
+
serverCommand,
|
|
37736
|
+
...filteredArgs
|
|
37737
|
+
],
|
|
37738
|
+
env: {
|
|
37739
|
+
...Deno.env.toObject(),
|
|
37740
|
+
...inspectorEnv
|
|
37741
|
+
},
|
|
37742
|
+
stdin: "inherit",
|
|
37743
|
+
stdout: "inherit",
|
|
37744
|
+
stderr: "inherit"
|
|
37745
|
+
});
|
|
37746
|
+
const process4 = command.spawn();
|
|
37747
|
+
if (shouldOpen) {
|
|
37748
|
+
setTimeout(() => {
|
|
37749
|
+
openBrowser(`http://localhost:${port}`).catch(() => {
|
|
37750
|
+
});
|
|
37751
|
+
}, 2e3);
|
|
37752
|
+
}
|
|
37753
|
+
const status = await process4.status;
|
|
37754
|
+
if (!status.success) {
|
|
37755
|
+
console.error(`[mcp-inspector] Inspector exited with code ${status.code}`);
|
|
37756
|
+
Deno.exit(status.code);
|
|
37757
|
+
}
|
|
37758
|
+
}
|
|
37759
|
+
async function openBrowser(url2) {
|
|
37760
|
+
const os = Deno.build.os;
|
|
37761
|
+
let cmd;
|
|
37762
|
+
if (os === "darwin") {
|
|
37763
|
+
cmd = ["open", url2];
|
|
37764
|
+
} else if (os === "windows") {
|
|
37765
|
+
cmd = ["cmd", "/c", "start", url2];
|
|
37766
|
+
} else {
|
|
37767
|
+
cmd = ["xdg-open", url2];
|
|
37768
|
+
}
|
|
37769
|
+
const process4 = new Deno.Command(cmd[0], {
|
|
37770
|
+
args: cmd.slice(1),
|
|
37771
|
+
stdin: "null",
|
|
37772
|
+
stdout: "null",
|
|
37773
|
+
stderr: "null"
|
|
37774
|
+
});
|
|
37775
|
+
await process4.spawn().status;
|
|
37776
|
+
}
|
|
37777
|
+
|
|
37778
|
+
// src/adapters/shared/errors.ts
|
|
37779
|
+
var NotSupportedError = class extends Error {
|
|
37780
|
+
constructor(adapter, method, alternative) {
|
|
37781
|
+
super(`[${adapter}] ${method} is not supported. ${alternative}`);
|
|
37782
|
+
this.name = "NotSupportedError";
|
|
37783
|
+
}
|
|
37784
|
+
};
|
|
37785
|
+
var AdapterAPIError = class extends Error {
|
|
37786
|
+
constructor(adapter, message2, status, body) {
|
|
37787
|
+
super(message2);
|
|
37788
|
+
this.status = status;
|
|
37789
|
+
this.body = body;
|
|
37790
|
+
this.name = `${adapter}APIError`;
|
|
37791
|
+
}
|
|
37792
|
+
};
|
|
37793
|
+
|
|
37794
|
+
// src/tools/error-mapper.ts
|
|
37795
|
+
function einvoiceErrorMapper(error2, toolName) {
|
|
37796
|
+
if (error2 instanceof NotSupportedError) {
|
|
37797
|
+
return error2.message;
|
|
37798
|
+
}
|
|
37799
|
+
if (error2 instanceof AdapterAPIError) {
|
|
37800
|
+
console.error(
|
|
37801
|
+
`[mcp-einvoice] [${toolName}] API error ${error2.status}: ${error2.message.slice(0, 200)}`
|
|
37802
|
+
);
|
|
37803
|
+
return `[${toolName}] API error ${error2.status}: ${error2.message.slice(0, 300)}`;
|
|
37804
|
+
}
|
|
37805
|
+
if (error2 instanceof Error) {
|
|
37806
|
+
if (error2.message.includes("is required") || error2.message.includes("must ")) {
|
|
37807
|
+
return error2.message;
|
|
37808
|
+
}
|
|
37809
|
+
console.error(
|
|
37810
|
+
`[mcp-einvoice] [${toolName}] error: ${error2.message.slice(0, 200)}`
|
|
37811
|
+
);
|
|
37812
|
+
return `[${toolName}] ${error2.message.slice(0, 300)}`;
|
|
37813
|
+
}
|
|
37814
|
+
console.error(`[mcp-einvoice] [${toolName}] unknown error:`, error2);
|
|
37815
|
+
return null;
|
|
37816
|
+
}
|
|
37817
|
+
|
|
37704
37818
|
// src/generated-store.ts
|
|
37705
37819
|
var EXPIRY_MS = 10 * 60 * 1e3;
|
|
37706
37820
|
var store = /* @__PURE__ */ new Map();
|
|
@@ -37724,7 +37838,7 @@ function getGenerated(id) {
|
|
|
37724
37838
|
return { file: entry.file, filename: entry.filename };
|
|
37725
37839
|
}
|
|
37726
37840
|
|
|
37727
|
-
// src/
|
|
37841
|
+
// src/adapters/shared/encoding.ts
|
|
37728
37842
|
function uint8ToBase64(data) {
|
|
37729
37843
|
let binary = "";
|
|
37730
37844
|
for (let i = 0; i < data.length; i += 8192) {
|
|
@@ -37732,72 +37846,52 @@ function uint8ToBase64(data) {
|
|
|
37732
37846
|
}
|
|
37733
37847
|
return btoa(binary);
|
|
37734
37848
|
}
|
|
37735
|
-
function
|
|
37736
|
-
|
|
37737
|
-
for (const party of ["seller", "buyer"]) {
|
|
37738
|
-
if (normalized[party] && !normalized[party].postalAddress) {
|
|
37739
|
-
normalized[party] = {
|
|
37740
|
-
...normalized[party],
|
|
37741
|
-
postalAddress: { country: normalized[party].country ?? "FR" }
|
|
37742
|
-
};
|
|
37743
|
-
}
|
|
37744
|
-
}
|
|
37745
|
-
for (const party of ["seller", "buyer"]) {
|
|
37746
|
-
const p = normalized[party];
|
|
37747
|
-
if (p && !p.electronicAddress && p.siren && p.siret) {
|
|
37748
|
-
normalized[party] = {
|
|
37749
|
-
...p,
|
|
37750
|
-
electronicAddress: `0225:${p.siren}_${p.siret}`,
|
|
37751
|
-
identifiers: p.identifiers ?? [
|
|
37752
|
-
{ type: "ELECTRONIC_ADDRESS", value: `${p.siren}_${p.siret}`, scheme: "0225" },
|
|
37753
|
-
{ type: "PARTY_LEGAL_IDENTIFIER", value: p.siren, scheme: "0002" }
|
|
37754
|
-
]
|
|
37755
|
-
};
|
|
37756
|
-
}
|
|
37757
|
-
}
|
|
37758
|
-
if (Array.isArray(normalized.paymentTerms)) {
|
|
37759
|
-
normalized.paymentTerms = normalized.paymentTerms.map((t) => t.description ?? t).join("; ");
|
|
37760
|
-
}
|
|
37761
|
-
return normalized;
|
|
37849
|
+
function encodePathSegment(s) {
|
|
37850
|
+
return encodeURIComponent(s);
|
|
37762
37851
|
}
|
|
37852
|
+
|
|
37853
|
+
// src/tools/invoice.ts
|
|
37763
37854
|
function mapToViewerPreview(inv) {
|
|
37764
37855
|
const lines = (inv.lines ?? []).map((l) => {
|
|
37765
37856
|
const line = l;
|
|
37766
37857
|
return {
|
|
37767
|
-
description: line.item?.name ?? line.description,
|
|
37858
|
+
description: line.item?.name ?? line.description ?? line.name,
|
|
37768
37859
|
quantity: line.billedQuantity?.quantity ?? line.quantity,
|
|
37769
|
-
unit_price: line.price?.netAmount?.amount ?? line.unit_price,
|
|
37770
|
-
tax_rate: line.taxDetail?.percent ?? line.tax_rate,
|
|
37860
|
+
unit_price: line.price?.netAmount?.amount ?? line.unitPrice ?? line.unit_price,
|
|
37861
|
+
tax_rate: line.taxDetail?.percent ?? line.taxRate ?? line.tax_rate,
|
|
37771
37862
|
amount: line.totalAmount?.amount ?? line.amount
|
|
37772
37863
|
};
|
|
37773
37864
|
});
|
|
37774
37865
|
return {
|
|
37775
37866
|
id: "(aper\xE7u)",
|
|
37776
|
-
invoice_number: inv.invoiceId,
|
|
37777
|
-
issue_date: inv.invoiceDate,
|
|
37778
|
-
due_date: inv.invoiceDueDate,
|
|
37779
|
-
invoice_type: inv.detailedType?.value ?? inv.type,
|
|
37780
|
-
sender_name: inv.seller?.name,
|
|
37781
|
-
sender_id: inv.seller?.siret ?? inv.seller?.siren,
|
|
37782
|
-
sender_vat: inv.seller?.vatNumber,
|
|
37783
|
-
receiver_name: inv.buyer?.name,
|
|
37784
|
-
receiver_id: inv.buyer?.siret ?? inv.buyer?.siren,
|
|
37785
|
-
receiver_vat: inv.buyer?.vatNumber,
|
|
37786
|
-
currency: inv.monetary?.invoiceCurrency ?? "EUR",
|
|
37787
|
-
total_ht: inv.monetary?.taxBasisTotalAmount?.amount ?? inv.monetary?.lineTotalAmount?.amount,
|
|
37788
|
-
total_tax: inv.monetary?.taxTotalAmount?.amount,
|
|
37789
|
-
total_ttc: inv.monetary?.invoiceAmount?.amount ?? inv.monetary?.payableAmount?.amount,
|
|
37867
|
+
invoice_number: inv.invoiceId ?? inv.invoice_id ?? inv.invoiceNumber,
|
|
37868
|
+
issue_date: inv.invoiceDate ?? inv.issue_date ?? inv.issueDate,
|
|
37869
|
+
due_date: inv.invoiceDueDate ?? inv.due_date ?? inv.dueDate,
|
|
37870
|
+
invoice_type: inv.detailedType?.value ?? inv.type ?? inv.invoiceType,
|
|
37871
|
+
sender_name: inv.seller?.name ?? inv.senderName,
|
|
37872
|
+
sender_id: inv.seller?.siret ?? inv.seller?.siren ?? inv.senderId,
|
|
37873
|
+
sender_vat: inv.seller?.vatNumber ?? inv.senderVat,
|
|
37874
|
+
receiver_name: inv.buyer?.name ?? inv.receiverName,
|
|
37875
|
+
receiver_id: inv.buyer?.siret ?? inv.buyer?.siren ?? inv.receiverId,
|
|
37876
|
+
receiver_vat: inv.buyer?.vatNumber ?? inv.receiverVat,
|
|
37877
|
+
currency: inv.monetary?.invoiceCurrency ?? inv.currency ?? "EUR",
|
|
37878
|
+
total_ht: inv.monetary?.taxBasisTotalAmount?.amount ?? inv.monetary?.lineTotalAmount?.amount ?? inv.totalHt,
|
|
37879
|
+
total_tax: inv.monetary?.taxTotalAmount?.amount ?? inv.totalTax,
|
|
37880
|
+
total_ttc: inv.monetary?.invoiceAmount?.amount ?? inv.monetary?.payableAmount?.amount ?? inv.totalTtc ?? inv.total_amount,
|
|
37790
37881
|
items: lines,
|
|
37791
37882
|
status: "aper\xE7u",
|
|
37792
37883
|
direction: "sent"
|
|
37793
37884
|
};
|
|
37794
37885
|
}
|
|
37795
|
-
var
|
|
37886
|
+
var GENERATE_HINT = "Before generating, call einvoice_config_entities_list to verify the seller is registered. After generating, the user can submit directly via the viewer button. ";
|
|
37887
|
+
var INVOICE_SCHEMA_DESCRIPTION = 'Invoice data (the adapter normalizes format-specific fields internally). Required fields: invoiceId (string): invoice number; invoiceDate (string, YYYY-MM-DD): issue date; type (number): invoice type code (380 = commercial invoice); invoiceDueDate (string, YYYY-MM-DD): due date; seller (object): { name, siren, siret, country, vatNumber }; buyer (object): same structure as seller; monetary (object): { invoiceCurrency, invoiceAmount: { amount }, taxTotalAmount: { amount }, lineTotalAmount: { amount }, taxBasisTotalAmount: { amount } }; taxDetails (array): [{ percent, taxType: "VAT", categoryCode: "S", taxableAmount: { amount }, taxAmount: { amount } }]; lines (array): [{ id, item: { name }, billedQuantity: { quantity, unitCode }, price: { netAmount: { amount } }, totalAmount: { amount }, taxDetail: { percent } }]';
|
|
37796
37888
|
var invoiceTools = [
|
|
37797
37889
|
// ── Emit ────────────────────────────────────────────────
|
|
37798
37890
|
{
|
|
37799
|
-
name: "
|
|
37800
|
-
|
|
37891
|
+
name: "einvoice_invoice_submit",
|
|
37892
|
+
annotations: { destructiveHint: true },
|
|
37893
|
+
requires: ["emitInvoice"],
|
|
37894
|
+
description: "Submit an invoice to the e-invoicing platform. Usually triggered by the viewer's Submit button \u2014 do not call manually after a generate preview. Accepts generated_id OR file_base64 + filename.",
|
|
37801
37895
|
category: "invoice",
|
|
37802
37896
|
inputSchema: {
|
|
37803
37897
|
type: "object",
|
|
@@ -37821,26 +37915,30 @@ var invoiceTools = [
|
|
|
37821
37915
|
const stored = getGenerated(input.generated_id);
|
|
37822
37916
|
if (!stored) {
|
|
37823
37917
|
throw new Error(
|
|
37824
|
-
"[
|
|
37918
|
+
"[einvoice_invoice_submit] Generated file expired or not found. Regenerate the invoice first."
|
|
37825
37919
|
);
|
|
37826
37920
|
}
|
|
37827
37921
|
return await ctx.adapter.emitInvoice(stored);
|
|
37828
37922
|
}
|
|
37829
37923
|
if (!input.file_base64 || !input.filename) {
|
|
37830
37924
|
throw new Error(
|
|
37831
|
-
"[
|
|
37925
|
+
"[einvoice_invoice_submit] Provide either 'generated_id' or both 'file_base64' and 'filename'"
|
|
37832
37926
|
);
|
|
37833
37927
|
}
|
|
37834
37928
|
const filename = input.filename;
|
|
37835
37929
|
const lower = filename.toLowerCase();
|
|
37836
37930
|
if (!lower.endsWith(".pdf") && !lower.endsWith(".xml")) {
|
|
37837
|
-
throw new Error(
|
|
37931
|
+
throw new Error(
|
|
37932
|
+
"[einvoice_invoice_submit] filename must end in .pdf or .xml"
|
|
37933
|
+
);
|
|
37838
37934
|
}
|
|
37839
37935
|
let binaryString;
|
|
37840
37936
|
try {
|
|
37841
37937
|
binaryString = atob(input.file_base64);
|
|
37842
37938
|
} catch {
|
|
37843
|
-
throw new Error(
|
|
37939
|
+
throw new Error(
|
|
37940
|
+
"[einvoice_invoice_submit] 'file_base64' is not valid base64"
|
|
37941
|
+
);
|
|
37844
37942
|
}
|
|
37845
37943
|
const bytes = new Uint8Array(binaryString.length);
|
|
37846
37944
|
for (let i = 0; i < binaryString.length; i++) {
|
|
@@ -37852,69 +37950,113 @@ var invoiceTools = [
|
|
|
37852
37950
|
// ── Search ──────────────────────────────────────────────
|
|
37853
37951
|
{
|
|
37854
37952
|
name: "einvoice_invoice_search",
|
|
37953
|
+
annotations: { readOnlyHint: true },
|
|
37855
37954
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
37856
|
-
|
|
37955
|
+
requires: ["searchInvoices"],
|
|
37956
|
+
description: "Search invoices. Use direction and status to filter. Query searches by sender name, receiver name, or invoice number. Use the direction and status parameters for filtering \u2014 not the query.",
|
|
37857
37957
|
category: "invoice",
|
|
37858
37958
|
inputSchema: {
|
|
37859
37959
|
type: "object",
|
|
37860
37960
|
properties: {
|
|
37861
37961
|
q: {
|
|
37862
37962
|
type: "string",
|
|
37863
|
-
description: "Search query (
|
|
37963
|
+
description: "Search query (e.g. company name, invoice number). Omit to list all."
|
|
37964
|
+
},
|
|
37965
|
+
direction: {
|
|
37966
|
+
type: "string",
|
|
37967
|
+
description: "Filter by direction",
|
|
37968
|
+
enum: ["received", "sent"]
|
|
37864
37969
|
},
|
|
37865
|
-
|
|
37970
|
+
status: {
|
|
37866
37971
|
type: "string",
|
|
37867
|
-
description: "
|
|
37972
|
+
description: "Filter by lifecycle status (after enrichment)",
|
|
37973
|
+
enum: [
|
|
37974
|
+
"SUBMITTED",
|
|
37975
|
+
"ISSUED",
|
|
37976
|
+
"MADE_AVAILABLE",
|
|
37977
|
+
"DELIVERED",
|
|
37978
|
+
"IN_HAND",
|
|
37979
|
+
"APPROVED",
|
|
37980
|
+
"PARTIALLY_APPROVED",
|
|
37981
|
+
"REFUSED",
|
|
37982
|
+
"DISPUTED",
|
|
37983
|
+
"SUSPENDED",
|
|
37984
|
+
"PAYMENT_SENT",
|
|
37985
|
+
"PAYMENT_RECEIVED",
|
|
37986
|
+
"COMPLETED",
|
|
37987
|
+
"WRONG_ROUTING",
|
|
37988
|
+
"INVALID",
|
|
37989
|
+
"DUPLICATED"
|
|
37990
|
+
]
|
|
37868
37991
|
},
|
|
37869
37992
|
offset: { type: "number", description: "Result offset (default 0)" },
|
|
37870
|
-
limit: {
|
|
37993
|
+
limit: {
|
|
37994
|
+
type: "number",
|
|
37995
|
+
description: "Max results (default 50, max 200)"
|
|
37996
|
+
}
|
|
37871
37997
|
}
|
|
37872
37998
|
},
|
|
37873
37999
|
handler: async (input, ctx) => {
|
|
37874
38000
|
let q = input.q;
|
|
37875
38001
|
if (q != null && /^\s*\*?\s*$/.test(q)) q = void 0;
|
|
37876
|
-
const
|
|
38002
|
+
const dirFilter = input.direction;
|
|
38003
|
+
const statusFilter = input.status;
|
|
38004
|
+
const { rows: rawRows, count } = await ctx.adapter.searchInvoices({
|
|
37877
38005
|
q,
|
|
37878
|
-
|
|
38006
|
+
direction: dirFilter,
|
|
38007
|
+
status: statusFilter,
|
|
37879
38008
|
offset: input.offset,
|
|
37880
38009
|
limit: input.limit
|
|
37881
38010
|
});
|
|
37882
|
-
|
|
37883
|
-
if (
|
|
37884
|
-
|
|
37885
|
-
|
|
37886
|
-
|
|
37887
|
-
|
|
37888
|
-
|
|
37889
|
-
|
|
37890
|
-
|
|
37891
|
-
|
|
37892
|
-
|
|
37893
|
-
|
|
37894
|
-
|
|
37895
|
-
|
|
37896
|
-
|
|
37897
|
-
|
|
37898
|
-
|
|
37899
|
-
|
|
37900
|
-
|
|
37901
|
-
|
|
37902
|
-
|
|
37903
|
-
|
|
37904
|
-
|
|
37905
|
-
|
|
38011
|
+
let rows = rawRows;
|
|
38012
|
+
if (dirFilter) rows = rows.filter((r) => r.direction === dirFilter);
|
|
38013
|
+
if (statusFilter) rows = rows.filter((r) => r.status === statusFilter);
|
|
38014
|
+
const data = rows.map((r) => {
|
|
38015
|
+
const shortDate = r.date ? new Date(r.date).toLocaleDateString("fr-FR", {
|
|
38016
|
+
day: "numeric",
|
|
38017
|
+
month: "short"
|
|
38018
|
+
}) : "";
|
|
38019
|
+
const tiers = r.direction === "sent" ? r.receiverName : r.senderName;
|
|
38020
|
+
return {
|
|
38021
|
+
_id: r.id,
|
|
38022
|
+
_direction: r.direction,
|
|
38023
|
+
"Direction": r.direction === "received" ? "Entrante" : r.direction === "sent" ? "Sortante" : "\u2014",
|
|
38024
|
+
"N\xB0": r.invoiceNumber ?? "\u2014",
|
|
38025
|
+
"Statut": r.status ?? "\u2014",
|
|
38026
|
+
"Tiers": tiers ?? "\u2014",
|
|
38027
|
+
"Date": shortDate || "\u2014",
|
|
38028
|
+
"Montant": r.amount != null ? `${Number(r.amount).toLocaleString("fr-FR")} ${r.currency ?? "EUR"}` : "\u2014"
|
|
38029
|
+
};
|
|
38030
|
+
});
|
|
38031
|
+
const dirLabel = dirFilter === "received" ? "re\xE7ues" : dirFilter === "sent" ? "envoy\xE9es" : "";
|
|
38032
|
+
const statusLabel = statusFilter ?? "";
|
|
38033
|
+
const titleParts = [
|
|
38034
|
+
"Factures",
|
|
38035
|
+
dirLabel,
|
|
38036
|
+
statusLabel ? `(${statusLabel})` : ""
|
|
38037
|
+
].filter(Boolean);
|
|
38038
|
+
const viewerData = {
|
|
38039
|
+
data,
|
|
38040
|
+
count: rows.length !== rawRows.length ? rows.length : count ?? rows.length,
|
|
38041
|
+
_title: titleParts.join(" "),
|
|
37906
38042
|
_rowAction: {
|
|
37907
38043
|
toolName: "einvoice_invoice_get",
|
|
37908
38044
|
idField: "_id",
|
|
37909
38045
|
argName: "id"
|
|
37910
38046
|
}
|
|
37911
38047
|
};
|
|
38048
|
+
return {
|
|
38049
|
+
content: `${rows.length} ${titleParts.join(" ")} trouv\xE9es`,
|
|
38050
|
+
structuredContent: viewerData
|
|
38051
|
+
};
|
|
37912
38052
|
}
|
|
37913
38053
|
},
|
|
37914
38054
|
// ── Get by ID ───────────────────────────────────────────
|
|
37915
38055
|
{
|
|
37916
38056
|
name: "einvoice_invoice_get",
|
|
38057
|
+
annotations: { readOnlyHint: true },
|
|
37917
38058
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38059
|
+
requires: ["getInvoice"],
|
|
37918
38060
|
description: "Get a single invoice by its ID. Returns full invoice details including status history, sender/receiver info, and line items.",
|
|
37919
38061
|
category: "invoice",
|
|
37920
38062
|
inputSchema: {
|
|
@@ -37928,73 +38070,59 @@ var invoiceTools = [
|
|
|
37928
38070
|
if (!input.id) {
|
|
37929
38071
|
throw new Error("[einvoice_invoice_get] 'id' is required");
|
|
37930
38072
|
}
|
|
37931
|
-
const
|
|
37932
|
-
const inv =
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
|
|
37937
|
-
|
|
37938
|
-
|
|
37939
|
-
|
|
37940
|
-
|
|
37941
|
-
|
|
37942
|
-
|
|
37943
|
-
|
|
37944
|
-
|
|
37945
|
-
|
|
37946
|
-
|
|
37947
|
-
|
|
37948
|
-
|
|
37949
|
-
|
|
37950
|
-
|
|
37951
|
-
|
|
37952
|
-
|
|
37953
|
-
|
|
37954
|
-
|
|
37955
|
-
|
|
37956
|
-
|
|
37957
|
-
|
|
37958
|
-
|
|
37959
|
-
|
|
37960
|
-
|
|
37961
|
-
|
|
37962
|
-
|
|
37963
|
-
|
|
37964
|
-
|
|
37965
|
-
|
|
37966
|
-
|
|
37967
|
-
|
|
37968
|
-
|
|
37969
|
-
|
|
37970
|
-
|
|
37971
|
-
|
|
37972
|
-
|
|
37973
|
-
|
|
37974
|
-
|
|
37975
|
-
sender_id: bd.seller?.siret ?? bd.seller?.siren,
|
|
37976
|
-
sender_vat: bd.seller?.vatNumber,
|
|
37977
|
-
receiver_name: bd.buyer?.name,
|
|
37978
|
-
receiver_id: bd.buyer?.siret ?? bd.buyer?.siren,
|
|
37979
|
-
receiver_vat: bd.buyer?.vatNumber,
|
|
37980
|
-
issue_date: bd.invoiceDate,
|
|
37981
|
-
due_date: bd.invoiceDueDate,
|
|
37982
|
-
receipt_date: bd.invoiceReceiptDate,
|
|
37983
|
-
currency: bd.monetary?.invoiceCurrency ?? "EUR",
|
|
37984
|
-
total_ht: bd.monetary?.taxBasisTotalAmount?.amount,
|
|
37985
|
-
total_tax: bd.monetary?.taxTotalAmount?.amount,
|
|
37986
|
-
total_ttc: bd.monetary?.invoiceAmount?.amount,
|
|
37987
|
-
items: lines,
|
|
37988
|
-
notes: (bd.notes ?? []).map((n) => {
|
|
37989
|
-
const note = n;
|
|
37990
|
-
return note.content;
|
|
37991
|
-
}).filter(Boolean)
|
|
38073
|
+
const id = input.id;
|
|
38074
|
+
const inv = await ctx.adapter.getInvoice(id);
|
|
38075
|
+
const isTerminal2 = [
|
|
38076
|
+
"REFUSED",
|
|
38077
|
+
"COMPLETED",
|
|
38078
|
+
"CANCELLED",
|
|
38079
|
+
"PAYMENT_RECEIVED",
|
|
38080
|
+
"UNKNOWN"
|
|
38081
|
+
].includes(inv.status ?? "");
|
|
38082
|
+
const viewerData = {
|
|
38083
|
+
id: inv.id,
|
|
38084
|
+
invoice_number: inv.invoiceNumber,
|
|
38085
|
+
status: inv.status,
|
|
38086
|
+
direction: inv.direction,
|
|
38087
|
+
format: inv.format,
|
|
38088
|
+
network: inv.network,
|
|
38089
|
+
invoice_type: inv.invoiceType,
|
|
38090
|
+
sender_name: inv.senderName,
|
|
38091
|
+
sender_id: inv.senderId,
|
|
38092
|
+
sender_vat: inv.senderVat,
|
|
38093
|
+
receiver_name: inv.receiverName,
|
|
38094
|
+
receiver_id: inv.receiverId,
|
|
38095
|
+
receiver_vat: inv.receiverVat,
|
|
38096
|
+
issue_date: inv.issueDate,
|
|
38097
|
+
due_date: inv.dueDate,
|
|
38098
|
+
receipt_date: inv.receiptDate,
|
|
38099
|
+
currency: inv.currency,
|
|
38100
|
+
total_ht: inv.totalHt,
|
|
38101
|
+
total_tax: inv.totalTax,
|
|
38102
|
+
total_ttc: inv.totalTtc,
|
|
38103
|
+
items: inv.lines?.map((l) => ({
|
|
38104
|
+
description: l.description,
|
|
38105
|
+
quantity: l.quantity,
|
|
38106
|
+
unit_price: l.unitPrice,
|
|
38107
|
+
tax_rate: l.taxRate,
|
|
38108
|
+
amount: l.amount
|
|
38109
|
+
})),
|
|
38110
|
+
notes: inv.notes,
|
|
38111
|
+
...!isTerminal2 && inv.direction !== "received" ? {
|
|
38112
|
+
refreshRequest: {
|
|
38113
|
+
toolName: "einvoice_invoice_get",
|
|
38114
|
+
arguments: { id }
|
|
38115
|
+
}
|
|
38116
|
+
} : {}
|
|
37992
38117
|
};
|
|
38118
|
+
const summary = `Invoice ${inv.invoiceNumber ?? inv.id} \u2014 ${inv.status ?? "unknown"}, ${inv.direction ?? ""}, ${inv.totalTtc != null ? inv.totalTtc + " " + (inv.currency ?? "EUR") : "no amount"}`;
|
|
38119
|
+
return { content: summary, structuredContent: viewerData };
|
|
37993
38120
|
}
|
|
37994
38121
|
},
|
|
37995
38122
|
// ── Download ────────────────────────────────────────────
|
|
37996
38123
|
{
|
|
37997
38124
|
name: "einvoice_invoice_download",
|
|
38125
|
+
requires: ["downloadInvoice"],
|
|
37998
38126
|
description: "Download the source file of an invoice (original CII/UBL/Factur-X XML). Returns base64-encoded content.",
|
|
37999
38127
|
category: "invoice",
|
|
38000
38128
|
inputSchema: {
|
|
@@ -38008,13 +38136,23 @@ var invoiceTools = [
|
|
|
38008
38136
|
if (!input.id) {
|
|
38009
38137
|
throw new Error("[einvoice_invoice_download] 'id' is required");
|
|
38010
38138
|
}
|
|
38011
|
-
const { data, contentType } = await ctx.adapter.downloadInvoice(
|
|
38012
|
-
|
|
38139
|
+
const { data, contentType } = await ctx.adapter.downloadInvoice(
|
|
38140
|
+
input.id
|
|
38141
|
+
);
|
|
38142
|
+
return {
|
|
38143
|
+
content: `Downloaded invoice source (${contentType}, ${data.length} bytes)`,
|
|
38144
|
+
structuredContent: {
|
|
38145
|
+
content_type: contentType,
|
|
38146
|
+
data_base64: uint8ToBase64(data),
|
|
38147
|
+
size_bytes: data.length
|
|
38148
|
+
}
|
|
38149
|
+
};
|
|
38013
38150
|
}
|
|
38014
38151
|
},
|
|
38015
38152
|
// ── Download readable ───────────────────────────────────
|
|
38016
38153
|
{
|
|
38017
38154
|
name: "einvoice_invoice_download_readable",
|
|
38155
|
+
requires: ["downloadReadable"],
|
|
38018
38156
|
description: "Download a human-readable PDF version of an invoice. Returns base64-encoded PDF.",
|
|
38019
38157
|
category: "invoice",
|
|
38020
38158
|
inputSchema: {
|
|
@@ -38026,15 +38164,27 @@ var invoiceTools = [
|
|
|
38026
38164
|
},
|
|
38027
38165
|
handler: async (input, ctx) => {
|
|
38028
38166
|
if (!input.id) {
|
|
38029
|
-
throw new Error(
|
|
38167
|
+
throw new Error(
|
|
38168
|
+
"[einvoice_invoice_download_readable] 'id' is required"
|
|
38169
|
+
);
|
|
38030
38170
|
}
|
|
38031
|
-
const { data, contentType } = await ctx.adapter.downloadReadable(
|
|
38032
|
-
|
|
38171
|
+
const { data, contentType } = await ctx.adapter.downloadReadable(
|
|
38172
|
+
input.id
|
|
38173
|
+
);
|
|
38174
|
+
return {
|
|
38175
|
+
content: `Downloaded readable PDF (${data.length} bytes)`,
|
|
38176
|
+
structuredContent: {
|
|
38177
|
+
content_type: contentType,
|
|
38178
|
+
data_base64: uint8ToBase64(data),
|
|
38179
|
+
size_bytes: data.length
|
|
38180
|
+
}
|
|
38181
|
+
};
|
|
38033
38182
|
}
|
|
38034
38183
|
},
|
|
38035
38184
|
// ── Invoice Files ─────────────────────────────────────────
|
|
38036
38185
|
{
|
|
38037
38186
|
name: "einvoice_invoice_files",
|
|
38187
|
+
requires: ["getInvoiceFiles"],
|
|
38038
38188
|
description: "Get metadata of ALL related files for an invoice (source XML, readable PDF, attachments). Use einvoice_invoice_attachments for only business attachments.",
|
|
38039
38189
|
category: "invoice",
|
|
38040
38190
|
inputSchema: {
|
|
@@ -38054,6 +38204,7 @@ var invoiceTools = [
|
|
|
38054
38204
|
// ── Attachments ─────────────────────────────────────────
|
|
38055
38205
|
{
|
|
38056
38206
|
name: "einvoice_invoice_attachments",
|
|
38207
|
+
requires: ["getAttachments"],
|
|
38057
38208
|
description: "Get only business attachments (supporting documents, purchase orders, etc.) for an invoice. Use einvoice_invoice_files for ALL related files including source XML and PDF.",
|
|
38058
38209
|
category: "invoice",
|
|
38059
38210
|
inputSchema: {
|
|
@@ -38073,6 +38224,7 @@ var invoiceTools = [
|
|
|
38073
38224
|
// ── Download File ───────────────────────────────────────
|
|
38074
38225
|
{
|
|
38075
38226
|
name: "einvoice_invoice_download_file",
|
|
38227
|
+
requires: ["downloadFile"],
|
|
38076
38228
|
description: "Download a specific file by its file ID. Returns base64-encoded content.",
|
|
38077
38229
|
category: "invoice",
|
|
38078
38230
|
inputSchema: {
|
|
@@ -38084,77 +38236,33 @@ var invoiceTools = [
|
|
|
38084
38236
|
},
|
|
38085
38237
|
handler: async (input, ctx) => {
|
|
38086
38238
|
if (!input.file_id) {
|
|
38087
|
-
throw new Error(
|
|
38088
|
-
|
|
38089
|
-
|
|
38090
|
-
return { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length };
|
|
38091
|
-
}
|
|
38092
|
-
},
|
|
38093
|
-
// ── Mark as seen ────────────────────────────────────────
|
|
38094
|
-
{
|
|
38095
|
-
name: "einvoice_invoice_mark_seen",
|
|
38096
|
-
description: "Mark an invoice as seen/read. Useful for tracking which invoices have been processed.",
|
|
38097
|
-
category: "invoice",
|
|
38098
|
-
inputSchema: {
|
|
38099
|
-
type: "object",
|
|
38100
|
-
properties: {
|
|
38101
|
-
id: { type: "string", description: "Invoice ID" }
|
|
38102
|
-
},
|
|
38103
|
-
required: ["id"]
|
|
38104
|
-
},
|
|
38105
|
-
handler: async (input, ctx) => {
|
|
38106
|
-
if (!input.id) {
|
|
38107
|
-
throw new Error("[einvoice_invoice_mark_seen] 'id' is required");
|
|
38108
|
-
}
|
|
38109
|
-
return await ctx.adapter.markInvoiceSeen(input.id);
|
|
38110
|
-
}
|
|
38111
|
-
},
|
|
38112
|
-
// ── Not seen ────────────────────────────────────────────
|
|
38113
|
-
{
|
|
38114
|
-
name: "einvoice_invoice_not_seen",
|
|
38115
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38116
|
-
description: "Get invoices that have not been marked as seen (PULL mode). Useful for polling new incoming invoices.",
|
|
38117
|
-
category: "invoice",
|
|
38118
|
-
inputSchema: {
|
|
38119
|
-
type: "object",
|
|
38120
|
-
properties: {
|
|
38121
|
-
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38122
|
-
limit: { type: "number", description: "Max results (default 50)" }
|
|
38123
|
-
}
|
|
38124
|
-
},
|
|
38125
|
-
handler: async (input, ctx) => {
|
|
38126
|
-
const result = await ctx.adapter.getUnseenInvoices({
|
|
38127
|
-
offset: input.offset,
|
|
38128
|
-
limit: input.limit
|
|
38129
|
-
});
|
|
38130
|
-
const data = result.data;
|
|
38131
|
-
if (Array.isArray(data)) {
|
|
38132
|
-
result.data = data.map((row) => {
|
|
38133
|
-
const m = row.metadata ?? row;
|
|
38134
|
-
const dateRaw = m.createDate?.split("T")[0];
|
|
38135
|
-
return {
|
|
38136
|
-
_id: m.invoiceId,
|
|
38137
|
-
"Statut": m.state,
|
|
38138
|
-
"Direction": m.direction === "INBOUND" ? "Re\xE7ue" : m.direction === "OUTBOUND" ? "\xC9mise" : m.direction,
|
|
38139
|
-
"Date": dateRaw ? new Date(dateRaw).toLocaleDateString("fr-FR", { day: "numeric", month: "short", year: "numeric" }) : "\u2014"
|
|
38140
|
-
};
|
|
38141
|
-
});
|
|
38239
|
+
throw new Error(
|
|
38240
|
+
"[einvoice_invoice_download_file] 'file_id' is required"
|
|
38241
|
+
);
|
|
38142
38242
|
}
|
|
38243
|
+
const { data, contentType } = await ctx.adapter.downloadFile(
|
|
38244
|
+
input.file_id
|
|
38245
|
+
);
|
|
38143
38246
|
return {
|
|
38144
|
-
|
|
38145
|
-
|
|
38146
|
-
|
|
38147
|
-
|
|
38148
|
-
|
|
38247
|
+
content: `Downloaded file (${contentType}, ${data.length} bytes)`,
|
|
38248
|
+
structuredContent: {
|
|
38249
|
+
content_type: contentType,
|
|
38250
|
+
data_base64: uint8ToBase64(data),
|
|
38251
|
+
size_bytes: data.length
|
|
38149
38252
|
}
|
|
38150
38253
|
};
|
|
38151
38254
|
}
|
|
38152
38255
|
},
|
|
38256
|
+
// ── seen/notSeen tools removed ──────────────────────────
|
|
38257
|
+
// Iopole's seen/notSeen mechanism is opaque: `seen` is not exposed in
|
|
38258
|
+
// search or getInvoice, and `notSeen` always returns empty in PUSH mode
|
|
38259
|
+
// (active webhook). Removed in v0.2.0 — see docs/CHANGELOG.md.
|
|
38153
38260
|
// ── Generate CII ────────────────────────────────────────
|
|
38154
38261
|
{
|
|
38155
38262
|
name: "einvoice_invoice_generate_cii",
|
|
38156
38263
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38157
|
-
|
|
38264
|
+
requires: ["generateCII"],
|
|
38265
|
+
description: GENERATE_HINT + "Generate a CII invoice preview. Validates and converts to CII XML. Returns a preview for review. Use einvoice_invoice_submit with the generated_id to send. Requires a flavor (e.g. EN16931).",
|
|
38158
38266
|
category: "invoice",
|
|
38159
38267
|
inputSchema: {
|
|
38160
38268
|
type: "object",
|
|
@@ -38165,28 +38273,33 @@ var invoiceTools = [
|
|
|
38165
38273
|
},
|
|
38166
38274
|
flavor: {
|
|
38167
38275
|
type: "string",
|
|
38168
|
-
description: "CII profile flavor
|
|
38276
|
+
description: "CII profile flavor",
|
|
38277
|
+
enum: ["EN16931", "EXTENDED"]
|
|
38169
38278
|
}
|
|
38170
38279
|
},
|
|
38171
38280
|
required: ["invoice", "flavor"]
|
|
38172
38281
|
},
|
|
38173
38282
|
handler: async (input, ctx) => {
|
|
38174
38283
|
if (!input.invoice || !input.flavor) {
|
|
38175
|
-
throw new Error(
|
|
38284
|
+
throw new Error(
|
|
38285
|
+
"[einvoice_invoice_generate_cii] 'invoice' and 'flavor' are required"
|
|
38286
|
+
);
|
|
38176
38287
|
}
|
|
38177
|
-
const inv =
|
|
38178
|
-
const
|
|
38288
|
+
const inv = input.invoice;
|
|
38289
|
+
const xml = await ctx.adapter.generateCII({
|
|
38179
38290
|
invoice: inv,
|
|
38180
38291
|
flavor: input.flavor
|
|
38181
38292
|
});
|
|
38182
|
-
const
|
|
38183
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38293
|
+
const bytes = new TextEncoder().encode(xml);
|
|
38184
38294
|
const filename = `${inv.invoiceId ?? "invoice"}.xml`;
|
|
38185
38295
|
const generated_id = storeGenerated(bytes, filename);
|
|
38186
38296
|
return {
|
|
38187
|
-
|
|
38188
|
-
|
|
38189
|
-
|
|
38297
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38298
|
+
structuredContent: {
|
|
38299
|
+
generated_id,
|
|
38300
|
+
filename,
|
|
38301
|
+
preview: mapToViewerPreview(inv)
|
|
38302
|
+
}
|
|
38190
38303
|
};
|
|
38191
38304
|
}
|
|
38192
38305
|
},
|
|
@@ -38194,7 +38307,8 @@ var invoiceTools = [
|
|
|
38194
38307
|
{
|
|
38195
38308
|
name: "einvoice_invoice_generate_ubl",
|
|
38196
38309
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38197
|
-
|
|
38310
|
+
requires: ["generateUBL"],
|
|
38311
|
+
description: GENERATE_HINT + "Generate a UBL invoice preview. Validates and converts to UBL XML. Returns a preview for review. Use einvoice_invoice_submit with the generated_id to send. Requires a flavor (e.g. EN16931).",
|
|
38198
38312
|
category: "invoice",
|
|
38199
38313
|
inputSchema: {
|
|
38200
38314
|
type: "object",
|
|
@@ -38205,28 +38319,33 @@ var invoiceTools = [
|
|
|
38205
38319
|
},
|
|
38206
38320
|
flavor: {
|
|
38207
38321
|
type: "string",
|
|
38208
|
-
description: "UBL profile flavor
|
|
38322
|
+
description: "UBL profile flavor",
|
|
38323
|
+
enum: ["EN16931", "PEPPOL_BIS_3"]
|
|
38209
38324
|
}
|
|
38210
38325
|
},
|
|
38211
38326
|
required: ["invoice", "flavor"]
|
|
38212
38327
|
},
|
|
38213
38328
|
handler: async (input, ctx) => {
|
|
38214
38329
|
if (!input.invoice || !input.flavor) {
|
|
38215
|
-
throw new Error(
|
|
38330
|
+
throw new Error(
|
|
38331
|
+
"[einvoice_invoice_generate_ubl] 'invoice' and 'flavor' are required"
|
|
38332
|
+
);
|
|
38216
38333
|
}
|
|
38217
|
-
const inv =
|
|
38218
|
-
const
|
|
38334
|
+
const inv = input.invoice;
|
|
38335
|
+
const xml = await ctx.adapter.generateUBL({
|
|
38219
38336
|
invoice: inv,
|
|
38220
38337
|
flavor: input.flavor
|
|
38221
38338
|
});
|
|
38222
|
-
const
|
|
38223
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38339
|
+
const bytes = new TextEncoder().encode(xml);
|
|
38224
38340
|
const filename = `${inv.invoiceId ?? "invoice"}.xml`;
|
|
38225
38341
|
const generated_id = storeGenerated(bytes, filename);
|
|
38226
38342
|
return {
|
|
38227
|
-
|
|
38228
|
-
|
|
38229
|
-
|
|
38343
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38344
|
+
structuredContent: {
|
|
38345
|
+
generated_id,
|
|
38346
|
+
filename,
|
|
38347
|
+
preview: mapToViewerPreview(inv)
|
|
38348
|
+
}
|
|
38230
38349
|
};
|
|
38231
38350
|
}
|
|
38232
38351
|
},
|
|
@@ -38234,7 +38353,8 @@ var invoiceTools = [
|
|
|
38234
38353
|
{
|
|
38235
38354
|
name: "einvoice_invoice_generate_facturx",
|
|
38236
38355
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38237
|
-
|
|
38356
|
+
requires: ["generateFacturX"],
|
|
38357
|
+
description: GENERATE_HINT + "Generate a Factur-X invoice preview. Creates a hybrid PDF/XML. Returns a preview for review. Use einvoice_invoice_submit with the generated_id to send. Requires a flavor (e.g. EN16931). Optionally accepts a language (FRENCH, ENGLISH, GERMAN).",
|
|
38238
38358
|
category: "invoice",
|
|
38239
38359
|
inputSchema: {
|
|
38240
38360
|
type: "object",
|
|
@@ -38245,7 +38365,8 @@ var invoiceTools = [
|
|
|
38245
38365
|
},
|
|
38246
38366
|
flavor: {
|
|
38247
38367
|
type: "string",
|
|
38248
|
-
description: "Factur-X profile flavor
|
|
38368
|
+
description: "Factur-X profile flavor",
|
|
38369
|
+
enum: ["BASICWL", "EN16931", "EXTENDED"]
|
|
38249
38370
|
},
|
|
38250
38371
|
language: {
|
|
38251
38372
|
type: "string",
|
|
@@ -38257,22 +38378,25 @@ var invoiceTools = [
|
|
|
38257
38378
|
},
|
|
38258
38379
|
handler: async (input, ctx) => {
|
|
38259
38380
|
if (!input.invoice || !input.flavor) {
|
|
38260
|
-
throw new Error(
|
|
38381
|
+
throw new Error(
|
|
38382
|
+
"[einvoice_invoice_generate_facturx] 'invoice' and 'flavor' are required"
|
|
38383
|
+
);
|
|
38261
38384
|
}
|
|
38262
|
-
const inv =
|
|
38263
|
-
const
|
|
38385
|
+
const inv = input.invoice;
|
|
38386
|
+
const { data: bytes } = await ctx.adapter.generateFacturX({
|
|
38264
38387
|
invoice: inv,
|
|
38265
38388
|
flavor: input.flavor,
|
|
38266
38389
|
language: input.language
|
|
38267
38390
|
});
|
|
38268
|
-
const raw2 = typeof result === "string" ? result : JSON.stringify(result);
|
|
38269
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38270
38391
|
const filename = `${inv.invoiceId ?? "invoice"}.pdf`;
|
|
38271
38392
|
const generated_id = storeGenerated(bytes, filename);
|
|
38272
38393
|
return {
|
|
38273
|
-
|
|
38274
|
-
|
|
38275
|
-
|
|
38394
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38395
|
+
structuredContent: {
|
|
38396
|
+
generated_id,
|
|
38397
|
+
filename,
|
|
38398
|
+
preview: mapToViewerPreview(inv)
|
|
38399
|
+
}
|
|
38276
38400
|
};
|
|
38277
38401
|
}
|
|
38278
38402
|
}
|
|
@@ -38283,47 +38407,26 @@ var ENTITY_TYPE_LABELS = {
|
|
|
38283
38407
|
LEGAL_UNIT: "Entit\xE9 juridique",
|
|
38284
38408
|
OFFICE: "\xC9tablissement"
|
|
38285
38409
|
};
|
|
38286
|
-
function formatDirectoryFrRow(row) {
|
|
38287
|
-
const ci = row.countryIdentifier ?? {};
|
|
38288
|
-
const siren = ci.siren ?? row.siren;
|
|
38289
|
-
const siret = ci.siret ?? row.siret;
|
|
38290
|
-
const country = ci.country ?? row.country ?? "FR";
|
|
38291
|
-
return {
|
|
38292
|
-
_id: row.businessEntityId,
|
|
38293
|
-
_identifiers: row.identifiers,
|
|
38294
|
-
"Nom": row.name ?? "\u2014",
|
|
38295
|
-
"Type": ENTITY_TYPE_LABELS[row.type] ?? row.type ?? "\u2014",
|
|
38296
|
-
"SIREN": siren ?? "\u2014",
|
|
38297
|
-
"SIRET": siret ?? "\u2014",
|
|
38298
|
-
"Pays": country
|
|
38299
|
-
};
|
|
38300
|
-
}
|
|
38301
|
-
function autoWrapDirectoryQuery(q) {
|
|
38302
|
-
const trimmed = q.trim();
|
|
38303
|
-
if (/^\d{14}$/.test(trimmed)) return `siret:"${trimmed}"`;
|
|
38304
|
-
if (/^\d{9}$/.test(trimmed)) return `siren:"${trimmed}"`;
|
|
38305
|
-
if (/^FR\d{11}$/i.test(trimmed)) return `vatNumber:"${trimmed.toUpperCase()}"`;
|
|
38306
|
-
if (trimmed.length >= 3 && !/^\d+$/.test(trimmed) && !trimmed.includes(":")) {
|
|
38307
|
-
return `name:"*${trimmed}*"`;
|
|
38308
|
-
}
|
|
38309
|
-
return trimmed;
|
|
38310
|
-
}
|
|
38311
38410
|
var directoryTools = [
|
|
38312
38411
|
// ── French Directory ────────────────────────────────────
|
|
38313
38412
|
{
|
|
38314
38413
|
name: "einvoice_directory_fr_search",
|
|
38315
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/
|
|
38316
|
-
|
|
38414
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-list" } },
|
|
38415
|
+
requires: ["searchDirectoryFr"],
|
|
38416
|
+
description: "Search the French PPF directory (Portail Public de Facturation). Find companies registered for e-invoicing in France. Search by SIRET (14 digits), SIREN (9 digits), TVA (FR + 11 digits), or company name. Format is auto-detected. Returns the company's registered platform (PDP) and routing information.",
|
|
38317
38417
|
category: "directory",
|
|
38318
38418
|
inputSchema: {
|
|
38319
38419
|
type: "object",
|
|
38320
38420
|
properties: {
|
|
38321
38421
|
q: {
|
|
38322
38422
|
type: "string",
|
|
38323
|
-
description:
|
|
38423
|
+
description: "Search query (required). SIRET, SIREN, VAT number, or company name."
|
|
38324
38424
|
},
|
|
38325
38425
|
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38326
|
-
limit: {
|
|
38426
|
+
limit: {
|
|
38427
|
+
type: "number",
|
|
38428
|
+
description: "Max results (default 50, max 200)"
|
|
38429
|
+
}
|
|
38327
38430
|
},
|
|
38328
38431
|
required: ["q"]
|
|
38329
38432
|
},
|
|
@@ -38331,24 +38434,47 @@ var directoryTools = [
|
|
|
38331
38434
|
if (!input.q) {
|
|
38332
38435
|
throw new Error("[einvoice_directory_fr_search] 'q' query is required");
|
|
38333
38436
|
}
|
|
38334
|
-
const
|
|
38335
|
-
q:
|
|
38437
|
+
const { rows, count } = await ctx.adapter.searchDirectoryFr({
|
|
38438
|
+
q: input.q,
|
|
38336
38439
|
offset: input.offset,
|
|
38337
38440
|
limit: input.limit
|
|
38338
38441
|
});
|
|
38339
|
-
const
|
|
38340
|
-
|
|
38341
|
-
|
|
38342
|
-
|
|
38343
|
-
|
|
38344
|
-
|
|
38345
|
-
|
|
38442
|
+
const viewerData = {
|
|
38443
|
+
data: rows.map((r) => {
|
|
38444
|
+
const typeLabel = r.type ? ENTITY_TYPE_LABELS[r.type] ?? r.type : void 0;
|
|
38445
|
+
return {
|
|
38446
|
+
_id: r.entityId,
|
|
38447
|
+
_detail: {
|
|
38448
|
+
name: r.name,
|
|
38449
|
+
type: typeLabel,
|
|
38450
|
+
siren: r.siren,
|
|
38451
|
+
siret: r.siret,
|
|
38452
|
+
country: r.country,
|
|
38453
|
+
directory: r.directory,
|
|
38454
|
+
status: r.status,
|
|
38455
|
+
createdAt: r.createdAt,
|
|
38456
|
+
identifiers: r.identifiers
|
|
38457
|
+
},
|
|
38458
|
+
"Nom": r.name ?? "\u2014",
|
|
38459
|
+
"Type": typeLabel ?? "\u2014",
|
|
38460
|
+
"SIRET": r.siret ?? "\u2014",
|
|
38461
|
+
"Pays": r.country ?? "\u2014"
|
|
38462
|
+
};
|
|
38463
|
+
}),
|
|
38464
|
+
count,
|
|
38465
|
+
_title: "Annuaire fran\xE7ais"
|
|
38466
|
+
};
|
|
38467
|
+
return {
|
|
38468
|
+
content: `${rows.length} entities found for "${input.q}"`,
|
|
38469
|
+
structuredContent: viewerData
|
|
38470
|
+
};
|
|
38346
38471
|
}
|
|
38347
38472
|
},
|
|
38348
38473
|
// ── International Directory ─────────────────────────────
|
|
38349
38474
|
{
|
|
38350
38475
|
name: "einvoice_directory_int_search",
|
|
38351
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/
|
|
38476
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-list" } },
|
|
38477
|
+
requires: ["searchDirectoryInt"],
|
|
38352
38478
|
description: "Search the international Peppol directory. Find companies registered on the Peppol network across 40+ countries. Search by participant identifier value (e.g. VAT number, GLN, DUNS).",
|
|
38353
38479
|
category: "directory",
|
|
38354
38480
|
inputSchema: {
|
|
@@ -38359,7 +38485,10 @@ var directoryTools = [
|
|
|
38359
38485
|
description: "Participant identifier value (required). E.g. a VAT number 'FR12345678901' or other identifier."
|
|
38360
38486
|
},
|
|
38361
38487
|
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38362
|
-
limit: {
|
|
38488
|
+
limit: {
|
|
38489
|
+
type: "number",
|
|
38490
|
+
description: "Max results (default 50, max 200)"
|
|
38491
|
+
}
|
|
38363
38492
|
},
|
|
38364
38493
|
required: ["value"]
|
|
38365
38494
|
},
|
|
@@ -38377,6 +38506,7 @@ var directoryTools = [
|
|
|
38377
38506
|
// ── Check Peppol Participant ────────────────────────────
|
|
38378
38507
|
{
|
|
38379
38508
|
name: "einvoice_directory_peppol_check",
|
|
38509
|
+
requires: ["checkPeppolParticipant"],
|
|
38380
38510
|
description: "Verify whether a specific Peppol participant exists in the international directory. Checks by scheme and identifier value.",
|
|
38381
38511
|
category: "directory",
|
|
38382
38512
|
inputSchema: {
|
|
@@ -38412,7 +38542,9 @@ var statusTools = [
|
|
|
38412
38542
|
// ── Send Status ─────────────────────────────────────────
|
|
38413
38543
|
{
|
|
38414
38544
|
name: "einvoice_status_send",
|
|
38415
|
-
|
|
38545
|
+
annotations: { destructiveHint: true },
|
|
38546
|
+
requires: ["sendStatus"],
|
|
38547
|
+
description: "Send a lifecycle status update for an invoice. Uses CDAR lifecycle codes: IN_HAND (204), APPROVED (205), REFUSED (210), DISPUTED (207), SUSPENDED (208), PAYMENT_SENT (211), PAYMENT_RECEIVED (212). Asynchronous \u2014 returns a confirmation.",
|
|
38416
38548
|
category: "status",
|
|
38417
38549
|
inputSchema: {
|
|
38418
38550
|
type: "object",
|
|
@@ -38464,7 +38596,9 @@ var statusTools = [
|
|
|
38464
38596
|
// ── Get Status History ────────────────────────────────────
|
|
38465
38597
|
{
|
|
38466
38598
|
name: "einvoice_status_history",
|
|
38599
|
+
annotations: { readOnlyHint: true },
|
|
38467
38600
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/status-timeline" } },
|
|
38601
|
+
requires: ["getStatusHistory"],
|
|
38468
38602
|
description: "Get the status history for an invoice. Returns all status changes in chronological order.",
|
|
38469
38603
|
category: "status",
|
|
38470
38604
|
inputSchema: {
|
|
@@ -38478,64 +38612,16 @@ var statusTools = [
|
|
|
38478
38612
|
if (!input.invoice_id) {
|
|
38479
38613
|
throw new Error("[einvoice_status_history] 'invoice_id' is required");
|
|
38480
38614
|
}
|
|
38481
|
-
const
|
|
38482
|
-
|
|
38483
|
-
|
|
38484
|
-
const obj = raw2;
|
|
38485
|
-
if (Array.isArray(obj.data)) return { entries: obj.data };
|
|
38486
|
-
if (Array.isArray(obj.entries)) return raw2;
|
|
38487
|
-
if (Array.isArray(obj.history)) return { entries: obj.history };
|
|
38488
|
-
}
|
|
38489
|
-
return { entries: [] };
|
|
38490
|
-
}
|
|
38491
|
-
},
|
|
38492
|
-
// ── Not Seen ────────────────────────────────────────────
|
|
38493
|
-
{
|
|
38494
|
-
name: "einvoice_status_not_seen",
|
|
38495
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38496
|
-
description: "Get status updates that have not been marked as seen. Useful for polling new incoming status changes on invoices.",
|
|
38497
|
-
category: "status",
|
|
38498
|
-
inputSchema: {
|
|
38499
|
-
type: "object",
|
|
38500
|
-
properties: {
|
|
38501
|
-
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38502
|
-
limit: { type: "number", description: "Max results (default 50)" }
|
|
38503
|
-
}
|
|
38504
|
-
},
|
|
38505
|
-
handler: async (input, ctx) => {
|
|
38506
|
-
const result = await ctx.adapter.getUnseenStatuses({
|
|
38507
|
-
offset: input.offset,
|
|
38508
|
-
limit: input.limit
|
|
38509
|
-
});
|
|
38615
|
+
const result = await ctx.adapter.getStatusHistory(
|
|
38616
|
+
input.invoice_id
|
|
38617
|
+
);
|
|
38510
38618
|
return {
|
|
38511
|
-
|
|
38512
|
-
|
|
38513
|
-
toolName: "einvoice_status_history",
|
|
38514
|
-
idField: "invoiceId",
|
|
38515
|
-
argName: "invoice_id"
|
|
38516
|
-
}
|
|
38619
|
+
content: `${result.entries.length} status entries for invoice ${input.invoice_id}`,
|
|
38620
|
+
structuredContent: result
|
|
38517
38621
|
};
|
|
38518
38622
|
}
|
|
38519
|
-
},
|
|
38520
|
-
// ── Mark as Seen ────────────────────────────────────────
|
|
38521
|
-
{
|
|
38522
|
-
name: "einvoice_status_mark_seen",
|
|
38523
|
-
description: "Mark a status update as seen/processed.",
|
|
38524
|
-
category: "status",
|
|
38525
|
-
inputSchema: {
|
|
38526
|
-
type: "object",
|
|
38527
|
-
properties: {
|
|
38528
|
-
status_id: { type: "string", description: "Status ID" }
|
|
38529
|
-
},
|
|
38530
|
-
required: ["status_id"]
|
|
38531
|
-
},
|
|
38532
|
-
handler: async (input, ctx) => {
|
|
38533
|
-
if (!input.status_id) {
|
|
38534
|
-
throw new Error("[einvoice_status_mark_seen] 'status_id' is required");
|
|
38535
|
-
}
|
|
38536
|
-
return await ctx.adapter.markStatusSeen(input.status_id);
|
|
38537
|
-
}
|
|
38538
38623
|
}
|
|
38624
|
+
// ── seen/notSeen tools removed — see invoice.ts comment and docs/CHANGELOG.md
|
|
38539
38625
|
];
|
|
38540
38626
|
|
|
38541
38627
|
// src/tools/reporting.ts
|
|
@@ -38543,6 +38629,7 @@ var reportingTools = [
|
|
|
38543
38629
|
// ── Invoice Transaction Reporting ───────────────────────
|
|
38544
38630
|
{
|
|
38545
38631
|
name: "einvoice_reporting_invoice_transaction",
|
|
38632
|
+
requires: ["reportInvoiceTransaction"],
|
|
38546
38633
|
description: "Report an invoice transaction to the French tax authority (e-reporting). Required for B2C and international invoice transactions. Asynchronous \u2014 returns a GUID.",
|
|
38547
38634
|
category: "reporting",
|
|
38548
38635
|
inputSchema: {
|
|
@@ -38550,7 +38637,7 @@ var reportingTools = [
|
|
|
38550
38637
|
properties: {
|
|
38551
38638
|
transaction: {
|
|
38552
38639
|
type: "object",
|
|
38553
|
-
description: "Invoice transaction data for DGFiP e-reporting. Typical fields: invoiceReference (string): the invoice number; transactionDate (string, YYYY-MM-DD): date of the transaction; transactionType (string): e.g. 'B2C', 'INTERNATIONAL'; totalAmount (number): total TTC amount; taxDetails (array): [{ vatRate: number, taxableAmount: number, taxAmount: number }]; counterparty (object): { name, country, identifier }; currency (string, default 'EUR'). Exact schema depends on the PA provider
|
|
38640
|
+
description: "Invoice transaction data for DGFiP e-reporting. Typical fields: invoiceReference (string): the invoice number; transactionDate (string, YYYY-MM-DD): date of the transaction; transactionType (string): e.g. 'B2C', 'INTERNATIONAL'; totalAmount (number): total TTC amount; taxDetails (array): [{ vatRate: number, taxableAmount: number, taxAmount: number }]; counterparty (object): { name, country, identifier }; currency (string, default 'EUR'). Exact schema depends on the PA provider."
|
|
38554
38641
|
}
|
|
38555
38642
|
},
|
|
38556
38643
|
required: ["transaction"]
|
|
@@ -38569,6 +38656,7 @@ var reportingTools = [
|
|
|
38569
38656
|
// ── Non-Invoice Transaction Reporting ───────────────────
|
|
38570
38657
|
{
|
|
38571
38658
|
name: "einvoice_reporting_transaction",
|
|
38659
|
+
requires: ["reportTransaction"],
|
|
38572
38660
|
description: "Report a non-invoice transaction to the French tax authority (e-reporting). Covers payment data, B2C cash transactions, etc. Requires the business entity ID. Asynchronous \u2014 returns a GUID.",
|
|
38573
38661
|
category: "reporting",
|
|
38574
38662
|
inputSchema: {
|
|
@@ -38580,7 +38668,7 @@ var reportingTools = [
|
|
|
38580
38668
|
},
|
|
38581
38669
|
transaction: {
|
|
38582
38670
|
type: "object",
|
|
38583
|
-
description: "Non-invoice transaction data for DGFiP e-reporting. Typical fields: transactionDate (string, YYYY-MM-DD); transactionType (string): e.g. 'CASH_PAYMENT', 'PAYMENT_DATA'; totalAmount (number): total TTC amount; taxDetails (array): [{ vatRate: number, taxableAmount: number, taxAmount: number }]; periodicity (string): 'MONTHLY' or 'QUARTERLY'; currency (string, default 'EUR'). Exact schema depends on the PA provider
|
|
38671
|
+
description: "Non-invoice transaction data for DGFiP e-reporting. Typical fields: transactionDate (string, YYYY-MM-DD); transactionType (string): e.g. 'CASH_PAYMENT', 'PAYMENT_DATA'; totalAmount (number): total TTC amount; taxDetails (array): [{ vatRate: number, taxableAmount: number, taxAmount: number }]; periodicity (string): 'MONTHLY' or 'QUARTERLY'; currency (string, default 'EUR'). Exact schema depends on the PA provider."
|
|
38584
38672
|
}
|
|
38585
38673
|
},
|
|
38586
38674
|
required: ["business_entity_id", "transaction"]
|
|
@@ -38607,12 +38695,29 @@ var webhookTools = [
|
|
|
38607
38695
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38608
38696
|
description: "List all configured webhooks for your account.",
|
|
38609
38697
|
category: "webhook",
|
|
38698
|
+
requires: ["listWebhooks"],
|
|
38610
38699
|
inputSchema: {
|
|
38611
38700
|
type: "object",
|
|
38612
38701
|
properties: {}
|
|
38613
38702
|
},
|
|
38614
38703
|
handler: async (_input, ctx) => {
|
|
38615
|
-
|
|
38704
|
+
const raw2 = await ctx.adapter.listWebhooks();
|
|
38705
|
+
const webhooks = Array.isArray(raw2) ? raw2 : raw2?.data ?? [raw2];
|
|
38706
|
+
const data = webhooks.map((w) => ({
|
|
38707
|
+
_id: w.id ?? w.webhookId,
|
|
38708
|
+
"Nom": w.name ?? "\u2014",
|
|
38709
|
+
"URL": w.url ?? "\u2014",
|
|
38710
|
+
"Actif": w.active !== false ? "Oui" : "Non",
|
|
38711
|
+
"\xC9v\xE9nements": Array.isArray(w.events) ? w.events.join(", ") : "\u2014"
|
|
38712
|
+
}));
|
|
38713
|
+
return {
|
|
38714
|
+
content: `${data.length} webhook(s) configur\xE9(s)`,
|
|
38715
|
+
structuredContent: {
|
|
38716
|
+
data,
|
|
38717
|
+
count: data.length,
|
|
38718
|
+
_title: "Webhooks"
|
|
38719
|
+
}
|
|
38720
|
+
};
|
|
38616
38721
|
}
|
|
38617
38722
|
},
|
|
38618
38723
|
// ── Get ─────────────────────────────────────────────────
|
|
@@ -38620,6 +38725,7 @@ var webhookTools = [
|
|
|
38620
38725
|
name: "einvoice_webhook_get",
|
|
38621
38726
|
description: "Get a single webhook configuration by its ID.",
|
|
38622
38727
|
category: "webhook",
|
|
38728
|
+
requires: ["getWebhook"],
|
|
38623
38729
|
inputSchema: {
|
|
38624
38730
|
type: "object",
|
|
38625
38731
|
properties: {
|
|
@@ -38637,6 +38743,7 @@ var webhookTools = [
|
|
|
38637
38743
|
// ── Create ──────────────────────────────────────────────
|
|
38638
38744
|
{
|
|
38639
38745
|
name: "einvoice_webhook_create",
|
|
38746
|
+
requires: ["createWebhook"],
|
|
38640
38747
|
description: "Create a new webhook to receive real-time notifications. Specify the target URL and which events to subscribe to (e.g. invoice.received, status.changed).",
|
|
38641
38748
|
category: "webhook",
|
|
38642
38749
|
inputSchema: {
|
|
@@ -38681,6 +38788,7 @@ var webhookTools = [
|
|
|
38681
38788
|
name: "einvoice_webhook_update",
|
|
38682
38789
|
description: "Update an existing webhook configuration.",
|
|
38683
38790
|
category: "webhook",
|
|
38791
|
+
requires: ["updateWebhook"],
|
|
38684
38792
|
inputSchema: {
|
|
38685
38793
|
type: "object",
|
|
38686
38794
|
properties: {
|
|
@@ -38713,6 +38821,7 @@ var webhookTools = [
|
|
|
38713
38821
|
name: "einvoice_webhook_delete",
|
|
38714
38822
|
description: "Delete a webhook configuration.",
|
|
38715
38823
|
category: "webhook",
|
|
38824
|
+
requires: ["deleteWebhook"],
|
|
38716
38825
|
inputSchema: {
|
|
38717
38826
|
type: "object",
|
|
38718
38827
|
properties: {
|
|
@@ -38729,160 +38838,1645 @@ var webhookTools = [
|
|
|
38729
38838
|
}
|
|
38730
38839
|
];
|
|
38731
38840
|
|
|
38732
|
-
// src/tools/
|
|
38733
|
-
var
|
|
38734
|
-
|
|
38735
|
-
|
|
38736
|
-
status: statusTools,
|
|
38737
|
-
reporting: reportingTools,
|
|
38738
|
-
webhook: webhookTools
|
|
38841
|
+
// src/tools/config.ts
|
|
38842
|
+
var ENTITY_TYPE_LABELS2 = {
|
|
38843
|
+
LEGAL_UNIT: "Entit\xE9 juridique",
|
|
38844
|
+
OFFICE: "\xC9tablissement"
|
|
38739
38845
|
};
|
|
38740
|
-
var
|
|
38741
|
-
|
|
38742
|
-
|
|
38743
|
-
|
|
38744
|
-
|
|
38745
|
-
|
|
38746
|
-
|
|
38747
|
-
|
|
38748
|
-
|
|
38749
|
-
}
|
|
38750
|
-
|
|
38751
|
-
|
|
38752
|
-
|
|
38753
|
-
|
|
38754
|
-
|
|
38846
|
+
var configTools = [
|
|
38847
|
+
// ── Customer ID ──────────────────────────────────────
|
|
38848
|
+
{
|
|
38849
|
+
name: "einvoice_config_customer_id",
|
|
38850
|
+
requires: ["getCustomerId"],
|
|
38851
|
+
description: "Get the current operator customer ID. This is your unique operator identifier on the e-invoicing platform.",
|
|
38852
|
+
category: "config",
|
|
38853
|
+
inputSchema: {
|
|
38854
|
+
type: "object",
|
|
38855
|
+
properties: {}
|
|
38856
|
+
},
|
|
38857
|
+
handler: async (_input, ctx) => {
|
|
38858
|
+
return await ctx.adapter.getCustomerId();
|
|
38859
|
+
}
|
|
38860
|
+
},
|
|
38861
|
+
// ── List Business Entities ───────────────────────────
|
|
38862
|
+
{
|
|
38863
|
+
name: "einvoice_config_entities_list",
|
|
38864
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38865
|
+
requires: ["listBusinessEntities"],
|
|
38866
|
+
description: "List all business entities managed by your operator account. Shows which companies/offices are enrolled for e-invoicing under your management. If this list is empty, you need to enroll entities before sending invoices.",
|
|
38867
|
+
category: "config",
|
|
38868
|
+
inputSchema: {
|
|
38869
|
+
type: "object",
|
|
38870
|
+
properties: {}
|
|
38871
|
+
},
|
|
38872
|
+
handler: async (_input, ctx) => {
|
|
38873
|
+
const { rows, count } = await ctx.adapter.listBusinessEntities();
|
|
38874
|
+
const viewerData = {
|
|
38875
|
+
data: rows.map((r) => ({
|
|
38876
|
+
_id: r.entityId,
|
|
38877
|
+
"Nom": r.name ?? "\u2014",
|
|
38878
|
+
"SIRET": r.siret ?? "\u2014",
|
|
38879
|
+
"Type": ENTITY_TYPE_LABELS2[r.type ?? ""] ?? r.type ?? "\u2014"
|
|
38880
|
+
})),
|
|
38881
|
+
count,
|
|
38882
|
+
_title: "Entit\xE9s op\xE9rateur",
|
|
38883
|
+
_rowAction: {
|
|
38884
|
+
toolName: "einvoice_config_entity_get",
|
|
38885
|
+
idField: "_id",
|
|
38886
|
+
argName: "id"
|
|
38887
|
+
}
|
|
38888
|
+
};
|
|
38889
|
+
return {
|
|
38890
|
+
content: `${rows.length} entit\xE9(s) op\xE9rateur`,
|
|
38891
|
+
structuredContent: viewerData
|
|
38892
|
+
};
|
|
38893
|
+
}
|
|
38894
|
+
},
|
|
38895
|
+
// ── Get Business Entity ──────────────────────────────
|
|
38896
|
+
{
|
|
38897
|
+
name: "einvoice_config_entity_get",
|
|
38898
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-card" } },
|
|
38899
|
+
requires: ["getBusinessEntity"],
|
|
38900
|
+
description: "Get details of a specific business entity managed by your operator. Shows registration info, identifiers, and enrollment status.",
|
|
38901
|
+
category: "config",
|
|
38902
|
+
inputSchema: {
|
|
38903
|
+
type: "object",
|
|
38904
|
+
properties: {
|
|
38905
|
+
id: { type: "string", description: "Business entity ID" }
|
|
38906
|
+
},
|
|
38907
|
+
required: ["id"]
|
|
38908
|
+
},
|
|
38909
|
+
handler: async (input, ctx) => {
|
|
38910
|
+
if (!input.id) {
|
|
38911
|
+
throw new Error("[einvoice_config_entity_get] 'id' is required");
|
|
38912
|
+
}
|
|
38913
|
+
return await ctx.adapter.getBusinessEntity(input.id);
|
|
38914
|
+
}
|
|
38915
|
+
},
|
|
38916
|
+
// ── Create Legal Unit ────────────────────────────────
|
|
38917
|
+
{
|
|
38918
|
+
name: "einvoice_config_entity_create_legal",
|
|
38919
|
+
requires: ["createLegalUnit"],
|
|
38920
|
+
description: "Create a new legal unit (company) under your operator account. A legal unit represents a legally registered entity with a unique SIREN. After creating the legal unit, create an office (establishment) with a SIRET, then enroll it for e-invoicing.",
|
|
38921
|
+
category: "config",
|
|
38922
|
+
inputSchema: {
|
|
38923
|
+
type: "object",
|
|
38924
|
+
properties: {
|
|
38925
|
+
siren: {
|
|
38926
|
+
type: "string",
|
|
38927
|
+
description: "SIREN number (9 digits) of the company to register"
|
|
38928
|
+
},
|
|
38929
|
+
name: {
|
|
38930
|
+
type: "string",
|
|
38931
|
+
description: "Company name"
|
|
38932
|
+
},
|
|
38933
|
+
country: {
|
|
38934
|
+
type: "string",
|
|
38935
|
+
description: "Country code (default: FR)"
|
|
38936
|
+
},
|
|
38937
|
+
scope: {
|
|
38938
|
+
type: "string",
|
|
38939
|
+
description: "Entity scope",
|
|
38940
|
+
enum: ["PRIVATE_TAX_PAYER", "PUBLIC", "PRIMARY", "SECONDARY"]
|
|
38941
|
+
}
|
|
38942
|
+
},
|
|
38943
|
+
required: ["siren"]
|
|
38944
|
+
},
|
|
38945
|
+
handler: async (input, ctx) => {
|
|
38946
|
+
if (!input.siren) {
|
|
38947
|
+
throw new Error(
|
|
38948
|
+
"[einvoice_config_entity_create_legal] 'siren' is required"
|
|
38949
|
+
);
|
|
38950
|
+
}
|
|
38951
|
+
return await ctx.adapter.createLegalUnit({
|
|
38952
|
+
identifierScheme: "0002",
|
|
38953
|
+
identifierValue: input.siren,
|
|
38954
|
+
name: input.name,
|
|
38955
|
+
country: input.country ?? "FR",
|
|
38956
|
+
scope: input.scope ?? "PRIMARY"
|
|
38957
|
+
});
|
|
38958
|
+
}
|
|
38959
|
+
},
|
|
38960
|
+
// ── Create Office ────────────────────────────────────
|
|
38961
|
+
{
|
|
38962
|
+
name: "einvoice_config_entity_create_office",
|
|
38963
|
+
requires: ["createOffice"],
|
|
38964
|
+
description: "Create a new office (establishment) for an existing legal unit. An office represents a physical location with a unique SIRET. The legal unit must exist first (use einvoice_config_entity_create_legal). After creating, enroll the office with einvoice_config_enroll_fr.",
|
|
38965
|
+
category: "config",
|
|
38966
|
+
inputSchema: {
|
|
38967
|
+
type: "object",
|
|
38968
|
+
properties: {
|
|
38969
|
+
siret: {
|
|
38970
|
+
type: "string",
|
|
38971
|
+
description: "SIRET number (14 digits) of the establishment"
|
|
38972
|
+
},
|
|
38973
|
+
legalUnitId: {
|
|
38974
|
+
type: "string",
|
|
38975
|
+
description: "Business entity ID of the parent legal unit"
|
|
38976
|
+
},
|
|
38977
|
+
name: {
|
|
38978
|
+
type: "string",
|
|
38979
|
+
description: "Office name"
|
|
38980
|
+
},
|
|
38981
|
+
scope: {
|
|
38982
|
+
type: "string",
|
|
38983
|
+
description: "Entity scope",
|
|
38984
|
+
enum: ["PRIVATE_TAX_PAYER", "PUBLIC", "PRIMARY", "SECONDARY"]
|
|
38985
|
+
}
|
|
38986
|
+
},
|
|
38987
|
+
required: ["siret", "legalUnitId"]
|
|
38988
|
+
},
|
|
38989
|
+
handler: async (input, ctx) => {
|
|
38990
|
+
if (!input.siret || !input.legalUnitId) {
|
|
38991
|
+
throw new Error(
|
|
38992
|
+
"[einvoice_config_entity_create_office] 'siret' and 'legalUnitId' are required"
|
|
38993
|
+
);
|
|
38994
|
+
}
|
|
38995
|
+
return await ctx.adapter.createOffice({
|
|
38996
|
+
identifierScheme: "0009",
|
|
38997
|
+
identifierValue: input.siret,
|
|
38998
|
+
legalBusinessEntityId: input.legalUnitId,
|
|
38999
|
+
name: input.name,
|
|
39000
|
+
scope: input.scope ?? "PRIMARY"
|
|
39001
|
+
});
|
|
39002
|
+
}
|
|
39003
|
+
},
|
|
39004
|
+
// ── Enroll French Entity ─────────────────────────────
|
|
39005
|
+
{
|
|
39006
|
+
name: "einvoice_config_enroll_fr",
|
|
39007
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39008
|
+
requires: ["enrollFrench"],
|
|
39009
|
+
description: "Enroll a French business entity for e-invoicing on the PPF (Portail Public de Facturation). REQUIRED before an entity can send or receive invoices. If invoice emission returns WRONG_ROUTING, the sender entity needs enrollment. Provide the SIRET of the entity to enroll. The entity must first exist under your operator (use einvoice_config_entities_list to check).",
|
|
39010
|
+
category: "config",
|
|
39011
|
+
inputSchema: {
|
|
39012
|
+
type: "object",
|
|
39013
|
+
properties: {
|
|
39014
|
+
siret: {
|
|
39015
|
+
type: "string",
|
|
39016
|
+
description: "SIRET (14 digits) of the entity to enroll"
|
|
39017
|
+
},
|
|
39018
|
+
siren: {
|
|
39019
|
+
type: "string",
|
|
39020
|
+
description: "SIREN (9 digits, first 9 digits of SIRET). If omitted, extracted from SIRET automatically."
|
|
39021
|
+
}
|
|
39022
|
+
},
|
|
39023
|
+
required: ["siret"]
|
|
39024
|
+
},
|
|
39025
|
+
handler: async (input, ctx) => {
|
|
39026
|
+
if (!input.siret) {
|
|
39027
|
+
throw new Error("[einvoice_config_enroll_fr] 'siret' is required");
|
|
39028
|
+
}
|
|
39029
|
+
const siret = input.siret;
|
|
39030
|
+
const siren = input.siren ?? siret.slice(0, 9);
|
|
39031
|
+
return await ctx.adapter.enrollFrench({ siret, siren });
|
|
39032
|
+
}
|
|
39033
|
+
},
|
|
39034
|
+
// ── Claim Business Entity ────────────────────────────
|
|
39035
|
+
{
|
|
39036
|
+
name: "einvoice_config_entity_claim",
|
|
39037
|
+
requires: ["claimBusinessEntityByIdentifier"],
|
|
39038
|
+
description: "Claim management of a business entity by its identifier (SIRET scheme 0009). Use this to take over management of an entity that exists in the directory but is not yet under your operator account.",
|
|
39039
|
+
category: "config",
|
|
39040
|
+
inputSchema: {
|
|
39041
|
+
type: "object",
|
|
39042
|
+
properties: {
|
|
39043
|
+
scheme: {
|
|
39044
|
+
type: "string",
|
|
39045
|
+
description: "Identifier scheme (e.g. '0009' for SIRET)"
|
|
39046
|
+
},
|
|
39047
|
+
value: {
|
|
39048
|
+
type: "string",
|
|
39049
|
+
description: "Identifier value (e.g. SIRET number)"
|
|
39050
|
+
}
|
|
39051
|
+
},
|
|
39052
|
+
required: ["scheme", "value"]
|
|
39053
|
+
},
|
|
39054
|
+
handler: async (input, ctx) => {
|
|
39055
|
+
if (!input.scheme || !input.value) {
|
|
39056
|
+
throw new Error(
|
|
39057
|
+
"[einvoice_config_entity_claim] 'scheme' and 'value' are required"
|
|
39058
|
+
);
|
|
39059
|
+
}
|
|
39060
|
+
return await ctx.adapter.claimBusinessEntityByIdentifier(
|
|
39061
|
+
input.scheme,
|
|
39062
|
+
input.value,
|
|
39063
|
+
{}
|
|
39064
|
+
);
|
|
39065
|
+
}
|
|
39066
|
+
},
|
|
39067
|
+
// ── Delete Business Entity ───────────────────────────
|
|
39068
|
+
{
|
|
39069
|
+
name: "einvoice_config_entity_delete",
|
|
39070
|
+
requires: ["deleteBusinessEntity"],
|
|
39071
|
+
description: "Remove a business entity from your operator account. This does not delete the entity from the national directory, only removes it from your management.",
|
|
39072
|
+
category: "config",
|
|
39073
|
+
inputSchema: {
|
|
39074
|
+
type: "object",
|
|
39075
|
+
properties: {
|
|
39076
|
+
id: { type: "string", description: "Business entity ID to remove" }
|
|
39077
|
+
},
|
|
39078
|
+
required: ["id"]
|
|
39079
|
+
},
|
|
39080
|
+
handler: async (input, ctx) => {
|
|
39081
|
+
if (!input.id) {
|
|
39082
|
+
throw new Error("[einvoice_config_entity_delete] 'id' is required");
|
|
39083
|
+
}
|
|
39084
|
+
return await ctx.adapter.deleteBusinessEntity(input.id);
|
|
39085
|
+
}
|
|
39086
|
+
},
|
|
39087
|
+
// ── Register on Network ──────────────────────────────
|
|
39088
|
+
{
|
|
39089
|
+
name: "einvoice_config_network_register",
|
|
39090
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39091
|
+
requires: ["registerNetwork"],
|
|
39092
|
+
description: "Register a business entity identifier on an e-invoicing network (DOMESTIC_FR or PEPPOL_INTERNATIONAL). REQUIRED for invoice routing. An entity can be enrolled but still get WRONG_ROUTING if its identifier is not registered on the target network. Use einvoice_config_entities_list to find the identifier ID, then register it here.",
|
|
39093
|
+
category: "config",
|
|
39094
|
+
inputSchema: {
|
|
39095
|
+
type: "object",
|
|
39096
|
+
properties: {
|
|
39097
|
+
identifier_id: {
|
|
39098
|
+
type: "string",
|
|
39099
|
+
description: "Business entity identifier ID (UUID, from the identifiers array in entity details)"
|
|
39100
|
+
},
|
|
39101
|
+
network: {
|
|
39102
|
+
type: "string",
|
|
39103
|
+
description: "Network to register on: DOMESTIC_FR (French PPF) or PEPPOL_INTERNATIONAL",
|
|
39104
|
+
enum: ["DOMESTIC_FR", "PEPPOL_INTERNATIONAL"]
|
|
39105
|
+
}
|
|
39106
|
+
},
|
|
39107
|
+
required: ["identifier_id", "network"]
|
|
39108
|
+
},
|
|
39109
|
+
handler: async (input, ctx) => {
|
|
39110
|
+
if (!input.identifier_id || !input.network) {
|
|
39111
|
+
throw new Error(
|
|
39112
|
+
"[einvoice_config_network_register] 'identifier_id' and 'network' are required"
|
|
39113
|
+
);
|
|
39114
|
+
}
|
|
39115
|
+
return await ctx.adapter.registerNetwork(
|
|
39116
|
+
input.identifier_id,
|
|
39117
|
+
input.network
|
|
39118
|
+
);
|
|
39119
|
+
}
|
|
39120
|
+
},
|
|
39121
|
+
// ── Register on Network by Scheme/Value ──────────────
|
|
39122
|
+
{
|
|
39123
|
+
name: "einvoice_config_network_register_by_id",
|
|
39124
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39125
|
+
requires: ["registerNetworkByScheme"],
|
|
39126
|
+
description: "Register an entity on an e-invoicing network using its identifier scheme and value directly. Shortcut when you know the SIRET but not the identifier UUID. Example: scheme='0009', value='43446637100011', network='DOMESTIC_FR'. Use scheme 0009 for SIRET, 0002 for SIREN.",
|
|
39127
|
+
category: "config",
|
|
39128
|
+
inputSchema: {
|
|
39129
|
+
type: "object",
|
|
39130
|
+
properties: {
|
|
39131
|
+
scheme: {
|
|
39132
|
+
type: "string",
|
|
39133
|
+
description: "Identifier scheme: '0009' for SIRET, '0002' for SIREN, '0225' for SIREN_SIRET"
|
|
39134
|
+
},
|
|
39135
|
+
value: {
|
|
39136
|
+
type: "string",
|
|
39137
|
+
description: "Identifier value (e.g. SIRET number)"
|
|
39138
|
+
},
|
|
39139
|
+
network: {
|
|
39140
|
+
type: "string",
|
|
39141
|
+
description: "Network: DOMESTIC_FR or PEPPOL_INTERNATIONAL",
|
|
39142
|
+
enum: ["DOMESTIC_FR", "PEPPOL_INTERNATIONAL"]
|
|
39143
|
+
}
|
|
39144
|
+
},
|
|
39145
|
+
required: ["scheme", "value", "network"]
|
|
39146
|
+
},
|
|
39147
|
+
handler: async (input, ctx) => {
|
|
39148
|
+
if (!input.scheme || !input.value || !input.network) {
|
|
39149
|
+
throw new Error(
|
|
39150
|
+
"[einvoice_config_network_register_by_id] 'scheme', 'value', and 'network' are required"
|
|
39151
|
+
);
|
|
39152
|
+
}
|
|
39153
|
+
return await ctx.adapter.registerNetworkByScheme(
|
|
39154
|
+
input.scheme,
|
|
39155
|
+
input.value,
|
|
39156
|
+
input.network
|
|
39157
|
+
);
|
|
39158
|
+
}
|
|
39159
|
+
},
|
|
39160
|
+
// ── Create Identifier ────────────────────────────────
|
|
39161
|
+
{
|
|
39162
|
+
name: "einvoice_config_identifier_create",
|
|
39163
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39164
|
+
requires: ["createIdentifier"],
|
|
39165
|
+
description: "Add a new identifier to a business entity. Identifiers are how entities are found in the directory and how invoices are routed. Common schemes: '0009' (SIRET), '0002' (SIREN), '0225' (SIREN_SIRET). After creating, register the identifier on a network (DOMESTIC_FR) for invoice routing.",
|
|
39166
|
+
category: "config",
|
|
39167
|
+
inputSchema: {
|
|
39168
|
+
type: "object",
|
|
39169
|
+
properties: {
|
|
39170
|
+
entity_id: {
|
|
39171
|
+
type: "string",
|
|
39172
|
+
description: "Business entity ID to add the identifier to"
|
|
39173
|
+
},
|
|
39174
|
+
scheme: {
|
|
39175
|
+
type: "string",
|
|
39176
|
+
description: "Identifier scheme: '0009' (SIRET), '0002' (SIREN), '0225' (SIREN_SIRET)"
|
|
39177
|
+
},
|
|
39178
|
+
value: {
|
|
39179
|
+
type: "string",
|
|
39180
|
+
description: "Identifier value (e.g. SIRET number)"
|
|
39181
|
+
},
|
|
39182
|
+
type: {
|
|
39183
|
+
type: "string",
|
|
39184
|
+
description: "Identifier type",
|
|
39185
|
+
enum: ["ROUTING_CODE", "SUFFIX"]
|
|
39186
|
+
}
|
|
39187
|
+
},
|
|
39188
|
+
required: ["entity_id", "scheme", "value", "type"]
|
|
39189
|
+
},
|
|
39190
|
+
handler: async (input, ctx) => {
|
|
39191
|
+
if (!input.entity_id || !input.scheme || !input.value || !input.type) {
|
|
39192
|
+
throw new Error(
|
|
39193
|
+
"[einvoice_config_identifier_create] 'entity_id', 'scheme', 'value', and 'type' are required"
|
|
39194
|
+
);
|
|
39195
|
+
}
|
|
39196
|
+
return await ctx.adapter.createIdentifier(input.entity_id, {
|
|
39197
|
+
scheme: input.scheme,
|
|
39198
|
+
value: input.value,
|
|
39199
|
+
type: input.type
|
|
39200
|
+
});
|
|
39201
|
+
}
|
|
39202
|
+
},
|
|
39203
|
+
// ── Create Identifier by Scheme/Value ──────────────────
|
|
39204
|
+
{
|
|
39205
|
+
name: "einvoice_config_identifier_create_by_scheme",
|
|
39206
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39207
|
+
requires: ["createIdentifierByScheme"],
|
|
39208
|
+
description: "Add a new identifier to a business entity, looking up the entity by an existing identifier. Shortcut when you know a SIRET/SIREN but not the entity UUID.",
|
|
39209
|
+
category: "config",
|
|
39210
|
+
inputSchema: {
|
|
39211
|
+
type: "object",
|
|
39212
|
+
properties: {
|
|
39213
|
+
lookup_scheme: {
|
|
39214
|
+
type: "string",
|
|
39215
|
+
description: "Scheme to find the entity (e.g. '0009' for SIRET)"
|
|
39216
|
+
},
|
|
39217
|
+
lookup_value: {
|
|
39218
|
+
type: "string",
|
|
39219
|
+
description: "Value to find the entity (e.g. SIRET number)"
|
|
39220
|
+
},
|
|
39221
|
+
new_scheme: {
|
|
39222
|
+
type: "string",
|
|
39223
|
+
description: "Scheme of the new identifier to add"
|
|
39224
|
+
},
|
|
39225
|
+
new_value: {
|
|
39226
|
+
type: "string",
|
|
39227
|
+
description: "Value of the new identifier to add"
|
|
39228
|
+
}
|
|
39229
|
+
},
|
|
39230
|
+
required: ["lookup_scheme", "lookup_value", "new_scheme", "new_value"]
|
|
39231
|
+
},
|
|
39232
|
+
handler: async (input, ctx) => {
|
|
39233
|
+
if (!input.lookup_scheme || !input.lookup_value || !input.new_scheme || !input.new_value) {
|
|
39234
|
+
throw new Error(
|
|
39235
|
+
"[einvoice_config_identifier_create_by_scheme] all fields are required"
|
|
39236
|
+
);
|
|
39237
|
+
}
|
|
39238
|
+
return await ctx.adapter.createIdentifierByScheme(
|
|
39239
|
+
input.lookup_scheme,
|
|
39240
|
+
input.lookup_value,
|
|
39241
|
+
{
|
|
39242
|
+
scheme: input.new_scheme,
|
|
39243
|
+
value: input.new_value
|
|
39244
|
+
}
|
|
39245
|
+
);
|
|
39246
|
+
}
|
|
39247
|
+
},
|
|
39248
|
+
// ── Delete Identifier ──────────────────────────────────
|
|
39249
|
+
{
|
|
39250
|
+
name: "einvoice_config_identifier_delete",
|
|
39251
|
+
requires: ["deleteIdentifier"],
|
|
39252
|
+
description: "Remove an identifier from a business entity. WARNING: if the identifier is registered on a network, unregister it first. Use the identifier UUID from the entity's identifiers array (einvoice_config_entity_get).",
|
|
39253
|
+
category: "config",
|
|
39254
|
+
inputSchema: {
|
|
39255
|
+
type: "object",
|
|
39256
|
+
properties: {
|
|
39257
|
+
identifier_id: {
|
|
39258
|
+
type: "string",
|
|
39259
|
+
description: "Identifier UUID to delete"
|
|
39260
|
+
}
|
|
39261
|
+
},
|
|
39262
|
+
required: ["identifier_id"]
|
|
39263
|
+
},
|
|
39264
|
+
handler: async (input, ctx) => {
|
|
39265
|
+
if (!input.identifier_id) {
|
|
39266
|
+
throw new Error(
|
|
39267
|
+
"[einvoice_config_identifier_delete] 'identifier_id' is required"
|
|
39268
|
+
);
|
|
39269
|
+
}
|
|
39270
|
+
return await ctx.adapter.deleteIdentifier(input.identifier_id);
|
|
39271
|
+
}
|
|
39272
|
+
},
|
|
39273
|
+
// ── Configure Business Entity ──────────────────────────
|
|
39274
|
+
{
|
|
39275
|
+
name: "einvoice_config_entity_configure",
|
|
39276
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39277
|
+
requires: ["configureBusinessEntity"],
|
|
39278
|
+
description: "Configure a business entity's settings (e.g. VAT regime). Use einvoice_config_entity_get to see current configuration first.",
|
|
39279
|
+
category: "config",
|
|
39280
|
+
inputSchema: {
|
|
39281
|
+
type: "object",
|
|
39282
|
+
properties: {
|
|
39283
|
+
entity_id: { type: "string", description: "Business entity ID" },
|
|
39284
|
+
vat_regime: {
|
|
39285
|
+
type: "string",
|
|
39286
|
+
description: "VAT regime: REAL_MONTHLY_TAX_REGIME, REAL_QUARTERLY_TAX_REGIME, SIMPLIFIED_TAX_REGIME, or VAT_EXEMPTION_REGIME",
|
|
39287
|
+
enum: [
|
|
39288
|
+
"REAL_MONTHLY_TAX_REGIME",
|
|
39289
|
+
"REAL_QUARTERLY_TAX_REGIME",
|
|
39290
|
+
"SIMPLIFIED_TAX_REGIME",
|
|
39291
|
+
"VAT_EXEMPTION_REGIME"
|
|
39292
|
+
]
|
|
39293
|
+
}
|
|
39294
|
+
},
|
|
39295
|
+
required: ["entity_id"]
|
|
39296
|
+
},
|
|
39297
|
+
handler: async (input, ctx) => {
|
|
39298
|
+
if (!input.entity_id) {
|
|
39299
|
+
throw new Error(
|
|
39300
|
+
"[einvoice_config_entity_configure] 'entity_id' is required"
|
|
39301
|
+
);
|
|
39302
|
+
}
|
|
39303
|
+
const config2 = {};
|
|
39304
|
+
if (input.vat_regime) config2.vatRegime = input.vat_regime;
|
|
39305
|
+
return await ctx.adapter.configureBusinessEntity(
|
|
39306
|
+
input.entity_id,
|
|
39307
|
+
config2
|
|
39308
|
+
);
|
|
39309
|
+
}
|
|
39310
|
+
},
|
|
39311
|
+
// ── Delete Claim ───────────────────────────────────────
|
|
39312
|
+
{
|
|
39313
|
+
name: "einvoice_config_claim_delete",
|
|
39314
|
+
requires: ["deleteClaim"],
|
|
39315
|
+
description: "Remove your operator's claim on a business entity. The entity remains in the national directory but is no longer managed by your operator. WARNING: after removing the claim, you lose management of this entity. Use einvoice_config_entity_claim to reclaim it later if needed.",
|
|
39316
|
+
category: "config",
|
|
39317
|
+
inputSchema: {
|
|
39318
|
+
type: "object",
|
|
39319
|
+
properties: {
|
|
39320
|
+
entity_id: {
|
|
39321
|
+
type: "string",
|
|
39322
|
+
description: "Business entity ID to unclaim"
|
|
39323
|
+
}
|
|
39324
|
+
},
|
|
39325
|
+
required: ["entity_id"]
|
|
39326
|
+
},
|
|
39327
|
+
handler: async (input, ctx) => {
|
|
39328
|
+
if (!input.entity_id) {
|
|
39329
|
+
throw new Error(
|
|
39330
|
+
"[einvoice_config_claim_delete] 'entity_id' is required"
|
|
39331
|
+
);
|
|
39332
|
+
}
|
|
39333
|
+
return await ctx.adapter.deleteClaim(input.entity_id);
|
|
39334
|
+
}
|
|
39335
|
+
},
|
|
39336
|
+
// ── Unregister from Network ──────────────────────────
|
|
39337
|
+
{
|
|
39338
|
+
name: "einvoice_config_network_unregister",
|
|
39339
|
+
requires: ["unregisterNetwork"],
|
|
39340
|
+
description: "Unregister an entity from a network. Removes the directory entry. After unregistration, the entity will no longer receive invoices on that network. Use the directoryId from the entity's networksRegistered array.",
|
|
39341
|
+
category: "config",
|
|
39342
|
+
inputSchema: {
|
|
39343
|
+
type: "object",
|
|
39344
|
+
properties: {
|
|
39345
|
+
directory_id: {
|
|
39346
|
+
type: "string",
|
|
39347
|
+
description: "Directory entry ID (UUID from the networksRegistered array in entity identifiers)"
|
|
39348
|
+
}
|
|
39349
|
+
},
|
|
39350
|
+
required: ["directory_id"]
|
|
39351
|
+
},
|
|
39352
|
+
handler: async (input, ctx) => {
|
|
39353
|
+
if (!input.directory_id) {
|
|
39354
|
+
throw new Error(
|
|
39355
|
+
"[einvoice_config_network_unregister] 'directory_id' is required"
|
|
39356
|
+
);
|
|
39357
|
+
}
|
|
39358
|
+
return await ctx.adapter.unregisterNetwork(input.directory_id);
|
|
39359
|
+
}
|
|
39360
|
+
}
|
|
39361
|
+
];
|
|
39362
|
+
|
|
39363
|
+
// src/tools/mod.ts
|
|
39364
|
+
var toolsByCategory = {
|
|
39365
|
+
invoice: invoiceTools,
|
|
39366
|
+
directory: directoryTools,
|
|
39367
|
+
status: statusTools,
|
|
39368
|
+
reporting: reportingTools,
|
|
39369
|
+
webhook: webhookTools,
|
|
39370
|
+
config: configTools
|
|
39371
|
+
};
|
|
39372
|
+
var allTools = [
|
|
39373
|
+
...invoiceTools,
|
|
39374
|
+
...directoryTools,
|
|
39375
|
+
...statusTools,
|
|
39376
|
+
...reportingTools,
|
|
39377
|
+
...webhookTools,
|
|
39378
|
+
...configTools
|
|
39379
|
+
];
|
|
39380
|
+
function getToolsByCategory(category) {
|
|
39381
|
+
return toolsByCategory[category] ?? [];
|
|
39382
|
+
}
|
|
39383
|
+
|
|
39384
|
+
// src/client.ts
|
|
39385
|
+
var EInvoiceToolsClient = class {
|
|
39386
|
+
tools;
|
|
39387
|
+
constructor(options) {
|
|
38755
39388
|
if (options?.categories) {
|
|
38756
39389
|
this.tools = options.categories.flatMap((cat) => getToolsByCategory(cat));
|
|
38757
39390
|
} else {
|
|
38758
39391
|
this.tools = allTools;
|
|
38759
39392
|
}
|
|
38760
39393
|
}
|
|
38761
|
-
/** List available tools */
|
|
38762
|
-
listTools() {
|
|
38763
|
-
return this.tools;
|
|
39394
|
+
/** List available tools */
|
|
39395
|
+
listTools() {
|
|
39396
|
+
return this.tools;
|
|
39397
|
+
}
|
|
39398
|
+
/** Filter tools to only those supported by the given adapter's capabilities. */
|
|
39399
|
+
supportedTools(adapter) {
|
|
39400
|
+
return this.tools.filter(
|
|
39401
|
+
(t) => !t.requires || t.requires.every((m) => adapter.capabilities.has(m))
|
|
39402
|
+
);
|
|
39403
|
+
}
|
|
39404
|
+
/** Convert tools to MCP wire format, filtered by adapter capabilities. */
|
|
39405
|
+
toMCPFormat(adapter) {
|
|
39406
|
+
return this.supportedTools(adapter).map((t) => {
|
|
39407
|
+
const wire = {
|
|
39408
|
+
name: t.name,
|
|
39409
|
+
description: t.description,
|
|
39410
|
+
inputSchema: t.inputSchema
|
|
39411
|
+
};
|
|
39412
|
+
if (t._meta) wire._meta = t._meta;
|
|
39413
|
+
if (t.annotations) wire.annotations = t.annotations;
|
|
39414
|
+
return wire;
|
|
39415
|
+
});
|
|
39416
|
+
}
|
|
39417
|
+
/**
|
|
39418
|
+
* Build a handlers Map for ConcurrentMCPServer.registerTools().
|
|
39419
|
+
* Each handler wraps the tool to inject the adapter context.
|
|
39420
|
+
* Only includes tools supported by the adapter's capabilities.
|
|
39421
|
+
*/
|
|
39422
|
+
buildHandlersMap(adapter) {
|
|
39423
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
39424
|
+
for (const tool of this.supportedTools(adapter)) {
|
|
39425
|
+
handlers.set(tool.name, async (args) => {
|
|
39426
|
+
const t0 = Date.now();
|
|
39427
|
+
try {
|
|
39428
|
+
const result = await tool.handler(args, { adapter });
|
|
39429
|
+
console.error(
|
|
39430
|
+
`[mcp-einvoice] ${tool.name} ok (${Date.now() - t0}ms)`
|
|
39431
|
+
);
|
|
39432
|
+
return result;
|
|
39433
|
+
} catch (err) {
|
|
39434
|
+
console.error(
|
|
39435
|
+
`[mcp-einvoice] ${tool.name} failed (${Date.now() - t0}ms):`,
|
|
39436
|
+
err.message?.slice(0, 200)
|
|
39437
|
+
);
|
|
39438
|
+
throw err;
|
|
39439
|
+
}
|
|
39440
|
+
});
|
|
39441
|
+
}
|
|
39442
|
+
return handlers;
|
|
39443
|
+
}
|
|
39444
|
+
/** Execute a tool by name */
|
|
39445
|
+
async execute(name, args, adapter) {
|
|
39446
|
+
const tool = this.tools.find((t) => t.name === name);
|
|
39447
|
+
if (!tool) {
|
|
39448
|
+
throw new Error(
|
|
39449
|
+
`[EInvoiceToolsClient] Unknown tool: "${name}". Available: ${this.tools.map((t) => t.name).join(", ")}`
|
|
39450
|
+
);
|
|
39451
|
+
}
|
|
39452
|
+
return await tool.handler(args, { adapter });
|
|
39453
|
+
}
|
|
39454
|
+
/** Get tool count */
|
|
39455
|
+
get count() {
|
|
39456
|
+
return this.tools.length;
|
|
39457
|
+
}
|
|
39458
|
+
};
|
|
39459
|
+
|
|
39460
|
+
// src/adapters/base-adapter.ts
|
|
39461
|
+
var BaseAdapter = class {
|
|
39462
|
+
notSupported(method, reason = "Not implemented by this adapter.") {
|
|
39463
|
+
return Promise.reject(new NotSupportedError(this.name, method, reason));
|
|
39464
|
+
}
|
|
39465
|
+
// ─── Invoice ───────────────────────────────────────────
|
|
39466
|
+
emitInvoice(_req) {
|
|
39467
|
+
return this.notSupported("emitInvoice");
|
|
39468
|
+
}
|
|
39469
|
+
searchInvoices(_filters) {
|
|
39470
|
+
return this.notSupported("searchInvoices");
|
|
39471
|
+
}
|
|
39472
|
+
getInvoice(_id) {
|
|
39473
|
+
return this.notSupported("getInvoice");
|
|
39474
|
+
}
|
|
39475
|
+
downloadInvoice(_id) {
|
|
39476
|
+
return this.notSupported("downloadInvoice");
|
|
39477
|
+
}
|
|
39478
|
+
downloadReadable(_id) {
|
|
39479
|
+
return this.notSupported("downloadReadable");
|
|
39480
|
+
}
|
|
39481
|
+
getInvoiceFiles(_id) {
|
|
39482
|
+
return this.notSupported("getInvoiceFiles");
|
|
39483
|
+
}
|
|
39484
|
+
getAttachments(_id) {
|
|
39485
|
+
return this.notSupported("getAttachments");
|
|
39486
|
+
}
|
|
39487
|
+
downloadFile(_fileId) {
|
|
39488
|
+
return this.notSupported("downloadFile");
|
|
39489
|
+
}
|
|
39490
|
+
markInvoiceSeen(_id) {
|
|
39491
|
+
return this.notSupported("markInvoiceSeen");
|
|
39492
|
+
}
|
|
39493
|
+
getUnseenInvoices(_pagination) {
|
|
39494
|
+
return this.notSupported("getUnseenInvoices");
|
|
39495
|
+
}
|
|
39496
|
+
generateCII(_req) {
|
|
39497
|
+
return this.notSupported("generateCII");
|
|
39498
|
+
}
|
|
39499
|
+
generateUBL(_req) {
|
|
39500
|
+
return this.notSupported("generateUBL");
|
|
39501
|
+
}
|
|
39502
|
+
generateFacturX(_req) {
|
|
39503
|
+
return this.notSupported("generateFacturX");
|
|
39504
|
+
}
|
|
39505
|
+
// ─── Directory ────────────────────────────────────────
|
|
39506
|
+
searchDirectoryFr(_filters) {
|
|
39507
|
+
return this.notSupported("searchDirectoryFr");
|
|
39508
|
+
}
|
|
39509
|
+
searchDirectoryInt(_filters) {
|
|
39510
|
+
return this.notSupported("searchDirectoryInt");
|
|
39511
|
+
}
|
|
39512
|
+
checkPeppolParticipant(_scheme, _value) {
|
|
39513
|
+
return this.notSupported("checkPeppolParticipant");
|
|
39514
|
+
}
|
|
39515
|
+
// ─── Status ────────────────────────────────────────────
|
|
39516
|
+
sendStatus(_req) {
|
|
39517
|
+
return this.notSupported("sendStatus");
|
|
39518
|
+
}
|
|
39519
|
+
getStatusHistory(_invoiceId) {
|
|
39520
|
+
return this.notSupported("getStatusHistory");
|
|
39521
|
+
}
|
|
39522
|
+
getUnseenStatuses(_pagination) {
|
|
39523
|
+
return this.notSupported("getUnseenStatuses");
|
|
39524
|
+
}
|
|
39525
|
+
markStatusSeen(_statusId) {
|
|
39526
|
+
return this.notSupported("markStatusSeen");
|
|
39527
|
+
}
|
|
39528
|
+
// ─── Reporting ─────────────────────────────────────────
|
|
39529
|
+
reportInvoiceTransaction(_transaction) {
|
|
39530
|
+
return this.notSupported("reportInvoiceTransaction");
|
|
39531
|
+
}
|
|
39532
|
+
reportTransaction(_businessEntityId, _transaction) {
|
|
39533
|
+
return this.notSupported("reportTransaction");
|
|
39534
|
+
}
|
|
39535
|
+
// ─── Webhooks ──────────────────────────────────────────
|
|
39536
|
+
listWebhooks() {
|
|
39537
|
+
return this.notSupported("listWebhooks");
|
|
39538
|
+
}
|
|
39539
|
+
getWebhook(_id) {
|
|
39540
|
+
return this.notSupported("getWebhook");
|
|
39541
|
+
}
|
|
39542
|
+
createWebhook(_req) {
|
|
39543
|
+
return this.notSupported("createWebhook");
|
|
39544
|
+
}
|
|
39545
|
+
updateWebhook(_id, _req) {
|
|
39546
|
+
return this.notSupported("updateWebhook");
|
|
39547
|
+
}
|
|
39548
|
+
deleteWebhook(_id) {
|
|
39549
|
+
return this.notSupported("deleteWebhook");
|
|
39550
|
+
}
|
|
39551
|
+
// ─── Operator Config ───────────────────────────────────
|
|
39552
|
+
getCustomerId() {
|
|
39553
|
+
return this.notSupported("getCustomerId");
|
|
39554
|
+
}
|
|
39555
|
+
listBusinessEntities() {
|
|
39556
|
+
return this.notSupported("listBusinessEntities");
|
|
39557
|
+
}
|
|
39558
|
+
getBusinessEntity(_id) {
|
|
39559
|
+
return this.notSupported("getBusinessEntity");
|
|
39560
|
+
}
|
|
39561
|
+
createLegalUnit(_data) {
|
|
39562
|
+
return this.notSupported("createLegalUnit");
|
|
39563
|
+
}
|
|
39564
|
+
createOffice(_data) {
|
|
39565
|
+
return this.notSupported("createOffice");
|
|
39566
|
+
}
|
|
39567
|
+
deleteBusinessEntity(_id) {
|
|
39568
|
+
return this.notSupported("deleteBusinessEntity");
|
|
39569
|
+
}
|
|
39570
|
+
configureBusinessEntity(_id, _data) {
|
|
39571
|
+
return this.notSupported("configureBusinessEntity");
|
|
39572
|
+
}
|
|
39573
|
+
claimBusinessEntity(_id, _data) {
|
|
39574
|
+
return this.notSupported("claimBusinessEntity");
|
|
39575
|
+
}
|
|
39576
|
+
claimBusinessEntityByIdentifier(_scheme, _value, _data) {
|
|
39577
|
+
return this.notSupported("claimBusinessEntityByIdentifier");
|
|
39578
|
+
}
|
|
39579
|
+
enrollFrench(_data) {
|
|
39580
|
+
return this.notSupported("enrollFrench");
|
|
39581
|
+
}
|
|
39582
|
+
enrollInternational(_data) {
|
|
39583
|
+
return this.notSupported("enrollInternational");
|
|
39584
|
+
}
|
|
39585
|
+
registerNetwork(_identifierId, _network) {
|
|
39586
|
+
return this.notSupported("registerNetwork");
|
|
39587
|
+
}
|
|
39588
|
+
registerNetworkByScheme(_scheme, _value, _network) {
|
|
39589
|
+
return this.notSupported("registerNetworkByScheme");
|
|
39590
|
+
}
|
|
39591
|
+
unregisterNetwork(_directoryId) {
|
|
39592
|
+
return this.notSupported("unregisterNetwork");
|
|
39593
|
+
}
|
|
39594
|
+
// ─── Identifier Management ─────────────────────────────
|
|
39595
|
+
createIdentifier(_entityId, _data) {
|
|
39596
|
+
return this.notSupported("createIdentifier");
|
|
39597
|
+
}
|
|
39598
|
+
createIdentifierByScheme(_scheme, _value, _data) {
|
|
39599
|
+
return this.notSupported("createIdentifierByScheme");
|
|
39600
|
+
}
|
|
39601
|
+
deleteIdentifier(_identifierId) {
|
|
39602
|
+
return this.notSupported("deleteIdentifier");
|
|
39603
|
+
}
|
|
39604
|
+
// ─── Claim Management ──────────────────────────────────
|
|
39605
|
+
deleteClaim(_entityId) {
|
|
39606
|
+
return this.notSupported("deleteClaim");
|
|
39607
|
+
}
|
|
39608
|
+
};
|
|
39609
|
+
|
|
39610
|
+
// src/adapters/shared/oauth2.ts
|
|
39611
|
+
function createOAuth2TokenProvider(config2) {
|
|
39612
|
+
let cachedToken;
|
|
39613
|
+
let expiresAt = 0;
|
|
39614
|
+
let inflight;
|
|
39615
|
+
const REFRESH_MARGIN_MS = 6e4;
|
|
39616
|
+
async function fetchToken() {
|
|
39617
|
+
const body = new URLSearchParams({
|
|
39618
|
+
grant_type: "client_credentials",
|
|
39619
|
+
client_id: config2.clientId,
|
|
39620
|
+
client_secret: config2.clientSecret
|
|
39621
|
+
});
|
|
39622
|
+
const response = await fetch(config2.authUrl, {
|
|
39623
|
+
method: "POST",
|
|
39624
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
39625
|
+
body: body.toString(),
|
|
39626
|
+
signal: AbortSignal.timeout(15e3)
|
|
39627
|
+
});
|
|
39628
|
+
if (!response.ok) {
|
|
39629
|
+
const text = await response.text();
|
|
39630
|
+
throw new Error(
|
|
39631
|
+
`[OAuth2] Token request failed: ${response.status} \u2014 ${text.slice(0, 500)}`
|
|
39632
|
+
);
|
|
39633
|
+
}
|
|
39634
|
+
const data = await response.json();
|
|
39635
|
+
if (!data.access_token) {
|
|
39636
|
+
throw new Error("[OAuth2] Token response missing access_token");
|
|
39637
|
+
}
|
|
39638
|
+
cachedToken = data.access_token;
|
|
39639
|
+
expiresAt = Date.now() + (data.expires_in ?? 600) * 1e3 - REFRESH_MARGIN_MS;
|
|
39640
|
+
return cachedToken;
|
|
39641
|
+
}
|
|
39642
|
+
return async () => {
|
|
39643
|
+
if (cachedToken && Date.now() < expiresAt) {
|
|
39644
|
+
return cachedToken;
|
|
39645
|
+
}
|
|
39646
|
+
if (!inflight) {
|
|
39647
|
+
inflight = fetchToken().finally(() => {
|
|
39648
|
+
inflight = void 0;
|
|
39649
|
+
});
|
|
39650
|
+
}
|
|
39651
|
+
return inflight;
|
|
39652
|
+
};
|
|
39653
|
+
}
|
|
39654
|
+
|
|
39655
|
+
// src/adapters/iopole/client.ts
|
|
39656
|
+
var IopoleAPIError = class extends Error {
|
|
39657
|
+
constructor(message2, status, body) {
|
|
39658
|
+
super(message2);
|
|
39659
|
+
this.status = status;
|
|
39660
|
+
this.body = body;
|
|
39661
|
+
this.name = "IopoleAPIError";
|
|
39662
|
+
}
|
|
39663
|
+
};
|
|
39664
|
+
var IopoleClient = class {
|
|
39665
|
+
config;
|
|
39666
|
+
constructor(config2) {
|
|
39667
|
+
this.config = config2;
|
|
39668
|
+
}
|
|
39669
|
+
// ─── Generic Request ────────────────────────────────────
|
|
39670
|
+
async request(method, path, options) {
|
|
39671
|
+
return this.requestWithBase(this.config.baseUrl, method, path, options);
|
|
39672
|
+
}
|
|
39673
|
+
async requestWithBase(baseUrl, method, path, options) {
|
|
39674
|
+
const url2 = new URL(`${baseUrl}${path}`);
|
|
39675
|
+
if (options?.query) {
|
|
39676
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
39677
|
+
if (value !== void 0) {
|
|
39678
|
+
url2.searchParams.set(key, String(value));
|
|
39679
|
+
}
|
|
39680
|
+
}
|
|
39681
|
+
}
|
|
39682
|
+
const token = await this.config.getToken();
|
|
39683
|
+
const headers = {
|
|
39684
|
+
Authorization: `Bearer ${token}`,
|
|
39685
|
+
"customer-id": this.config.customerId,
|
|
39686
|
+
Accept: "application/json",
|
|
39687
|
+
...options?.headers
|
|
39688
|
+
};
|
|
39689
|
+
if (options?.body) {
|
|
39690
|
+
headers["Content-Type"] = "application/json";
|
|
39691
|
+
}
|
|
39692
|
+
const controller = new AbortController();
|
|
39693
|
+
const timeout = setTimeout(
|
|
39694
|
+
() => controller.abort(),
|
|
39695
|
+
this.config.timeoutMs ?? 3e4
|
|
39696
|
+
);
|
|
39697
|
+
try {
|
|
39698
|
+
const response = await fetch(url2.toString(), {
|
|
39699
|
+
method,
|
|
39700
|
+
headers,
|
|
39701
|
+
body: options?.body ? JSON.stringify(options.body) : void 0,
|
|
39702
|
+
signal: controller.signal
|
|
39703
|
+
});
|
|
39704
|
+
if (!response.ok) {
|
|
39705
|
+
const body = await response.text();
|
|
39706
|
+
throw new IopoleAPIError(
|
|
39707
|
+
`[IopoleClient] ${method} ${path} \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
39708
|
+
response.status,
|
|
39709
|
+
body
|
|
39710
|
+
);
|
|
39711
|
+
}
|
|
39712
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
39713
|
+
if (contentType.includes("application/json")) {
|
|
39714
|
+
return await response.json();
|
|
39715
|
+
}
|
|
39716
|
+
return await response.text();
|
|
39717
|
+
} finally {
|
|
39718
|
+
clearTimeout(timeout);
|
|
39719
|
+
}
|
|
39720
|
+
}
|
|
39721
|
+
// ─── Convenience Methods ────────────────────────────────
|
|
39722
|
+
async get(path, query) {
|
|
39723
|
+
return this.request("GET", path, { query });
|
|
39724
|
+
}
|
|
39725
|
+
/**
|
|
39726
|
+
* GET request against the v1.1 API endpoint.
|
|
39727
|
+
* Builds a v1.1 URL without mutating this.config.baseUrl,
|
|
39728
|
+
* so concurrent calls (e.g. getV11 + get) cannot interfere.
|
|
39729
|
+
*/
|
|
39730
|
+
async getV11(path, query) {
|
|
39731
|
+
const baseV11 = this.config.baseUrl.replace(/\/v1\b/, "/v1.1");
|
|
39732
|
+
return this.requestWithBase(baseV11, "GET", path, { query });
|
|
39733
|
+
}
|
|
39734
|
+
async post(path, body) {
|
|
39735
|
+
return this.request("POST", path, { body });
|
|
39736
|
+
}
|
|
39737
|
+
async put(path, body) {
|
|
39738
|
+
return this.request("PUT", path, { body });
|
|
39739
|
+
}
|
|
39740
|
+
async delete(path) {
|
|
39741
|
+
return this.request("DELETE", path);
|
|
39742
|
+
}
|
|
39743
|
+
/**
|
|
39744
|
+
* Upload a file via multipart/form-data.
|
|
39745
|
+
* Used for POST /invoice (emitInvoice) — the Swagger spec requires
|
|
39746
|
+
* Content-Type: multipart/form-data with a `file` field (binary, PDF or XML).
|
|
39747
|
+
*/
|
|
39748
|
+
async upload(path, file2, filename) {
|
|
39749
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
39750
|
+
const token = await this.config.getToken();
|
|
39751
|
+
const controller = new AbortController();
|
|
39752
|
+
const timeout = setTimeout(
|
|
39753
|
+
() => controller.abort(),
|
|
39754
|
+
this.config.timeoutMs ?? 3e4
|
|
39755
|
+
);
|
|
39756
|
+
try {
|
|
39757
|
+
const form = new FormData();
|
|
39758
|
+
form.append("file", new Blob([file2]), filename);
|
|
39759
|
+
const response = await fetch(url2, {
|
|
39760
|
+
method: "POST",
|
|
39761
|
+
headers: {
|
|
39762
|
+
Authorization: `Bearer ${token}`,
|
|
39763
|
+
"customer-id": this.config.customerId,
|
|
39764
|
+
Accept: "application/json"
|
|
39765
|
+
// Do NOT set Content-Type — fetch sets it with the multipart boundary
|
|
39766
|
+
},
|
|
39767
|
+
body: form,
|
|
39768
|
+
signal: controller.signal
|
|
39769
|
+
});
|
|
39770
|
+
if (!response.ok) {
|
|
39771
|
+
const body = await response.text();
|
|
39772
|
+
throw new IopoleAPIError(
|
|
39773
|
+
`[IopoleClient] POST ${path} (upload) \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
39774
|
+
response.status,
|
|
39775
|
+
body
|
|
39776
|
+
);
|
|
39777
|
+
}
|
|
39778
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
39779
|
+
if (contentType.includes("application/json")) {
|
|
39780
|
+
return await response.json();
|
|
39781
|
+
}
|
|
39782
|
+
return await response.text();
|
|
39783
|
+
} finally {
|
|
39784
|
+
clearTimeout(timeout);
|
|
39785
|
+
}
|
|
39786
|
+
}
|
|
39787
|
+
/**
|
|
39788
|
+
* POST with query parameters.
|
|
39789
|
+
* Used for /tools/{cii,ubl}/generate which return text (XML).
|
|
39790
|
+
*/
|
|
39791
|
+
async postWithQuery(path, body, query) {
|
|
39792
|
+
return this.request("POST", path, { body, query });
|
|
39793
|
+
}
|
|
39794
|
+
/**
|
|
39795
|
+
* POST with query parameters, returning raw binary.
|
|
39796
|
+
* Used for /tools/facturx/generate which returns a PDF (binary).
|
|
39797
|
+
* Using request() would corrupt binary data by treating it as text.
|
|
39798
|
+
*/
|
|
39799
|
+
async postBinary(path, body, query) {
|
|
39800
|
+
const url2 = new URL(`${this.config.baseUrl}${path}`);
|
|
39801
|
+
for (const [key, value] of Object.entries(query)) {
|
|
39802
|
+
if (value !== void 0) url2.searchParams.set(key, String(value));
|
|
39803
|
+
}
|
|
39804
|
+
const token = await this.config.getToken();
|
|
39805
|
+
const controller = new AbortController();
|
|
39806
|
+
const timeout = setTimeout(
|
|
39807
|
+
() => controller.abort(),
|
|
39808
|
+
this.config.timeoutMs ?? 6e4
|
|
39809
|
+
);
|
|
39810
|
+
try {
|
|
39811
|
+
const response = await fetch(url2.toString(), {
|
|
39812
|
+
method: "POST",
|
|
39813
|
+
headers: {
|
|
39814
|
+
Authorization: `Bearer ${token}`,
|
|
39815
|
+
"customer-id": this.config.customerId,
|
|
39816
|
+
"Content-Type": "application/json",
|
|
39817
|
+
Accept: "*/*"
|
|
39818
|
+
},
|
|
39819
|
+
body: JSON.stringify(body),
|
|
39820
|
+
signal: controller.signal
|
|
39821
|
+
});
|
|
39822
|
+
if (!response.ok) {
|
|
39823
|
+
const errBody = await response.text();
|
|
39824
|
+
throw new IopoleAPIError(
|
|
39825
|
+
`[IopoleClient] POST ${path} \u2192 ${response.status}: ${errBody.slice(0, 500)}`,
|
|
39826
|
+
response.status,
|
|
39827
|
+
errBody
|
|
39828
|
+
);
|
|
39829
|
+
}
|
|
39830
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
39831
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
39832
|
+
return { data, contentType };
|
|
39833
|
+
} finally {
|
|
39834
|
+
clearTimeout(timeout);
|
|
39835
|
+
}
|
|
39836
|
+
}
|
|
39837
|
+
/**
|
|
39838
|
+
* Download a binary resource (invoice PDF, attachment, etc.)
|
|
39839
|
+
* Returns the raw Response for streaming.
|
|
39840
|
+
*/
|
|
39841
|
+
async download(path) {
|
|
39842
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
39843
|
+
const token = await this.config.getToken();
|
|
39844
|
+
const controller = new AbortController();
|
|
39845
|
+
const timeout = setTimeout(
|
|
39846
|
+
() => controller.abort(),
|
|
39847
|
+
this.config.timeoutMs ?? 3e4
|
|
39848
|
+
);
|
|
39849
|
+
try {
|
|
39850
|
+
const response = await fetch(url2, {
|
|
39851
|
+
method: "GET",
|
|
39852
|
+
headers: {
|
|
39853
|
+
Authorization: `Bearer ${token}`,
|
|
39854
|
+
"customer-id": this.config.customerId
|
|
39855
|
+
},
|
|
39856
|
+
signal: controller.signal
|
|
39857
|
+
});
|
|
39858
|
+
if (!response.ok) {
|
|
39859
|
+
const body = await response.text();
|
|
39860
|
+
throw new IopoleAPIError(
|
|
39861
|
+
`[IopoleClient] GET ${path} \u2192 ${response.status}`,
|
|
39862
|
+
response.status,
|
|
39863
|
+
body
|
|
39864
|
+
);
|
|
39865
|
+
}
|
|
39866
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
39867
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
39868
|
+
return { data, contentType };
|
|
39869
|
+
} finally {
|
|
39870
|
+
clearTimeout(timeout);
|
|
39871
|
+
}
|
|
39872
|
+
}
|
|
39873
|
+
};
|
|
39874
|
+
|
|
39875
|
+
// src/runtime.ts
|
|
39876
|
+
import { statSync as fsStatSync } from "node:fs";
|
|
39877
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
39878
|
+
function env2(key) {
|
|
39879
|
+
return process.env[key];
|
|
39880
|
+
}
|
|
39881
|
+
async function readTextFile2(path) {
|
|
39882
|
+
return await readFile2(path, "utf-8");
|
|
39883
|
+
}
|
|
39884
|
+
function statSync(path) {
|
|
39885
|
+
try {
|
|
39886
|
+
fsStatSync(path);
|
|
39887
|
+
return true;
|
|
39888
|
+
} catch {
|
|
39889
|
+
return false;
|
|
39890
|
+
}
|
|
39891
|
+
}
|
|
39892
|
+
function getArgs() {
|
|
39893
|
+
return process.argv.slice(2);
|
|
39894
|
+
}
|
|
39895
|
+
function exit(code) {
|
|
39896
|
+
process.exit(code);
|
|
39897
|
+
}
|
|
39898
|
+
function onSignal(signal, handler) {
|
|
39899
|
+
process.on(signal, handler);
|
|
39900
|
+
}
|
|
39901
|
+
|
|
39902
|
+
// src/adapters/shared/env.ts
|
|
39903
|
+
function requireEnv(adapter, name, hint) {
|
|
39904
|
+
const value = env2(name);
|
|
39905
|
+
if (!value) {
|
|
39906
|
+
throw new Error(`[${adapter}] ${name} is required. ${hint}`);
|
|
39907
|
+
}
|
|
39908
|
+
return value;
|
|
39909
|
+
}
|
|
39910
|
+
|
|
39911
|
+
// src/adapters/shared/direction.ts
|
|
39912
|
+
var RECEIVED = /* @__PURE__ */ new Set(["received", "inbound", "in"]);
|
|
39913
|
+
var SENT = /* @__PURE__ */ new Set(["sent", "emitted", "outbound", "out"]);
|
|
39914
|
+
function normalizeDirection(raw2) {
|
|
39915
|
+
if (!raw2) return void 0;
|
|
39916
|
+
const l = raw2.toLowerCase();
|
|
39917
|
+
if (RECEIVED.has(l)) return "received";
|
|
39918
|
+
if (SENT.has(l)) return "sent";
|
|
39919
|
+
return void 0;
|
|
39920
|
+
}
|
|
39921
|
+
|
|
39922
|
+
// src/adapters/iopole/adapter.ts
|
|
39923
|
+
var IOPOLE_DEFAULT_AUTH_URL = "https://auth.ppd.iopole.fr/realms/iopole/protocol/openid-connect/token";
|
|
39924
|
+
var IopoleAdapter = class extends BaseAdapter {
|
|
39925
|
+
name = "iopole";
|
|
39926
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
39927
|
+
"emitInvoice",
|
|
39928
|
+
"searchInvoices",
|
|
39929
|
+
"getInvoice",
|
|
39930
|
+
"downloadInvoice",
|
|
39931
|
+
"downloadReadable",
|
|
39932
|
+
"getInvoiceFiles",
|
|
39933
|
+
"getAttachments",
|
|
39934
|
+
"downloadFile",
|
|
39935
|
+
"markInvoiceSeen",
|
|
39936
|
+
"getUnseenInvoices",
|
|
39937
|
+
"generateCII",
|
|
39938
|
+
"generateUBL",
|
|
39939
|
+
"generateFacturX",
|
|
39940
|
+
"searchDirectoryFr",
|
|
39941
|
+
"searchDirectoryInt",
|
|
39942
|
+
"checkPeppolParticipant",
|
|
39943
|
+
"sendStatus",
|
|
39944
|
+
"getStatusHistory",
|
|
39945
|
+
"getUnseenStatuses",
|
|
39946
|
+
"markStatusSeen",
|
|
39947
|
+
"reportInvoiceTransaction",
|
|
39948
|
+
"reportTransaction",
|
|
39949
|
+
"listWebhooks",
|
|
39950
|
+
"getWebhook",
|
|
39951
|
+
"createWebhook",
|
|
39952
|
+
"updateWebhook",
|
|
39953
|
+
"deleteWebhook",
|
|
39954
|
+
"getCustomerId",
|
|
39955
|
+
"listBusinessEntities",
|
|
39956
|
+
"getBusinessEntity",
|
|
39957
|
+
"createLegalUnit",
|
|
39958
|
+
"createOffice",
|
|
39959
|
+
"deleteBusinessEntity",
|
|
39960
|
+
"configureBusinessEntity",
|
|
39961
|
+
"claimBusinessEntity",
|
|
39962
|
+
"claimBusinessEntityByIdentifier",
|
|
39963
|
+
"enrollFrench",
|
|
39964
|
+
"enrollInternational",
|
|
39965
|
+
"registerNetwork",
|
|
39966
|
+
"registerNetworkByScheme",
|
|
39967
|
+
"unregisterNetwork",
|
|
39968
|
+
"createIdentifier",
|
|
39969
|
+
"createIdentifierByScheme",
|
|
39970
|
+
"deleteIdentifier",
|
|
39971
|
+
"deleteClaim"
|
|
39972
|
+
]);
|
|
39973
|
+
client;
|
|
39974
|
+
constructor(client) {
|
|
39975
|
+
super();
|
|
39976
|
+
this.client = client;
|
|
39977
|
+
}
|
|
39978
|
+
// ─── Invoice Operations ───────────────────────────────
|
|
39979
|
+
async emitInvoice(req) {
|
|
39980
|
+
return await this.client.upload("/invoice", req.file, req.filename);
|
|
39981
|
+
}
|
|
39982
|
+
async searchInvoices(filters) {
|
|
39983
|
+
const raw2 = await this.client.getV11("/invoice/search", {
|
|
39984
|
+
q: filters.q,
|
|
39985
|
+
expand: filters.expand ?? "businessData",
|
|
39986
|
+
offset: filters.offset ?? 0,
|
|
39987
|
+
limit: filters.limit ?? 50
|
|
39988
|
+
});
|
|
39989
|
+
const data = raw2.data ?? [];
|
|
39990
|
+
const count = raw2.meta?.count ?? raw2.count ?? data.length;
|
|
39991
|
+
const rows = data.map((row) => {
|
|
39992
|
+
const m = row.metadata ?? {};
|
|
39993
|
+
const bd = row.businessData ?? {};
|
|
39994
|
+
return {
|
|
39995
|
+
id: m.invoiceId ?? "",
|
|
39996
|
+
invoiceNumber: bd.invoiceId,
|
|
39997
|
+
status: m.state,
|
|
39998
|
+
// will be enriched below
|
|
39999
|
+
direction: normalizeDirection(m.direction),
|
|
40000
|
+
senderName: bd.seller?.name,
|
|
40001
|
+
receiverName: bd.buyer?.name,
|
|
40002
|
+
date: bd.invoiceDate ?? m.createDate?.split("T")[0],
|
|
40003
|
+
amount: bd.monetary?.invoiceAmount?.amount,
|
|
40004
|
+
currency: bd.monetary?.invoiceCurrency ?? "EUR"
|
|
40005
|
+
};
|
|
40006
|
+
});
|
|
40007
|
+
const CONCURRENCY = 5;
|
|
40008
|
+
for (let i = 0; i < rows.length; i += CONCURRENCY) {
|
|
40009
|
+
const batch = rows.slice(i, i + CONCURRENCY);
|
|
40010
|
+
await Promise.all(batch.map(async (row) => {
|
|
40011
|
+
if (!row.id) return;
|
|
40012
|
+
try {
|
|
40013
|
+
const history = await this.getStatusHistory(row.id);
|
|
40014
|
+
if (history.entries.length > 0) {
|
|
40015
|
+
const sorted = [...history.entries].sort(
|
|
40016
|
+
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
40017
|
+
);
|
|
40018
|
+
row.status = sorted[0].code;
|
|
40019
|
+
}
|
|
40020
|
+
} catch {
|
|
40021
|
+
}
|
|
40022
|
+
}));
|
|
40023
|
+
}
|
|
40024
|
+
return { rows, count };
|
|
40025
|
+
}
|
|
40026
|
+
async getInvoice(id) {
|
|
40027
|
+
const [raw2, history] = await Promise.all([
|
|
40028
|
+
this.client.get(`/invoice/${encodePathSegment(id)}`, {
|
|
40029
|
+
expand: "businessData"
|
|
40030
|
+
}),
|
|
40031
|
+
this.getStatusHistory(id).catch(() => ({ entries: [] }))
|
|
40032
|
+
]);
|
|
40033
|
+
const inv = Array.isArray(raw2) ? raw2[0] : raw2;
|
|
40034
|
+
const latestStatus = history.entries.length > 0 ? [...history.entries].sort(
|
|
40035
|
+
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
40036
|
+
)[0].code : void 0;
|
|
40037
|
+
const bd = inv?.businessData;
|
|
40038
|
+
return {
|
|
40039
|
+
id: inv?.invoiceId ?? id,
|
|
40040
|
+
invoiceNumber: bd?.invoiceId,
|
|
40041
|
+
status: latestStatus ?? inv?.state ?? inv?.status ?? "UNKNOWN",
|
|
40042
|
+
direction: normalizeDirection(inv?.way ?? inv?.metadata?.direction),
|
|
40043
|
+
format: inv?.originalFormat,
|
|
40044
|
+
network: inv?.originalNetwork,
|
|
40045
|
+
invoiceType: bd?.detailedType?.value,
|
|
40046
|
+
senderName: bd?.seller?.name,
|
|
40047
|
+
senderId: bd?.seller?.siret ?? bd?.seller?.siren,
|
|
40048
|
+
senderVat: bd?.seller?.vatNumber,
|
|
40049
|
+
receiverName: bd?.buyer?.name,
|
|
40050
|
+
receiverId: bd?.buyer?.siret ?? bd?.buyer?.siren,
|
|
40051
|
+
receiverVat: bd?.buyer?.vatNumber,
|
|
40052
|
+
issueDate: bd?.invoiceDate,
|
|
40053
|
+
dueDate: bd?.invoiceDueDate,
|
|
40054
|
+
receiptDate: bd?.invoiceReceiptDate,
|
|
40055
|
+
currency: bd?.monetary?.invoiceCurrency ?? "EUR",
|
|
40056
|
+
totalHt: bd?.monetary?.taxBasisTotalAmount?.amount,
|
|
40057
|
+
totalTax: bd?.monetary?.taxTotalAmount?.amount,
|
|
40058
|
+
totalTtc: bd?.monetary?.invoiceAmount?.amount,
|
|
40059
|
+
lines: bd?.lines?.map((l) => {
|
|
40060
|
+
const line = l;
|
|
40061
|
+
return {
|
|
40062
|
+
description: line.item?.name,
|
|
40063
|
+
quantity: line.billedQuantity?.quantity,
|
|
40064
|
+
unitPrice: line.price?.netAmount?.amount,
|
|
40065
|
+
taxRate: line.taxDetail?.percent,
|
|
40066
|
+
amount: line.totalAmount?.amount
|
|
40067
|
+
};
|
|
40068
|
+
}),
|
|
40069
|
+
notes: bd?.notes?.map(
|
|
40070
|
+
(n) => n.content
|
|
40071
|
+
).filter(Boolean)
|
|
40072
|
+
};
|
|
38764
40073
|
}
|
|
38765
|
-
|
|
38766
|
-
|
|
38767
|
-
|
|
38768
|
-
|
|
38769
|
-
|
|
38770
|
-
|
|
38771
|
-
|
|
40074
|
+
async downloadInvoice(id) {
|
|
40075
|
+
return await this.client.download(
|
|
40076
|
+
`/invoice/${encodePathSegment(id)}/download`
|
|
40077
|
+
);
|
|
40078
|
+
}
|
|
40079
|
+
async downloadReadable(id) {
|
|
40080
|
+
return await this.client.download(
|
|
40081
|
+
`/invoice/${encodePathSegment(id)}/download/readable`
|
|
40082
|
+
);
|
|
40083
|
+
}
|
|
40084
|
+
async getInvoiceFiles(id) {
|
|
40085
|
+
return await this.client.get(`/invoice/${encodePathSegment(id)}/files`);
|
|
40086
|
+
}
|
|
40087
|
+
async getAttachments(id) {
|
|
40088
|
+
return await this.client.get(
|
|
40089
|
+
`/invoice/${encodePathSegment(id)}/files/attachments`
|
|
40090
|
+
);
|
|
40091
|
+
}
|
|
40092
|
+
async downloadFile(fileId) {
|
|
40093
|
+
return await this.client.download(
|
|
40094
|
+
`/invoice/file/${encodePathSegment(fileId)}/download`
|
|
40095
|
+
);
|
|
40096
|
+
}
|
|
40097
|
+
async markInvoiceSeen(id) {
|
|
40098
|
+
return await this.client.put(
|
|
40099
|
+
`/invoice/${encodePathSegment(id)}/markAsSeen`
|
|
40100
|
+
);
|
|
40101
|
+
}
|
|
40102
|
+
async getUnseenInvoices(pagination) {
|
|
40103
|
+
return await this.client.get("/invoice/notSeen", {
|
|
40104
|
+
offset: pagination.offset,
|
|
40105
|
+
limit: pagination.limit
|
|
40106
|
+
});
|
|
40107
|
+
}
|
|
40108
|
+
async generateCII(req) {
|
|
40109
|
+
return await this.client.postWithQuery(
|
|
40110
|
+
"/tools/cii/generate",
|
|
40111
|
+
normalizeForIopole(req.invoice),
|
|
40112
|
+
{
|
|
40113
|
+
flavor: req.flavor
|
|
40114
|
+
}
|
|
40115
|
+
);
|
|
40116
|
+
}
|
|
40117
|
+
async generateUBL(req) {
|
|
40118
|
+
return await this.client.postWithQuery(
|
|
40119
|
+
"/tools/ubl/generate",
|
|
40120
|
+
normalizeForIopole(req.invoice),
|
|
40121
|
+
{
|
|
40122
|
+
flavor: req.flavor
|
|
40123
|
+
}
|
|
40124
|
+
);
|
|
40125
|
+
}
|
|
40126
|
+
async generateFacturX(req) {
|
|
40127
|
+
return await this.client.postBinary(
|
|
40128
|
+
"/tools/facturx/generate",
|
|
40129
|
+
normalizeForIopole(req.invoice),
|
|
40130
|
+
{
|
|
40131
|
+
flavor: req.flavor,
|
|
40132
|
+
language: req.language
|
|
40133
|
+
}
|
|
40134
|
+
);
|
|
40135
|
+
}
|
|
40136
|
+
// ─── Directory ────────────────────────────────────────
|
|
40137
|
+
async searchDirectoryFr(filters) {
|
|
40138
|
+
const q = autoWrapDirectoryQuery(filters.q);
|
|
40139
|
+
const raw2 = await this.client.get("/directory/french", {
|
|
40140
|
+
q,
|
|
40141
|
+
offset: filters.offset,
|
|
40142
|
+
limit: filters.limit
|
|
40143
|
+
});
|
|
40144
|
+
const data = raw2.data ?? [];
|
|
40145
|
+
const count = raw2.meta?.count ?? raw2.count ?? data.length;
|
|
40146
|
+
const rows = data.map((row) => {
|
|
40147
|
+
const ci = row.countryIdentifier ?? {};
|
|
40148
|
+
return {
|
|
40149
|
+
entityId: row.businessEntityId ?? "",
|
|
40150
|
+
name: row.name,
|
|
40151
|
+
type: row.type,
|
|
40152
|
+
siren: ci.siren ?? row.siren,
|
|
40153
|
+
siret: ci.siret ?? row.siret,
|
|
40154
|
+
country: ci.country ?? row.country ?? "FR",
|
|
40155
|
+
identifiers: row.identifiers
|
|
38772
40156
|
};
|
|
38773
|
-
if (t._meta) wire._meta = t._meta;
|
|
38774
|
-
return wire;
|
|
38775
40157
|
});
|
|
40158
|
+
return { rows, count };
|
|
38776
40159
|
}
|
|
38777
|
-
|
|
38778
|
-
|
|
38779
|
-
|
|
38780
|
-
|
|
38781
|
-
|
|
38782
|
-
|
|
38783
|
-
for (const tool of this.tools) {
|
|
38784
|
-
handlers.set(tool.name, async (args) => {
|
|
38785
|
-
return await tool.handler(args, { adapter });
|
|
38786
|
-
});
|
|
38787
|
-
}
|
|
38788
|
-
return handlers;
|
|
40160
|
+
async searchDirectoryInt(filters) {
|
|
40161
|
+
return await this.client.get("/directory/international", {
|
|
40162
|
+
value: filters.value,
|
|
40163
|
+
offset: filters.offset,
|
|
40164
|
+
limit: filters.limit
|
|
40165
|
+
});
|
|
38789
40166
|
}
|
|
38790
|
-
|
|
38791
|
-
|
|
38792
|
-
|
|
38793
|
-
|
|
38794
|
-
throw new Error(
|
|
38795
|
-
`[EInvoiceToolsClient] Unknown tool: "${name}". Available: ${this.tools.map((t) => t.name).join(", ")}`
|
|
38796
|
-
);
|
|
38797
|
-
}
|
|
38798
|
-
return await tool.handler(args, { adapter });
|
|
40167
|
+
async checkPeppolParticipant(scheme, value) {
|
|
40168
|
+
return await this.client.get(
|
|
40169
|
+
`/directory/international/check/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}`
|
|
40170
|
+
);
|
|
38799
40171
|
}
|
|
38800
|
-
|
|
38801
|
-
|
|
38802
|
-
return this.
|
|
40172
|
+
// ─── Status ───────────────────────────────────────────
|
|
40173
|
+
async sendStatus(req) {
|
|
40174
|
+
return await this.client.post(
|
|
40175
|
+
`/invoice/${encodePathSegment(req.invoiceId)}/status`,
|
|
40176
|
+
{
|
|
40177
|
+
code: req.code,
|
|
40178
|
+
message: req.message,
|
|
40179
|
+
payment: req.payment
|
|
40180
|
+
}
|
|
40181
|
+
);
|
|
38803
40182
|
}
|
|
38804
|
-
|
|
38805
|
-
|
|
38806
|
-
|
|
38807
|
-
|
|
38808
|
-
|
|
38809
|
-
|
|
38810
|
-
|
|
38811
|
-
|
|
38812
|
-
|
|
38813
|
-
|
|
38814
|
-
grant_type: "client_credentials",
|
|
38815
|
-
client_id: config2.clientId,
|
|
38816
|
-
client_secret: config2.clientSecret
|
|
40183
|
+
async getStatusHistory(invoiceId) {
|
|
40184
|
+
const raw2 = await this.client.get(
|
|
40185
|
+
`/invoice/${encodePathSegment(invoiceId)}/status-history`
|
|
40186
|
+
);
|
|
40187
|
+
return normalizeStatusHistory(raw2);
|
|
40188
|
+
}
|
|
40189
|
+
async getUnseenStatuses(pagination) {
|
|
40190
|
+
return await this.client.get("/invoice/status/notSeen", {
|
|
40191
|
+
offset: pagination.offset,
|
|
40192
|
+
limit: pagination.limit
|
|
38817
40193
|
});
|
|
38818
|
-
|
|
38819
|
-
|
|
38820
|
-
|
|
38821
|
-
|
|
40194
|
+
}
|
|
40195
|
+
async markStatusSeen(statusId) {
|
|
40196
|
+
return await this.client.put(
|
|
40197
|
+
`/invoice/status/${encodePathSegment(statusId)}/markAsSeen`
|
|
40198
|
+
);
|
|
40199
|
+
}
|
|
40200
|
+
// ─── Reporting ────────────────────────────────────────
|
|
40201
|
+
async reportInvoiceTransaction(transaction) {
|
|
40202
|
+
return await this.client.post(
|
|
40203
|
+
"/reporting/fr/invoice/transaction",
|
|
40204
|
+
transaction
|
|
40205
|
+
);
|
|
40206
|
+
}
|
|
40207
|
+
async reportTransaction(businessEntityId, transaction) {
|
|
40208
|
+
return await this.client.post(
|
|
40209
|
+
`/reporting/fr/transaction/${encodePathSegment(businessEntityId)}`,
|
|
40210
|
+
transaction
|
|
40211
|
+
);
|
|
40212
|
+
}
|
|
40213
|
+
// ─── Webhooks ─────────────────────────────────────────
|
|
40214
|
+
async listWebhooks() {
|
|
40215
|
+
return await this.client.get("/config/webhook");
|
|
40216
|
+
}
|
|
40217
|
+
async getWebhook(id) {
|
|
40218
|
+
return await this.client.get(`/config/webhook/${encodePathSegment(id)}`);
|
|
40219
|
+
}
|
|
40220
|
+
async createWebhook(req) {
|
|
40221
|
+
return await this.client.post("/config/webhook", req);
|
|
40222
|
+
}
|
|
40223
|
+
async updateWebhook(id, req) {
|
|
40224
|
+
return await this.client.put(
|
|
40225
|
+
`/config/webhook/${encodePathSegment(id)}`,
|
|
40226
|
+
req
|
|
40227
|
+
);
|
|
40228
|
+
}
|
|
40229
|
+
async deleteWebhook(id) {
|
|
40230
|
+
return await this.client.delete(`/config/webhook/${encodePathSegment(id)}`);
|
|
40231
|
+
}
|
|
40232
|
+
// ─── Operator Config ───────────────────────────────────
|
|
40233
|
+
async getCustomerId() {
|
|
40234
|
+
return await this.client.get("/config/customer/id");
|
|
40235
|
+
}
|
|
40236
|
+
async listBusinessEntities() {
|
|
40237
|
+
const raw2 = await this.client.get("/config/business/entity");
|
|
40238
|
+
const data = raw2.data ?? [];
|
|
40239
|
+
const rows = data.map((row) => {
|
|
40240
|
+
const ci = row.countryIdentifier ?? {};
|
|
40241
|
+
return {
|
|
40242
|
+
entityId: row.businessEntityId ?? "",
|
|
40243
|
+
name: row.name,
|
|
40244
|
+
type: row.type,
|
|
40245
|
+
siren: ci.siren ?? row.siren,
|
|
40246
|
+
siret: ci.siret ?? row.siret,
|
|
40247
|
+
scope: row.scope,
|
|
40248
|
+
country: ci.country ?? row.country ?? "FR"
|
|
40249
|
+
};
|
|
38822
40250
|
});
|
|
38823
|
-
|
|
38824
|
-
|
|
38825
|
-
|
|
38826
|
-
|
|
38827
|
-
)
|
|
40251
|
+
return { rows, count: rows.length };
|
|
40252
|
+
}
|
|
40253
|
+
async getBusinessEntity(id) {
|
|
40254
|
+
return await this.client.get(
|
|
40255
|
+
`/config/business/entity/${encodePathSegment(id)}`
|
|
40256
|
+
);
|
|
40257
|
+
}
|
|
40258
|
+
async createLegalUnit(data) {
|
|
40259
|
+
return await this.client.post("/config/business/entity/legalunit", data);
|
|
40260
|
+
}
|
|
40261
|
+
async createOffice(data) {
|
|
40262
|
+
return await this.client.post("/config/business/entity/office", data);
|
|
40263
|
+
}
|
|
40264
|
+
async deleteBusinessEntity(id) {
|
|
40265
|
+
return await this.client.delete(
|
|
40266
|
+
`/config/business/entity/${encodePathSegment(id)}`
|
|
40267
|
+
);
|
|
40268
|
+
}
|
|
40269
|
+
async configureBusinessEntity(id, data) {
|
|
40270
|
+
return await this.client.post(
|
|
40271
|
+
`/config/business/entity/${encodePathSegment(id)}/configure`,
|
|
40272
|
+
data
|
|
40273
|
+
);
|
|
40274
|
+
}
|
|
40275
|
+
async claimBusinessEntity(id, data) {
|
|
40276
|
+
return await this.client.post(
|
|
40277
|
+
`/config/business/entity/${encodePathSegment(id)}/claim`,
|
|
40278
|
+
data
|
|
40279
|
+
);
|
|
40280
|
+
}
|
|
40281
|
+
async claimBusinessEntityByIdentifier(scheme, value, data) {
|
|
40282
|
+
return await this.client.post(
|
|
40283
|
+
`/config/business/entity/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/claim`,
|
|
40284
|
+
data
|
|
40285
|
+
);
|
|
40286
|
+
}
|
|
40287
|
+
async enrollFrench(data) {
|
|
40288
|
+
return await this.client.put("/config/french/enrollment", data);
|
|
40289
|
+
}
|
|
40290
|
+
async enrollInternational(data) {
|
|
40291
|
+
return await this.client.put("/config/international/enrollment", data);
|
|
40292
|
+
}
|
|
40293
|
+
async registerNetwork(identifierId, network) {
|
|
40294
|
+
return await this.client.post(
|
|
40295
|
+
`/config/business/entity/identifier/${encodePathSegment(identifierId)}/network/${encodePathSegment(network)}`
|
|
40296
|
+
);
|
|
40297
|
+
}
|
|
40298
|
+
async registerNetworkByScheme(scheme, value, network) {
|
|
40299
|
+
return await this.client.post(
|
|
40300
|
+
`/config/business/entity/identifier/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/network/${encodePathSegment(network)}`
|
|
40301
|
+
);
|
|
40302
|
+
}
|
|
40303
|
+
async unregisterNetwork(directoryId) {
|
|
40304
|
+
return await this.client.delete(
|
|
40305
|
+
`/config/business/entity/identifier/directory/${encodePathSegment(directoryId)}`
|
|
40306
|
+
);
|
|
40307
|
+
}
|
|
40308
|
+
// ─── Identifier Management ───────────────────────────────
|
|
40309
|
+
async createIdentifier(entityId, data) {
|
|
40310
|
+
return await this.client.post(
|
|
40311
|
+
`/config/business/entity/${encodePathSegment(entityId)}/identifier`,
|
|
40312
|
+
data
|
|
40313
|
+
);
|
|
40314
|
+
}
|
|
40315
|
+
async createIdentifierByScheme(scheme, value, data) {
|
|
40316
|
+
return await this.client.post(
|
|
40317
|
+
`/config/business/entity/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/identifier`,
|
|
40318
|
+
data
|
|
40319
|
+
);
|
|
40320
|
+
}
|
|
40321
|
+
async deleteIdentifier(identifierId) {
|
|
40322
|
+
return await this.client.delete(
|
|
40323
|
+
`/config/business/entity/identifier/${encodePathSegment(identifierId)}`
|
|
40324
|
+
);
|
|
40325
|
+
}
|
|
40326
|
+
// ─── Claim Management ────────────────────────────────────
|
|
40327
|
+
async deleteClaim(entityId) {
|
|
40328
|
+
return await this.client.delete(
|
|
40329
|
+
`/config/business/entity/${encodePathSegment(entityId)}/claim`
|
|
40330
|
+
);
|
|
40331
|
+
}
|
|
40332
|
+
};
|
|
40333
|
+
var normalizeForIopole = (inv) => {
|
|
40334
|
+
const normalized = { ...inv };
|
|
40335
|
+
for (const party of ["seller", "buyer"]) {
|
|
40336
|
+
if (normalized[party] && !normalized[party].postalAddress) {
|
|
40337
|
+
normalized[party] = {
|
|
40338
|
+
...normalized[party],
|
|
40339
|
+
postalAddress: { country: normalized[party].country ?? "FR" }
|
|
40340
|
+
};
|
|
38828
40341
|
}
|
|
38829
|
-
|
|
38830
|
-
|
|
38831
|
-
|
|
38832
|
-
|
|
38833
|
-
|
|
40342
|
+
}
|
|
40343
|
+
for (const party of ["seller", "buyer"]) {
|
|
40344
|
+
const p = normalized[party];
|
|
40345
|
+
if (p && !p.electronicAddress && p.siren && p.siret) {
|
|
40346
|
+
normalized[party] = {
|
|
40347
|
+
...p,
|
|
40348
|
+
electronicAddress: `0225:${p.siren}_${p.siret}`,
|
|
40349
|
+
identifiers: p.identifiers ?? [
|
|
40350
|
+
{
|
|
40351
|
+
type: "ELECTRONIC_ADDRESS",
|
|
40352
|
+
value: `${p.siren}_${p.siret}`,
|
|
40353
|
+
scheme: "0225"
|
|
40354
|
+
},
|
|
40355
|
+
{ type: "PARTY_LEGAL_IDENTIFIER", value: p.siren, scheme: "0002" }
|
|
40356
|
+
]
|
|
40357
|
+
};
|
|
38834
40358
|
}
|
|
38835
|
-
cachedToken = data.access_token;
|
|
38836
|
-
expiresAt = Date.now() + (data.expires_in ?? 600) * 1e3 - REFRESH_MARGIN_MS;
|
|
38837
|
-
return cachedToken;
|
|
38838
40359
|
}
|
|
38839
|
-
|
|
38840
|
-
|
|
38841
|
-
|
|
38842
|
-
|
|
38843
|
-
|
|
38844
|
-
|
|
38845
|
-
|
|
38846
|
-
|
|
38847
|
-
|
|
38848
|
-
|
|
38849
|
-
|
|
40360
|
+
if (Array.isArray(normalized.paymentTerms)) {
|
|
40361
|
+
normalized.paymentTerms = normalized.paymentTerms.map((t) => t.description ?? t).join("; ");
|
|
40362
|
+
}
|
|
40363
|
+
if (normalized.monetary) {
|
|
40364
|
+
const m = { ...normalized.monetary };
|
|
40365
|
+
const currency = m.invoiceCurrency ?? "EUR";
|
|
40366
|
+
if (!m.payableAmount) m.payableAmount = m.invoiceAmount;
|
|
40367
|
+
if (m.taxTotalAmount && !m.taxTotalAmount.currency) {
|
|
40368
|
+
m.taxTotalAmount = { ...m.taxTotalAmount, currency };
|
|
40369
|
+
}
|
|
40370
|
+
normalized.monetary = m;
|
|
40371
|
+
}
|
|
40372
|
+
if (Array.isArray(normalized.lines)) {
|
|
40373
|
+
normalized.lines = normalized.lines.map((line) => {
|
|
40374
|
+
if (line.taxDetail && !line.taxDetail.categoryCode) {
|
|
40375
|
+
return { ...line, taxDetail: { ...line.taxDetail, categoryCode: "S" } };
|
|
40376
|
+
}
|
|
40377
|
+
return line;
|
|
40378
|
+
});
|
|
40379
|
+
}
|
|
40380
|
+
return normalized;
|
|
40381
|
+
};
|
|
40382
|
+
function autoWrapDirectoryQuery(q) {
|
|
40383
|
+
const trimmed = q.trim();
|
|
40384
|
+
if (/^\d{14}$/.test(trimmed)) return `siret:"${trimmed}"`;
|
|
40385
|
+
if (/^\d{9}$/.test(trimmed)) return `siren:"${trimmed}"`;
|
|
40386
|
+
if (/^FR\d{11}$/i.test(trimmed)) {
|
|
40387
|
+
return `vatNumber:"${trimmed.toUpperCase()}"`;
|
|
40388
|
+
}
|
|
40389
|
+
if (trimmed.length >= 3 && !/^\d+$/.test(trimmed) && !trimmed.includes(":")) {
|
|
40390
|
+
return `name:"*${trimmed}*"`;
|
|
40391
|
+
}
|
|
40392
|
+
return trimmed;
|
|
38850
40393
|
}
|
|
38851
|
-
|
|
38852
|
-
|
|
38853
|
-
|
|
38854
|
-
|
|
38855
|
-
|
|
38856
|
-
|
|
40394
|
+
function normalizeStatusHistory(raw2) {
|
|
40395
|
+
let entries = [];
|
|
40396
|
+
if (Array.isArray(raw2)) {
|
|
40397
|
+
entries = raw2;
|
|
40398
|
+
} else if (raw2 && typeof raw2 === "object") {
|
|
40399
|
+
const obj = raw2;
|
|
40400
|
+
if (Array.isArray(obj.data)) entries = obj.data;
|
|
40401
|
+
else if (Array.isArray(obj.entries)) entries = obj.entries;
|
|
40402
|
+
else if (Array.isArray(obj.history)) entries = obj.history;
|
|
38857
40403
|
}
|
|
38858
|
-
|
|
38859
|
-
|
|
40404
|
+
return {
|
|
40405
|
+
entries: entries.map((e) => ({
|
|
40406
|
+
date: e.date ?? e.createdAt ?? "",
|
|
40407
|
+
code: e.status?.code ?? e.code ?? e.statusCode ?? "",
|
|
40408
|
+
message: e.status?.message ?? e.message,
|
|
40409
|
+
destType: e.destType
|
|
40410
|
+
}))
|
|
40411
|
+
};
|
|
40412
|
+
}
|
|
40413
|
+
function createIopoleAdapter() {
|
|
40414
|
+
const baseUrl = requireEnv(
|
|
40415
|
+
"IopoleAdapter",
|
|
40416
|
+
"IOPOLE_API_URL",
|
|
40417
|
+
"Set it to https://api.ppd.iopole.fr/v1 (sandbox) or https://api.iopole.com/v1 (production)."
|
|
40418
|
+
);
|
|
40419
|
+
const clientId = requireEnv(
|
|
40420
|
+
"IopoleAdapter",
|
|
40421
|
+
"IOPOLE_CLIENT_ID",
|
|
40422
|
+
"Get your client ID from the Iopole dashboard or admin console."
|
|
40423
|
+
);
|
|
40424
|
+
const clientSecret = requireEnv(
|
|
40425
|
+
"IopoleAdapter",
|
|
40426
|
+
"IOPOLE_CLIENT_SECRET",
|
|
40427
|
+
"Get your client secret from the Iopole dashboard or admin console."
|
|
40428
|
+
);
|
|
40429
|
+
const customerId = requireEnv(
|
|
40430
|
+
"IopoleAdapter",
|
|
40431
|
+
"IOPOLE_CUSTOMER_ID",
|
|
40432
|
+
"Find it in Settings \u2192 Unique Identifier (sandbox) or admin console."
|
|
40433
|
+
);
|
|
40434
|
+
const authUrl = env2("IOPOLE_AUTH_URL") || IOPOLE_DEFAULT_AUTH_URL;
|
|
40435
|
+
const getToken = createOAuth2TokenProvider({
|
|
40436
|
+
authUrl,
|
|
40437
|
+
clientId,
|
|
40438
|
+
clientSecret
|
|
40439
|
+
});
|
|
40440
|
+
const client = new IopoleClient({ baseUrl, customerId, getToken });
|
|
40441
|
+
return new IopoleAdapter(client);
|
|
40442
|
+
}
|
|
40443
|
+
|
|
40444
|
+
// src/adapters/shared/http-client.ts
|
|
40445
|
+
var BaseHttpClient = class {
|
|
38860
40446
|
config;
|
|
38861
|
-
|
|
40447
|
+
adapterName;
|
|
40448
|
+
constructor(adapterName, config2) {
|
|
40449
|
+
this.adapterName = adapterName;
|
|
38862
40450
|
this.config = config2;
|
|
38863
40451
|
}
|
|
38864
40452
|
// ─── Generic Request ────────────────────────────────────
|
|
38865
40453
|
async request(method, path, options) {
|
|
38866
|
-
|
|
38867
|
-
}
|
|
38868
|
-
async requestWithBase(baseUrl, method, path, options) {
|
|
38869
|
-
const url2 = new URL(`${baseUrl}${path}`);
|
|
40454
|
+
const url2 = new URL(`${this.config.baseUrl}${path}`);
|
|
38870
40455
|
if (options?.query) {
|
|
38871
40456
|
for (const [key, value] of Object.entries(options.query)) {
|
|
38872
|
-
if (value
|
|
40457
|
+
if (value === void 0) continue;
|
|
40458
|
+
if (Array.isArray(value)) {
|
|
40459
|
+
for (const v of value) url2.searchParams.append(key, v);
|
|
40460
|
+
} else {
|
|
38873
40461
|
url2.searchParams.set(key, String(value));
|
|
38874
40462
|
}
|
|
38875
40463
|
}
|
|
38876
40464
|
}
|
|
38877
|
-
const
|
|
40465
|
+
const authHeaders = await this.getAuthHeaders();
|
|
38878
40466
|
const headers = {
|
|
38879
|
-
|
|
38880
|
-
"customer-id": this.config.customerId,
|
|
40467
|
+
...authHeaders,
|
|
38881
40468
|
Accept: "application/json",
|
|
38882
40469
|
...options?.headers
|
|
38883
40470
|
};
|
|
40471
|
+
let bodyPayload;
|
|
38884
40472
|
if (options?.body) {
|
|
38885
|
-
|
|
40473
|
+
if (options.contentType && options.body instanceof Uint8Array) {
|
|
40474
|
+
headers["Content-Type"] = options.contentType;
|
|
40475
|
+
bodyPayload = options.body;
|
|
40476
|
+
} else {
|
|
40477
|
+
headers["Content-Type"] = options.contentType ?? "application/json";
|
|
40478
|
+
bodyPayload = JSON.stringify(options.body);
|
|
40479
|
+
}
|
|
38886
40480
|
}
|
|
38887
40481
|
const controller = new AbortController();
|
|
38888
40482
|
const timeout = setTimeout(
|
|
@@ -38893,19 +40487,20 @@ var IopoleClient = class {
|
|
|
38893
40487
|
const response = await fetch(url2.toString(), {
|
|
38894
40488
|
method,
|
|
38895
40489
|
headers,
|
|
38896
|
-
body:
|
|
40490
|
+
body: bodyPayload,
|
|
38897
40491
|
signal: controller.signal
|
|
38898
40492
|
});
|
|
38899
40493
|
if (!response.ok) {
|
|
38900
40494
|
const body = await response.text();
|
|
38901
|
-
throw new
|
|
38902
|
-
|
|
40495
|
+
throw new AdapterAPIError(
|
|
40496
|
+
this.adapterName,
|
|
40497
|
+
`[${this.adapterName}] ${method} ${path} \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
38903
40498
|
response.status,
|
|
38904
40499
|
body
|
|
38905
40500
|
);
|
|
38906
40501
|
}
|
|
38907
|
-
const
|
|
38908
|
-
if (
|
|
40502
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
40503
|
+
if (ct.includes("application/json")) {
|
|
38909
40504
|
return await response.json();
|
|
38910
40505
|
}
|
|
38911
40506
|
return await response.text();
|
|
@@ -38913,36 +40508,632 @@ var IopoleClient = class {
|
|
|
38913
40508
|
clearTimeout(timeout);
|
|
38914
40509
|
}
|
|
38915
40510
|
}
|
|
38916
|
-
// ─── Convenience Methods ────────────────────────────────
|
|
38917
|
-
async get(path, query) {
|
|
38918
|
-
return this.request("GET", path, { query });
|
|
40511
|
+
// ─── Convenience Methods ────────────────────────────────
|
|
40512
|
+
async get(path, query) {
|
|
40513
|
+
return this.request("GET", path, { query });
|
|
40514
|
+
}
|
|
40515
|
+
async post(path, body) {
|
|
40516
|
+
return this.request("POST", path, { body });
|
|
40517
|
+
}
|
|
40518
|
+
async put(path, body) {
|
|
40519
|
+
return this.request("PUT", path, { body });
|
|
40520
|
+
}
|
|
40521
|
+
async patch(path, body) {
|
|
40522
|
+
return this.request("PATCH", path, { body });
|
|
40523
|
+
}
|
|
40524
|
+
async delete(path) {
|
|
40525
|
+
return this.request("DELETE", path);
|
|
40526
|
+
}
|
|
40527
|
+
/**
|
|
40528
|
+
* Download a binary resource. Returns raw bytes + content type.
|
|
40529
|
+
*/
|
|
40530
|
+
async download(path) {
|
|
40531
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
40532
|
+
const authHeaders = await this.getAuthHeaders();
|
|
40533
|
+
const controller = new AbortController();
|
|
40534
|
+
const timeout = setTimeout(
|
|
40535
|
+
() => controller.abort(),
|
|
40536
|
+
this.config.timeoutMs ?? 3e4
|
|
40537
|
+
);
|
|
40538
|
+
try {
|
|
40539
|
+
const response = await fetch(url2, {
|
|
40540
|
+
method: "GET",
|
|
40541
|
+
headers: authHeaders,
|
|
40542
|
+
signal: controller.signal
|
|
40543
|
+
});
|
|
40544
|
+
if (!response.ok) {
|
|
40545
|
+
const body = await response.text();
|
|
40546
|
+
throw new AdapterAPIError(
|
|
40547
|
+
this.adapterName,
|
|
40548
|
+
`[${this.adapterName}] GET ${path} \u2192 ${response.status}`,
|
|
40549
|
+
response.status,
|
|
40550
|
+
body
|
|
40551
|
+
);
|
|
40552
|
+
}
|
|
40553
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
40554
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
40555
|
+
return { data, contentType };
|
|
40556
|
+
} finally {
|
|
40557
|
+
clearTimeout(timeout);
|
|
40558
|
+
}
|
|
40559
|
+
}
|
|
40560
|
+
};
|
|
40561
|
+
|
|
40562
|
+
// src/adapters/storecove/client.ts
|
|
40563
|
+
var StorecoveClient = class extends BaseHttpClient {
|
|
40564
|
+
apiKey;
|
|
40565
|
+
constructor(config2) {
|
|
40566
|
+
super("Storecove", {
|
|
40567
|
+
baseUrl: config2.baseUrl,
|
|
40568
|
+
timeoutMs: config2.timeoutMs
|
|
40569
|
+
});
|
|
40570
|
+
this.apiKey = config2.apiKey;
|
|
40571
|
+
}
|
|
40572
|
+
async getAuthHeaders() {
|
|
40573
|
+
return { Authorization: `Bearer ${this.apiKey}` };
|
|
40574
|
+
}
|
|
40575
|
+
};
|
|
40576
|
+
|
|
40577
|
+
// src/adapters/storecove/adapter.ts
|
|
40578
|
+
var StorecoveAdapter = class extends BaseAdapter {
|
|
40579
|
+
name = "storecove";
|
|
40580
|
+
/** Only methods with real Storecove API mappings. */
|
|
40581
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
40582
|
+
// Invoice
|
|
40583
|
+
"emitInvoice",
|
|
40584
|
+
"getInvoice",
|
|
40585
|
+
"downloadInvoice",
|
|
40586
|
+
// Directory
|
|
40587
|
+
"searchDirectoryFr",
|
|
40588
|
+
"searchDirectoryInt",
|
|
40589
|
+
"checkPeppolParticipant",
|
|
40590
|
+
// Status
|
|
40591
|
+
"getStatusHistory",
|
|
40592
|
+
// Webhooks
|
|
40593
|
+
"listWebhooks",
|
|
40594
|
+
"deleteWebhook",
|
|
40595
|
+
// Config / Entities
|
|
40596
|
+
"getBusinessEntity",
|
|
40597
|
+
"createLegalUnit",
|
|
40598
|
+
"deleteBusinessEntity",
|
|
40599
|
+
"configureBusinessEntity",
|
|
40600
|
+
// Identifiers / Network
|
|
40601
|
+
"enrollInternational",
|
|
40602
|
+
"registerNetwork",
|
|
40603
|
+
"registerNetworkByScheme",
|
|
40604
|
+
"unregisterNetwork",
|
|
40605
|
+
"createIdentifier",
|
|
40606
|
+
"createIdentifierByScheme",
|
|
40607
|
+
"deleteIdentifier"
|
|
40608
|
+
]);
|
|
40609
|
+
client;
|
|
40610
|
+
/** Default legal entity ID for operations that need one. */
|
|
40611
|
+
defaultLegalEntityId;
|
|
40612
|
+
constructor(client, defaultLegalEntityId) {
|
|
40613
|
+
super();
|
|
40614
|
+
this.client = client;
|
|
40615
|
+
this.defaultLegalEntityId = defaultLegalEntityId;
|
|
40616
|
+
}
|
|
40617
|
+
// ─── Invoice Operations ───────────────────────────────
|
|
40618
|
+
async emitInvoice(req) {
|
|
40619
|
+
const base643 = uint8ToBase64(req.file);
|
|
40620
|
+
const isXml = req.filename.toLowerCase().endsWith(".xml");
|
|
40621
|
+
return await this.client.post("/document_submissions", {
|
|
40622
|
+
document: {
|
|
40623
|
+
document_type: "invoice",
|
|
40624
|
+
raw_document: base643,
|
|
40625
|
+
raw_document_content_type: isXml ? "application/xml" : "application/pdf"
|
|
40626
|
+
},
|
|
40627
|
+
...this.defaultLegalEntityId ? { legal_entity_id: Number(this.defaultLegalEntityId) } : {}
|
|
40628
|
+
});
|
|
40629
|
+
}
|
|
40630
|
+
async searchInvoices(_filters) {
|
|
40631
|
+
throw new NotSupportedError(
|
|
40632
|
+
this.name,
|
|
40633
|
+
"searchInvoices",
|
|
40634
|
+
"Storecove delivers invoices via webhooks (push) or pull queue. There is no search endpoint. Use webhook pull mode to retrieve pending documents."
|
|
40635
|
+
);
|
|
40636
|
+
}
|
|
40637
|
+
async getInvoice(id) {
|
|
40638
|
+
const doc = await this.client.get(
|
|
40639
|
+
`/received_documents/${encodePathSegment(id)}/json`
|
|
40640
|
+
);
|
|
40641
|
+
return {
|
|
40642
|
+
id,
|
|
40643
|
+
invoiceNumber: doc.invoiceNumber ?? doc.document?.invoiceNumber,
|
|
40644
|
+
status: doc.status ?? "received",
|
|
40645
|
+
direction: "received",
|
|
40646
|
+
senderName: doc.accountingSupplierParty?.party?.partyName,
|
|
40647
|
+
receiverName: doc.accountingCustomerParty?.party?.partyName,
|
|
40648
|
+
issueDate: doc.issueDate,
|
|
40649
|
+
dueDate: doc.dueDate,
|
|
40650
|
+
currency: doc.documentCurrencyCode ?? "EUR",
|
|
40651
|
+
totalTtc: doc.legalMonetaryTotal?.payableAmount
|
|
40652
|
+
};
|
|
40653
|
+
}
|
|
40654
|
+
async downloadInvoice(id) {
|
|
40655
|
+
return await this.client.download(
|
|
40656
|
+
`/received_documents/${encodePathSegment(id)}/original`
|
|
40657
|
+
);
|
|
40658
|
+
}
|
|
40659
|
+
async downloadReadable(_id) {
|
|
40660
|
+
throw new NotSupportedError(
|
|
40661
|
+
this.name,
|
|
40662
|
+
"downloadReadable",
|
|
40663
|
+
"Storecove does not generate readable PDFs. Use getInvoice for JSON or downloadInvoice for the original document."
|
|
40664
|
+
);
|
|
40665
|
+
}
|
|
40666
|
+
async getInvoiceFiles(_id) {
|
|
40667
|
+
throw new NotSupportedError(
|
|
40668
|
+
this.name,
|
|
40669
|
+
"getInvoiceFiles",
|
|
40670
|
+
"Storecove documents are atomic \u2014 no separate files list. Use getInvoice or downloadInvoice."
|
|
40671
|
+
);
|
|
40672
|
+
}
|
|
40673
|
+
async getAttachments(_id) {
|
|
40674
|
+
throw new NotSupportedError(
|
|
40675
|
+
this.name,
|
|
40676
|
+
"getAttachments",
|
|
40677
|
+
"Attachments are embedded in the Storecove document. Use getInvoice to access them."
|
|
40678
|
+
);
|
|
40679
|
+
}
|
|
40680
|
+
async downloadFile(_fileId) {
|
|
40681
|
+
throw new NotSupportedError(
|
|
40682
|
+
this.name,
|
|
40683
|
+
"downloadFile",
|
|
40684
|
+
"Storecove has no separate file download. Use downloadInvoice for the full document."
|
|
40685
|
+
);
|
|
40686
|
+
}
|
|
40687
|
+
async markInvoiceSeen(_id) {
|
|
40688
|
+
throw new NotSupportedError(
|
|
40689
|
+
this.name,
|
|
40690
|
+
"markInvoiceSeen",
|
|
40691
|
+
"Storecove does not track seen/unseen state via API."
|
|
40692
|
+
);
|
|
40693
|
+
}
|
|
40694
|
+
async getUnseenInvoices(_pagination) {
|
|
40695
|
+
throw new NotSupportedError(
|
|
40696
|
+
this.name,
|
|
40697
|
+
"getUnseenInvoices",
|
|
40698
|
+
"Use Storecove webhook pull mode to poll for new documents."
|
|
40699
|
+
);
|
|
40700
|
+
}
|
|
40701
|
+
async generateCII(_req) {
|
|
40702
|
+
throw new NotSupportedError(
|
|
40703
|
+
this.name,
|
|
40704
|
+
"generateCII",
|
|
40705
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40706
|
+
);
|
|
40707
|
+
}
|
|
40708
|
+
async generateUBL(_req) {
|
|
40709
|
+
throw new NotSupportedError(
|
|
40710
|
+
this.name,
|
|
40711
|
+
"generateUBL",
|
|
40712
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40713
|
+
);
|
|
40714
|
+
}
|
|
40715
|
+
async generateFacturX(_req) {
|
|
40716
|
+
throw new NotSupportedError(
|
|
40717
|
+
this.name,
|
|
40718
|
+
"generateFacturX",
|
|
40719
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40720
|
+
);
|
|
40721
|
+
}
|
|
40722
|
+
// ─── Directory ────────────────────────────────────────
|
|
40723
|
+
async searchDirectoryFr(filters) {
|
|
40724
|
+
const raw2 = await this.client.post("/discovery/exists", {
|
|
40725
|
+
identifier: filters.q
|
|
40726
|
+
});
|
|
40727
|
+
const participant = raw2?.participant ?? raw2;
|
|
40728
|
+
if (!participant || !participant.identifier) {
|
|
40729
|
+
return { rows: [], count: 0 };
|
|
40730
|
+
}
|
|
40731
|
+
return {
|
|
40732
|
+
rows: [{
|
|
40733
|
+
entityId: participant.identifier ?? "",
|
|
40734
|
+
name: participant.name ?? participant.partyName,
|
|
40735
|
+
country: participant.country
|
|
40736
|
+
}],
|
|
40737
|
+
count: 1
|
|
40738
|
+
};
|
|
40739
|
+
}
|
|
40740
|
+
async searchDirectoryInt(filters) {
|
|
40741
|
+
return await this.client.post("/discovery/receives", {
|
|
40742
|
+
identifier: filters.value
|
|
40743
|
+
});
|
|
40744
|
+
}
|
|
40745
|
+
async checkPeppolParticipant(scheme, value) {
|
|
40746
|
+
return await this.client.post("/discovery/exists", {
|
|
40747
|
+
identifier: { scheme, identifier: value }
|
|
40748
|
+
});
|
|
40749
|
+
}
|
|
40750
|
+
// ─── Status ───────────────────────────────────────────
|
|
40751
|
+
async sendStatus(_req) {
|
|
40752
|
+
throw new NotSupportedError(
|
|
40753
|
+
this.name,
|
|
40754
|
+
"sendStatus",
|
|
40755
|
+
"Storecove status is managed by the receiving Access Point. The sender receives delivery evidence via the evidence endpoint."
|
|
40756
|
+
);
|
|
40757
|
+
}
|
|
40758
|
+
async getStatusHistory(invoiceId) {
|
|
40759
|
+
const raw2 = await this.client.get(
|
|
40760
|
+
`/document_submissions/${encodePathSegment(invoiceId)}/evidence/delivery`
|
|
40761
|
+
);
|
|
40762
|
+
return {
|
|
40763
|
+
entries: raw2 ? [{
|
|
40764
|
+
date: raw2.timestamp ?? raw2.date ?? "",
|
|
40765
|
+
code: raw2.status ?? "delivered",
|
|
40766
|
+
message: raw2.description
|
|
40767
|
+
}] : []
|
|
40768
|
+
};
|
|
40769
|
+
}
|
|
40770
|
+
async getUnseenStatuses(_pagination) {
|
|
40771
|
+
throw new NotSupportedError(
|
|
40772
|
+
this.name,
|
|
40773
|
+
"getUnseenStatuses",
|
|
40774
|
+
"Storecove delivers status changes via webhooks."
|
|
40775
|
+
);
|
|
40776
|
+
}
|
|
40777
|
+
async markStatusSeen(_statusId) {
|
|
40778
|
+
throw new NotSupportedError(
|
|
40779
|
+
this.name,
|
|
40780
|
+
"markStatusSeen",
|
|
40781
|
+
"Storecove does not track seen/unseen status via API."
|
|
40782
|
+
);
|
|
40783
|
+
}
|
|
40784
|
+
// ─── Reporting ────────────────────────────────────────
|
|
40785
|
+
async reportInvoiceTransaction(_transaction) {
|
|
40786
|
+
throw new NotSupportedError(
|
|
40787
|
+
this.name,
|
|
40788
|
+
"reportInvoiceTransaction",
|
|
40789
|
+
"Storecove handles tax reporting internally based on the destination country."
|
|
40790
|
+
);
|
|
40791
|
+
}
|
|
40792
|
+
async reportTransaction(_businessEntityId, _transaction) {
|
|
40793
|
+
throw new NotSupportedError(
|
|
40794
|
+
this.name,
|
|
40795
|
+
"reportTransaction",
|
|
40796
|
+
"Storecove handles tax reporting internally based on the destination country."
|
|
40797
|
+
);
|
|
40798
|
+
}
|
|
40799
|
+
// ─── Webhooks ─────────────────────────────────────────
|
|
40800
|
+
async listWebhooks() {
|
|
40801
|
+
throw new NotSupportedError(
|
|
40802
|
+
this.name,
|
|
40803
|
+
"listWebhooks",
|
|
40804
|
+
"Storecove /webhook_instances is a pull queue, not webhook config CRUD."
|
|
40805
|
+
);
|
|
40806
|
+
}
|
|
40807
|
+
async getWebhook(_id) {
|
|
40808
|
+
throw new NotSupportedError(
|
|
40809
|
+
this.name,
|
|
40810
|
+
"getWebhook",
|
|
40811
|
+
"Storecove has no get-by-id webhook endpoint. Use listWebhooks instead."
|
|
40812
|
+
);
|
|
40813
|
+
}
|
|
40814
|
+
async createWebhook(_req) {
|
|
40815
|
+
throw new NotSupportedError(
|
|
40816
|
+
this.name,
|
|
40817
|
+
"createWebhook",
|
|
40818
|
+
"Storecove webhooks are configured via the Storecove dashboard UI, not the API."
|
|
40819
|
+
);
|
|
40820
|
+
}
|
|
40821
|
+
async updateWebhook(_id, _req) {
|
|
40822
|
+
throw new NotSupportedError(
|
|
40823
|
+
this.name,
|
|
40824
|
+
"updateWebhook",
|
|
40825
|
+
"Storecove webhooks are configured via the Storecove dashboard UI, not the API."
|
|
40826
|
+
);
|
|
40827
|
+
}
|
|
40828
|
+
async deleteWebhook(_id) {
|
|
40829
|
+
throw new NotSupportedError(
|
|
40830
|
+
this.name,
|
|
40831
|
+
"deleteWebhook",
|
|
40832
|
+
"Storecove /webhook_instances is a pull queue, not webhook config CRUD."
|
|
40833
|
+
);
|
|
40834
|
+
}
|
|
40835
|
+
// ─── Operator Config ───────────────────────────────────
|
|
40836
|
+
async getCustomerId() {
|
|
40837
|
+
throw new NotSupportedError(
|
|
40838
|
+
this.name,
|
|
40839
|
+
"getCustomerId",
|
|
40840
|
+
"Storecove uses API keys, not customer IDs. Your identity is implicit in the API key."
|
|
40841
|
+
);
|
|
40842
|
+
}
|
|
40843
|
+
async listBusinessEntities() {
|
|
40844
|
+
throw new NotSupportedError(
|
|
40845
|
+
this.name,
|
|
40846
|
+
"listBusinessEntities",
|
|
40847
|
+
"Storecove has no list-all endpoint for legal entities. Track entity IDs locally after creation, or use getBusinessEntity with a known ID."
|
|
40848
|
+
);
|
|
40849
|
+
}
|
|
40850
|
+
async getBusinessEntity(id) {
|
|
40851
|
+
return await this.client.get(`/legal_entities/${encodePathSegment(id)}`);
|
|
40852
|
+
}
|
|
40853
|
+
async createLegalUnit(_data) {
|
|
40854
|
+
throw new NotSupportedError(
|
|
40855
|
+
this.name,
|
|
40856
|
+
"createLegalUnit",
|
|
40857
|
+
"Storecove requires address fields (line1, city, zip) not collected by the generic tool schema. Implement normalizeForStorecove when ready."
|
|
40858
|
+
);
|
|
40859
|
+
}
|
|
40860
|
+
async createOffice(_data) {
|
|
40861
|
+
throw new NotSupportedError(
|
|
40862
|
+
this.name,
|
|
40863
|
+
"createOffice",
|
|
40864
|
+
"Storecove has no office/establishment concept. Use createLegalUnit for all entities."
|
|
40865
|
+
);
|
|
40866
|
+
}
|
|
40867
|
+
async deleteBusinessEntity(id) {
|
|
40868
|
+
return await this.client.delete(`/legal_entities/${encodePathSegment(id)}`);
|
|
40869
|
+
}
|
|
40870
|
+
async configureBusinessEntity(_id, _data) {
|
|
40871
|
+
throw new NotSupportedError(
|
|
40872
|
+
this.name,
|
|
40873
|
+
"configureBusinessEntity",
|
|
40874
|
+
"Storecove PATCH /legal_entities uses a different model than the generic tool schema."
|
|
40875
|
+
);
|
|
40876
|
+
}
|
|
40877
|
+
async claimBusinessEntity(_id, _data) {
|
|
40878
|
+
throw new NotSupportedError(
|
|
40879
|
+
this.name,
|
|
40880
|
+
"claimBusinessEntity",
|
|
40881
|
+
"Storecove has no entity claim workflow. Entities are created and owned directly."
|
|
40882
|
+
);
|
|
40883
|
+
}
|
|
40884
|
+
async claimBusinessEntityByIdentifier(_scheme, _value, _data) {
|
|
40885
|
+
throw new NotSupportedError(
|
|
40886
|
+
this.name,
|
|
40887
|
+
"claimBusinessEntityByIdentifier",
|
|
40888
|
+
"Storecove has no entity claim workflow. Entities are created and owned directly."
|
|
40889
|
+
);
|
|
40890
|
+
}
|
|
40891
|
+
async enrollFrench(_data) {
|
|
40892
|
+
throw new NotSupportedError(
|
|
40893
|
+
this.name,
|
|
40894
|
+
"enrollFrench",
|
|
40895
|
+
"Use enrollInternational with Peppol identifiers for French entity registration on Storecove."
|
|
40896
|
+
);
|
|
40897
|
+
}
|
|
40898
|
+
async enrollInternational(data) {
|
|
40899
|
+
const legalEntityId = data.legalEntityId ?? this.defaultLegalEntityId;
|
|
40900
|
+
if (!legalEntityId) {
|
|
40901
|
+
throw new Error(
|
|
40902
|
+
"[StorecoveAdapter] enrollInternational requires legalEntityId"
|
|
40903
|
+
);
|
|
40904
|
+
}
|
|
40905
|
+
return await this.client.post(
|
|
40906
|
+
`/legal_entities/${encodePathSegment(String(legalEntityId))}/peppol_identifiers`,
|
|
40907
|
+
{
|
|
40908
|
+
superscheme: data.superscheme ?? "iso6523-actorid-upis",
|
|
40909
|
+
scheme: data.scheme,
|
|
40910
|
+
identifier: data.identifier ?? data.value
|
|
40911
|
+
}
|
|
40912
|
+
);
|
|
40913
|
+
}
|
|
40914
|
+
async registerNetwork(identifierId, _network) {
|
|
40915
|
+
return {
|
|
40916
|
+
message: `Peppol identifier ${identifierId} is registered on creation in Storecove.`
|
|
40917
|
+
};
|
|
40918
|
+
}
|
|
40919
|
+
async registerNetworkByScheme(scheme, value, _network) {
|
|
40920
|
+
return {
|
|
40921
|
+
message: `Peppol identifier ${scheme}:${value} is registered on creation in Storecove. Use enrollInternational to create the identifier.`
|
|
40922
|
+
};
|
|
40923
|
+
}
|
|
40924
|
+
async unregisterNetwork(directoryId) {
|
|
40925
|
+
const parts = directoryId.split("/");
|
|
40926
|
+
if (parts.length < 4) {
|
|
40927
|
+
throw new Error(
|
|
40928
|
+
"[StorecoveAdapter] unregisterNetwork expects directoryId as 'legalEntityId/superscheme/scheme/identifier'"
|
|
40929
|
+
);
|
|
40930
|
+
}
|
|
40931
|
+
const [legalEntityId, ...rest] = parts;
|
|
40932
|
+
return await this.client.delete(
|
|
40933
|
+
`/legal_entities/${encodePathSegment(legalEntityId)}/peppol_identifiers/${rest.map(encodePathSegment).join("/")}`
|
|
40934
|
+
);
|
|
40935
|
+
}
|
|
40936
|
+
// ─── Identifier Management ───────────────────────────────
|
|
40937
|
+
async createIdentifier(entityId, data) {
|
|
40938
|
+
if (data.scheme && String(data.scheme).startsWith("0")) {
|
|
40939
|
+
return await this.client.post(
|
|
40940
|
+
`/legal_entities/${encodePathSegment(entityId)}/peppol_identifiers`,
|
|
40941
|
+
{
|
|
40942
|
+
superscheme: data.superscheme ?? "iso6523-actorid-upis",
|
|
40943
|
+
scheme: data.scheme,
|
|
40944
|
+
identifier: data.value
|
|
40945
|
+
}
|
|
40946
|
+
);
|
|
40947
|
+
}
|
|
40948
|
+
return await this.client.post(
|
|
40949
|
+
`/legal_entities/${encodePathSegment(entityId)}/additional_tax_identifiers`,
|
|
40950
|
+
data
|
|
40951
|
+
);
|
|
40952
|
+
}
|
|
40953
|
+
async createIdentifierByScheme(_scheme, _value, _data) {
|
|
40954
|
+
throw new NotSupportedError(
|
|
40955
|
+
this.name,
|
|
40956
|
+
"createIdentifierByScheme",
|
|
40957
|
+
"Storecove cannot look up entities by scheme/value \u2014 requires legalEntityId. Implement when tool schema supports it."
|
|
40958
|
+
);
|
|
40959
|
+
}
|
|
40960
|
+
async deleteIdentifier(identifierId) {
|
|
40961
|
+
if (identifierId.includes("/")) {
|
|
40962
|
+
const segments = identifierId.split("/").map(encodePathSegment).join("/");
|
|
40963
|
+
return await this.client.delete(
|
|
40964
|
+
`/legal_entities/${segments}`
|
|
40965
|
+
);
|
|
40966
|
+
}
|
|
40967
|
+
throw new Error(
|
|
40968
|
+
"[StorecoveAdapter] deleteIdentifier for tax identifiers requires the full path: 'legalEntityId/additional_tax_identifiers/identifierId'"
|
|
40969
|
+
);
|
|
40970
|
+
}
|
|
40971
|
+
// ─── Claim Management ────────────────────────────────────
|
|
40972
|
+
async deleteClaim(_entityId) {
|
|
40973
|
+
throw new NotSupportedError(
|
|
40974
|
+
this.name,
|
|
40975
|
+
"deleteClaim",
|
|
40976
|
+
"Storecove has no entity claim concept. Delete the entity directly with deleteBusinessEntity."
|
|
40977
|
+
);
|
|
40978
|
+
}
|
|
40979
|
+
};
|
|
40980
|
+
function createStorecoveAdapter() {
|
|
40981
|
+
const baseUrl = requireEnv(
|
|
40982
|
+
"StorecoveAdapter",
|
|
40983
|
+
"STORECOVE_API_URL",
|
|
40984
|
+
"Set it to https://api.storecove.com/api/v2 (production/sandbox)."
|
|
40985
|
+
);
|
|
40986
|
+
const apiKey = requireEnv(
|
|
40987
|
+
"StorecoveAdapter",
|
|
40988
|
+
"STORECOVE_API_KEY",
|
|
40989
|
+
"Get your API key from the Storecove dashboard."
|
|
40990
|
+
);
|
|
40991
|
+
const defaultLegalEntityId = env2("STORECOVE_LEGAL_ENTITY_ID") || void 0;
|
|
40992
|
+
const client = new StorecoveClient({ baseUrl, apiKey });
|
|
40993
|
+
return new StorecoveAdapter(client, defaultLegalEntityId);
|
|
40994
|
+
}
|
|
40995
|
+
|
|
40996
|
+
// src/adapters/afnor/base-adapter.ts
|
|
40997
|
+
var AfnorBaseAdapter = class extends BaseAdapter {
|
|
40998
|
+
afnor;
|
|
40999
|
+
constructor(afnor) {
|
|
41000
|
+
super();
|
|
41001
|
+
this.afnor = afnor;
|
|
41002
|
+
}
|
|
41003
|
+
// ─── Invoice Operations (AFNOR: submitFlow, searchFlows, downloadFlow) ───
|
|
41004
|
+
async emitInvoice(req) {
|
|
41005
|
+
const afnor = this.requireAfnor("emitInvoice");
|
|
41006
|
+
const syntax = req.filename.toLowerCase().endsWith(".pdf") ? "Factur-X" : "CII";
|
|
41007
|
+
return await afnor.submitFlow(
|
|
41008
|
+
req.file,
|
|
41009
|
+
{ flowSyntax: syntax, name: req.filename, processingRule: "B2B" }
|
|
41010
|
+
);
|
|
41011
|
+
}
|
|
41012
|
+
async searchInvoices(filters) {
|
|
41013
|
+
const afnor = this.requireAfnor("searchInvoices");
|
|
41014
|
+
const result = await afnor.searchFlows(
|
|
41015
|
+
{
|
|
41016
|
+
flowType: ["CustomerInvoice", "SupplierInvoice"],
|
|
41017
|
+
...filters.q ? { trackingId: filters.q } : {}
|
|
41018
|
+
},
|
|
41019
|
+
filters.limit
|
|
41020
|
+
);
|
|
41021
|
+
const rows = (result.results ?? []).map((r) => ({
|
|
41022
|
+
id: r.flowId ?? "",
|
|
41023
|
+
status: r.ackStatus,
|
|
41024
|
+
direction: normalizeDirection(r.flowDirection),
|
|
41025
|
+
date: r.updatedAt ?? r.submittedAt
|
|
41026
|
+
}));
|
|
41027
|
+
return { rows, count: rows.length };
|
|
41028
|
+
}
|
|
41029
|
+
async getInvoice(id) {
|
|
41030
|
+
const afnor = this.requireAfnor("getInvoice");
|
|
41031
|
+
const { data, contentType } = await afnor.downloadFlow(id);
|
|
41032
|
+
if (contentType.includes("json")) {
|
|
41033
|
+
const doc = JSON.parse(new TextDecoder().decode(data));
|
|
41034
|
+
return {
|
|
41035
|
+
id,
|
|
41036
|
+
invoiceNumber: doc.invoiceId ?? doc.invoiceNumber,
|
|
41037
|
+
status: doc.ackStatus ?? doc.status,
|
|
41038
|
+
direction: normalizeDirection(doc.flowDirection),
|
|
41039
|
+
senderName: doc.seller?.name,
|
|
41040
|
+
receiverName: doc.buyer?.name,
|
|
41041
|
+
issueDate: doc.invoiceDate,
|
|
41042
|
+
currency: doc.currency ?? "EUR"
|
|
41043
|
+
};
|
|
41044
|
+
}
|
|
41045
|
+
return { id, status: "UNKNOWN" };
|
|
41046
|
+
}
|
|
41047
|
+
async downloadInvoice(id) {
|
|
41048
|
+
const afnor = this.requireAfnor("downloadInvoice");
|
|
41049
|
+
return await afnor.downloadFlow(id, "Original");
|
|
41050
|
+
}
|
|
41051
|
+
// ─── Status (AFNOR: lifecycle flows) ───────────────────
|
|
41052
|
+
async sendStatus(req) {
|
|
41053
|
+
const afnor = this.requireAfnor("sendStatus");
|
|
41054
|
+
const cdarPayload = JSON.stringify({
|
|
41055
|
+
invoiceId: req.invoiceId,
|
|
41056
|
+
statusCode: req.code,
|
|
41057
|
+
message: req.message,
|
|
41058
|
+
payment: req.payment
|
|
41059
|
+
});
|
|
41060
|
+
return await afnor.submitFlow(
|
|
41061
|
+
new TextEncoder().encode(cdarPayload),
|
|
41062
|
+
{
|
|
41063
|
+
flowSyntax: "CDAR",
|
|
41064
|
+
name: `status-${req.invoiceId}.json`,
|
|
41065
|
+
processingRule: "B2B"
|
|
41066
|
+
}
|
|
41067
|
+
);
|
|
41068
|
+
}
|
|
41069
|
+
async getStatusHistory(invoiceId) {
|
|
41070
|
+
const afnor = this.requireAfnor("getStatusHistory");
|
|
41071
|
+
const result = await afnor.searchFlows({
|
|
41072
|
+
flowType: ["CustomerInvoiceLC", "SupplierInvoiceLC"],
|
|
41073
|
+
trackingId: invoiceId
|
|
41074
|
+
});
|
|
41075
|
+
const entries = (result.results ?? []).map((r) => ({
|
|
41076
|
+
date: r.updatedAt ?? r.submittedAt ?? "",
|
|
41077
|
+
code: r.ackStatus ?? "",
|
|
41078
|
+
message: r.flowType,
|
|
41079
|
+
destType: r.flowDirection === "In" ? "PLATFORM" : "OPERATOR"
|
|
41080
|
+
}));
|
|
41081
|
+
return { entries };
|
|
38919
41082
|
}
|
|
38920
|
-
|
|
38921
|
-
|
|
38922
|
-
|
|
38923
|
-
|
|
38924
|
-
|
|
38925
|
-
|
|
38926
|
-
|
|
38927
|
-
|
|
41083
|
+
// ─── Reporting (AFNOR: e-reporting flows) ──────────────
|
|
41084
|
+
async reportInvoiceTransaction(transaction) {
|
|
41085
|
+
const afnor = this.requireAfnor("reportInvoiceTransaction");
|
|
41086
|
+
const payload = new TextEncoder().encode(JSON.stringify(transaction));
|
|
41087
|
+
return await afnor.submitFlow(
|
|
41088
|
+
payload,
|
|
41089
|
+
{ flowSyntax: "FRR", name: "report.json", processingRule: "B2C" }
|
|
41090
|
+
);
|
|
38928
41091
|
}
|
|
38929
|
-
async
|
|
38930
|
-
|
|
41092
|
+
async reportTransaction(businessEntityId, transaction) {
|
|
41093
|
+
const afnor = this.requireAfnor("reportTransaction");
|
|
41094
|
+
const payload = new TextEncoder().encode(
|
|
41095
|
+
JSON.stringify({ businessEntityId, ...transaction })
|
|
41096
|
+
);
|
|
41097
|
+
return await afnor.submitFlow(
|
|
41098
|
+
payload,
|
|
41099
|
+
{ flowSyntax: "FRR", name: "report.json", processingRule: "B2C" }
|
|
41100
|
+
);
|
|
38931
41101
|
}
|
|
38932
|
-
|
|
38933
|
-
|
|
41102
|
+
// All other methods inherit NotSupportedError stubs from BaseAdapter.
|
|
41103
|
+
// Subclasses override with native API implementations.
|
|
41104
|
+
// ─── Helpers ───────────────────────────────────────────
|
|
41105
|
+
/** Get the AFNOR client or throw if not configured. */
|
|
41106
|
+
requireAfnor(method) {
|
|
41107
|
+
if (!this.afnor) {
|
|
41108
|
+
throw new NotSupportedError(
|
|
41109
|
+
this.name,
|
|
41110
|
+
method,
|
|
41111
|
+
"AFNOR API not configured. Override this method with native implementation."
|
|
41112
|
+
);
|
|
41113
|
+
}
|
|
41114
|
+
return this.afnor;
|
|
38934
41115
|
}
|
|
38935
|
-
|
|
38936
|
-
|
|
41116
|
+
};
|
|
41117
|
+
|
|
41118
|
+
// src/adapters/afnor/client.ts
|
|
41119
|
+
var AfnorClient = class extends BaseHttpClient {
|
|
41120
|
+
getToken;
|
|
41121
|
+
constructor(config2) {
|
|
41122
|
+
super("AFNOR", { baseUrl: config2.baseUrl, timeoutMs: config2.timeoutMs });
|
|
41123
|
+
this.getToken = config2.getToken;
|
|
41124
|
+
}
|
|
41125
|
+
async getAuthHeaders() {
|
|
41126
|
+
const token = await this.getToken();
|
|
41127
|
+
return { Authorization: `Bearer ${token}` };
|
|
38937
41128
|
}
|
|
41129
|
+
// ─── AFNOR-Specific Methods ──────────────────────────
|
|
38938
41130
|
/**
|
|
38939
|
-
*
|
|
38940
|
-
*
|
|
38941
|
-
* Content-Type: multipart/form-data with a `file` field (binary, PDF or XML).
|
|
41131
|
+
* Submit a new flow (invoice, lifecycle event, or e-reporting).
|
|
41132
|
+
* POST /v1/flows — multipart: flowInfo (JSON) + file (binary).
|
|
38942
41133
|
*/
|
|
38943
|
-
async
|
|
38944
|
-
const url2 = `${this.config.baseUrl}
|
|
38945
|
-
const
|
|
41134
|
+
async submitFlow(file2, flowInfo) {
|
|
41135
|
+
const url2 = new URL(`${this.config.baseUrl}/v1/flows`);
|
|
41136
|
+
const authHeaders = await this.getAuthHeaders();
|
|
38946
41137
|
const controller = new AbortController();
|
|
38947
41138
|
const timeout = setTimeout(
|
|
38948
41139
|
() => controller.abort(),
|
|
@@ -38950,67 +41141,74 @@ var IopoleClient = class {
|
|
|
38950
41141
|
);
|
|
38951
41142
|
try {
|
|
38952
41143
|
const form = new FormData();
|
|
38953
|
-
form.append("
|
|
38954
|
-
|
|
41144
|
+
form.append("flowInfo", JSON.stringify(flowInfo));
|
|
41145
|
+
form.append(
|
|
41146
|
+
"file",
|
|
41147
|
+
new Blob([file2]),
|
|
41148
|
+
flowInfo.name ?? "invoice.xml"
|
|
41149
|
+
);
|
|
41150
|
+
const response = await fetch(url2.toString(), {
|
|
38955
41151
|
method: "POST",
|
|
38956
|
-
headers: {
|
|
38957
|
-
Authorization: `Bearer ${token}`,
|
|
38958
|
-
"customer-id": this.config.customerId,
|
|
38959
|
-
Accept: "application/json"
|
|
38960
|
-
// Do NOT set Content-Type — fetch sets it with the multipart boundary
|
|
38961
|
-
},
|
|
41152
|
+
headers: { ...authHeaders, Accept: "application/json" },
|
|
38962
41153
|
body: form,
|
|
38963
41154
|
signal: controller.signal
|
|
38964
41155
|
});
|
|
38965
41156
|
if (!response.ok) {
|
|
38966
41157
|
const body = await response.text();
|
|
38967
|
-
throw new
|
|
38968
|
-
|
|
41158
|
+
throw new AdapterAPIError(
|
|
41159
|
+
"AFNOR",
|
|
41160
|
+
`[AFNOR] POST /v1/flows \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
38969
41161
|
response.status,
|
|
38970
41162
|
body
|
|
38971
41163
|
);
|
|
38972
41164
|
}
|
|
38973
|
-
|
|
38974
|
-
if (contentType.includes("application/json")) {
|
|
38975
|
-
return await response.json();
|
|
38976
|
-
}
|
|
38977
|
-
return await response.text();
|
|
41165
|
+
return await response.json();
|
|
38978
41166
|
} finally {
|
|
38979
41167
|
clearTimeout(timeout);
|
|
38980
41168
|
}
|
|
38981
41169
|
}
|
|
38982
41170
|
/**
|
|
38983
|
-
*
|
|
38984
|
-
*
|
|
41171
|
+
* Search flows by criteria.
|
|
41172
|
+
* POST /v1/flows/search
|
|
38985
41173
|
*/
|
|
38986
|
-
async
|
|
38987
|
-
return this.request("POST",
|
|
41174
|
+
async searchFlows(filters, limit) {
|
|
41175
|
+
return await this.request("POST", "/v1/flows/search", {
|
|
41176
|
+
body: {
|
|
41177
|
+
where: filters,
|
|
41178
|
+
...limit ? { limit } : {}
|
|
41179
|
+
}
|
|
41180
|
+
});
|
|
38988
41181
|
}
|
|
38989
41182
|
/**
|
|
38990
|
-
* Download a
|
|
38991
|
-
*
|
|
41183
|
+
* Download a flow file.
|
|
41184
|
+
* GET /v1/flows/{flowId}
|
|
38992
41185
|
*/
|
|
38993
|
-
async
|
|
38994
|
-
const
|
|
38995
|
-
const
|
|
41186
|
+
async downloadFlow(flowId, docType) {
|
|
41187
|
+
const query = docType ? { docType } : void 0;
|
|
41188
|
+
const url2 = `${this.config.baseUrl}/v1/flows/${flowId}`;
|
|
41189
|
+
const authHeaders = await this.getAuthHeaders();
|
|
38996
41190
|
const controller = new AbortController();
|
|
38997
41191
|
const timeout = setTimeout(
|
|
38998
41192
|
() => controller.abort(),
|
|
38999
41193
|
this.config.timeoutMs ?? 3e4
|
|
39000
41194
|
);
|
|
39001
41195
|
try {
|
|
39002
|
-
const
|
|
41196
|
+
const fullUrl = new URL(url2);
|
|
41197
|
+
if (query) {
|
|
41198
|
+
for (const [k, v] of Object.entries(query)) {
|
|
41199
|
+
fullUrl.searchParams.set(k, v);
|
|
41200
|
+
}
|
|
41201
|
+
}
|
|
41202
|
+
const response = await fetch(fullUrl.toString(), {
|
|
39003
41203
|
method: "GET",
|
|
39004
|
-
headers:
|
|
39005
|
-
Authorization: `Bearer ${token}`,
|
|
39006
|
-
"customer-id": this.config.customerId
|
|
39007
|
-
},
|
|
41204
|
+
headers: authHeaders,
|
|
39008
41205
|
signal: controller.signal
|
|
39009
41206
|
});
|
|
39010
41207
|
if (!response.ok) {
|
|
39011
41208
|
const body = await response.text();
|
|
39012
|
-
throw new
|
|
39013
|
-
|
|
41209
|
+
throw new AdapterAPIError(
|
|
41210
|
+
"AFNOR",
|
|
41211
|
+
`[AFNOR] GET /v1/flows/${flowId} \u2192 ${response.status}`,
|
|
39014
41212
|
response.status,
|
|
39015
41213
|
body
|
|
39016
41214
|
);
|
|
@@ -39022,191 +41220,596 @@ var IopoleClient = class {
|
|
|
39022
41220
|
clearTimeout(timeout);
|
|
39023
41221
|
}
|
|
39024
41222
|
}
|
|
41223
|
+
/**
|
|
41224
|
+
* Health check — GET /v1/healthcheck
|
|
41225
|
+
*/
|
|
41226
|
+
async healthcheck() {
|
|
41227
|
+
try {
|
|
41228
|
+
await this.request("GET", "/v1/healthcheck");
|
|
41229
|
+
return true;
|
|
41230
|
+
} catch {
|
|
41231
|
+
return false;
|
|
41232
|
+
}
|
|
41233
|
+
}
|
|
39025
41234
|
};
|
|
39026
41235
|
|
|
39027
|
-
// src/
|
|
39028
|
-
|
|
39029
|
-
|
|
39030
|
-
|
|
39031
|
-
|
|
39032
|
-
|
|
39033
|
-
async function readTextFile2(path) {
|
|
39034
|
-
return await readFile2(path, "utf-8");
|
|
39035
|
-
}
|
|
39036
|
-
function statSync(path) {
|
|
39037
|
-
try {
|
|
39038
|
-
fsStatSync(path);
|
|
39039
|
-
return true;
|
|
39040
|
-
} catch {
|
|
39041
|
-
return false;
|
|
41236
|
+
// src/adapters/superpdp/client.ts
|
|
41237
|
+
var SuperPDPClient = class extends BaseHttpClient {
|
|
41238
|
+
getToken;
|
|
41239
|
+
constructor(config2) {
|
|
41240
|
+
super("SuperPDP", { baseUrl: config2.baseUrl, timeoutMs: config2.timeoutMs });
|
|
41241
|
+
this.getToken = config2.getToken;
|
|
39042
41242
|
}
|
|
39043
|
-
|
|
39044
|
-
|
|
39045
|
-
|
|
39046
|
-
}
|
|
39047
|
-
function exit(code) {
|
|
39048
|
-
process.exit(code);
|
|
39049
|
-
}
|
|
39050
|
-
function onSignal(signal, handler) {
|
|
39051
|
-
process.on(signal, handler);
|
|
39052
|
-
}
|
|
39053
|
-
|
|
39054
|
-
// src/adapters/iopole.ts
|
|
39055
|
-
var IOPOLE_DEFAULT_AUTH_URL = "https://auth.iopole.com/realms/iopole/protocol/openid-connect/token";
|
|
39056
|
-
var IopoleAdapter = class {
|
|
39057
|
-
name = "iopole";
|
|
39058
|
-
client;
|
|
39059
|
-
constructor(client) {
|
|
39060
|
-
this.client = client;
|
|
41243
|
+
async getAuthHeaders() {
|
|
41244
|
+
const token = await this.getToken();
|
|
41245
|
+
return { Authorization: `Bearer ${token}` };
|
|
39061
41246
|
}
|
|
39062
|
-
|
|
39063
|
-
|
|
39064
|
-
|
|
41247
|
+
/**
|
|
41248
|
+
* Submit XML invoice content.
|
|
41249
|
+
* Super PDP accepts raw XML body for invoice creation.
|
|
41250
|
+
*/
|
|
41251
|
+
async postXml(path, xmlData, query) {
|
|
41252
|
+
return this.request("POST", path, {
|
|
41253
|
+
body: xmlData,
|
|
41254
|
+
contentType: "application/xml",
|
|
41255
|
+
query
|
|
41256
|
+
});
|
|
39065
41257
|
}
|
|
39066
|
-
|
|
39067
|
-
|
|
39068
|
-
|
|
39069
|
-
|
|
39070
|
-
|
|
39071
|
-
|
|
41258
|
+
/**
|
|
41259
|
+
* Convert invoice format.
|
|
41260
|
+
* POST /invoices/convert with body + from/to query params.
|
|
41261
|
+
*/
|
|
41262
|
+
async convert(data, from, to) {
|
|
41263
|
+
const contentType = from === "en16931" ? "application/json" : "application/xml";
|
|
41264
|
+
return this.request("POST", "/invoices/convert", {
|
|
41265
|
+
body: data,
|
|
41266
|
+
contentType,
|
|
41267
|
+
query: { from, to }
|
|
39072
41268
|
});
|
|
39073
41269
|
}
|
|
39074
|
-
|
|
39075
|
-
|
|
41270
|
+
};
|
|
41271
|
+
|
|
41272
|
+
// src/adapters/superpdp/normalize.ts
|
|
41273
|
+
function toDecimal(v) {
|
|
41274
|
+
if (v == null) return "0.00";
|
|
41275
|
+
const n = typeof v === "string" ? parseFloat(v) : Number(v);
|
|
41276
|
+
if (!Number.isFinite(n)) return "0.00";
|
|
41277
|
+
return n.toFixed(2);
|
|
41278
|
+
}
|
|
41279
|
+
function setIfAbsent(obj, key, value) {
|
|
41280
|
+
if (obj[key] == null && value != null) obj[key] = value;
|
|
41281
|
+
}
|
|
41282
|
+
function stripNulls(obj) {
|
|
41283
|
+
const clean = {};
|
|
41284
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
41285
|
+
if (v != null) clean[k] = v;
|
|
39076
41286
|
}
|
|
39077
|
-
|
|
39078
|
-
|
|
41287
|
+
return clean;
|
|
41288
|
+
}
|
|
41289
|
+
var PARTY_SOURCE_FIELDS = [
|
|
41290
|
+
"country",
|
|
41291
|
+
"address",
|
|
41292
|
+
"siret",
|
|
41293
|
+
"siretNumber",
|
|
41294
|
+
"siren",
|
|
41295
|
+
"sirenNumber",
|
|
41296
|
+
"vatNumber",
|
|
41297
|
+
"vat_number",
|
|
41298
|
+
"vatId"
|
|
41299
|
+
];
|
|
41300
|
+
function normalizeParty(party, requireElectronicAddress) {
|
|
41301
|
+
if (!party || typeof party !== "object") return party;
|
|
41302
|
+
const p = { ...party };
|
|
41303
|
+
if (!p.postal_address) {
|
|
41304
|
+
if (p.address && typeof p.address === "object") {
|
|
41305
|
+
p.postal_address = stripNulls({
|
|
41306
|
+
country_code: p.address.country_code ?? p.address.country ?? p.country ?? "FR",
|
|
41307
|
+
address_line1: p.address.street ?? p.address.address_line1 ?? p.address.line1,
|
|
41308
|
+
city: p.address.city,
|
|
41309
|
+
post_code: p.address.postal_code ?? p.address.post_code ?? p.address.zip
|
|
41310
|
+
});
|
|
41311
|
+
} else if (p.country) {
|
|
41312
|
+
p.postal_address = { country_code: p.country };
|
|
41313
|
+
}
|
|
39079
41314
|
}
|
|
39080
|
-
|
|
39081
|
-
|
|
41315
|
+
if (!p.electronic_address && requireElectronicAddress) {
|
|
41316
|
+
const siret = p.siret ?? p.siretNumber;
|
|
41317
|
+
if (siret) {
|
|
41318
|
+
p.electronic_address = { scheme: "0009", value: String(siret) };
|
|
41319
|
+
}
|
|
39082
41320
|
}
|
|
39083
|
-
|
|
39084
|
-
|
|
41321
|
+
if (!p.legal_registration_identifier) {
|
|
41322
|
+
const siren = p.siren ?? p.sirenNumber;
|
|
41323
|
+
const siret = p.siret ?? p.siretNumber;
|
|
41324
|
+
const id = siren ?? siret;
|
|
41325
|
+
if (id) {
|
|
41326
|
+
p.legal_registration_identifier = { scheme: "0002", value: String(id) };
|
|
41327
|
+
}
|
|
39085
41328
|
}
|
|
39086
|
-
|
|
39087
|
-
|
|
41329
|
+
if (!p.vat_identifier) {
|
|
41330
|
+
const vat = p.vatNumber ?? p.vat_number ?? p.vatId;
|
|
41331
|
+
if (vat) p.vat_identifier = String(vat);
|
|
39088
41332
|
}
|
|
39089
|
-
|
|
39090
|
-
|
|
41333
|
+
for (const f of PARTY_SOURCE_FIELDS) delete p[f];
|
|
41334
|
+
return p;
|
|
41335
|
+
}
|
|
41336
|
+
var TOTALS_SOURCE_FIELDS = [
|
|
41337
|
+
"line_extension_amount",
|
|
41338
|
+
"lineTotalAmount",
|
|
41339
|
+
"tax_exclusive_amount",
|
|
41340
|
+
"taxBasisTotalAmount",
|
|
41341
|
+
"tax_inclusive_amount",
|
|
41342
|
+
"grandTotalAmount",
|
|
41343
|
+
"payableAmount"
|
|
41344
|
+
];
|
|
41345
|
+
function normalizeTotals(totals) {
|
|
41346
|
+
if (!totals || typeof totals !== "object") return totals;
|
|
41347
|
+
const t = { ...totals };
|
|
41348
|
+
setIfAbsent(
|
|
41349
|
+
t,
|
|
41350
|
+
"sum_invoice_lines_amount",
|
|
41351
|
+
t.line_extension_amount ?? t.lineTotalAmount
|
|
41352
|
+
);
|
|
41353
|
+
setIfAbsent(
|
|
41354
|
+
t,
|
|
41355
|
+
"total_without_vat",
|
|
41356
|
+
t.tax_exclusive_amount ?? t.taxBasisTotalAmount
|
|
41357
|
+
);
|
|
41358
|
+
setIfAbsent(
|
|
41359
|
+
t,
|
|
41360
|
+
"total_with_vat",
|
|
41361
|
+
t.tax_inclusive_amount ?? t.grandTotalAmount
|
|
41362
|
+
);
|
|
41363
|
+
setIfAbsent(t, "amount_due_for_payment", t.payableAmount);
|
|
41364
|
+
for (const key of [
|
|
41365
|
+
"sum_invoice_lines_amount",
|
|
41366
|
+
"total_without_vat",
|
|
41367
|
+
"total_with_vat",
|
|
41368
|
+
"amount_due_for_payment"
|
|
41369
|
+
]) {
|
|
41370
|
+
if (t[key] != null) t[key] = toDecimal(t[key]);
|
|
41371
|
+
}
|
|
41372
|
+
if (t.total_vat_amount != null && typeof t.total_vat_amount !== "object") {
|
|
41373
|
+
t.total_vat_amount = { value: toDecimal(t.total_vat_amount) };
|
|
41374
|
+
} else if (t.total_vat_amount?.value != null) {
|
|
41375
|
+
t.total_vat_amount = {
|
|
41376
|
+
...t.total_vat_amount,
|
|
41377
|
+
value: toDecimal(t.total_vat_amount.value)
|
|
41378
|
+
};
|
|
41379
|
+
}
|
|
41380
|
+
for (const f of TOTALS_SOURCE_FIELDS) delete t[f];
|
|
41381
|
+
return t;
|
|
41382
|
+
}
|
|
41383
|
+
var VAT_SOURCE_FIELDS = [
|
|
41384
|
+
"category_code",
|
|
41385
|
+
"categoryCode",
|
|
41386
|
+
"rate",
|
|
41387
|
+
"percent",
|
|
41388
|
+
"vat_rate",
|
|
41389
|
+
"taxable_amount",
|
|
41390
|
+
"taxableAmount",
|
|
41391
|
+
"tax_amount",
|
|
41392
|
+
"taxAmount"
|
|
41393
|
+
];
|
|
41394
|
+
function normalizeVatBreakdown(vat) {
|
|
41395
|
+
if (!vat || typeof vat !== "object") return vat;
|
|
41396
|
+
const v = { ...vat };
|
|
41397
|
+
setIfAbsent(v, "vat_category_code", v.category_code ?? v.categoryCode);
|
|
41398
|
+
setIfAbsent(v, "vat_category_rate", v.rate ?? v.percent ?? v.vat_rate);
|
|
41399
|
+
setIfAbsent(
|
|
41400
|
+
v,
|
|
41401
|
+
"vat_category_taxable_amount",
|
|
41402
|
+
v.taxable_amount ?? v.taxableAmount
|
|
41403
|
+
);
|
|
41404
|
+
setIfAbsent(v, "vat_category_tax_amount", v.tax_amount ?? v.taxAmount);
|
|
41405
|
+
if (v.vat_category_rate != null) {
|
|
41406
|
+
v.vat_category_rate = toDecimal(v.vat_category_rate);
|
|
41407
|
+
}
|
|
41408
|
+
if (v.vat_category_taxable_amount != null) {
|
|
41409
|
+
v.vat_category_taxable_amount = toDecimal(v.vat_category_taxable_amount);
|
|
41410
|
+
}
|
|
41411
|
+
if (v.vat_category_tax_amount != null) {
|
|
41412
|
+
v.vat_category_tax_amount = toDecimal(v.vat_category_tax_amount);
|
|
41413
|
+
}
|
|
41414
|
+
for (const f of VAT_SOURCE_FIELDS) delete v[f];
|
|
41415
|
+
return v;
|
|
41416
|
+
}
|
|
41417
|
+
var LINE_SOURCE_FIELDS = [
|
|
41418
|
+
"id",
|
|
41419
|
+
"name",
|
|
41420
|
+
"item_name",
|
|
41421
|
+
"description",
|
|
41422
|
+
"quantity",
|
|
41423
|
+
"billed_quantity",
|
|
41424
|
+
"unit_code",
|
|
41425
|
+
"unitCode",
|
|
41426
|
+
"net_price",
|
|
41427
|
+
"price",
|
|
41428
|
+
"unit_price",
|
|
41429
|
+
"unitPrice",
|
|
41430
|
+
"line_amount",
|
|
41431
|
+
"line_total_amount",
|
|
41432
|
+
"line_net_amount",
|
|
41433
|
+
"amount",
|
|
41434
|
+
"totalAmount",
|
|
41435
|
+
"tax_category",
|
|
41436
|
+
"vat_category_code",
|
|
41437
|
+
"line_vat_category_code",
|
|
41438
|
+
"vatCategoryCode",
|
|
41439
|
+
"tax_percent",
|
|
41440
|
+
"vat_rate",
|
|
41441
|
+
"line_vat_rate",
|
|
41442
|
+
"vatRate"
|
|
41443
|
+
];
|
|
41444
|
+
function normalizeLine(line) {
|
|
41445
|
+
if (!line || typeof line !== "object") return line;
|
|
41446
|
+
const l = { ...line };
|
|
41447
|
+
setIfAbsent(l, "identifier", l.id);
|
|
41448
|
+
if (!l.item_information) {
|
|
41449
|
+
const name = l.name ?? l.item_name ?? l.description;
|
|
41450
|
+
if (name) l.item_information = { name: String(name) };
|
|
41451
|
+
}
|
|
41452
|
+
setIfAbsent(l, "invoiced_quantity", l.quantity ?? l.billed_quantity);
|
|
41453
|
+
if (l.invoiced_quantity != null) {
|
|
41454
|
+
l.invoiced_quantity = toDecimal(l.invoiced_quantity);
|
|
41455
|
+
}
|
|
41456
|
+
setIfAbsent(l, "invoiced_quantity_code", l.unit_code ?? l.unitCode ?? "C62");
|
|
41457
|
+
if (!l.price_details) {
|
|
41458
|
+
const price = l.net_price ?? l.price ?? l.unit_price ?? l.unitPrice;
|
|
41459
|
+
if (price != null) l.price_details = { item_net_price: toDecimal(price) };
|
|
41460
|
+
}
|
|
41461
|
+
setIfAbsent(
|
|
41462
|
+
l,
|
|
41463
|
+
"net_amount",
|
|
41464
|
+
l.line_amount ?? l.line_total_amount ?? l.line_net_amount ?? l.amount ?? l.totalAmount
|
|
41465
|
+
);
|
|
41466
|
+
if (l.net_amount != null) l.net_amount = toDecimal(l.net_amount);
|
|
41467
|
+
if (!l.vat_information) {
|
|
41468
|
+
const catCode = l.tax_category ?? l.vat_category_code ?? l.line_vat_category_code ?? l.vatCategoryCode;
|
|
41469
|
+
const rate = l.tax_percent ?? l.vat_rate ?? l.line_vat_rate ?? l.vatRate;
|
|
41470
|
+
if (catCode) {
|
|
41471
|
+
l.vat_information = {
|
|
41472
|
+
invoiced_item_vat_category_code: String(catCode),
|
|
41473
|
+
...rate != null ? { invoiced_item_vat_rate: toDecimal(rate) } : {}
|
|
41474
|
+
};
|
|
41475
|
+
}
|
|
39091
41476
|
}
|
|
39092
|
-
|
|
39093
|
-
|
|
41477
|
+
for (const f of LINE_SOURCE_FIELDS) delete l[f];
|
|
41478
|
+
return l;
|
|
41479
|
+
}
|
|
41480
|
+
var normalizeForSuperPDP = (inv) => {
|
|
41481
|
+
const n = { ...inv };
|
|
41482
|
+
if (!n.process_control) {
|
|
41483
|
+
n.process_control = { specification_identifier: "urn:cen.eu:en16931:2017" };
|
|
41484
|
+
}
|
|
41485
|
+
if (!n.payment_due_date) {
|
|
41486
|
+
const due = n.due_date ?? n.dueDate ?? n.invoiceDueDate;
|
|
41487
|
+
if (due) n.payment_due_date = String(due);
|
|
41488
|
+
}
|
|
41489
|
+
for (const f of ["due_date", "dueDate", "invoiceDueDate"]) delete n[f];
|
|
41490
|
+
const buyerSiret = n.buyer?.siret ?? n.buyer?.siretNumber;
|
|
41491
|
+
if (n.seller) n.seller = normalizeParty(n.seller, true);
|
|
41492
|
+
if (n.buyer) n.buyer = normalizeParty(n.buyer, false);
|
|
41493
|
+
if (n.totals) n.totals = normalizeTotals(n.totals);
|
|
41494
|
+
let vatSource;
|
|
41495
|
+
if (Array.isArray(n.vat_break_down)) vatSource = n.vat_break_down;
|
|
41496
|
+
else if (Array.isArray(n.taxDetails)) vatSource = n.taxDetails;
|
|
41497
|
+
else if (Array.isArray(n.vatBreakdown)) vatSource = n.vatBreakdown;
|
|
41498
|
+
if (vatSource) {
|
|
41499
|
+
n.vat_break_down = vatSource.map(normalizeVatBreakdown);
|
|
41500
|
+
delete n.taxDetails;
|
|
41501
|
+
delete n.vatBreakdown;
|
|
41502
|
+
}
|
|
41503
|
+
if (Array.isArray(n.lines)) {
|
|
41504
|
+
n.lines = n.lines.map(normalizeLine);
|
|
41505
|
+
}
|
|
41506
|
+
if (n.payment_instructions) {
|
|
41507
|
+
const pi = { ...n.payment_instructions };
|
|
41508
|
+
if (pi.credit_transfer && !pi.credit_transfers) {
|
|
41509
|
+
pi.credit_transfers = Array.isArray(pi.credit_transfer) ? pi.credit_transfer : [pi.credit_transfer];
|
|
41510
|
+
delete pi.credit_transfer;
|
|
41511
|
+
}
|
|
41512
|
+
if (Array.isArray(pi.credit_transfers)) {
|
|
41513
|
+
pi.credit_transfers = pi.credit_transfers.map((ct) => {
|
|
41514
|
+
if (ct?.payment_account_identifier?.scheme?.toUpperCase() === "IBAN") {
|
|
41515
|
+
return {
|
|
41516
|
+
...ct,
|
|
41517
|
+
payment_account_identifier: {
|
|
41518
|
+
...ct.payment_account_identifier,
|
|
41519
|
+
scheme: ""
|
|
41520
|
+
}
|
|
41521
|
+
};
|
|
41522
|
+
}
|
|
41523
|
+
return ct;
|
|
41524
|
+
});
|
|
41525
|
+
}
|
|
41526
|
+
n.payment_instructions = pi;
|
|
39094
41527
|
}
|
|
39095
|
-
|
|
39096
|
-
|
|
39097
|
-
|
|
39098
|
-
|
|
39099
|
-
});
|
|
41528
|
+
if (!n.delivery_information) {
|
|
41529
|
+
n.delivery_information = {
|
|
41530
|
+
delivery_date: n.issue_date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
41531
|
+
};
|
|
39100
41532
|
}
|
|
39101
|
-
|
|
39102
|
-
|
|
39103
|
-
|
|
41533
|
+
if (!n.notes || !Array.isArray(n.notes)) n.notes = [];
|
|
41534
|
+
const noteCodes = new Set(n.notes.map((note) => note?.subject_code));
|
|
41535
|
+
if (!noteCodes.has("PMT")) {
|
|
41536
|
+
n.notes.push({
|
|
41537
|
+
note: "En cas de retard de paiement, indemnite forfaitaire de 40 euros pour frais de recouvrement (art. L441-10 C.com).",
|
|
41538
|
+
subject_code: "PMT"
|
|
39104
41539
|
});
|
|
39105
41540
|
}
|
|
39106
|
-
|
|
39107
|
-
|
|
39108
|
-
|
|
41541
|
+
if (!noteCodes.has("PMD")) {
|
|
41542
|
+
n.notes.push({
|
|
41543
|
+
note: "Penalites de retard : 3 fois le taux d'interet legal (art. L441-10 C.com).",
|
|
41544
|
+
subject_code: "PMD"
|
|
39109
41545
|
});
|
|
39110
41546
|
}
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
39114
|
-
|
|
41547
|
+
if (!noteCodes.has("AAB")) {
|
|
41548
|
+
n.notes.push({
|
|
41549
|
+
note: "Pas d'escompte pour paiement anticipe.",
|
|
41550
|
+
subject_code: "AAB"
|
|
39115
41551
|
});
|
|
39116
41552
|
}
|
|
39117
|
-
|
|
39118
|
-
|
|
39119
|
-
|
|
39120
|
-
|
|
39121
|
-
|
|
39122
|
-
|
|
41553
|
+
const buyerCountry = n.buyer?.postal_address?.country_code ?? n.buyer?.country;
|
|
41554
|
+
if (n.buyer && !n.buyer.electronic_address && buyerCountry === "FR") {
|
|
41555
|
+
const siret = buyerSiret ?? n.buyer.legal_registration_identifier?.value;
|
|
41556
|
+
if (!siret) {
|
|
41557
|
+
throw new Error(
|
|
41558
|
+
"BR-FR-12: buyer.electronic_address is required for French buyers. Provide buyer.siret, buyer.electronic_address, or buyer.legal_registration_identifier."
|
|
41559
|
+
);
|
|
41560
|
+
}
|
|
41561
|
+
n.buyer.electronic_address = { scheme: "0009", value: siret };
|
|
41562
|
+
}
|
|
41563
|
+
return n;
|
|
41564
|
+
};
|
|
41565
|
+
|
|
41566
|
+
// src/adapters/superpdp/adapter.ts
|
|
41567
|
+
var SuperPDPAdapter = class extends AfnorBaseAdapter {
|
|
41568
|
+
name = "superpdp";
|
|
41569
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
41570
|
+
// Native overrides
|
|
41571
|
+
"emitInvoice",
|
|
41572
|
+
"searchInvoices",
|
|
41573
|
+
"getInvoice",
|
|
41574
|
+
"downloadInvoice",
|
|
41575
|
+
"generateCII",
|
|
41576
|
+
"generateUBL",
|
|
41577
|
+
"sendStatus",
|
|
41578
|
+
"getStatusHistory",
|
|
41579
|
+
"getCustomerId",
|
|
41580
|
+
"getBusinessEntity",
|
|
41581
|
+
"createOffice",
|
|
41582
|
+
"enrollFrench",
|
|
41583
|
+
"registerNetwork",
|
|
41584
|
+
"registerNetworkByScheme",
|
|
41585
|
+
"unregisterNetwork",
|
|
41586
|
+
"createIdentifier",
|
|
41587
|
+
"createIdentifierByScheme",
|
|
41588
|
+
"deleteIdentifier",
|
|
41589
|
+
"searchDirectoryFr",
|
|
41590
|
+
// Inherited from AFNOR base
|
|
41591
|
+
"reportInvoiceTransaction",
|
|
41592
|
+
"reportTransaction"
|
|
41593
|
+
]);
|
|
41594
|
+
client;
|
|
41595
|
+
constructor(client, afnor) {
|
|
41596
|
+
super(afnor);
|
|
41597
|
+
this.client = client;
|
|
41598
|
+
}
|
|
41599
|
+
// ─── Invoice Operations (native) ──────────────────────
|
|
41600
|
+
async emitInvoice(req) {
|
|
41601
|
+
return await this.client.postXml("/invoices", req.file, {
|
|
41602
|
+
external_id: req.filename
|
|
39123
41603
|
});
|
|
39124
41604
|
}
|
|
39125
|
-
async
|
|
39126
|
-
|
|
39127
|
-
|
|
39128
|
-
|
|
39129
|
-
|
|
41605
|
+
async searchInvoices(filters) {
|
|
41606
|
+
const direction = filters.direction === "received" ? "in" : filters.direction === "sent" ? "out" : filters.q === "in" || filters.q === "out" ? filters.q : void 0;
|
|
41607
|
+
const raw2 = await this.client.get("/invoices", {
|
|
41608
|
+
"expand[]": ["en_invoice", "events"],
|
|
41609
|
+
...direction ? { direction } : {},
|
|
41610
|
+
...filters.limit ? { limit: filters.limit } : {}
|
|
41611
|
+
// Note: SuperPDP uses cursor-based pagination (starting_after_id),
|
|
41612
|
+
// not offset-based. Offset is ignored — use last row ID for next page.
|
|
39130
41613
|
});
|
|
41614
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41615
|
+
const rows = data.map((inv) => ({
|
|
41616
|
+
id: String(inv.id ?? ""),
|
|
41617
|
+
invoiceNumber: inv.en_invoice?.number ?? inv.external_id,
|
|
41618
|
+
status: lastEventCode(inv.events),
|
|
41619
|
+
direction: normalizeDirection(inv.direction),
|
|
41620
|
+
senderName: inv.en_invoice?.seller?.name,
|
|
41621
|
+
receiverName: inv.en_invoice?.buyer?.name,
|
|
41622
|
+
date: inv.en_invoice?.issue_date,
|
|
41623
|
+
amount: inv.en_invoice?.totals?.amount_due_for_payment,
|
|
41624
|
+
currency: inv.en_invoice?.currency_code ?? "EUR"
|
|
41625
|
+
}));
|
|
41626
|
+
return { rows, count: raw2?.count ?? rows.length };
|
|
39131
41627
|
}
|
|
39132
|
-
async
|
|
39133
|
-
|
|
39134
|
-
`/
|
|
41628
|
+
async getInvoice(id) {
|
|
41629
|
+
const inv = await this.client.get(
|
|
41630
|
+
`/invoices/${encodePathSegment(id)}`
|
|
39135
41631
|
);
|
|
41632
|
+
const en = inv.en_invoice;
|
|
41633
|
+
return {
|
|
41634
|
+
id: String(inv.id ?? id),
|
|
41635
|
+
invoiceNumber: en?.number ?? inv.external_id,
|
|
41636
|
+
status: lastEventCode(inv.events),
|
|
41637
|
+
direction: normalizeDirection(inv.direction),
|
|
41638
|
+
senderName: en?.seller?.name,
|
|
41639
|
+
receiverName: en?.buyer?.name,
|
|
41640
|
+
issueDate: en?.issue_date,
|
|
41641
|
+
dueDate: en?.due_date,
|
|
41642
|
+
currency: en?.currency_code ?? "EUR",
|
|
41643
|
+
totalHt: en?.totals?.tax_exclusive_amount,
|
|
41644
|
+
totalTtc: en?.totals?.tax_inclusive_amount
|
|
41645
|
+
};
|
|
39136
41646
|
}
|
|
39137
|
-
|
|
41647
|
+
async downloadInvoice(id) {
|
|
41648
|
+
return await this.client.download(
|
|
41649
|
+
`/invoices/${encodePathSegment(id)}/download`
|
|
41650
|
+
);
|
|
41651
|
+
}
|
|
41652
|
+
// ─── Format Conversion (native) ───────────────────────
|
|
41653
|
+
async generateCII(req) {
|
|
41654
|
+
const invoice = normalizeForSuperPDP(req.invoice);
|
|
41655
|
+
const payload = new TextEncoder().encode(JSON.stringify(invoice));
|
|
41656
|
+
return await this.client.convert(payload, "en16931", "cii");
|
|
41657
|
+
}
|
|
41658
|
+
async generateUBL(req) {
|
|
41659
|
+
const invoice = normalizeForSuperPDP(req.invoice);
|
|
41660
|
+
const payload = new TextEncoder().encode(JSON.stringify(invoice));
|
|
41661
|
+
return await this.client.convert(payload, "en16931", "ubl");
|
|
41662
|
+
}
|
|
41663
|
+
// ─── Status / Events (native) ─────────────────────────
|
|
39138
41664
|
async sendStatus(req) {
|
|
39139
|
-
|
|
39140
|
-
|
|
39141
|
-
|
|
39142
|
-
|
|
41665
|
+
const details = [];
|
|
41666
|
+
if (req.message) {
|
|
41667
|
+
details.push({ reason: req.message });
|
|
41668
|
+
}
|
|
41669
|
+
if (req.payment) {
|
|
41670
|
+
const amounts = Array.isArray(req.payment.amounts) ? req.payment.amounts : [req.payment];
|
|
41671
|
+
details.push({ amounts });
|
|
41672
|
+
}
|
|
41673
|
+
return await this.client.post("/invoice_events", {
|
|
41674
|
+
invoice_id: toInvoiceId(req.invoiceId),
|
|
41675
|
+
status_code: req.code,
|
|
41676
|
+
...details.length > 0 ? { details } : {}
|
|
39143
41677
|
});
|
|
39144
41678
|
}
|
|
39145
41679
|
async getStatusHistory(invoiceId) {
|
|
39146
|
-
|
|
39147
|
-
|
|
39148
|
-
async getUnseenStatuses(pagination) {
|
|
39149
|
-
return await this.client.get("/invoice/status/notSeen", {
|
|
39150
|
-
offset: pagination.offset,
|
|
39151
|
-
limit: pagination.limit
|
|
41680
|
+
const raw2 = await this.client.get("/invoice_events", {
|
|
41681
|
+
invoice_id: invoiceId
|
|
39152
41682
|
});
|
|
41683
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41684
|
+
return {
|
|
41685
|
+
// deno-lint-ignore no-explicit-any
|
|
41686
|
+
entries: data.map((e) => ({
|
|
41687
|
+
date: e.created_at ?? e.date ?? "",
|
|
41688
|
+
code: e.status_code ?? e.code ?? "",
|
|
41689
|
+
message: e.message
|
|
41690
|
+
}))
|
|
41691
|
+
};
|
|
41692
|
+
}
|
|
41693
|
+
// ─── Reporting (AFNOR — inherited) ────────────────────
|
|
41694
|
+
// reportInvoiceTransaction → AfnorBaseAdapter.submitFlow (FRR)
|
|
41695
|
+
// reportTransaction → AfnorBaseAdapter.submitFlow (FRR)
|
|
41696
|
+
// ─── Directory (native) ───────────────────────────────
|
|
41697
|
+
async searchDirectoryFr(_filters) {
|
|
41698
|
+
const raw2 = await this.client.get("/directory_entries");
|
|
41699
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41700
|
+
const rows = data.map((entry) => ({
|
|
41701
|
+
entityId: String(entry.id ?? ""),
|
|
41702
|
+
name: entry.company?.formal_name ?? entry.company?.trade_name ?? entry.name,
|
|
41703
|
+
siret: entry.identifier,
|
|
41704
|
+
country: entry.company?.country ?? "FR",
|
|
41705
|
+
directory: entry.directory,
|
|
41706
|
+
status: entry.status,
|
|
41707
|
+
createdAt: entry.created_at
|
|
41708
|
+
}));
|
|
41709
|
+
return { rows, count: rows.length };
|
|
39153
41710
|
}
|
|
39154
|
-
|
|
39155
|
-
|
|
39156
|
-
|
|
39157
|
-
// ─── Reporting ────────────────────────────────────────
|
|
39158
|
-
async reportInvoiceTransaction(transaction) {
|
|
39159
|
-
return await this.client.post("/reporting/fr/invoice/transaction", transaction);
|
|
39160
|
-
}
|
|
39161
|
-
async reportTransaction(businessEntityId, transaction) {
|
|
39162
|
-
return await this.client.post(`/reporting/fr/transaction/${businessEntityId}`, transaction);
|
|
41711
|
+
// ─── Operator Config (native) ─────────────────────────
|
|
41712
|
+
async getCustomerId() {
|
|
41713
|
+
return await this.client.get("/companies/me");
|
|
39163
41714
|
}
|
|
39164
|
-
|
|
39165
|
-
|
|
39166
|
-
return await this.client.get("/config/webhook");
|
|
41715
|
+
async getBusinessEntity(_id) {
|
|
41716
|
+
return await this.client.get("/companies/me");
|
|
39167
41717
|
}
|
|
39168
|
-
async
|
|
39169
|
-
return await this.client.
|
|
41718
|
+
async createOffice(data) {
|
|
41719
|
+
return await this.client.post("/directory_entries", data);
|
|
39170
41720
|
}
|
|
39171
|
-
async
|
|
39172
|
-
return await this.client.post("/
|
|
41721
|
+
async enrollFrench(data) {
|
|
41722
|
+
return await this.client.post("/directory_entries", data);
|
|
39173
41723
|
}
|
|
39174
|
-
async
|
|
39175
|
-
return await this.client.
|
|
41724
|
+
async registerNetwork(identifierId, network) {
|
|
41725
|
+
return await this.client.post("/directory_entries", {
|
|
41726
|
+
directory: mapNetworkToDirectory(network),
|
|
41727
|
+
identifier: identifierId
|
|
41728
|
+
});
|
|
39176
41729
|
}
|
|
39177
|
-
async
|
|
39178
|
-
return await this.client.
|
|
41730
|
+
async registerNetworkByScheme(scheme, value, network) {
|
|
41731
|
+
return await this.client.post("/directory_entries", {
|
|
41732
|
+
directory: mapNetworkToDirectory(network),
|
|
41733
|
+
identifier: `${scheme}:${value}`
|
|
41734
|
+
});
|
|
39179
41735
|
}
|
|
39180
|
-
|
|
39181
|
-
|
|
39182
|
-
|
|
39183
|
-
const clientId = env2("IOPOLE_CLIENT_ID");
|
|
39184
|
-
const clientSecret = env2("IOPOLE_CLIENT_SECRET");
|
|
39185
|
-
const customerId = env2("IOPOLE_CUSTOMER_ID");
|
|
39186
|
-
const authUrl = env2("IOPOLE_AUTH_URL") || IOPOLE_DEFAULT_AUTH_URL;
|
|
39187
|
-
if (!baseUrl) {
|
|
39188
|
-
throw new Error(
|
|
39189
|
-
"[IopoleAdapter] IOPOLE_API_URL is required. Set it to https://api.ppd.iopole.fr/v1 (sandbox) or https://api.iopole.com/v1 (production)."
|
|
41736
|
+
async unregisterNetwork(directoryId) {
|
|
41737
|
+
return await this.client.delete(
|
|
41738
|
+
`/directory_entries/${encodePathSegment(directoryId)}`
|
|
39190
41739
|
);
|
|
39191
41740
|
}
|
|
39192
|
-
|
|
39193
|
-
|
|
39194
|
-
|
|
39195
|
-
|
|
41741
|
+
// ─── Identifier Management (native via directory) ─────
|
|
41742
|
+
async createIdentifier(_entityId, data) {
|
|
41743
|
+
return await this.client.post("/directory_entries", {
|
|
41744
|
+
directory: data.directory ?? "ppf",
|
|
41745
|
+
identifier: data.identifier
|
|
41746
|
+
});
|
|
39196
41747
|
}
|
|
39197
|
-
|
|
39198
|
-
|
|
39199
|
-
|
|
39200
|
-
|
|
41748
|
+
async createIdentifierByScheme(scheme, value, data) {
|
|
41749
|
+
return await this.client.post("/directory_entries", {
|
|
41750
|
+
directory: data.directory ?? "ppf",
|
|
41751
|
+
identifier: `${scheme}:${value}`
|
|
41752
|
+
});
|
|
39201
41753
|
}
|
|
39202
|
-
|
|
39203
|
-
|
|
39204
|
-
|
|
41754
|
+
async deleteIdentifier(identifierId) {
|
|
41755
|
+
return await this.client.delete(
|
|
41756
|
+
`/directory_entries/${encodePathSegment(identifierId)}`
|
|
39205
41757
|
);
|
|
39206
41758
|
}
|
|
39207
|
-
|
|
39208
|
-
|
|
39209
|
-
|
|
41759
|
+
// ─── Stubs (21 methods — inherited from AfnorBaseAdapter) ─
|
|
41760
|
+
// downloadReadable, getInvoiceFiles, getAttachments, downloadFile,
|
|
41761
|
+
// markInvoiceSeen, getUnseenInvoices, getUnseenStatuses, markStatusSeen,
|
|
41762
|
+
// listWebhooks, getWebhook, createWebhook, updateWebhook, deleteWebhook,
|
|
41763
|
+
// listBusinessEntities, createLegalUnit, deleteBusinessEntity,
|
|
41764
|
+
// configureBusinessEntity, claimBusinessEntity, claimBusinessEntityByIdentifier,
|
|
41765
|
+
// enrollInternational, searchDirectoryInt, checkPeppolParticipant, deleteClaim
|
|
41766
|
+
};
|
|
41767
|
+
function lastEventCode(events) {
|
|
41768
|
+
if (!events?.length) return void 0;
|
|
41769
|
+
return events[events.length - 1].status_code;
|
|
41770
|
+
}
|
|
41771
|
+
function mapNetworkToDirectory(network) {
|
|
41772
|
+
if (network === "DOMESTIC_FR" || network === "ppf") return "ppf";
|
|
41773
|
+
if (network === "PEPPOL_INTERNATIONAL" || network === "peppol") {
|
|
41774
|
+
return "peppol";
|
|
41775
|
+
}
|
|
41776
|
+
throw new Error(
|
|
41777
|
+
`[SuperPDP] Unknown network "${network}". Supported: "DOMESTIC_FR"/"ppf", "PEPPOL_INTERNATIONAL"/"peppol".`
|
|
41778
|
+
);
|
|
41779
|
+
}
|
|
41780
|
+
function toInvoiceId(id) {
|
|
41781
|
+
const n = Number(id);
|
|
41782
|
+
if (!Number.isFinite(n)) {
|
|
41783
|
+
throw new Error(`[SuperPDP] invoice_id must be numeric, got "${id}".`);
|
|
41784
|
+
}
|
|
41785
|
+
return n;
|
|
41786
|
+
}
|
|
41787
|
+
function createSuperPDPAdapter() {
|
|
41788
|
+
const baseUrl = requireEnv(
|
|
41789
|
+
"SuperPDPAdapter",
|
|
41790
|
+
"SUPERPDP_API_URL",
|
|
41791
|
+
"Set it to https://api.superpdp.tech/v1.beta"
|
|
41792
|
+
);
|
|
41793
|
+
const clientId = requireEnv(
|
|
41794
|
+
"SuperPDPAdapter",
|
|
41795
|
+
"SUPERPDP_CLIENT_ID",
|
|
41796
|
+
"Get your client ID from the Super PDP dashboard."
|
|
41797
|
+
);
|
|
41798
|
+
const clientSecret = requireEnv(
|
|
41799
|
+
"SuperPDPAdapter",
|
|
41800
|
+
"SUPERPDP_CLIENT_SECRET",
|
|
41801
|
+
"Get your client secret from the Super PDP dashboard."
|
|
41802
|
+
);
|
|
41803
|
+
const authUrl = env2("SUPERPDP_AUTH_URL") || "https://api.superpdp.tech/oauth2/token";
|
|
41804
|
+
const afnorUrl = env2("SUPERPDP_AFNOR_URL") || "https://api.superpdp.tech/afnor-flow";
|
|
41805
|
+
const getToken = createOAuth2TokenProvider({
|
|
41806
|
+
authUrl,
|
|
41807
|
+
clientId,
|
|
41808
|
+
clientSecret
|
|
41809
|
+
});
|
|
41810
|
+
const client = new SuperPDPClient({ baseUrl, getToken });
|
|
41811
|
+
const afnor = new AfnorClient({ baseUrl: afnorUrl, getToken });
|
|
41812
|
+
return new SuperPDPAdapter(client, afnor);
|
|
39210
41813
|
}
|
|
39211
41814
|
|
|
39212
41815
|
// server.ts
|
|
@@ -39216,14 +41819,26 @@ function createAdapter(adapterName) {
|
|
|
39216
41819
|
switch (adapterName) {
|
|
39217
41820
|
case "iopole":
|
|
39218
41821
|
return createIopoleAdapter();
|
|
41822
|
+
case "storecove":
|
|
41823
|
+
return createStorecoveAdapter();
|
|
41824
|
+
case "superpdp":
|
|
41825
|
+
return createSuperPDPAdapter();
|
|
39219
41826
|
default:
|
|
39220
41827
|
throw new Error(
|
|
39221
|
-
`${LOG_PREFIX} Unknown adapter: "${adapterName}". Available adapters: iopole`
|
|
41828
|
+
`${LOG_PREFIX} Unknown adapter: "${adapterName}". Available adapters: iopole, storecove, superpdp`
|
|
39222
41829
|
);
|
|
39223
41830
|
}
|
|
39224
41831
|
}
|
|
39225
41832
|
async function main() {
|
|
39226
41833
|
const args = getArgs();
|
|
41834
|
+
if (args.includes("--inspect")) {
|
|
41835
|
+
await launchInspector("deno", [
|
|
41836
|
+
"run",
|
|
41837
|
+
"--allow-all",
|
|
41838
|
+
import.meta.filename
|
|
41839
|
+
]);
|
|
41840
|
+
return;
|
|
41841
|
+
}
|
|
39227
41842
|
const adapterArg = args.find((arg) => arg.startsWith("--adapter="));
|
|
39228
41843
|
const adapterName = adapterArg ? adapterArg.split("=")[1] : env2("EINVOICE_ADAPTER") || "iopole";
|
|
39229
41844
|
const categoriesArg = args.find((arg) => arg.startsWith("--categories="));
|
|
@@ -39232,7 +41847,7 @@ async function main() {
|
|
|
39232
41847
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
|
39233
41848
|
const httpPort = portArg ? parseInt(portArg.split("=")[1], 10) : DEFAULT_HTTP_PORT;
|
|
39234
41849
|
const hostnameArg = args.find((arg) => arg.startsWith("--hostname="));
|
|
39235
|
-
const hostname3 = hostnameArg ? hostnameArg.split("=")[1] : "
|
|
41850
|
+
const hostname3 = hostnameArg ? hostnameArg.split("=")[1] : "localhost";
|
|
39236
41851
|
const adapter = createAdapter(adapterName);
|
|
39237
41852
|
const toolsClient = new EInvoiceToolsClient(
|
|
39238
41853
|
categories ? { categories } : void 0
|
|
@@ -39243,15 +41858,23 @@ async function main() {
|
|
|
39243
41858
|
maxConcurrent: 10,
|
|
39244
41859
|
backpressureStrategy: "queue",
|
|
39245
41860
|
validateSchema: true,
|
|
41861
|
+
toolErrorMapper: einvoiceErrorMapper,
|
|
39246
41862
|
logger: (msg) => console.error(`${LOG_PREFIX} ${msg}`)
|
|
39247
41863
|
});
|
|
39248
|
-
const mcpTools = toolsClient.toMCPFormat();
|
|
41864
|
+
const mcpTools = toolsClient.toMCPFormat(adapter);
|
|
39249
41865
|
const handlers = toolsClient.buildHandlersMap(adapter);
|
|
39250
41866
|
server.registerTools(mcpTools, handlers);
|
|
39251
41867
|
server.registerViewers({
|
|
39252
41868
|
prefix: "mcp-einvoice",
|
|
39253
41869
|
moduleUrl: import.meta.url,
|
|
39254
|
-
viewers: [
|
|
41870
|
+
viewers: [
|
|
41871
|
+
"invoice-viewer",
|
|
41872
|
+
"doclist-viewer",
|
|
41873
|
+
"status-timeline",
|
|
41874
|
+
"directory-card",
|
|
41875
|
+
"directory-list",
|
|
41876
|
+
"action-result"
|
|
41877
|
+
],
|
|
39255
41878
|
exists: statSync,
|
|
39256
41879
|
readFile: readTextFile2
|
|
39257
41880
|
});
|