@casys/mcp-einvoice 0.1.0 → 0.1.2
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 +115 -170
- package/mcp-einvoice.mjs +2908 -591
- package/package.json +2 -2
- package/ui-dist/action-result/index.html +128 -0
- package/ui-dist/directory-card/index.html +19 -19
- package/ui-dist/directory-list/index.html +128 -0
- package/ui-dist/doclist-viewer/index.html +34 -34
- package/ui-dist/invoice-viewer/index.html +36 -36
- package/ui-dist/status-timeline/index.html +21 -21
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")) {
|
|
@@ -35496,7 +35509,7 @@ function isCloudflareWorkers() {
|
|
|
35496
35509
|
var USER_AGENT;
|
|
35497
35510
|
if (typeof navigator === "undefined" || !navigator.userAgent?.startsWith?.("Mozilla/5.0 ")) {
|
|
35498
35511
|
const NAME = "jose";
|
|
35499
|
-
const VERSION2 = "v6.2.
|
|
35512
|
+
const VERSION2 = "v6.2.2";
|
|
35500
35513
|
USER_AGENT = `${NAME}/${VERSION2}`;
|
|
35501
35514
|
}
|
|
35502
35515
|
var customFetch = Symbol();
|
|
@@ -35907,6 +35920,37 @@ async function loadYamlAuth(path) {
|
|
|
35907
35920
|
var MCP_APP_MIME_TYPE = "text/html;profile=mcp-app";
|
|
35908
35921
|
var MCP_APP_URI_SCHEME = "ui:";
|
|
35909
35922
|
|
|
35923
|
+
// node_modules/@casys/mcp-server/src/ui/viewer-utils.ts
|
|
35924
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set(["shared", "dist", "node_modules", ".cache", ".vite"]);
|
|
35925
|
+
function fileUrlToPath(url2) {
|
|
35926
|
+
const decoded = decodeURIComponent(url2.pathname);
|
|
35927
|
+
if (/^\/[A-Za-z]:\//.test(decoded)) return decoded.slice(1);
|
|
35928
|
+
if (url2.host.length > 0) return `//${url2.host}${decoded}`;
|
|
35929
|
+
return decoded;
|
|
35930
|
+
}
|
|
35931
|
+
function resolveViewerDistPath(moduleUrl, viewerName, exists) {
|
|
35932
|
+
const candidates = [
|
|
35933
|
+
fileUrlToPath(new URL(`./src/ui/dist/${viewerName}/index.html`, moduleUrl)),
|
|
35934
|
+
fileUrlToPath(new URL(`./ui-dist/${viewerName}/index.html`, moduleUrl))
|
|
35935
|
+
];
|
|
35936
|
+
for (const candidate of candidates) {
|
|
35937
|
+
if (exists(candidate)) return candidate;
|
|
35938
|
+
}
|
|
35939
|
+
return null;
|
|
35940
|
+
}
|
|
35941
|
+
function discoverViewers(uiDir, fs) {
|
|
35942
|
+
const entries = fs.readDir(uiDir);
|
|
35943
|
+
const viewers = [];
|
|
35944
|
+
for (const entry of entries) {
|
|
35945
|
+
if (!entry.isDirectory) continue;
|
|
35946
|
+
if (entry.name.startsWith(".")) continue;
|
|
35947
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
35948
|
+
if (!fs.hasIndexHtml(uiDir, entry.name)) continue;
|
|
35949
|
+
viewers.push(entry.name);
|
|
35950
|
+
}
|
|
35951
|
+
return viewers.sort();
|
|
35952
|
+
}
|
|
35953
|
+
|
|
35910
35954
|
// node_modules/@casys/mcp-server/src/security/csp.ts
|
|
35911
35955
|
function buildCspHeader(options = {}) {
|
|
35912
35956
|
const allowInline = options.allowInline !== false;
|
|
@@ -36748,6 +36792,63 @@ var ConcurrentMCPServer = class _ConcurrentMCPServer {
|
|
|
36748
36792
|
}
|
|
36749
36793
|
this.log(`Registered ${resources.length} resources`);
|
|
36750
36794
|
}
|
|
36795
|
+
/**
|
|
36796
|
+
* Register MCP Apps viewers with automatic dist path resolution.
|
|
36797
|
+
*
|
|
36798
|
+
* Replaces the manual pattern of: enumerate viewers → resolve paths → register resources.
|
|
36799
|
+
* Each viewer gets a `ui://{prefix}/{viewerName}` resource URI.
|
|
36800
|
+
*
|
|
36801
|
+
* Viewers whose dist is not found are skipped with a warning (not an error),
|
|
36802
|
+
* so that the server can start in dev without building UIs first.
|
|
36803
|
+
*
|
|
36804
|
+
* @returns Summary of registered and skipped viewers
|
|
36805
|
+
*/
|
|
36806
|
+
registerViewers(config2) {
|
|
36807
|
+
if (!config2.prefix) {
|
|
36808
|
+
throw new Error("[ConcurrentMCPServer] registerViewers: prefix is required");
|
|
36809
|
+
}
|
|
36810
|
+
let viewerNames;
|
|
36811
|
+
if (config2.viewers) {
|
|
36812
|
+
viewerNames = config2.viewers;
|
|
36813
|
+
} else if (config2.discover) {
|
|
36814
|
+
viewerNames = discoverViewers(config2.discover.uiDir, config2.discover);
|
|
36815
|
+
} else {
|
|
36816
|
+
viewerNames = [];
|
|
36817
|
+
}
|
|
36818
|
+
const humanNameFn = config2.humanName ?? defaultHumanName;
|
|
36819
|
+
const registered = [];
|
|
36820
|
+
const skipped = [];
|
|
36821
|
+
for (const viewerName of viewerNames) {
|
|
36822
|
+
const distPath = resolveViewerDistPath(config2.moduleUrl, viewerName, config2.exists);
|
|
36823
|
+
if (!distPath) {
|
|
36824
|
+
this.log(
|
|
36825
|
+
`Warning: UI not built for ui://${config2.prefix}/${viewerName}. Run the UI build step first.`
|
|
36826
|
+
);
|
|
36827
|
+
skipped.push(viewerName);
|
|
36828
|
+
continue;
|
|
36829
|
+
}
|
|
36830
|
+
const resourceUri = `ui://${config2.prefix}/${viewerName}`;
|
|
36831
|
+
const readFile3 = config2.readFile;
|
|
36832
|
+
const currentDistPath = distPath;
|
|
36833
|
+
this.registerResource(
|
|
36834
|
+
{
|
|
36835
|
+
uri: resourceUri,
|
|
36836
|
+
name: humanNameFn(viewerName),
|
|
36837
|
+
description: `MCP App: ${viewerName}`,
|
|
36838
|
+
mimeType: MCP_APP_MIME_TYPE
|
|
36839
|
+
},
|
|
36840
|
+
async () => {
|
|
36841
|
+
const html = await Promise.resolve(readFile3(currentDistPath));
|
|
36842
|
+
return { uri: resourceUri, mimeType: MCP_APP_MIME_TYPE, text: html };
|
|
36843
|
+
}
|
|
36844
|
+
);
|
|
36845
|
+
registered.push(viewerName);
|
|
36846
|
+
}
|
|
36847
|
+
if (registered.length > 0) {
|
|
36848
|
+
this.log(`Registered ${registered.length} viewer(s): ${registered.join(", ")}`);
|
|
36849
|
+
}
|
|
36850
|
+
return { registered, skipped };
|
|
36851
|
+
}
|
|
36751
36852
|
/**
|
|
36752
36853
|
* Start the MCP server with stdio transport
|
|
36753
36854
|
*/
|
|
@@ -37609,6 +37710,100 @@ data: ${JSON.stringify(message2)}
|
|
|
37609
37710
|
}
|
|
37610
37711
|
}
|
|
37611
37712
|
};
|
|
37713
|
+
function defaultHumanName(name) {
|
|
37714
|
+
return name.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
37715
|
+
}
|
|
37716
|
+
|
|
37717
|
+
// node_modules/@casys/mcp-server/src/inspector/launcher.ts
|
|
37718
|
+
async function launchInspector(serverCommand, serverArgs, options) {
|
|
37719
|
+
const port = options?.port ?? 6274;
|
|
37720
|
+
const shouldOpen = options?.open ?? true;
|
|
37721
|
+
const filteredArgs = serverArgs.filter((a) => a !== "--inspect");
|
|
37722
|
+
const inspectorEnv = {
|
|
37723
|
+
...options?.env,
|
|
37724
|
+
CLIENT_PORT: String(port)
|
|
37725
|
+
};
|
|
37726
|
+
console.error(`[mcp-inspector] Starting inspector on http://localhost:${port}`);
|
|
37727
|
+
console.error(`[mcp-inspector] Server: ${serverCommand} ${filteredArgs.join(" ")}`);
|
|
37728
|
+
const command = new Deno.Command("npx", {
|
|
37729
|
+
args: [
|
|
37730
|
+
"-y",
|
|
37731
|
+
"@modelcontextprotocol/inspector",
|
|
37732
|
+
serverCommand,
|
|
37733
|
+
...filteredArgs
|
|
37734
|
+
],
|
|
37735
|
+
env: {
|
|
37736
|
+
...Deno.env.toObject(),
|
|
37737
|
+
...inspectorEnv
|
|
37738
|
+
},
|
|
37739
|
+
stdin: "inherit",
|
|
37740
|
+
stdout: "inherit",
|
|
37741
|
+
stderr: "inherit"
|
|
37742
|
+
});
|
|
37743
|
+
const process4 = command.spawn();
|
|
37744
|
+
if (shouldOpen) {
|
|
37745
|
+
setTimeout(() => {
|
|
37746
|
+
openBrowser(`http://localhost:${port}`).catch(() => {
|
|
37747
|
+
});
|
|
37748
|
+
}, 2e3);
|
|
37749
|
+
}
|
|
37750
|
+
const status = await process4.status;
|
|
37751
|
+
if (!status.success) {
|
|
37752
|
+
console.error(`[mcp-inspector] Inspector exited with code ${status.code}`);
|
|
37753
|
+
Deno.exit(status.code);
|
|
37754
|
+
}
|
|
37755
|
+
}
|
|
37756
|
+
async function openBrowser(url2) {
|
|
37757
|
+
const os = Deno.build.os;
|
|
37758
|
+
let cmd;
|
|
37759
|
+
if (os === "darwin") {
|
|
37760
|
+
cmd = ["open", url2];
|
|
37761
|
+
} else if (os === "windows") {
|
|
37762
|
+
cmd = ["cmd", "/c", "start", url2];
|
|
37763
|
+
} else {
|
|
37764
|
+
cmd = ["xdg-open", url2];
|
|
37765
|
+
}
|
|
37766
|
+
const process4 = new Deno.Command(cmd[0], {
|
|
37767
|
+
args: cmd.slice(1),
|
|
37768
|
+
stdin: "null",
|
|
37769
|
+
stdout: "null",
|
|
37770
|
+
stderr: "null"
|
|
37771
|
+
});
|
|
37772
|
+
await process4.spawn().status;
|
|
37773
|
+
}
|
|
37774
|
+
|
|
37775
|
+
// src/adapters/shared/errors.ts
|
|
37776
|
+
var NotSupportedError = class extends Error {
|
|
37777
|
+
constructor(adapter, method, alternative) {
|
|
37778
|
+
super(`[${adapter}] ${method} is not supported. ${alternative}`);
|
|
37779
|
+
this.name = "NotSupportedError";
|
|
37780
|
+
}
|
|
37781
|
+
};
|
|
37782
|
+
var AdapterAPIError = class extends Error {
|
|
37783
|
+
constructor(adapter, message2, status, body) {
|
|
37784
|
+
super(message2);
|
|
37785
|
+
this.status = status;
|
|
37786
|
+
this.body = body;
|
|
37787
|
+
this.name = `${adapter}APIError`;
|
|
37788
|
+
}
|
|
37789
|
+
};
|
|
37790
|
+
|
|
37791
|
+
// src/tools/error-mapper.ts
|
|
37792
|
+
function einvoiceErrorMapper(error2, toolName) {
|
|
37793
|
+
if (error2 instanceof NotSupportedError) {
|
|
37794
|
+
return error2.message;
|
|
37795
|
+
}
|
|
37796
|
+
if (error2 instanceof AdapterAPIError) {
|
|
37797
|
+
return `[${toolName}] API error ${error2.status}: ${error2.message.slice(0, 300)}`;
|
|
37798
|
+
}
|
|
37799
|
+
if (error2 instanceof Error) {
|
|
37800
|
+
if (error2.message.includes("is required") || error2.message.includes("must ")) {
|
|
37801
|
+
return error2.message;
|
|
37802
|
+
}
|
|
37803
|
+
return `[${toolName}] ${error2.message.slice(0, 300)}`;
|
|
37804
|
+
}
|
|
37805
|
+
return null;
|
|
37806
|
+
}
|
|
37612
37807
|
|
|
37613
37808
|
// src/generated-store.ts
|
|
37614
37809
|
var EXPIRY_MS = 10 * 60 * 1e3;
|
|
@@ -37633,72 +37828,60 @@ function getGenerated(id) {
|
|
|
37633
37828
|
return { file: entry.file, filename: entry.filename };
|
|
37634
37829
|
}
|
|
37635
37830
|
|
|
37636
|
-
// src/
|
|
37637
|
-
function
|
|
37638
|
-
|
|
37639
|
-
for (
|
|
37640
|
-
|
|
37641
|
-
normalized[party] = {
|
|
37642
|
-
...normalized[party],
|
|
37643
|
-
postalAddress: { country: normalized[party].country ?? "FR" }
|
|
37644
|
-
};
|
|
37645
|
-
}
|
|
37646
|
-
}
|
|
37647
|
-
for (const party of ["seller", "buyer"]) {
|
|
37648
|
-
const p = normalized[party];
|
|
37649
|
-
if (p && !p.electronicAddress && p.siren && p.siret) {
|
|
37650
|
-
normalized[party] = {
|
|
37651
|
-
...p,
|
|
37652
|
-
electronicAddress: `0225:${p.siren}_${p.siret}`,
|
|
37653
|
-
identifiers: p.identifiers ?? [
|
|
37654
|
-
{ type: "ELECTRONIC_ADDRESS", value: `${p.siren}_${p.siret}`, scheme: "0225" },
|
|
37655
|
-
{ type: "PARTY_LEGAL_IDENTIFIER", value: p.siren, scheme: "0002" }
|
|
37656
|
-
]
|
|
37657
|
-
};
|
|
37658
|
-
}
|
|
37659
|
-
}
|
|
37660
|
-
if (Array.isArray(normalized.paymentTerms)) {
|
|
37661
|
-
normalized.paymentTerms = normalized.paymentTerms.map((t) => t.description ?? t).join("; ");
|
|
37831
|
+
// src/adapters/shared/encoding.ts
|
|
37832
|
+
function uint8ToBase64(data) {
|
|
37833
|
+
let binary = "";
|
|
37834
|
+
for (let i = 0; i < data.length; i += 8192) {
|
|
37835
|
+
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37662
37836
|
}
|
|
37663
|
-
return
|
|
37837
|
+
return btoa(binary);
|
|
37664
37838
|
}
|
|
37839
|
+
function encodePathSegment(s) {
|
|
37840
|
+
return encodeURIComponent(s);
|
|
37841
|
+
}
|
|
37842
|
+
|
|
37843
|
+
// src/tools/invoice.ts
|
|
37665
37844
|
function mapToViewerPreview(inv) {
|
|
37666
37845
|
const lines = (inv.lines ?? []).map((l) => {
|
|
37667
37846
|
const line = l;
|
|
37668
37847
|
return {
|
|
37669
|
-
description: line.item?.name ?? line.description,
|
|
37848
|
+
description: line.item?.name ?? line.description ?? line.name,
|
|
37670
37849
|
quantity: line.billedQuantity?.quantity ?? line.quantity,
|
|
37671
|
-
unit_price: line.price?.netAmount?.amount ?? line.unit_price,
|
|
37672
|
-
tax_rate: line.taxDetail?.percent ?? line.tax_rate,
|
|
37850
|
+
unit_price: line.price?.netAmount?.amount ?? line.unitPrice ?? line.unit_price,
|
|
37851
|
+
tax_rate: line.taxDetail?.percent ?? line.taxRate ?? line.tax_rate,
|
|
37673
37852
|
amount: line.totalAmount?.amount ?? line.amount
|
|
37674
37853
|
};
|
|
37675
37854
|
});
|
|
37676
37855
|
return {
|
|
37677
37856
|
id: "(aper\xE7u)",
|
|
37678
|
-
invoice_number: inv.invoiceId,
|
|
37679
|
-
issue_date: inv.invoiceDate,
|
|
37680
|
-
due_date: inv.invoiceDueDate,
|
|
37681
|
-
invoice_type: inv.detailedType?.value ?? inv.type,
|
|
37682
|
-
sender_name: inv.seller?.name,
|
|
37683
|
-
sender_id: inv.seller?.siret ?? inv.seller?.siren,
|
|
37684
|
-
sender_vat: inv.seller?.vatNumber,
|
|
37685
|
-
receiver_name: inv.buyer?.name,
|
|
37686
|
-
receiver_id: inv.buyer?.siret ?? inv.buyer?.siren,
|
|
37687
|
-
receiver_vat: inv.buyer?.vatNumber,
|
|
37688
|
-
currency: inv.monetary?.invoiceCurrency ?? "EUR",
|
|
37689
|
-
total_ht: inv.monetary?.taxBasisTotalAmount?.amount ?? inv.monetary?.lineTotalAmount?.amount,
|
|
37690
|
-
total_tax: inv.monetary?.taxTotalAmount?.amount,
|
|
37691
|
-
total_ttc: inv.monetary?.invoiceAmount?.amount ?? inv.monetary?.payableAmount?.amount,
|
|
37857
|
+
invoice_number: inv.invoiceId ?? inv.invoice_id ?? inv.invoiceNumber,
|
|
37858
|
+
issue_date: inv.invoiceDate ?? inv.issue_date ?? inv.issueDate,
|
|
37859
|
+
due_date: inv.invoiceDueDate ?? inv.due_date ?? inv.dueDate,
|
|
37860
|
+
invoice_type: inv.detailedType?.value ?? inv.type ?? inv.invoiceType,
|
|
37861
|
+
sender_name: inv.seller?.name ?? inv.senderName,
|
|
37862
|
+
sender_id: inv.seller?.siret ?? inv.seller?.siren ?? inv.senderId,
|
|
37863
|
+
sender_vat: inv.seller?.vatNumber ?? inv.senderVat,
|
|
37864
|
+
receiver_name: inv.buyer?.name ?? inv.receiverName,
|
|
37865
|
+
receiver_id: inv.buyer?.siret ?? inv.buyer?.siren ?? inv.receiverId,
|
|
37866
|
+
receiver_vat: inv.buyer?.vatNumber ?? inv.receiverVat,
|
|
37867
|
+
currency: inv.monetary?.invoiceCurrency ?? inv.currency ?? "EUR",
|
|
37868
|
+
total_ht: inv.monetary?.taxBasisTotalAmount?.amount ?? inv.monetary?.lineTotalAmount?.amount ?? inv.totalHt,
|
|
37869
|
+
total_tax: inv.monetary?.taxTotalAmount?.amount ?? inv.totalTax,
|
|
37870
|
+
total_ttc: inv.monetary?.invoiceAmount?.amount ?? inv.monetary?.payableAmount?.amount ?? inv.totalTtc ?? inv.total_amount,
|
|
37692
37871
|
items: lines,
|
|
37693
37872
|
status: "aper\xE7u",
|
|
37694
37873
|
direction: "sent"
|
|
37695
37874
|
};
|
|
37696
37875
|
}
|
|
37876
|
+
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. ";
|
|
37877
|
+
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 } }]';
|
|
37697
37878
|
var invoiceTools = [
|
|
37698
37879
|
// ── Emit ────────────────────────────────────────────────
|
|
37699
37880
|
{
|
|
37700
|
-
name: "
|
|
37701
|
-
|
|
37881
|
+
name: "einvoice_invoice_submit",
|
|
37882
|
+
annotations: { destructiveHint: true },
|
|
37883
|
+
requires: ["emitInvoice"],
|
|
37884
|
+
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.",
|
|
37702
37885
|
category: "invoice",
|
|
37703
37886
|
inputSchema: {
|
|
37704
37887
|
type: "object",
|
|
@@ -37722,22 +37905,27 @@ var invoiceTools = [
|
|
|
37722
37905
|
const stored = getGenerated(input.generated_id);
|
|
37723
37906
|
if (!stored) {
|
|
37724
37907
|
throw new Error(
|
|
37725
|
-
"[
|
|
37908
|
+
"[einvoice_invoice_submit] Generated file expired or not found. Regenerate the invoice first."
|
|
37726
37909
|
);
|
|
37727
37910
|
}
|
|
37728
37911
|
return await ctx.adapter.emitInvoice(stored);
|
|
37729
37912
|
}
|
|
37730
37913
|
if (!input.file_base64 || !input.filename) {
|
|
37731
37914
|
throw new Error(
|
|
37732
|
-
"[
|
|
37915
|
+
"[einvoice_invoice_submit] Provide either 'generated_id' or both 'file_base64' and 'filename'"
|
|
37733
37916
|
);
|
|
37734
37917
|
}
|
|
37735
37918
|
const filename = input.filename;
|
|
37736
37919
|
const lower = filename.toLowerCase();
|
|
37737
37920
|
if (!lower.endsWith(".pdf") && !lower.endsWith(".xml")) {
|
|
37738
|
-
throw new Error("[
|
|
37921
|
+
throw new Error("[einvoice_invoice_submit] filename must end in .pdf or .xml");
|
|
37922
|
+
}
|
|
37923
|
+
let binaryString;
|
|
37924
|
+
try {
|
|
37925
|
+
binaryString = atob(input.file_base64);
|
|
37926
|
+
} catch {
|
|
37927
|
+
throw new Error("[einvoice_invoice_submit] 'file_base64' is not valid base64");
|
|
37739
37928
|
}
|
|
37740
|
-
const binaryString = atob(input.file_base64);
|
|
37741
37929
|
const bytes = new Uint8Array(binaryString.length);
|
|
37742
37930
|
for (let i = 0; i < binaryString.length; i++) {
|
|
37743
37931
|
bytes[i] = binaryString.charCodeAt(i);
|
|
@@ -37748,69 +37936,103 @@ var invoiceTools = [
|
|
|
37748
37936
|
// ── Search ──────────────────────────────────────────────
|
|
37749
37937
|
{
|
|
37750
37938
|
name: "einvoice_invoice_search",
|
|
37939
|
+
annotations: { readOnlyHint: true },
|
|
37751
37940
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
37752
|
-
|
|
37941
|
+
requires: ["searchInvoices"],
|
|
37942
|
+
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.",
|
|
37753
37943
|
category: "invoice",
|
|
37754
37944
|
inputSchema: {
|
|
37755
37945
|
type: "object",
|
|
37756
37946
|
properties: {
|
|
37757
37947
|
q: {
|
|
37758
37948
|
type: "string",
|
|
37759
|
-
description: "Search query (
|
|
37949
|
+
description: "Search query (e.g. company name, invoice number). Omit to list all."
|
|
37760
37950
|
},
|
|
37761
|
-
|
|
37951
|
+
direction: {
|
|
37762
37952
|
type: "string",
|
|
37763
|
-
description: "
|
|
37953
|
+
description: "Filter by direction",
|
|
37954
|
+
enum: ["received", "sent"]
|
|
37955
|
+
},
|
|
37956
|
+
status: {
|
|
37957
|
+
type: "string",
|
|
37958
|
+
description: "Filter by lifecycle status (after enrichment)",
|
|
37959
|
+
enum: [
|
|
37960
|
+
"SUBMITTED",
|
|
37961
|
+
"ISSUED",
|
|
37962
|
+
"MADE_AVAILABLE",
|
|
37963
|
+
"DELIVERED",
|
|
37964
|
+
"IN_HAND",
|
|
37965
|
+
"APPROVED",
|
|
37966
|
+
"PARTIALLY_APPROVED",
|
|
37967
|
+
"REFUSED",
|
|
37968
|
+
"DISPUTED",
|
|
37969
|
+
"SUSPENDED",
|
|
37970
|
+
"PAYMENT_SENT",
|
|
37971
|
+
"PAYMENT_RECEIVED",
|
|
37972
|
+
"COMPLETED",
|
|
37973
|
+
"WRONG_ROUTING",
|
|
37974
|
+
"INVALID",
|
|
37975
|
+
"DUPLICATED"
|
|
37976
|
+
]
|
|
37764
37977
|
},
|
|
37765
37978
|
offset: { type: "number", description: "Result offset (default 0)" },
|
|
37766
|
-
limit: { type: "number", description: "Max results
|
|
37979
|
+
limit: { type: "number", description: "Max results (default 50, max 200)" }
|
|
37767
37980
|
}
|
|
37768
37981
|
},
|
|
37769
37982
|
handler: async (input, ctx) => {
|
|
37770
37983
|
let q = input.q;
|
|
37771
37984
|
if (q != null && /^\s*\*?\s*$/.test(q)) q = void 0;
|
|
37772
|
-
const
|
|
37985
|
+
const dirFilter = input.direction;
|
|
37986
|
+
const statusFilter = input.status;
|
|
37987
|
+
const { rows: rawRows, count } = await ctx.adapter.searchInvoices({
|
|
37773
37988
|
q,
|
|
37774
|
-
|
|
37989
|
+
direction: dirFilter,
|
|
37990
|
+
status: statusFilter,
|
|
37775
37991
|
offset: input.offset,
|
|
37776
37992
|
limit: input.limit
|
|
37777
37993
|
});
|
|
37778
|
-
|
|
37779
|
-
if (
|
|
37780
|
-
|
|
37781
|
-
|
|
37782
|
-
|
|
37783
|
-
|
|
37784
|
-
|
|
37785
|
-
|
|
37786
|
-
|
|
37787
|
-
|
|
37788
|
-
|
|
37789
|
-
|
|
37790
|
-
|
|
37791
|
-
|
|
37792
|
-
|
|
37793
|
-
|
|
37794
|
-
|
|
37795
|
-
|
|
37796
|
-
|
|
37797
|
-
|
|
37798
|
-
|
|
37799
|
-
|
|
37800
|
-
|
|
37801
|
-
|
|
37994
|
+
let rows = rawRows;
|
|
37995
|
+
if (dirFilter) rows = rows.filter((r) => r.direction === dirFilter);
|
|
37996
|
+
if (statusFilter) rows = rows.filter((r) => r.status === statusFilter);
|
|
37997
|
+
const data = rows.map((r) => {
|
|
37998
|
+
const shortDate = r.date ? new Date(r.date).toLocaleDateString("fr-FR", { day: "numeric", month: "short" }) : "";
|
|
37999
|
+
const tiers = r.direction === "sent" ? r.receiverName : r.senderName;
|
|
38000
|
+
return {
|
|
38001
|
+
_id: r.id,
|
|
38002
|
+
_direction: r.direction,
|
|
38003
|
+
"Direction": r.direction === "received" ? "Entrante" : r.direction === "sent" ? "Sortante" : "\u2014",
|
|
38004
|
+
"N\xB0": r.invoiceNumber ?? "\u2014",
|
|
38005
|
+
"Statut": r.status ?? "\u2014",
|
|
38006
|
+
"Tiers": tiers ?? "\u2014",
|
|
38007
|
+
"Date": shortDate || "\u2014",
|
|
38008
|
+
"Montant": r.amount != null ? `${Number(r.amount).toLocaleString("fr-FR")} ${r.currency ?? "EUR"}` : "\u2014"
|
|
38009
|
+
};
|
|
38010
|
+
});
|
|
38011
|
+
const dirLabel = dirFilter === "received" ? "re\xE7ues" : dirFilter === "sent" ? "envoy\xE9es" : "";
|
|
38012
|
+
const statusLabel = statusFilter ?? "";
|
|
38013
|
+
const titleParts = ["Factures", dirLabel, statusLabel ? `(${statusLabel})` : ""].filter(Boolean);
|
|
38014
|
+
const viewerData = {
|
|
38015
|
+
data,
|
|
38016
|
+
count: rows.length !== rawRows.length ? rows.length : count ?? rows.length,
|
|
38017
|
+
_title: titleParts.join(" "),
|
|
37802
38018
|
_rowAction: {
|
|
37803
38019
|
toolName: "einvoice_invoice_get",
|
|
37804
38020
|
idField: "_id",
|
|
37805
38021
|
argName: "id"
|
|
37806
38022
|
}
|
|
37807
38023
|
};
|
|
38024
|
+
return {
|
|
38025
|
+
content: `${rows.length} ${titleParts.join(" ")} trouv\xE9es`,
|
|
38026
|
+
structuredContent: viewerData
|
|
38027
|
+
};
|
|
37808
38028
|
}
|
|
37809
38029
|
},
|
|
37810
38030
|
// ── Get by ID ───────────────────────────────────────────
|
|
37811
38031
|
{
|
|
37812
38032
|
name: "einvoice_invoice_get",
|
|
38033
|
+
annotations: { readOnlyHint: true },
|
|
37813
38034
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38035
|
+
requires: ["getInvoice"],
|
|
37814
38036
|
description: "Get a single invoice by its ID. Returns full invoice details including status history, sender/receiver info, and line items.",
|
|
37815
38037
|
category: "invoice",
|
|
37816
38038
|
inputSchema: {
|
|
@@ -37824,73 +38046,48 @@ var invoiceTools = [
|
|
|
37824
38046
|
if (!input.id) {
|
|
37825
38047
|
throw new Error("[einvoice_invoice_get] 'id' is required");
|
|
37826
38048
|
}
|
|
37827
|
-
const
|
|
37828
|
-
const inv =
|
|
37829
|
-
|
|
37830
|
-
|
|
37831
|
-
|
|
37832
|
-
|
|
37833
|
-
|
|
37834
|
-
|
|
37835
|
-
|
|
37836
|
-
|
|
37837
|
-
|
|
37838
|
-
|
|
37839
|
-
|
|
37840
|
-
|
|
37841
|
-
|
|
37842
|
-
|
|
37843
|
-
|
|
37844
|
-
|
|
37845
|
-
|
|
37846
|
-
|
|
37847
|
-
|
|
37848
|
-
|
|
37849
|
-
|
|
37850
|
-
|
|
37851
|
-
|
|
37852
|
-
|
|
37853
|
-
|
|
37854
|
-
|
|
37855
|
-
|
|
37856
|
-
|
|
37857
|
-
|
|
37858
|
-
|
|
37859
|
-
|
|
37860
|
-
direction: (() => {
|
|
37861
|
-
const d = inv.way ?? inv.metadata?.direction;
|
|
37862
|
-
if (!d) return void 0;
|
|
37863
|
-
if (d === "RECEIVED" || d === "INBOUND") return "received";
|
|
37864
|
-
if (d === "SENT" || d === "EMITTED" || d === "OUTBOUND") return "sent";
|
|
37865
|
-
return d.toLowerCase();
|
|
37866
|
-
})(),
|
|
37867
|
-
format: inv.originalFormat,
|
|
37868
|
-
network: inv.originalNetwork,
|
|
37869
|
-
invoice_type: bd.detailedType?.value,
|
|
37870
|
-
sender_name: bd.seller?.name,
|
|
37871
|
-
sender_id: bd.seller?.siret ?? bd.seller?.siren,
|
|
37872
|
-
sender_vat: bd.seller?.vatNumber,
|
|
37873
|
-
receiver_name: bd.buyer?.name,
|
|
37874
|
-
receiver_id: bd.buyer?.siret ?? bd.buyer?.siren,
|
|
37875
|
-
receiver_vat: bd.buyer?.vatNumber,
|
|
37876
|
-
issue_date: bd.invoiceDate,
|
|
37877
|
-
due_date: bd.invoiceDueDate,
|
|
37878
|
-
receipt_date: bd.invoiceReceiptDate,
|
|
37879
|
-
currency: bd.monetary?.invoiceCurrency ?? "EUR",
|
|
37880
|
-
total_ht: bd.monetary?.taxBasisTotalAmount?.amount,
|
|
37881
|
-
total_tax: bd.monetary?.taxTotalAmount?.amount,
|
|
37882
|
-
total_ttc: bd.monetary?.invoiceAmount?.amount,
|
|
37883
|
-
items: lines,
|
|
37884
|
-
notes: (bd.notes ?? []).map((n) => {
|
|
37885
|
-
const note = n;
|
|
37886
|
-
return note.content;
|
|
37887
|
-
}).filter(Boolean)
|
|
38049
|
+
const id = input.id;
|
|
38050
|
+
const inv = await ctx.adapter.getInvoice(id);
|
|
38051
|
+
const isTerminal2 = ["REFUSED", "COMPLETED", "CANCELLED", "PAYMENT_RECEIVED", "UNKNOWN"].includes(inv.status ?? "");
|
|
38052
|
+
const viewerData = {
|
|
38053
|
+
id: inv.id,
|
|
38054
|
+
invoice_number: inv.invoiceNumber,
|
|
38055
|
+
status: inv.status,
|
|
38056
|
+
direction: inv.direction,
|
|
38057
|
+
format: inv.format,
|
|
38058
|
+
network: inv.network,
|
|
38059
|
+
invoice_type: inv.invoiceType,
|
|
38060
|
+
sender_name: inv.senderName,
|
|
38061
|
+
sender_id: inv.senderId,
|
|
38062
|
+
sender_vat: inv.senderVat,
|
|
38063
|
+
receiver_name: inv.receiverName,
|
|
38064
|
+
receiver_id: inv.receiverId,
|
|
38065
|
+
receiver_vat: inv.receiverVat,
|
|
38066
|
+
issue_date: inv.issueDate,
|
|
38067
|
+
due_date: inv.dueDate,
|
|
38068
|
+
receipt_date: inv.receiptDate,
|
|
38069
|
+
currency: inv.currency,
|
|
38070
|
+
total_ht: inv.totalHt,
|
|
38071
|
+
total_tax: inv.totalTax,
|
|
38072
|
+
total_ttc: inv.totalTtc,
|
|
38073
|
+
items: inv.lines?.map((l) => ({
|
|
38074
|
+
description: l.description,
|
|
38075
|
+
quantity: l.quantity,
|
|
38076
|
+
unit_price: l.unitPrice,
|
|
38077
|
+
tax_rate: l.taxRate,
|
|
38078
|
+
amount: l.amount
|
|
38079
|
+
})),
|
|
38080
|
+
notes: inv.notes,
|
|
38081
|
+
...!isTerminal2 && inv.direction !== "received" ? { refreshRequest: { toolName: "einvoice_invoice_get", arguments: { id } } } : {}
|
|
37888
38082
|
};
|
|
38083
|
+
const summary = `Invoice ${inv.invoiceNumber ?? inv.id} \u2014 ${inv.status ?? "unknown"}, ${inv.direction ?? ""}, ${inv.totalTtc != null ? inv.totalTtc + " " + (inv.currency ?? "EUR") : "no amount"}`;
|
|
38084
|
+
return { content: summary, structuredContent: viewerData };
|
|
37889
38085
|
}
|
|
37890
38086
|
},
|
|
37891
38087
|
// ── Download ────────────────────────────────────────────
|
|
37892
38088
|
{
|
|
37893
38089
|
name: "einvoice_invoice_download",
|
|
38090
|
+
requires: ["downloadInvoice"],
|
|
37894
38091
|
description: "Download the source file of an invoice (original CII/UBL/Factur-X XML). Returns base64-encoded content.",
|
|
37895
38092
|
category: "invoice",
|
|
37896
38093
|
inputSchema: {
|
|
@@ -37905,17 +38102,16 @@ var invoiceTools = [
|
|
|
37905
38102
|
throw new Error("[einvoice_invoice_download] 'id' is required");
|
|
37906
38103
|
}
|
|
37907
38104
|
const { data, contentType } = await ctx.adapter.downloadInvoice(input.id);
|
|
37908
|
-
|
|
37909
|
-
|
|
37910
|
-
|
|
37911
|
-
}
|
|
37912
|
-
const base643 = btoa(binary);
|
|
37913
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38105
|
+
return {
|
|
38106
|
+
content: `Downloaded invoice source (${contentType}, ${data.length} bytes)`,
|
|
38107
|
+
structuredContent: { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length }
|
|
38108
|
+
};
|
|
37914
38109
|
}
|
|
37915
38110
|
},
|
|
37916
38111
|
// ── Download readable ───────────────────────────────────
|
|
37917
38112
|
{
|
|
37918
38113
|
name: "einvoice_invoice_download_readable",
|
|
38114
|
+
requires: ["downloadReadable"],
|
|
37919
38115
|
description: "Download a human-readable PDF version of an invoice. Returns base64-encoded PDF.",
|
|
37920
38116
|
category: "invoice",
|
|
37921
38117
|
inputSchema: {
|
|
@@ -37930,17 +38126,16 @@ var invoiceTools = [
|
|
|
37930
38126
|
throw new Error("[einvoice_invoice_download_readable] 'id' is required");
|
|
37931
38127
|
}
|
|
37932
38128
|
const { data, contentType } = await ctx.adapter.downloadReadable(input.id);
|
|
37933
|
-
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
}
|
|
37937
|
-
const base643 = btoa(binary);
|
|
37938
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38129
|
+
return {
|
|
38130
|
+
content: `Downloaded readable PDF (${data.length} bytes)`,
|
|
38131
|
+
structuredContent: { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length }
|
|
38132
|
+
};
|
|
37939
38133
|
}
|
|
37940
38134
|
},
|
|
37941
38135
|
// ── Invoice Files ─────────────────────────────────────────
|
|
37942
38136
|
{
|
|
37943
38137
|
name: "einvoice_invoice_files",
|
|
38138
|
+
requires: ["getInvoiceFiles"],
|
|
37944
38139
|
description: "Get metadata of ALL related files for an invoice (source XML, readable PDF, attachments). Use einvoice_invoice_attachments for only business attachments.",
|
|
37945
38140
|
category: "invoice",
|
|
37946
38141
|
inputSchema: {
|
|
@@ -37960,6 +38155,7 @@ var invoiceTools = [
|
|
|
37960
38155
|
// ── Attachments ─────────────────────────────────────────
|
|
37961
38156
|
{
|
|
37962
38157
|
name: "einvoice_invoice_attachments",
|
|
38158
|
+
requires: ["getAttachments"],
|
|
37963
38159
|
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.",
|
|
37964
38160
|
category: "invoice",
|
|
37965
38161
|
inputSchema: {
|
|
@@ -37979,6 +38175,7 @@ var invoiceTools = [
|
|
|
37979
38175
|
// ── Download File ───────────────────────────────────────
|
|
37980
38176
|
{
|
|
37981
38177
|
name: "einvoice_invoice_download_file",
|
|
38178
|
+
requires: ["downloadFile"],
|
|
37982
38179
|
description: "Download a specific file by its file ID. Returns base64-encoded content.",
|
|
37983
38180
|
category: "invoice",
|
|
37984
38181
|
inputSchema: {
|
|
@@ -37993,90 +38190,34 @@ var invoiceTools = [
|
|
|
37993
38190
|
throw new Error("[einvoice_invoice_download_file] 'file_id' is required");
|
|
37994
38191
|
}
|
|
37995
38192
|
const { data, contentType } = await ctx.adapter.downloadFile(input.file_id);
|
|
37996
|
-
let binary = "";
|
|
37997
|
-
for (let i = 0; i < data.length; i += 8192) {
|
|
37998
|
-
binary += String.fromCharCode(...data.subarray(i, i + 8192));
|
|
37999
|
-
}
|
|
38000
|
-
const base643 = btoa(binary);
|
|
38001
|
-
return { content_type: contentType, data_base64: base643, size_bytes: data.length };
|
|
38002
|
-
}
|
|
38003
|
-
},
|
|
38004
|
-
// ── Mark as seen ────────────────────────────────────────
|
|
38005
|
-
{
|
|
38006
|
-
name: "einvoice_invoice_mark_seen",
|
|
38007
|
-
description: "Mark an invoice as seen/read. Useful for tracking which invoices have been processed.",
|
|
38008
|
-
category: "invoice",
|
|
38009
|
-
inputSchema: {
|
|
38010
|
-
type: "object",
|
|
38011
|
-
properties: {
|
|
38012
|
-
id: { type: "string", description: "Invoice ID" }
|
|
38013
|
-
},
|
|
38014
|
-
required: ["id"]
|
|
38015
|
-
},
|
|
38016
|
-
handler: async (input, ctx) => {
|
|
38017
|
-
if (!input.id) {
|
|
38018
|
-
throw new Error("[einvoice_invoice_mark_seen] 'id' is required");
|
|
38019
|
-
}
|
|
38020
|
-
return await ctx.adapter.markInvoiceSeen(input.id);
|
|
38021
|
-
}
|
|
38022
|
-
},
|
|
38023
|
-
// ── Not seen ────────────────────────────────────────────
|
|
38024
|
-
{
|
|
38025
|
-
name: "einvoice_invoice_not_seen",
|
|
38026
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38027
|
-
description: "Get invoices that have not been marked as seen (PULL mode). Useful for polling new incoming invoices.",
|
|
38028
|
-
category: "invoice",
|
|
38029
|
-
inputSchema: {
|
|
38030
|
-
type: "object",
|
|
38031
|
-
properties: {
|
|
38032
|
-
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38033
|
-
limit: { type: "number", description: "Max results (default 50)" }
|
|
38034
|
-
}
|
|
38035
|
-
},
|
|
38036
|
-
handler: async (input, ctx) => {
|
|
38037
|
-
const result = await ctx.adapter.getUnseenInvoices({
|
|
38038
|
-
offset: input.offset,
|
|
38039
|
-
limit: input.limit
|
|
38040
|
-
});
|
|
38041
|
-
const data = result.data;
|
|
38042
|
-
if (Array.isArray(data)) {
|
|
38043
|
-
result.data = data.map((row) => {
|
|
38044
|
-
const m = row.metadata ?? row;
|
|
38045
|
-
const dateRaw = m.createDate?.split("T")[0];
|
|
38046
|
-
return {
|
|
38047
|
-
_id: m.invoiceId,
|
|
38048
|
-
"Statut": m.state,
|
|
38049
|
-
"Direction": m.direction === "INBOUND" ? "Re\xE7ue" : m.direction === "OUTBOUND" ? "\xC9mise" : m.direction,
|
|
38050
|
-
"Date": dateRaw ? new Date(dateRaw).toLocaleDateString("fr-FR", { day: "numeric", month: "short", year: "numeric" }) : "\u2014"
|
|
38051
|
-
};
|
|
38052
|
-
});
|
|
38053
|
-
}
|
|
38054
38193
|
return {
|
|
38055
|
-
|
|
38056
|
-
|
|
38057
|
-
toolName: "einvoice_invoice_get",
|
|
38058
|
-
idField: "_id",
|
|
38059
|
-
argName: "id"
|
|
38060
|
-
}
|
|
38194
|
+
content: `Downloaded file (${contentType}, ${data.length} bytes)`,
|
|
38195
|
+
structuredContent: { content_type: contentType, data_base64: uint8ToBase64(data), size_bytes: data.length }
|
|
38061
38196
|
};
|
|
38062
38197
|
}
|
|
38063
38198
|
},
|
|
38199
|
+
// ── seen/notSeen tools removed ──────────────────────────
|
|
38200
|
+
// Iopole's seen/notSeen mechanism is opaque: `seen` is not exposed in
|
|
38201
|
+
// search or getInvoice, and `notSeen` always returns empty in PUSH mode
|
|
38202
|
+
// (active webhook). Removed in v0.2.0 — see docs/CHANGELOG.md.
|
|
38064
38203
|
// ── Generate CII ────────────────────────────────────────
|
|
38065
38204
|
{
|
|
38066
38205
|
name: "einvoice_invoice_generate_cii",
|
|
38067
38206
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38068
|
-
|
|
38207
|
+
requires: ["generateCII"],
|
|
38208
|
+
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).",
|
|
38069
38209
|
category: "invoice",
|
|
38070
38210
|
inputSchema: {
|
|
38071
38211
|
type: "object",
|
|
38072
38212
|
properties: {
|
|
38073
38213
|
invoice: {
|
|
38074
38214
|
type: "object",
|
|
38075
|
-
description:
|
|
38215
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38076
38216
|
},
|
|
38077
38217
|
flavor: {
|
|
38078
38218
|
type: "string",
|
|
38079
|
-
description: "CII profile flavor
|
|
38219
|
+
description: "CII profile flavor",
|
|
38220
|
+
enum: ["EN16931", "EXTENDED"]
|
|
38080
38221
|
}
|
|
38081
38222
|
},
|
|
38082
38223
|
required: ["invoice", "flavor"]
|
|
@@ -38085,19 +38226,21 @@ var invoiceTools = [
|
|
|
38085
38226
|
if (!input.invoice || !input.flavor) {
|
|
38086
38227
|
throw new Error("[einvoice_invoice_generate_cii] 'invoice' and 'flavor' are required");
|
|
38087
38228
|
}
|
|
38088
|
-
const inv =
|
|
38089
|
-
const
|
|
38229
|
+
const inv = input.invoice;
|
|
38230
|
+
const xml = await ctx.adapter.generateCII({
|
|
38090
38231
|
invoice: inv,
|
|
38091
38232
|
flavor: input.flavor
|
|
38092
38233
|
});
|
|
38093
|
-
const
|
|
38094
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38234
|
+
const bytes = new TextEncoder().encode(xml);
|
|
38095
38235
|
const filename = `${inv.invoiceId ?? "invoice"}.xml`;
|
|
38096
38236
|
const generated_id = storeGenerated(bytes, filename);
|
|
38097
38237
|
return {
|
|
38098
|
-
|
|
38099
|
-
|
|
38100
|
-
|
|
38238
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38239
|
+
structuredContent: {
|
|
38240
|
+
generated_id,
|
|
38241
|
+
filename,
|
|
38242
|
+
preview: mapToViewerPreview(inv)
|
|
38243
|
+
}
|
|
38101
38244
|
};
|
|
38102
38245
|
}
|
|
38103
38246
|
},
|
|
@@ -38105,18 +38248,20 @@ var invoiceTools = [
|
|
|
38105
38248
|
{
|
|
38106
38249
|
name: "einvoice_invoice_generate_ubl",
|
|
38107
38250
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38108
|
-
|
|
38251
|
+
requires: ["generateUBL"],
|
|
38252
|
+
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).",
|
|
38109
38253
|
category: "invoice",
|
|
38110
38254
|
inputSchema: {
|
|
38111
38255
|
type: "object",
|
|
38112
38256
|
properties: {
|
|
38113
38257
|
invoice: {
|
|
38114
38258
|
type: "object",
|
|
38115
|
-
description:
|
|
38259
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38116
38260
|
},
|
|
38117
38261
|
flavor: {
|
|
38118
38262
|
type: "string",
|
|
38119
|
-
description: "UBL profile flavor
|
|
38263
|
+
description: "UBL profile flavor",
|
|
38264
|
+
enum: ["EN16931", "PEPPOL_BIS_3"]
|
|
38120
38265
|
}
|
|
38121
38266
|
},
|
|
38122
38267
|
required: ["invoice", "flavor"]
|
|
@@ -38125,19 +38270,21 @@ var invoiceTools = [
|
|
|
38125
38270
|
if (!input.invoice || !input.flavor) {
|
|
38126
38271
|
throw new Error("[einvoice_invoice_generate_ubl] 'invoice' and 'flavor' are required");
|
|
38127
38272
|
}
|
|
38128
|
-
const inv =
|
|
38129
|
-
const
|
|
38273
|
+
const inv = input.invoice;
|
|
38274
|
+
const xml = await ctx.adapter.generateUBL({
|
|
38130
38275
|
invoice: inv,
|
|
38131
38276
|
flavor: input.flavor
|
|
38132
38277
|
});
|
|
38133
|
-
const
|
|
38134
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38278
|
+
const bytes = new TextEncoder().encode(xml);
|
|
38135
38279
|
const filename = `${inv.invoiceId ?? "invoice"}.xml`;
|
|
38136
38280
|
const generated_id = storeGenerated(bytes, filename);
|
|
38137
38281
|
return {
|
|
38138
|
-
|
|
38139
|
-
|
|
38140
|
-
|
|
38282
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38283
|
+
structuredContent: {
|
|
38284
|
+
generated_id,
|
|
38285
|
+
filename,
|
|
38286
|
+
preview: mapToViewerPreview(inv)
|
|
38287
|
+
}
|
|
38141
38288
|
};
|
|
38142
38289
|
}
|
|
38143
38290
|
},
|
|
@@ -38145,18 +38292,20 @@ var invoiceTools = [
|
|
|
38145
38292
|
{
|
|
38146
38293
|
name: "einvoice_invoice_generate_facturx",
|
|
38147
38294
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/invoice-viewer" } },
|
|
38148
|
-
|
|
38295
|
+
requires: ["generateFacturX"],
|
|
38296
|
+
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).",
|
|
38149
38297
|
category: "invoice",
|
|
38150
38298
|
inputSchema: {
|
|
38151
38299
|
type: "object",
|
|
38152
38300
|
properties: {
|
|
38153
38301
|
invoice: {
|
|
38154
38302
|
type: "object",
|
|
38155
|
-
description:
|
|
38303
|
+
description: INVOICE_SCHEMA_DESCRIPTION
|
|
38156
38304
|
},
|
|
38157
38305
|
flavor: {
|
|
38158
38306
|
type: "string",
|
|
38159
|
-
description: "Factur-X profile flavor
|
|
38307
|
+
description: "Factur-X profile flavor",
|
|
38308
|
+
enum: ["BASICWL", "EN16931", "EXTENDED"]
|
|
38160
38309
|
},
|
|
38161
38310
|
language: {
|
|
38162
38311
|
type: "string",
|
|
@@ -38170,20 +38319,21 @@ var invoiceTools = [
|
|
|
38170
38319
|
if (!input.invoice || !input.flavor) {
|
|
38171
38320
|
throw new Error("[einvoice_invoice_generate_facturx] 'invoice' and 'flavor' are required");
|
|
38172
38321
|
}
|
|
38173
|
-
const inv =
|
|
38174
|
-
const
|
|
38322
|
+
const inv = input.invoice;
|
|
38323
|
+
const { data: bytes } = await ctx.adapter.generateFacturX({
|
|
38175
38324
|
invoice: inv,
|
|
38176
38325
|
flavor: input.flavor,
|
|
38177
38326
|
language: input.language
|
|
38178
38327
|
});
|
|
38179
|
-
const raw2 = typeof result === "string" ? result : JSON.stringify(result);
|
|
38180
|
-
const bytes = new TextEncoder().encode(raw2);
|
|
38181
38328
|
const filename = `${inv.invoiceId ?? "invoice"}.pdf`;
|
|
38182
38329
|
const generated_id = storeGenerated(bytes, filename);
|
|
38183
38330
|
return {
|
|
38184
|
-
|
|
38185
|
-
|
|
38186
|
-
|
|
38331
|
+
content: `Invoice preview generated (${filename}). The user can review and submit via the viewer button.`,
|
|
38332
|
+
structuredContent: {
|
|
38333
|
+
generated_id,
|
|
38334
|
+
filename,
|
|
38335
|
+
preview: mapToViewerPreview(inv)
|
|
38336
|
+
}
|
|
38187
38337
|
};
|
|
38188
38338
|
}
|
|
38189
38339
|
}
|
|
@@ -38194,44 +38344,20 @@ var ENTITY_TYPE_LABELS = {
|
|
|
38194
38344
|
LEGAL_UNIT: "Entit\xE9 juridique",
|
|
38195
38345
|
OFFICE: "\xC9tablissement"
|
|
38196
38346
|
};
|
|
38197
|
-
function formatDirectoryFrRow(row) {
|
|
38198
|
-
const ci = row.countryIdentifier ?? {};
|
|
38199
|
-
const siren = ci.siren ?? row.siren;
|
|
38200
|
-
const siret = ci.siret ?? row.siret;
|
|
38201
|
-
const country = ci.country ?? row.country ?? "FR";
|
|
38202
|
-
return {
|
|
38203
|
-
_id: row.businessEntityId,
|
|
38204
|
-
_identifiers: row.identifiers,
|
|
38205
|
-
"Nom": row.name ?? "\u2014",
|
|
38206
|
-
"Type": ENTITY_TYPE_LABELS[row.type] ?? row.type ?? "\u2014",
|
|
38207
|
-
"SIREN": siren ?? "\u2014",
|
|
38208
|
-
"SIRET": siret ?? "\u2014",
|
|
38209
|
-
"Pays": country
|
|
38210
|
-
};
|
|
38211
|
-
}
|
|
38212
|
-
function autoWrapDirectoryQuery(q) {
|
|
38213
|
-
const trimmed = q.trim();
|
|
38214
|
-
if (/^\d{14}$/.test(trimmed)) return `siret:"${trimmed}"`;
|
|
38215
|
-
if (/^\d{9}$/.test(trimmed)) return `siren:"${trimmed}"`;
|
|
38216
|
-
if (/^FR\d{11}$/i.test(trimmed)) return `vatNumber:"${trimmed.toUpperCase()}"`;
|
|
38217
|
-
if (trimmed.length >= 3 && !/^\d+$/.test(trimmed) && !trimmed.includes(":")) {
|
|
38218
|
-
return `name:"*${trimmed}*"`;
|
|
38219
|
-
}
|
|
38220
|
-
return trimmed;
|
|
38221
|
-
}
|
|
38222
38347
|
var directoryTools = [
|
|
38223
38348
|
// ── French Directory ────────────────────────────────────
|
|
38224
38349
|
{
|
|
38225
38350
|
name: "einvoice_directory_fr_search",
|
|
38226
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/
|
|
38227
|
-
|
|
38351
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-list" } },
|
|
38352
|
+
requires: ["searchDirectoryFr"],
|
|
38353
|
+
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.",
|
|
38228
38354
|
category: "directory",
|
|
38229
38355
|
inputSchema: {
|
|
38230
38356
|
type: "object",
|
|
38231
38357
|
properties: {
|
|
38232
38358
|
q: {
|
|
38233
38359
|
type: "string",
|
|
38234
|
-
description:
|
|
38360
|
+
description: "Search query (required). SIRET, SIREN, VAT number, or company name."
|
|
38235
38361
|
},
|
|
38236
38362
|
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38237
38363
|
limit: { type: "number", description: "Max results (default 50, max 200)" }
|
|
@@ -38242,24 +38368,47 @@ var directoryTools = [
|
|
|
38242
38368
|
if (!input.q) {
|
|
38243
38369
|
throw new Error("[einvoice_directory_fr_search] 'q' query is required");
|
|
38244
38370
|
}
|
|
38245
|
-
const
|
|
38246
|
-
q:
|
|
38371
|
+
const { rows, count } = await ctx.adapter.searchDirectoryFr({
|
|
38372
|
+
q: input.q,
|
|
38247
38373
|
offset: input.offset,
|
|
38248
38374
|
limit: input.limit
|
|
38249
38375
|
});
|
|
38250
|
-
const
|
|
38251
|
-
|
|
38252
|
-
|
|
38253
|
-
|
|
38254
|
-
|
|
38255
|
-
|
|
38256
|
-
|
|
38376
|
+
const viewerData = {
|
|
38377
|
+
data: rows.map((r) => {
|
|
38378
|
+
const typeLabel = r.type ? ENTITY_TYPE_LABELS[r.type] ?? r.type : void 0;
|
|
38379
|
+
return {
|
|
38380
|
+
_id: r.entityId,
|
|
38381
|
+
_detail: {
|
|
38382
|
+
name: r.name,
|
|
38383
|
+
type: typeLabel,
|
|
38384
|
+
siren: r.siren,
|
|
38385
|
+
siret: r.siret,
|
|
38386
|
+
country: r.country,
|
|
38387
|
+
directory: r.directory,
|
|
38388
|
+
status: r.status,
|
|
38389
|
+
createdAt: r.createdAt,
|
|
38390
|
+
identifiers: r.identifiers
|
|
38391
|
+
},
|
|
38392
|
+
"Nom": r.name ?? "\u2014",
|
|
38393
|
+
"Type": typeLabel ?? "\u2014",
|
|
38394
|
+
"SIRET": r.siret ?? "\u2014",
|
|
38395
|
+
"Pays": r.country ?? "\u2014"
|
|
38396
|
+
};
|
|
38397
|
+
}),
|
|
38398
|
+
count,
|
|
38399
|
+
_title: "Annuaire fran\xE7ais"
|
|
38400
|
+
};
|
|
38401
|
+
return {
|
|
38402
|
+
content: `${rows.length} entities found for "${input.q}"`,
|
|
38403
|
+
structuredContent: viewerData
|
|
38404
|
+
};
|
|
38257
38405
|
}
|
|
38258
38406
|
},
|
|
38259
38407
|
// ── International Directory ─────────────────────────────
|
|
38260
38408
|
{
|
|
38261
38409
|
name: "einvoice_directory_int_search",
|
|
38262
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/
|
|
38410
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-list" } },
|
|
38411
|
+
requires: ["searchDirectoryInt"],
|
|
38263
38412
|
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).",
|
|
38264
38413
|
category: "directory",
|
|
38265
38414
|
inputSchema: {
|
|
@@ -38288,6 +38437,7 @@ var directoryTools = [
|
|
|
38288
38437
|
// ── Check Peppol Participant ────────────────────────────
|
|
38289
38438
|
{
|
|
38290
38439
|
name: "einvoice_directory_peppol_check",
|
|
38440
|
+
requires: ["checkPeppolParticipant"],
|
|
38291
38441
|
description: "Verify whether a specific Peppol participant exists in the international directory. Checks by scheme and identifier value.",
|
|
38292
38442
|
category: "directory",
|
|
38293
38443
|
inputSchema: {
|
|
@@ -38323,7 +38473,9 @@ var statusTools = [
|
|
|
38323
38473
|
// ── Send Status ─────────────────────────────────────────
|
|
38324
38474
|
{
|
|
38325
38475
|
name: "einvoice_status_send",
|
|
38326
|
-
|
|
38476
|
+
annotations: { destructiveHint: true },
|
|
38477
|
+
requires: ["sendStatus"],
|
|
38478
|
+
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.",
|
|
38327
38479
|
category: "status",
|
|
38328
38480
|
inputSchema: {
|
|
38329
38481
|
type: "object",
|
|
@@ -38375,7 +38527,9 @@ var statusTools = [
|
|
|
38375
38527
|
// ── Get Status History ────────────────────────────────────
|
|
38376
38528
|
{
|
|
38377
38529
|
name: "einvoice_status_history",
|
|
38530
|
+
annotations: { readOnlyHint: true },
|
|
38378
38531
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/status-timeline" } },
|
|
38532
|
+
requires: ["getStatusHistory"],
|
|
38379
38533
|
description: "Get the status history for an invoice. Returns all status changes in chronological order.",
|
|
38380
38534
|
category: "status",
|
|
38381
38535
|
inputSchema: {
|
|
@@ -38389,64 +38543,14 @@ var statusTools = [
|
|
|
38389
38543
|
if (!input.invoice_id) {
|
|
38390
38544
|
throw new Error("[einvoice_status_history] 'invoice_id' is required");
|
|
38391
38545
|
}
|
|
38392
|
-
const
|
|
38393
|
-
if (Array.isArray(raw2)) return { entries: raw2 };
|
|
38394
|
-
if (raw2 && typeof raw2 === "object") {
|
|
38395
|
-
const obj = raw2;
|
|
38396
|
-
if (Array.isArray(obj.data)) return { entries: obj.data };
|
|
38397
|
-
if (Array.isArray(obj.entries)) return raw2;
|
|
38398
|
-
if (Array.isArray(obj.history)) return { entries: obj.history };
|
|
38399
|
-
}
|
|
38400
|
-
return { entries: [] };
|
|
38401
|
-
}
|
|
38402
|
-
},
|
|
38403
|
-
// ── Not Seen ────────────────────────────────────────────
|
|
38404
|
-
{
|
|
38405
|
-
name: "einvoice_status_not_seen",
|
|
38406
|
-
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38407
|
-
description: "Get status updates that have not been marked as seen. Useful for polling new incoming status changes on invoices.",
|
|
38408
|
-
category: "status",
|
|
38409
|
-
inputSchema: {
|
|
38410
|
-
type: "object",
|
|
38411
|
-
properties: {
|
|
38412
|
-
offset: { type: "number", description: "Result offset (default 0)" },
|
|
38413
|
-
limit: { type: "number", description: "Max results (default 50)" }
|
|
38414
|
-
}
|
|
38415
|
-
},
|
|
38416
|
-
handler: async (input, ctx) => {
|
|
38417
|
-
const result = await ctx.adapter.getUnseenStatuses({
|
|
38418
|
-
offset: input.offset,
|
|
38419
|
-
limit: input.limit
|
|
38420
|
-
});
|
|
38546
|
+
const result = await ctx.adapter.getStatusHistory(input.invoice_id);
|
|
38421
38547
|
return {
|
|
38422
|
-
|
|
38423
|
-
|
|
38424
|
-
toolName: "einvoice_status_history",
|
|
38425
|
-
idField: "invoiceId",
|
|
38426
|
-
argName: "invoice_id"
|
|
38427
|
-
}
|
|
38548
|
+
content: `${result.entries.length} status entries for invoice ${input.invoice_id}`,
|
|
38549
|
+
structuredContent: result
|
|
38428
38550
|
};
|
|
38429
38551
|
}
|
|
38430
|
-
},
|
|
38431
|
-
// ── Mark as Seen ────────────────────────────────────────
|
|
38432
|
-
{
|
|
38433
|
-
name: "einvoice_status_mark_seen",
|
|
38434
|
-
description: "Mark a status update as seen/processed.",
|
|
38435
|
-
category: "status",
|
|
38436
|
-
inputSchema: {
|
|
38437
|
-
type: "object",
|
|
38438
|
-
properties: {
|
|
38439
|
-
status_id: { type: "string", description: "Status ID" }
|
|
38440
|
-
},
|
|
38441
|
-
required: ["status_id"]
|
|
38442
|
-
},
|
|
38443
|
-
handler: async (input, ctx) => {
|
|
38444
|
-
if (!input.status_id) {
|
|
38445
|
-
throw new Error("[einvoice_status_mark_seen] 'status_id' is required");
|
|
38446
|
-
}
|
|
38447
|
-
return await ctx.adapter.markStatusSeen(input.status_id);
|
|
38448
|
-
}
|
|
38449
38552
|
}
|
|
38553
|
+
// ── seen/notSeen tools removed — see invoice.ts comment and docs/CHANGELOG.md
|
|
38450
38554
|
];
|
|
38451
38555
|
|
|
38452
38556
|
// src/tools/reporting.ts
|
|
@@ -38454,6 +38558,7 @@ var reportingTools = [
|
|
|
38454
38558
|
// ── Invoice Transaction Reporting ───────────────────────
|
|
38455
38559
|
{
|
|
38456
38560
|
name: "einvoice_reporting_invoice_transaction",
|
|
38561
|
+
requires: ["reportInvoiceTransaction"],
|
|
38457
38562
|
description: "Report an invoice transaction to the French tax authority (e-reporting). Required for B2C and international invoice transactions. Asynchronous \u2014 returns a GUID.",
|
|
38458
38563
|
category: "reporting",
|
|
38459
38564
|
inputSchema: {
|
|
@@ -38461,7 +38566,7 @@ var reportingTools = [
|
|
|
38461
38566
|
properties: {
|
|
38462
38567
|
transaction: {
|
|
38463
38568
|
type: "object",
|
|
38464
|
-
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
|
|
38569
|
+
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."
|
|
38465
38570
|
}
|
|
38466
38571
|
},
|
|
38467
38572
|
required: ["transaction"]
|
|
@@ -38480,6 +38585,7 @@ var reportingTools = [
|
|
|
38480
38585
|
// ── Non-Invoice Transaction Reporting ───────────────────
|
|
38481
38586
|
{
|
|
38482
38587
|
name: "einvoice_reporting_transaction",
|
|
38588
|
+
requires: ["reportTransaction"],
|
|
38483
38589
|
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.",
|
|
38484
38590
|
category: "reporting",
|
|
38485
38591
|
inputSchema: {
|
|
@@ -38491,7 +38597,7 @@ var reportingTools = [
|
|
|
38491
38597
|
},
|
|
38492
38598
|
transaction: {
|
|
38493
38599
|
type: "object",
|
|
38494
|
-
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
|
|
38600
|
+
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."
|
|
38495
38601
|
}
|
|
38496
38602
|
},
|
|
38497
38603
|
required: ["business_entity_id", "transaction"]
|
|
@@ -38518,6 +38624,7 @@ var webhookTools = [
|
|
|
38518
38624
|
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38519
38625
|
description: "List all configured webhooks for your account.",
|
|
38520
38626
|
category: "webhook",
|
|
38627
|
+
requires: ["listWebhooks"],
|
|
38521
38628
|
inputSchema: {
|
|
38522
38629
|
type: "object",
|
|
38523
38630
|
properties: {}
|
|
@@ -38531,6 +38638,7 @@ var webhookTools = [
|
|
|
38531
38638
|
name: "einvoice_webhook_get",
|
|
38532
38639
|
description: "Get a single webhook configuration by its ID.",
|
|
38533
38640
|
category: "webhook",
|
|
38641
|
+
requires: ["getWebhook"],
|
|
38534
38642
|
inputSchema: {
|
|
38535
38643
|
type: "object",
|
|
38536
38644
|
properties: {
|
|
@@ -38548,6 +38656,7 @@ var webhookTools = [
|
|
|
38548
38656
|
// ── Create ──────────────────────────────────────────────
|
|
38549
38657
|
{
|
|
38550
38658
|
name: "einvoice_webhook_create",
|
|
38659
|
+
requires: ["createWebhook"],
|
|
38551
38660
|
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).",
|
|
38552
38661
|
category: "webhook",
|
|
38553
38662
|
inputSchema: {
|
|
@@ -38592,6 +38701,7 @@ var webhookTools = [
|
|
|
38592
38701
|
name: "einvoice_webhook_update",
|
|
38593
38702
|
description: "Update an existing webhook configuration.",
|
|
38594
38703
|
category: "webhook",
|
|
38704
|
+
requires: ["updateWebhook"],
|
|
38595
38705
|
inputSchema: {
|
|
38596
38706
|
type: "object",
|
|
38597
38707
|
properties: {
|
|
@@ -38624,6 +38734,7 @@ var webhookTools = [
|
|
|
38624
38734
|
name: "einvoice_webhook_delete",
|
|
38625
38735
|
description: "Delete a webhook configuration.",
|
|
38626
38736
|
category: "webhook",
|
|
38737
|
+
requires: ["deleteWebhook"],
|
|
38627
38738
|
inputSchema: {
|
|
38628
38739
|
type: "object",
|
|
38629
38740
|
properties: {
|
|
@@ -38640,27 +38751,490 @@ var webhookTools = [
|
|
|
38640
38751
|
}
|
|
38641
38752
|
];
|
|
38642
38753
|
|
|
38643
|
-
// src/tools/
|
|
38644
|
-
var
|
|
38645
|
-
|
|
38646
|
-
|
|
38647
|
-
status: statusTools,
|
|
38648
|
-
reporting: reportingTools,
|
|
38649
|
-
webhook: webhookTools
|
|
38754
|
+
// src/tools/config.ts
|
|
38755
|
+
var ENTITY_TYPE_LABELS2 = {
|
|
38756
|
+
LEGAL_UNIT: "Entit\xE9 juridique",
|
|
38757
|
+
OFFICE: "\xC9tablissement"
|
|
38650
38758
|
};
|
|
38651
|
-
var
|
|
38652
|
-
|
|
38653
|
-
|
|
38654
|
-
|
|
38655
|
-
|
|
38656
|
-
|
|
38657
|
-
|
|
38658
|
-
|
|
38659
|
-
|
|
38660
|
-
}
|
|
38661
|
-
|
|
38662
|
-
|
|
38663
|
-
|
|
38759
|
+
var configTools = [
|
|
38760
|
+
// ── Customer ID ──────────────────────────────────────
|
|
38761
|
+
{
|
|
38762
|
+
name: "einvoice_config_customer_id",
|
|
38763
|
+
requires: ["getCustomerId"],
|
|
38764
|
+
description: "Get the current operator customer ID. This is your unique operator identifier on the e-invoicing platform.",
|
|
38765
|
+
category: "config",
|
|
38766
|
+
inputSchema: {
|
|
38767
|
+
type: "object",
|
|
38768
|
+
properties: {}
|
|
38769
|
+
},
|
|
38770
|
+
handler: async (_input, ctx) => {
|
|
38771
|
+
return await ctx.adapter.getCustomerId();
|
|
38772
|
+
}
|
|
38773
|
+
},
|
|
38774
|
+
// ── List Business Entities ───────────────────────────
|
|
38775
|
+
{
|
|
38776
|
+
name: "einvoice_config_entities_list",
|
|
38777
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/doclist-viewer" } },
|
|
38778
|
+
requires: ["listBusinessEntities"],
|
|
38779
|
+
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.",
|
|
38780
|
+
category: "config",
|
|
38781
|
+
inputSchema: {
|
|
38782
|
+
type: "object",
|
|
38783
|
+
properties: {}
|
|
38784
|
+
},
|
|
38785
|
+
handler: async (_input, ctx) => {
|
|
38786
|
+
const { rows, count } = await ctx.adapter.listBusinessEntities();
|
|
38787
|
+
return {
|
|
38788
|
+
data: rows.map((r) => ({
|
|
38789
|
+
_id: r.entityId,
|
|
38790
|
+
"Nom": r.name ?? "\u2014",
|
|
38791
|
+
"SIRET": r.siret ?? "\u2014",
|
|
38792
|
+
"Type": ENTITY_TYPE_LABELS2[r.type ?? ""] ?? r.type ?? "\u2014"
|
|
38793
|
+
})),
|
|
38794
|
+
count,
|
|
38795
|
+
_title: "Entit\xE9s op\xE9rateur",
|
|
38796
|
+
_rowAction: {
|
|
38797
|
+
toolName: "einvoice_config_entity_get",
|
|
38798
|
+
idField: "_id",
|
|
38799
|
+
argName: "id"
|
|
38800
|
+
}
|
|
38801
|
+
};
|
|
38802
|
+
}
|
|
38803
|
+
},
|
|
38804
|
+
// ── Get Business Entity ──────────────────────────────
|
|
38805
|
+
{
|
|
38806
|
+
name: "einvoice_config_entity_get",
|
|
38807
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/directory-card" } },
|
|
38808
|
+
requires: ["getBusinessEntity"],
|
|
38809
|
+
description: "Get details of a specific business entity managed by your operator. Shows registration info, identifiers, and enrollment status.",
|
|
38810
|
+
category: "config",
|
|
38811
|
+
inputSchema: {
|
|
38812
|
+
type: "object",
|
|
38813
|
+
properties: {
|
|
38814
|
+
id: { type: "string", description: "Business entity ID" }
|
|
38815
|
+
},
|
|
38816
|
+
required: ["id"]
|
|
38817
|
+
},
|
|
38818
|
+
handler: async (input, ctx) => {
|
|
38819
|
+
if (!input.id) {
|
|
38820
|
+
throw new Error("[einvoice_config_entity_get] 'id' is required");
|
|
38821
|
+
}
|
|
38822
|
+
return await ctx.adapter.getBusinessEntity(input.id);
|
|
38823
|
+
}
|
|
38824
|
+
},
|
|
38825
|
+
// ── Create Legal Unit ────────────────────────────────
|
|
38826
|
+
{
|
|
38827
|
+
name: "einvoice_config_entity_create_legal",
|
|
38828
|
+
requires: ["createLegalUnit"],
|
|
38829
|
+
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.",
|
|
38830
|
+
category: "config",
|
|
38831
|
+
inputSchema: {
|
|
38832
|
+
type: "object",
|
|
38833
|
+
properties: {
|
|
38834
|
+
siren: {
|
|
38835
|
+
type: "string",
|
|
38836
|
+
description: "SIREN number (9 digits) of the company to register"
|
|
38837
|
+
},
|
|
38838
|
+
name: {
|
|
38839
|
+
type: "string",
|
|
38840
|
+
description: "Company name"
|
|
38841
|
+
},
|
|
38842
|
+
country: {
|
|
38843
|
+
type: "string",
|
|
38844
|
+
description: "Country code (default: FR)"
|
|
38845
|
+
},
|
|
38846
|
+
scope: {
|
|
38847
|
+
type: "string",
|
|
38848
|
+
description: "Entity scope",
|
|
38849
|
+
enum: ["PRIVATE_TAX_PAYER", "PUBLIC", "PRIMARY", "SECONDARY"]
|
|
38850
|
+
}
|
|
38851
|
+
},
|
|
38852
|
+
required: ["siren"]
|
|
38853
|
+
},
|
|
38854
|
+
handler: async (input, ctx) => {
|
|
38855
|
+
if (!input.siren) {
|
|
38856
|
+
throw new Error("[einvoice_config_entity_create_legal] 'siren' is required");
|
|
38857
|
+
}
|
|
38858
|
+
return await ctx.adapter.createLegalUnit({
|
|
38859
|
+
identifierScheme: "0002",
|
|
38860
|
+
identifierValue: input.siren,
|
|
38861
|
+
name: input.name,
|
|
38862
|
+
country: input.country ?? "FR",
|
|
38863
|
+
scope: input.scope ?? "PRIMARY"
|
|
38864
|
+
});
|
|
38865
|
+
}
|
|
38866
|
+
},
|
|
38867
|
+
// ── Create Office ────────────────────────────────────
|
|
38868
|
+
{
|
|
38869
|
+
name: "einvoice_config_entity_create_office",
|
|
38870
|
+
requires: ["createOffice"],
|
|
38871
|
+
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.",
|
|
38872
|
+
category: "config",
|
|
38873
|
+
inputSchema: {
|
|
38874
|
+
type: "object",
|
|
38875
|
+
properties: {
|
|
38876
|
+
siret: {
|
|
38877
|
+
type: "string",
|
|
38878
|
+
description: "SIRET number (14 digits) of the establishment"
|
|
38879
|
+
},
|
|
38880
|
+
legalUnitId: {
|
|
38881
|
+
type: "string",
|
|
38882
|
+
description: "Business entity ID of the parent legal unit"
|
|
38883
|
+
},
|
|
38884
|
+
name: {
|
|
38885
|
+
type: "string",
|
|
38886
|
+
description: "Office name"
|
|
38887
|
+
},
|
|
38888
|
+
scope: {
|
|
38889
|
+
type: "string",
|
|
38890
|
+
description: "Entity scope",
|
|
38891
|
+
enum: ["PRIVATE_TAX_PAYER", "PUBLIC", "PRIMARY", "SECONDARY"]
|
|
38892
|
+
}
|
|
38893
|
+
},
|
|
38894
|
+
required: ["siret", "legalUnitId"]
|
|
38895
|
+
},
|
|
38896
|
+
handler: async (input, ctx) => {
|
|
38897
|
+
if (!input.siret || !input.legalUnitId) {
|
|
38898
|
+
throw new Error("[einvoice_config_entity_create_office] 'siret' and 'legalUnitId' are required");
|
|
38899
|
+
}
|
|
38900
|
+
return await ctx.adapter.createOffice({
|
|
38901
|
+
identifierScheme: "0009",
|
|
38902
|
+
identifierValue: input.siret,
|
|
38903
|
+
legalBusinessEntityId: input.legalUnitId,
|
|
38904
|
+
name: input.name,
|
|
38905
|
+
scope: input.scope ?? "PRIMARY"
|
|
38906
|
+
});
|
|
38907
|
+
}
|
|
38908
|
+
},
|
|
38909
|
+
// ── Enroll French Entity ─────────────────────────────
|
|
38910
|
+
{
|
|
38911
|
+
name: "einvoice_config_enroll_fr",
|
|
38912
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
38913
|
+
requires: ["enrollFrench"],
|
|
38914
|
+
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).",
|
|
38915
|
+
category: "config",
|
|
38916
|
+
inputSchema: {
|
|
38917
|
+
type: "object",
|
|
38918
|
+
properties: {
|
|
38919
|
+
siret: {
|
|
38920
|
+
type: "string",
|
|
38921
|
+
description: "SIRET (14 digits) of the entity to enroll"
|
|
38922
|
+
},
|
|
38923
|
+
siren: {
|
|
38924
|
+
type: "string",
|
|
38925
|
+
description: "SIREN (9 digits, first 9 digits of SIRET). If omitted, extracted from SIRET automatically."
|
|
38926
|
+
}
|
|
38927
|
+
},
|
|
38928
|
+
required: ["siret"]
|
|
38929
|
+
},
|
|
38930
|
+
handler: async (input, ctx) => {
|
|
38931
|
+
if (!input.siret) {
|
|
38932
|
+
throw new Error("[einvoice_config_enroll_fr] 'siret' is required");
|
|
38933
|
+
}
|
|
38934
|
+
const siret = input.siret;
|
|
38935
|
+
const siren = input.siren ?? siret.slice(0, 9);
|
|
38936
|
+
return await ctx.adapter.enrollFrench({ siret, siren });
|
|
38937
|
+
}
|
|
38938
|
+
},
|
|
38939
|
+
// ── Claim Business Entity ────────────────────────────
|
|
38940
|
+
{
|
|
38941
|
+
name: "einvoice_config_entity_claim",
|
|
38942
|
+
requires: ["claimBusinessEntityByIdentifier"],
|
|
38943
|
+
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.",
|
|
38944
|
+
category: "config",
|
|
38945
|
+
inputSchema: {
|
|
38946
|
+
type: "object",
|
|
38947
|
+
properties: {
|
|
38948
|
+
scheme: {
|
|
38949
|
+
type: "string",
|
|
38950
|
+
description: "Identifier scheme (e.g. '0009' for SIRET)"
|
|
38951
|
+
},
|
|
38952
|
+
value: {
|
|
38953
|
+
type: "string",
|
|
38954
|
+
description: "Identifier value (e.g. SIRET number)"
|
|
38955
|
+
}
|
|
38956
|
+
},
|
|
38957
|
+
required: ["scheme", "value"]
|
|
38958
|
+
},
|
|
38959
|
+
handler: async (input, ctx) => {
|
|
38960
|
+
if (!input.scheme || !input.value) {
|
|
38961
|
+
throw new Error("[einvoice_config_entity_claim] 'scheme' and 'value' are required");
|
|
38962
|
+
}
|
|
38963
|
+
return await ctx.adapter.claimBusinessEntityByIdentifier(
|
|
38964
|
+
input.scheme,
|
|
38965
|
+
input.value,
|
|
38966
|
+
{}
|
|
38967
|
+
);
|
|
38968
|
+
}
|
|
38969
|
+
},
|
|
38970
|
+
// ── Delete Business Entity ───────────────────────────
|
|
38971
|
+
{
|
|
38972
|
+
name: "einvoice_config_entity_delete",
|
|
38973
|
+
requires: ["deleteBusinessEntity"],
|
|
38974
|
+
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.",
|
|
38975
|
+
category: "config",
|
|
38976
|
+
inputSchema: {
|
|
38977
|
+
type: "object",
|
|
38978
|
+
properties: {
|
|
38979
|
+
id: { type: "string", description: "Business entity ID to remove" }
|
|
38980
|
+
},
|
|
38981
|
+
required: ["id"]
|
|
38982
|
+
},
|
|
38983
|
+
handler: async (input, ctx) => {
|
|
38984
|
+
if (!input.id) {
|
|
38985
|
+
throw new Error("[einvoice_config_entity_delete] 'id' is required");
|
|
38986
|
+
}
|
|
38987
|
+
return await ctx.adapter.deleteBusinessEntity(input.id);
|
|
38988
|
+
}
|
|
38989
|
+
},
|
|
38990
|
+
// ── Register on Network ──────────────────────────────
|
|
38991
|
+
{
|
|
38992
|
+
name: "einvoice_config_network_register",
|
|
38993
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
38994
|
+
requires: ["registerNetwork"],
|
|
38995
|
+
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.",
|
|
38996
|
+
category: "config",
|
|
38997
|
+
inputSchema: {
|
|
38998
|
+
type: "object",
|
|
38999
|
+
properties: {
|
|
39000
|
+
identifier_id: {
|
|
39001
|
+
type: "string",
|
|
39002
|
+
description: "Business entity identifier ID (UUID, from the identifiers array in entity details)"
|
|
39003
|
+
},
|
|
39004
|
+
network: {
|
|
39005
|
+
type: "string",
|
|
39006
|
+
description: "Network to register on: DOMESTIC_FR (French PPF) or PEPPOL_INTERNATIONAL",
|
|
39007
|
+
enum: ["DOMESTIC_FR", "PEPPOL_INTERNATIONAL"]
|
|
39008
|
+
}
|
|
39009
|
+
},
|
|
39010
|
+
required: ["identifier_id", "network"]
|
|
39011
|
+
},
|
|
39012
|
+
handler: async (input, ctx) => {
|
|
39013
|
+
if (!input.identifier_id || !input.network) {
|
|
39014
|
+
throw new Error("[einvoice_config_network_register] 'identifier_id' and 'network' are required");
|
|
39015
|
+
}
|
|
39016
|
+
return await ctx.adapter.registerNetwork(
|
|
39017
|
+
input.identifier_id,
|
|
39018
|
+
input.network
|
|
39019
|
+
);
|
|
39020
|
+
}
|
|
39021
|
+
},
|
|
39022
|
+
// ── Register on Network by Scheme/Value ──────────────
|
|
39023
|
+
{
|
|
39024
|
+
name: "einvoice_config_network_register_by_id",
|
|
39025
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39026
|
+
requires: ["registerNetworkByScheme"],
|
|
39027
|
+
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.",
|
|
39028
|
+
category: "config",
|
|
39029
|
+
inputSchema: {
|
|
39030
|
+
type: "object",
|
|
39031
|
+
properties: {
|
|
39032
|
+
scheme: {
|
|
39033
|
+
type: "string",
|
|
39034
|
+
description: "Identifier scheme: '0009' for SIRET, '0002' for SIREN, '0225' for SIREN_SIRET"
|
|
39035
|
+
},
|
|
39036
|
+
value: {
|
|
39037
|
+
type: "string",
|
|
39038
|
+
description: "Identifier value (e.g. SIRET number)"
|
|
39039
|
+
},
|
|
39040
|
+
network: {
|
|
39041
|
+
type: "string",
|
|
39042
|
+
description: "Network: DOMESTIC_FR or PEPPOL_INTERNATIONAL",
|
|
39043
|
+
enum: ["DOMESTIC_FR", "PEPPOL_INTERNATIONAL"]
|
|
39044
|
+
}
|
|
39045
|
+
},
|
|
39046
|
+
required: ["scheme", "value", "network"]
|
|
39047
|
+
},
|
|
39048
|
+
handler: async (input, ctx) => {
|
|
39049
|
+
if (!input.scheme || !input.value || !input.network) {
|
|
39050
|
+
throw new Error("[einvoice_config_network_register_by_id] 'scheme', 'value', and 'network' are required");
|
|
39051
|
+
}
|
|
39052
|
+
return await ctx.adapter.registerNetworkByScheme(
|
|
39053
|
+
input.scheme,
|
|
39054
|
+
input.value,
|
|
39055
|
+
input.network
|
|
39056
|
+
);
|
|
39057
|
+
}
|
|
39058
|
+
},
|
|
39059
|
+
// ── Create Identifier ────────────────────────────────
|
|
39060
|
+
{
|
|
39061
|
+
name: "einvoice_config_identifier_create",
|
|
39062
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39063
|
+
requires: ["createIdentifier"],
|
|
39064
|
+
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.",
|
|
39065
|
+
category: "config",
|
|
39066
|
+
inputSchema: {
|
|
39067
|
+
type: "object",
|
|
39068
|
+
properties: {
|
|
39069
|
+
entity_id: { type: "string", description: "Business entity ID to add the identifier to" },
|
|
39070
|
+
scheme: {
|
|
39071
|
+
type: "string",
|
|
39072
|
+
description: "Identifier scheme: '0009' (SIRET), '0002' (SIREN), '0225' (SIREN_SIRET)"
|
|
39073
|
+
},
|
|
39074
|
+
value: { type: "string", description: "Identifier value (e.g. SIRET number)" },
|
|
39075
|
+
type: {
|
|
39076
|
+
type: "string",
|
|
39077
|
+
description: "Identifier type",
|
|
39078
|
+
enum: ["ROUTING_CODE", "SUFFIX"]
|
|
39079
|
+
}
|
|
39080
|
+
},
|
|
39081
|
+
required: ["entity_id", "scheme", "value", "type"]
|
|
39082
|
+
},
|
|
39083
|
+
handler: async (input, ctx) => {
|
|
39084
|
+
if (!input.entity_id || !input.scheme || !input.value || !input.type) {
|
|
39085
|
+
throw new Error("[einvoice_config_identifier_create] 'entity_id', 'scheme', 'value', and 'type' are required");
|
|
39086
|
+
}
|
|
39087
|
+
return await ctx.adapter.createIdentifier(input.entity_id, {
|
|
39088
|
+
scheme: input.scheme,
|
|
39089
|
+
value: input.value,
|
|
39090
|
+
type: input.type
|
|
39091
|
+
});
|
|
39092
|
+
}
|
|
39093
|
+
},
|
|
39094
|
+
// ── Create Identifier by Scheme/Value ──────────────────
|
|
39095
|
+
{
|
|
39096
|
+
name: "einvoice_config_identifier_create_by_scheme",
|
|
39097
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39098
|
+
requires: ["createIdentifierByScheme"],
|
|
39099
|
+
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.",
|
|
39100
|
+
category: "config",
|
|
39101
|
+
inputSchema: {
|
|
39102
|
+
type: "object",
|
|
39103
|
+
properties: {
|
|
39104
|
+
lookup_scheme: { type: "string", description: "Scheme to find the entity (e.g. '0009' for SIRET)" },
|
|
39105
|
+
lookup_value: { type: "string", description: "Value to find the entity (e.g. SIRET number)" },
|
|
39106
|
+
new_scheme: { type: "string", description: "Scheme of the new identifier to add" },
|
|
39107
|
+
new_value: { type: "string", description: "Value of the new identifier to add" }
|
|
39108
|
+
},
|
|
39109
|
+
required: ["lookup_scheme", "lookup_value", "new_scheme", "new_value"]
|
|
39110
|
+
},
|
|
39111
|
+
handler: async (input, ctx) => {
|
|
39112
|
+
if (!input.lookup_scheme || !input.lookup_value || !input.new_scheme || !input.new_value) {
|
|
39113
|
+
throw new Error("[einvoice_config_identifier_create_by_scheme] all fields are required");
|
|
39114
|
+
}
|
|
39115
|
+
return await ctx.adapter.createIdentifierByScheme(
|
|
39116
|
+
input.lookup_scheme,
|
|
39117
|
+
input.lookup_value,
|
|
39118
|
+
{ scheme: input.new_scheme, value: input.new_value }
|
|
39119
|
+
);
|
|
39120
|
+
}
|
|
39121
|
+
},
|
|
39122
|
+
// ── Delete Identifier ──────────────────────────────────
|
|
39123
|
+
{
|
|
39124
|
+
name: "einvoice_config_identifier_delete",
|
|
39125
|
+
requires: ["deleteIdentifier"],
|
|
39126
|
+
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).",
|
|
39127
|
+
category: "config",
|
|
39128
|
+
inputSchema: {
|
|
39129
|
+
type: "object",
|
|
39130
|
+
properties: {
|
|
39131
|
+
identifier_id: { type: "string", description: "Identifier UUID to delete" }
|
|
39132
|
+
},
|
|
39133
|
+
required: ["identifier_id"]
|
|
39134
|
+
},
|
|
39135
|
+
handler: async (input, ctx) => {
|
|
39136
|
+
if (!input.identifier_id) {
|
|
39137
|
+
throw new Error("[einvoice_config_identifier_delete] 'identifier_id' is required");
|
|
39138
|
+
}
|
|
39139
|
+
return await ctx.adapter.deleteIdentifier(input.identifier_id);
|
|
39140
|
+
}
|
|
39141
|
+
},
|
|
39142
|
+
// ── Configure Business Entity ──────────────────────────
|
|
39143
|
+
{
|
|
39144
|
+
name: "einvoice_config_entity_configure",
|
|
39145
|
+
_meta: { ui: { resourceUri: "ui://mcp-einvoice/action-result" } },
|
|
39146
|
+
requires: ["configureBusinessEntity"],
|
|
39147
|
+
description: "Configure a business entity's settings (e.g. VAT regime). Use einvoice_config_entity_get to see current configuration first.",
|
|
39148
|
+
category: "config",
|
|
39149
|
+
inputSchema: {
|
|
39150
|
+
type: "object",
|
|
39151
|
+
properties: {
|
|
39152
|
+
entity_id: { type: "string", description: "Business entity ID" },
|
|
39153
|
+
vat_regime: {
|
|
39154
|
+
type: "string",
|
|
39155
|
+
description: "VAT regime: REAL_MONTHLY_TAX_REGIME, REAL_QUARTERLY_TAX_REGIME, SIMPLIFIED_TAX_REGIME, or VAT_EXEMPTION_REGIME",
|
|
39156
|
+
enum: ["REAL_MONTHLY_TAX_REGIME", "REAL_QUARTERLY_TAX_REGIME", "SIMPLIFIED_TAX_REGIME", "VAT_EXEMPTION_REGIME"]
|
|
39157
|
+
}
|
|
39158
|
+
},
|
|
39159
|
+
required: ["entity_id"]
|
|
39160
|
+
},
|
|
39161
|
+
handler: async (input, ctx) => {
|
|
39162
|
+
if (!input.entity_id) {
|
|
39163
|
+
throw new Error("[einvoice_config_entity_configure] 'entity_id' is required");
|
|
39164
|
+
}
|
|
39165
|
+
const config2 = {};
|
|
39166
|
+
if (input.vat_regime) config2.vatRegime = input.vat_regime;
|
|
39167
|
+
return await ctx.adapter.configureBusinessEntity(input.entity_id, config2);
|
|
39168
|
+
}
|
|
39169
|
+
},
|
|
39170
|
+
// ── Delete Claim ───────────────────────────────────────
|
|
39171
|
+
{
|
|
39172
|
+
name: "einvoice_config_claim_delete",
|
|
39173
|
+
requires: ["deleteClaim"],
|
|
39174
|
+
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.",
|
|
39175
|
+
category: "config",
|
|
39176
|
+
inputSchema: {
|
|
39177
|
+
type: "object",
|
|
39178
|
+
properties: {
|
|
39179
|
+
entity_id: { type: "string", description: "Business entity ID to unclaim" }
|
|
39180
|
+
},
|
|
39181
|
+
required: ["entity_id"]
|
|
39182
|
+
},
|
|
39183
|
+
handler: async (input, ctx) => {
|
|
39184
|
+
if (!input.entity_id) {
|
|
39185
|
+
throw new Error("[einvoice_config_claim_delete] 'entity_id' is required");
|
|
39186
|
+
}
|
|
39187
|
+
return await ctx.adapter.deleteClaim(input.entity_id);
|
|
39188
|
+
}
|
|
39189
|
+
},
|
|
39190
|
+
// ── Unregister from Network ──────────────────────────
|
|
39191
|
+
{
|
|
39192
|
+
name: "einvoice_config_network_unregister",
|
|
39193
|
+
requires: ["unregisterNetwork"],
|
|
39194
|
+
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.",
|
|
39195
|
+
category: "config",
|
|
39196
|
+
inputSchema: {
|
|
39197
|
+
type: "object",
|
|
39198
|
+
properties: {
|
|
39199
|
+
directory_id: {
|
|
39200
|
+
type: "string",
|
|
39201
|
+
description: "Directory entry ID (UUID from the networksRegistered array in entity identifiers)"
|
|
39202
|
+
}
|
|
39203
|
+
},
|
|
39204
|
+
required: ["directory_id"]
|
|
39205
|
+
},
|
|
39206
|
+
handler: async (input, ctx) => {
|
|
39207
|
+
if (!input.directory_id) {
|
|
39208
|
+
throw new Error("[einvoice_config_network_unregister] 'directory_id' is required");
|
|
39209
|
+
}
|
|
39210
|
+
return await ctx.adapter.unregisterNetwork(input.directory_id);
|
|
39211
|
+
}
|
|
39212
|
+
}
|
|
39213
|
+
];
|
|
39214
|
+
|
|
39215
|
+
// src/tools/mod.ts
|
|
39216
|
+
var toolsByCategory = {
|
|
39217
|
+
invoice: invoiceTools,
|
|
39218
|
+
directory: directoryTools,
|
|
39219
|
+
status: statusTools,
|
|
39220
|
+
reporting: reportingTools,
|
|
39221
|
+
webhook: webhookTools,
|
|
39222
|
+
config: configTools
|
|
39223
|
+
};
|
|
39224
|
+
var allTools = [
|
|
39225
|
+
...invoiceTools,
|
|
39226
|
+
...directoryTools,
|
|
39227
|
+
...statusTools,
|
|
39228
|
+
...reportingTools,
|
|
39229
|
+
...webhookTools,
|
|
39230
|
+
...configTools
|
|
39231
|
+
];
|
|
39232
|
+
function getToolsByCategory(category) {
|
|
39233
|
+
return toolsByCategory[category] ?? [];
|
|
39234
|
+
}
|
|
39235
|
+
|
|
39236
|
+
// src/client.ts
|
|
39237
|
+
var EInvoiceToolsClient = class {
|
|
38664
39238
|
tools;
|
|
38665
39239
|
constructor(options) {
|
|
38666
39240
|
if (options?.categories) {
|
|
@@ -38673,25 +39247,33 @@ var EInvoiceToolsClient = class {
|
|
|
38673
39247
|
listTools() {
|
|
38674
39248
|
return this.tools;
|
|
38675
39249
|
}
|
|
38676
|
-
/**
|
|
38677
|
-
|
|
38678
|
-
return this.tools.
|
|
39250
|
+
/** Filter tools to only those supported by the given adapter's capabilities. */
|
|
39251
|
+
supportedTools(adapter) {
|
|
39252
|
+
return this.tools.filter(
|
|
39253
|
+
(t) => !t.requires || t.requires.every((m) => adapter.capabilities.has(m))
|
|
39254
|
+
);
|
|
39255
|
+
}
|
|
39256
|
+
/** Convert tools to MCP wire format, filtered by adapter capabilities. */
|
|
39257
|
+
toMCPFormat(adapter) {
|
|
39258
|
+
return this.supportedTools(adapter).map((t) => {
|
|
38679
39259
|
const wire = {
|
|
38680
39260
|
name: t.name,
|
|
38681
39261
|
description: t.description,
|
|
38682
39262
|
inputSchema: t.inputSchema
|
|
38683
39263
|
};
|
|
38684
39264
|
if (t._meta) wire._meta = t._meta;
|
|
39265
|
+
if (t.annotations) wire.annotations = t.annotations;
|
|
38685
39266
|
return wire;
|
|
38686
39267
|
});
|
|
38687
39268
|
}
|
|
38688
39269
|
/**
|
|
38689
39270
|
* Build a handlers Map for ConcurrentMCPServer.registerTools().
|
|
38690
39271
|
* Each handler wraps the tool to inject the adapter context.
|
|
39272
|
+
* Only includes tools supported by the adapter's capabilities.
|
|
38691
39273
|
*/
|
|
38692
39274
|
buildHandlersMap(adapter) {
|
|
38693
39275
|
const handlers = /* @__PURE__ */ new Map();
|
|
38694
|
-
for (const tool of this.
|
|
39276
|
+
for (const tool of this.supportedTools(adapter)) {
|
|
38695
39277
|
handlers.set(tool.name, async (args) => {
|
|
38696
39278
|
return await tool.handler(args, { adapter });
|
|
38697
39279
|
});
|
|
@@ -38714,9 +39296,159 @@ var EInvoiceToolsClient = class {
|
|
|
38714
39296
|
}
|
|
38715
39297
|
};
|
|
38716
39298
|
|
|
38717
|
-
// src/
|
|
38718
|
-
|
|
38719
|
-
|
|
39299
|
+
// src/adapters/base-adapter.ts
|
|
39300
|
+
var BaseAdapter = class {
|
|
39301
|
+
notSupported(method, reason = "Not implemented by this adapter.") {
|
|
39302
|
+
return Promise.reject(new NotSupportedError(this.name, method, reason));
|
|
39303
|
+
}
|
|
39304
|
+
// ─── Invoice ───────────────────────────────────────────
|
|
39305
|
+
emitInvoice(_req) {
|
|
39306
|
+
return this.notSupported("emitInvoice");
|
|
39307
|
+
}
|
|
39308
|
+
searchInvoices(_filters) {
|
|
39309
|
+
return this.notSupported("searchInvoices");
|
|
39310
|
+
}
|
|
39311
|
+
getInvoice(_id) {
|
|
39312
|
+
return this.notSupported("getInvoice");
|
|
39313
|
+
}
|
|
39314
|
+
downloadInvoice(_id) {
|
|
39315
|
+
return this.notSupported("downloadInvoice");
|
|
39316
|
+
}
|
|
39317
|
+
downloadReadable(_id) {
|
|
39318
|
+
return this.notSupported("downloadReadable");
|
|
39319
|
+
}
|
|
39320
|
+
getInvoiceFiles(_id) {
|
|
39321
|
+
return this.notSupported("getInvoiceFiles");
|
|
39322
|
+
}
|
|
39323
|
+
getAttachments(_id) {
|
|
39324
|
+
return this.notSupported("getAttachments");
|
|
39325
|
+
}
|
|
39326
|
+
downloadFile(_fileId) {
|
|
39327
|
+
return this.notSupported("downloadFile");
|
|
39328
|
+
}
|
|
39329
|
+
markInvoiceSeen(_id) {
|
|
39330
|
+
return this.notSupported("markInvoiceSeen");
|
|
39331
|
+
}
|
|
39332
|
+
getUnseenInvoices(_pagination) {
|
|
39333
|
+
return this.notSupported("getUnseenInvoices");
|
|
39334
|
+
}
|
|
39335
|
+
generateCII(_req) {
|
|
39336
|
+
return this.notSupported("generateCII");
|
|
39337
|
+
}
|
|
39338
|
+
generateUBL(_req) {
|
|
39339
|
+
return this.notSupported("generateUBL");
|
|
39340
|
+
}
|
|
39341
|
+
generateFacturX(_req) {
|
|
39342
|
+
return this.notSupported("generateFacturX");
|
|
39343
|
+
}
|
|
39344
|
+
// ─── Directory ────────────────────────────────────────
|
|
39345
|
+
searchDirectoryFr(_filters) {
|
|
39346
|
+
return this.notSupported("searchDirectoryFr");
|
|
39347
|
+
}
|
|
39348
|
+
searchDirectoryInt(_filters) {
|
|
39349
|
+
return this.notSupported("searchDirectoryInt");
|
|
39350
|
+
}
|
|
39351
|
+
checkPeppolParticipant(_scheme, _value) {
|
|
39352
|
+
return this.notSupported("checkPeppolParticipant");
|
|
39353
|
+
}
|
|
39354
|
+
// ─── Status ────────────────────────────────────────────
|
|
39355
|
+
sendStatus(_req) {
|
|
39356
|
+
return this.notSupported("sendStatus");
|
|
39357
|
+
}
|
|
39358
|
+
getStatusHistory(_invoiceId) {
|
|
39359
|
+
return this.notSupported("getStatusHistory");
|
|
39360
|
+
}
|
|
39361
|
+
getUnseenStatuses(_pagination) {
|
|
39362
|
+
return this.notSupported("getUnseenStatuses");
|
|
39363
|
+
}
|
|
39364
|
+
markStatusSeen(_statusId) {
|
|
39365
|
+
return this.notSupported("markStatusSeen");
|
|
39366
|
+
}
|
|
39367
|
+
// ─── Reporting ─────────────────────────────────────────
|
|
39368
|
+
reportInvoiceTransaction(_transaction) {
|
|
39369
|
+
return this.notSupported("reportInvoiceTransaction");
|
|
39370
|
+
}
|
|
39371
|
+
reportTransaction(_businessEntityId, _transaction) {
|
|
39372
|
+
return this.notSupported("reportTransaction");
|
|
39373
|
+
}
|
|
39374
|
+
// ─── Webhooks ──────────────────────────────────────────
|
|
39375
|
+
listWebhooks() {
|
|
39376
|
+
return this.notSupported("listWebhooks");
|
|
39377
|
+
}
|
|
39378
|
+
getWebhook(_id) {
|
|
39379
|
+
return this.notSupported("getWebhook");
|
|
39380
|
+
}
|
|
39381
|
+
createWebhook(_req) {
|
|
39382
|
+
return this.notSupported("createWebhook");
|
|
39383
|
+
}
|
|
39384
|
+
updateWebhook(_id, _req) {
|
|
39385
|
+
return this.notSupported("updateWebhook");
|
|
39386
|
+
}
|
|
39387
|
+
deleteWebhook(_id) {
|
|
39388
|
+
return this.notSupported("deleteWebhook");
|
|
39389
|
+
}
|
|
39390
|
+
// ─── Operator Config ───────────────────────────────────
|
|
39391
|
+
getCustomerId() {
|
|
39392
|
+
return this.notSupported("getCustomerId");
|
|
39393
|
+
}
|
|
39394
|
+
listBusinessEntities() {
|
|
39395
|
+
return this.notSupported("listBusinessEntities");
|
|
39396
|
+
}
|
|
39397
|
+
getBusinessEntity(_id) {
|
|
39398
|
+
return this.notSupported("getBusinessEntity");
|
|
39399
|
+
}
|
|
39400
|
+
createLegalUnit(_data) {
|
|
39401
|
+
return this.notSupported("createLegalUnit");
|
|
39402
|
+
}
|
|
39403
|
+
createOffice(_data) {
|
|
39404
|
+
return this.notSupported("createOffice");
|
|
39405
|
+
}
|
|
39406
|
+
deleteBusinessEntity(_id) {
|
|
39407
|
+
return this.notSupported("deleteBusinessEntity");
|
|
39408
|
+
}
|
|
39409
|
+
configureBusinessEntity(_id, _data) {
|
|
39410
|
+
return this.notSupported("configureBusinessEntity");
|
|
39411
|
+
}
|
|
39412
|
+
claimBusinessEntity(_id, _data) {
|
|
39413
|
+
return this.notSupported("claimBusinessEntity");
|
|
39414
|
+
}
|
|
39415
|
+
claimBusinessEntityByIdentifier(_scheme, _value, _data) {
|
|
39416
|
+
return this.notSupported("claimBusinessEntityByIdentifier");
|
|
39417
|
+
}
|
|
39418
|
+
enrollFrench(_data) {
|
|
39419
|
+
return this.notSupported("enrollFrench");
|
|
39420
|
+
}
|
|
39421
|
+
enrollInternational(_data) {
|
|
39422
|
+
return this.notSupported("enrollInternational");
|
|
39423
|
+
}
|
|
39424
|
+
registerNetwork(_identifierId, _network) {
|
|
39425
|
+
return this.notSupported("registerNetwork");
|
|
39426
|
+
}
|
|
39427
|
+
registerNetworkByScheme(_scheme, _value, _network) {
|
|
39428
|
+
return this.notSupported("registerNetworkByScheme");
|
|
39429
|
+
}
|
|
39430
|
+
unregisterNetwork(_directoryId) {
|
|
39431
|
+
return this.notSupported("unregisterNetwork");
|
|
39432
|
+
}
|
|
39433
|
+
// ─── Identifier Management ─────────────────────────────
|
|
39434
|
+
createIdentifier(_entityId, _data) {
|
|
39435
|
+
return this.notSupported("createIdentifier");
|
|
39436
|
+
}
|
|
39437
|
+
createIdentifierByScheme(_scheme, _value, _data) {
|
|
39438
|
+
return this.notSupported("createIdentifierByScheme");
|
|
39439
|
+
}
|
|
39440
|
+
deleteIdentifier(_identifierId) {
|
|
39441
|
+
return this.notSupported("deleteIdentifier");
|
|
39442
|
+
}
|
|
39443
|
+
// ─── Claim Management ──────────────────────────────────
|
|
39444
|
+
deleteClaim(_entityId) {
|
|
39445
|
+
return this.notSupported("deleteClaim");
|
|
39446
|
+
}
|
|
39447
|
+
};
|
|
39448
|
+
|
|
39449
|
+
// src/adapters/shared/oauth2.ts
|
|
39450
|
+
function createOAuth2TokenProvider(config2) {
|
|
39451
|
+
let cachedToken;
|
|
38720
39452
|
let expiresAt = 0;
|
|
38721
39453
|
let inflight;
|
|
38722
39454
|
const REFRESH_MARGIN_MS = 6e4;
|
|
@@ -38729,19 +39461,18 @@ function createOAuth2TokenProvider(config2) {
|
|
|
38729
39461
|
const response = await fetch(config2.authUrl, {
|
|
38730
39462
|
method: "POST",
|
|
38731
39463
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
38732
|
-
body: body.toString()
|
|
39464
|
+
body: body.toString(),
|
|
39465
|
+
signal: AbortSignal.timeout(15e3)
|
|
38733
39466
|
});
|
|
38734
39467
|
if (!response.ok) {
|
|
38735
39468
|
const text = await response.text();
|
|
38736
39469
|
throw new Error(
|
|
38737
|
-
`[
|
|
39470
|
+
`[OAuth2] Token request failed: ${response.status} \u2014 ${text.slice(0, 500)}`
|
|
38738
39471
|
);
|
|
38739
39472
|
}
|
|
38740
39473
|
const data = await response.json();
|
|
38741
39474
|
if (!data.access_token) {
|
|
38742
|
-
throw new Error(
|
|
38743
|
-
"[IopoleOAuth2] Token response missing access_token"
|
|
38744
|
-
);
|
|
39475
|
+
throw new Error("[OAuth2] Token response missing access_token");
|
|
38745
39476
|
}
|
|
38746
39477
|
cachedToken = data.access_token;
|
|
38747
39478
|
expiresAt = Date.now() + (data.expires_in ?? 600) * 1e3 - REFRESH_MARGIN_MS;
|
|
@@ -38759,6 +39490,8 @@ function createOAuth2TokenProvider(config2) {
|
|
|
38759
39490
|
return inflight;
|
|
38760
39491
|
};
|
|
38761
39492
|
}
|
|
39493
|
+
|
|
39494
|
+
// src/adapters/iopole/client.ts
|
|
38762
39495
|
var IopoleAPIError = class extends Error {
|
|
38763
39496
|
constructor(message2, status, body) {
|
|
38764
39497
|
super(message2);
|
|
@@ -38774,7 +39507,10 @@ var IopoleClient = class {
|
|
|
38774
39507
|
}
|
|
38775
39508
|
// ─── Generic Request ────────────────────────────────────
|
|
38776
39509
|
async request(method, path, options) {
|
|
38777
|
-
|
|
39510
|
+
return this.requestWithBase(this.config.baseUrl, method, path, options);
|
|
39511
|
+
}
|
|
39512
|
+
async requestWithBase(baseUrl, method, path, options) {
|
|
39513
|
+
const url2 = new URL(`${baseUrl}${path}`);
|
|
38778
39514
|
if (options?.query) {
|
|
38779
39515
|
for (const [key, value] of Object.entries(options.query)) {
|
|
38780
39516
|
if (value !== void 0) {
|
|
@@ -38821,41 +39557,1330 @@ var IopoleClient = class {
|
|
|
38821
39557
|
clearTimeout(timeout);
|
|
38822
39558
|
}
|
|
38823
39559
|
}
|
|
38824
|
-
// ─── Convenience Methods ────────────────────────────────
|
|
38825
|
-
async get(path, query) {
|
|
38826
|
-
return this.request("GET", path, { query });
|
|
39560
|
+
// ─── Convenience Methods ────────────────────────────────
|
|
39561
|
+
async get(path, query) {
|
|
39562
|
+
return this.request("GET", path, { query });
|
|
39563
|
+
}
|
|
39564
|
+
/**
|
|
39565
|
+
* GET request against the v1.1 API endpoint.
|
|
39566
|
+
* Builds a v1.1 URL without mutating this.config.baseUrl,
|
|
39567
|
+
* so concurrent calls (e.g. getV11 + get) cannot interfere.
|
|
39568
|
+
*/
|
|
39569
|
+
async getV11(path, query) {
|
|
39570
|
+
const baseV11 = this.config.baseUrl.replace(/\/v1\b/, "/v1.1");
|
|
39571
|
+
return this.requestWithBase(baseV11, "GET", path, { query });
|
|
39572
|
+
}
|
|
39573
|
+
async post(path, body) {
|
|
39574
|
+
return this.request("POST", path, { body });
|
|
39575
|
+
}
|
|
39576
|
+
async put(path, body) {
|
|
39577
|
+
return this.request("PUT", path, { body });
|
|
39578
|
+
}
|
|
39579
|
+
async delete(path) {
|
|
39580
|
+
return this.request("DELETE", path);
|
|
39581
|
+
}
|
|
39582
|
+
/**
|
|
39583
|
+
* Upload a file via multipart/form-data.
|
|
39584
|
+
* Used for POST /invoice (emitInvoice) — the Swagger spec requires
|
|
39585
|
+
* Content-Type: multipart/form-data with a `file` field (binary, PDF or XML).
|
|
39586
|
+
*/
|
|
39587
|
+
async upload(path, file2, filename) {
|
|
39588
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
39589
|
+
const token = await this.config.getToken();
|
|
39590
|
+
const controller = new AbortController();
|
|
39591
|
+
const timeout = setTimeout(
|
|
39592
|
+
() => controller.abort(),
|
|
39593
|
+
this.config.timeoutMs ?? 3e4
|
|
39594
|
+
);
|
|
39595
|
+
try {
|
|
39596
|
+
const form = new FormData();
|
|
39597
|
+
form.append("file", new Blob([file2]), filename);
|
|
39598
|
+
const response = await fetch(url2, {
|
|
39599
|
+
method: "POST",
|
|
39600
|
+
headers: {
|
|
39601
|
+
Authorization: `Bearer ${token}`,
|
|
39602
|
+
"customer-id": this.config.customerId,
|
|
39603
|
+
Accept: "application/json"
|
|
39604
|
+
// Do NOT set Content-Type — fetch sets it with the multipart boundary
|
|
39605
|
+
},
|
|
39606
|
+
body: form,
|
|
39607
|
+
signal: controller.signal
|
|
39608
|
+
});
|
|
39609
|
+
if (!response.ok) {
|
|
39610
|
+
const body = await response.text();
|
|
39611
|
+
throw new IopoleAPIError(
|
|
39612
|
+
`[IopoleClient] POST ${path} (upload) \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
39613
|
+
response.status,
|
|
39614
|
+
body
|
|
39615
|
+
);
|
|
39616
|
+
}
|
|
39617
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
39618
|
+
if (contentType.includes("application/json")) {
|
|
39619
|
+
return await response.json();
|
|
39620
|
+
}
|
|
39621
|
+
return await response.text();
|
|
39622
|
+
} finally {
|
|
39623
|
+
clearTimeout(timeout);
|
|
39624
|
+
}
|
|
39625
|
+
}
|
|
39626
|
+
/**
|
|
39627
|
+
* POST with query parameters.
|
|
39628
|
+
* Used for /tools/{cii,ubl}/generate which return text (XML).
|
|
39629
|
+
*/
|
|
39630
|
+
async postWithQuery(path, body, query) {
|
|
39631
|
+
return this.request("POST", path, { body, query });
|
|
39632
|
+
}
|
|
39633
|
+
/**
|
|
39634
|
+
* POST with query parameters, returning raw binary.
|
|
39635
|
+
* Used for /tools/facturx/generate which returns a PDF (binary).
|
|
39636
|
+
* Using request() would corrupt binary data by treating it as text.
|
|
39637
|
+
*/
|
|
39638
|
+
async postBinary(path, body, query) {
|
|
39639
|
+
const url2 = new URL(`${this.config.baseUrl}${path}`);
|
|
39640
|
+
for (const [key, value] of Object.entries(query)) {
|
|
39641
|
+
if (value !== void 0) url2.searchParams.set(key, String(value));
|
|
39642
|
+
}
|
|
39643
|
+
const token = await this.config.getToken();
|
|
39644
|
+
const controller = new AbortController();
|
|
39645
|
+
const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 6e4);
|
|
39646
|
+
try {
|
|
39647
|
+
const response = await fetch(url2.toString(), {
|
|
39648
|
+
method: "POST",
|
|
39649
|
+
headers: {
|
|
39650
|
+
Authorization: `Bearer ${token}`,
|
|
39651
|
+
"customer-id": this.config.customerId,
|
|
39652
|
+
"Content-Type": "application/json",
|
|
39653
|
+
Accept: "*/*"
|
|
39654
|
+
},
|
|
39655
|
+
body: JSON.stringify(body),
|
|
39656
|
+
signal: controller.signal
|
|
39657
|
+
});
|
|
39658
|
+
if (!response.ok) {
|
|
39659
|
+
const errBody = await response.text();
|
|
39660
|
+
throw new IopoleAPIError(`[IopoleClient] POST ${path} \u2192 ${response.status}: ${errBody.slice(0, 500)}`, response.status, errBody);
|
|
39661
|
+
}
|
|
39662
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
39663
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
39664
|
+
return { data, contentType };
|
|
39665
|
+
} finally {
|
|
39666
|
+
clearTimeout(timeout);
|
|
39667
|
+
}
|
|
39668
|
+
}
|
|
39669
|
+
/**
|
|
39670
|
+
* Download a binary resource (invoice PDF, attachment, etc.)
|
|
39671
|
+
* Returns the raw Response for streaming.
|
|
39672
|
+
*/
|
|
39673
|
+
async download(path) {
|
|
39674
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
39675
|
+
const token = await this.config.getToken();
|
|
39676
|
+
const controller = new AbortController();
|
|
39677
|
+
const timeout = setTimeout(
|
|
39678
|
+
() => controller.abort(),
|
|
39679
|
+
this.config.timeoutMs ?? 3e4
|
|
39680
|
+
);
|
|
39681
|
+
try {
|
|
39682
|
+
const response = await fetch(url2, {
|
|
39683
|
+
method: "GET",
|
|
39684
|
+
headers: {
|
|
39685
|
+
Authorization: `Bearer ${token}`,
|
|
39686
|
+
"customer-id": this.config.customerId
|
|
39687
|
+
},
|
|
39688
|
+
signal: controller.signal
|
|
39689
|
+
});
|
|
39690
|
+
if (!response.ok) {
|
|
39691
|
+
const body = await response.text();
|
|
39692
|
+
throw new IopoleAPIError(
|
|
39693
|
+
`[IopoleClient] GET ${path} \u2192 ${response.status}`,
|
|
39694
|
+
response.status,
|
|
39695
|
+
body
|
|
39696
|
+
);
|
|
39697
|
+
}
|
|
39698
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
39699
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
39700
|
+
return { data, contentType };
|
|
39701
|
+
} finally {
|
|
39702
|
+
clearTimeout(timeout);
|
|
39703
|
+
}
|
|
39704
|
+
}
|
|
39705
|
+
};
|
|
39706
|
+
|
|
39707
|
+
// src/runtime.ts
|
|
39708
|
+
import { statSync as fsStatSync } from "node:fs";
|
|
39709
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
39710
|
+
function env2(key) {
|
|
39711
|
+
return process.env[key];
|
|
39712
|
+
}
|
|
39713
|
+
async function readTextFile2(path) {
|
|
39714
|
+
return await readFile2(path, "utf-8");
|
|
39715
|
+
}
|
|
39716
|
+
function statSync(path) {
|
|
39717
|
+
try {
|
|
39718
|
+
fsStatSync(path);
|
|
39719
|
+
return true;
|
|
39720
|
+
} catch {
|
|
39721
|
+
return false;
|
|
39722
|
+
}
|
|
39723
|
+
}
|
|
39724
|
+
function getArgs() {
|
|
39725
|
+
return process.argv.slice(2);
|
|
39726
|
+
}
|
|
39727
|
+
function exit(code) {
|
|
39728
|
+
process.exit(code);
|
|
39729
|
+
}
|
|
39730
|
+
function onSignal(signal, handler) {
|
|
39731
|
+
process.on(signal, handler);
|
|
39732
|
+
}
|
|
39733
|
+
|
|
39734
|
+
// src/adapters/shared/env.ts
|
|
39735
|
+
function requireEnv(adapter, name, hint) {
|
|
39736
|
+
const value = env2(name);
|
|
39737
|
+
if (!value) {
|
|
39738
|
+
throw new Error(`[${adapter}] ${name} is required. ${hint}`);
|
|
39739
|
+
}
|
|
39740
|
+
return value;
|
|
39741
|
+
}
|
|
39742
|
+
|
|
39743
|
+
// src/adapters/shared/direction.ts
|
|
39744
|
+
var RECEIVED = /* @__PURE__ */ new Set(["received", "inbound", "in"]);
|
|
39745
|
+
var SENT = /* @__PURE__ */ new Set(["sent", "emitted", "outbound", "out"]);
|
|
39746
|
+
function normalizeDirection(raw2) {
|
|
39747
|
+
if (!raw2) return void 0;
|
|
39748
|
+
const l = raw2.toLowerCase();
|
|
39749
|
+
if (RECEIVED.has(l)) return "received";
|
|
39750
|
+
if (SENT.has(l)) return "sent";
|
|
39751
|
+
return void 0;
|
|
39752
|
+
}
|
|
39753
|
+
|
|
39754
|
+
// src/adapters/iopole/adapter.ts
|
|
39755
|
+
var IOPOLE_DEFAULT_AUTH_URL = "https://auth.ppd.iopole.fr/realms/iopole/protocol/openid-connect/token";
|
|
39756
|
+
var IopoleAdapter = class extends BaseAdapter {
|
|
39757
|
+
name = "iopole";
|
|
39758
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
39759
|
+
"emitInvoice",
|
|
39760
|
+
"searchInvoices",
|
|
39761
|
+
"getInvoice",
|
|
39762
|
+
"downloadInvoice",
|
|
39763
|
+
"downloadReadable",
|
|
39764
|
+
"getInvoiceFiles",
|
|
39765
|
+
"getAttachments",
|
|
39766
|
+
"downloadFile",
|
|
39767
|
+
"markInvoiceSeen",
|
|
39768
|
+
"getUnseenInvoices",
|
|
39769
|
+
"generateCII",
|
|
39770
|
+
"generateUBL",
|
|
39771
|
+
"generateFacturX",
|
|
39772
|
+
"searchDirectoryFr",
|
|
39773
|
+
"searchDirectoryInt",
|
|
39774
|
+
"checkPeppolParticipant",
|
|
39775
|
+
"sendStatus",
|
|
39776
|
+
"getStatusHistory",
|
|
39777
|
+
"getUnseenStatuses",
|
|
39778
|
+
"markStatusSeen",
|
|
39779
|
+
"reportInvoiceTransaction",
|
|
39780
|
+
"reportTransaction",
|
|
39781
|
+
"listWebhooks",
|
|
39782
|
+
"getWebhook",
|
|
39783
|
+
"createWebhook",
|
|
39784
|
+
"updateWebhook",
|
|
39785
|
+
"deleteWebhook",
|
|
39786
|
+
"getCustomerId",
|
|
39787
|
+
"listBusinessEntities",
|
|
39788
|
+
"getBusinessEntity",
|
|
39789
|
+
"createLegalUnit",
|
|
39790
|
+
"createOffice",
|
|
39791
|
+
"deleteBusinessEntity",
|
|
39792
|
+
"configureBusinessEntity",
|
|
39793
|
+
"claimBusinessEntity",
|
|
39794
|
+
"claimBusinessEntityByIdentifier",
|
|
39795
|
+
"enrollFrench",
|
|
39796
|
+
"enrollInternational",
|
|
39797
|
+
"registerNetwork",
|
|
39798
|
+
"registerNetworkByScheme",
|
|
39799
|
+
"unregisterNetwork",
|
|
39800
|
+
"createIdentifier",
|
|
39801
|
+
"createIdentifierByScheme",
|
|
39802
|
+
"deleteIdentifier",
|
|
39803
|
+
"deleteClaim"
|
|
39804
|
+
]);
|
|
39805
|
+
client;
|
|
39806
|
+
constructor(client) {
|
|
39807
|
+
super();
|
|
39808
|
+
this.client = client;
|
|
39809
|
+
}
|
|
39810
|
+
// ─── Invoice Operations ───────────────────────────────
|
|
39811
|
+
async emitInvoice(req) {
|
|
39812
|
+
return await this.client.upload("/invoice", req.file, req.filename);
|
|
39813
|
+
}
|
|
39814
|
+
async searchInvoices(filters) {
|
|
39815
|
+
const raw2 = await this.client.getV11("/invoice/search", {
|
|
39816
|
+
q: filters.q,
|
|
39817
|
+
expand: filters.expand ?? "businessData",
|
|
39818
|
+
offset: filters.offset ?? 0,
|
|
39819
|
+
limit: filters.limit ?? 50
|
|
39820
|
+
});
|
|
39821
|
+
const data = raw2.data ?? [];
|
|
39822
|
+
const count = raw2.meta?.count ?? raw2.count ?? data.length;
|
|
39823
|
+
const rows = data.map((row) => {
|
|
39824
|
+
const m = row.metadata ?? {};
|
|
39825
|
+
const bd = row.businessData ?? {};
|
|
39826
|
+
return {
|
|
39827
|
+
id: m.invoiceId ?? "",
|
|
39828
|
+
invoiceNumber: bd.invoiceId,
|
|
39829
|
+
status: m.state,
|
|
39830
|
+
// will be enriched below
|
|
39831
|
+
direction: normalizeDirection(m.direction),
|
|
39832
|
+
senderName: bd.seller?.name,
|
|
39833
|
+
receiverName: bd.buyer?.name,
|
|
39834
|
+
date: bd.invoiceDate ?? m.createDate?.split("T")[0],
|
|
39835
|
+
amount: bd.monetary?.invoiceAmount?.amount,
|
|
39836
|
+
currency: bd.monetary?.invoiceCurrency ?? "EUR"
|
|
39837
|
+
};
|
|
39838
|
+
});
|
|
39839
|
+
const CONCURRENCY = 5;
|
|
39840
|
+
for (let i = 0; i < rows.length; i += CONCURRENCY) {
|
|
39841
|
+
const batch = rows.slice(i, i + CONCURRENCY);
|
|
39842
|
+
await Promise.all(batch.map(async (row) => {
|
|
39843
|
+
if (!row.id) return;
|
|
39844
|
+
try {
|
|
39845
|
+
const history = await this.getStatusHistory(row.id);
|
|
39846
|
+
if (history.entries.length > 0) {
|
|
39847
|
+
const sorted = [...history.entries].sort(
|
|
39848
|
+
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
|
39849
|
+
);
|
|
39850
|
+
row.status = sorted[0].code;
|
|
39851
|
+
}
|
|
39852
|
+
} catch {
|
|
39853
|
+
}
|
|
39854
|
+
}));
|
|
39855
|
+
}
|
|
39856
|
+
return { rows, count };
|
|
39857
|
+
}
|
|
39858
|
+
async getInvoice(id) {
|
|
39859
|
+
const [raw2, history] = await Promise.all([
|
|
39860
|
+
this.client.get(`/invoice/${encodePathSegment(id)}`, { expand: "businessData" }),
|
|
39861
|
+
this.getStatusHistory(id).catch(() => ({ entries: [] }))
|
|
39862
|
+
]);
|
|
39863
|
+
const inv = Array.isArray(raw2) ? raw2[0] : raw2;
|
|
39864
|
+
const latestStatus = history.entries.length > 0 ? [...history.entries].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())[0].code : void 0;
|
|
39865
|
+
const bd = inv?.businessData;
|
|
39866
|
+
return {
|
|
39867
|
+
id: inv?.invoiceId ?? id,
|
|
39868
|
+
invoiceNumber: bd?.invoiceId,
|
|
39869
|
+
status: latestStatus ?? inv?.state ?? inv?.status ?? "UNKNOWN",
|
|
39870
|
+
direction: normalizeDirection(inv?.way ?? inv?.metadata?.direction),
|
|
39871
|
+
format: inv?.originalFormat,
|
|
39872
|
+
network: inv?.originalNetwork,
|
|
39873
|
+
invoiceType: bd?.detailedType?.value,
|
|
39874
|
+
senderName: bd?.seller?.name,
|
|
39875
|
+
senderId: bd?.seller?.siret ?? bd?.seller?.siren,
|
|
39876
|
+
senderVat: bd?.seller?.vatNumber,
|
|
39877
|
+
receiverName: bd?.buyer?.name,
|
|
39878
|
+
receiverId: bd?.buyer?.siret ?? bd?.buyer?.siren,
|
|
39879
|
+
receiverVat: bd?.buyer?.vatNumber,
|
|
39880
|
+
issueDate: bd?.invoiceDate,
|
|
39881
|
+
dueDate: bd?.invoiceDueDate,
|
|
39882
|
+
receiptDate: bd?.invoiceReceiptDate,
|
|
39883
|
+
currency: bd?.monetary?.invoiceCurrency ?? "EUR",
|
|
39884
|
+
totalHt: bd?.monetary?.taxBasisTotalAmount?.amount,
|
|
39885
|
+
totalTax: bd?.monetary?.taxTotalAmount?.amount,
|
|
39886
|
+
totalTtc: bd?.monetary?.invoiceAmount?.amount,
|
|
39887
|
+
lines: bd?.lines?.map((l) => {
|
|
39888
|
+
const line = l;
|
|
39889
|
+
return {
|
|
39890
|
+
description: line.item?.name,
|
|
39891
|
+
quantity: line.billedQuantity?.quantity,
|
|
39892
|
+
unitPrice: line.price?.netAmount?.amount,
|
|
39893
|
+
taxRate: line.taxDetail?.percent,
|
|
39894
|
+
amount: line.totalAmount?.amount
|
|
39895
|
+
};
|
|
39896
|
+
}),
|
|
39897
|
+
notes: bd?.notes?.map((n) => n.content).filter(Boolean)
|
|
39898
|
+
};
|
|
39899
|
+
}
|
|
39900
|
+
async downloadInvoice(id) {
|
|
39901
|
+
return await this.client.download(`/invoice/${encodePathSegment(id)}/download`);
|
|
39902
|
+
}
|
|
39903
|
+
async downloadReadable(id) {
|
|
39904
|
+
return await this.client.download(`/invoice/${encodePathSegment(id)}/download/readable`);
|
|
39905
|
+
}
|
|
39906
|
+
async getInvoiceFiles(id) {
|
|
39907
|
+
return await this.client.get(`/invoice/${encodePathSegment(id)}/files`);
|
|
39908
|
+
}
|
|
39909
|
+
async getAttachments(id) {
|
|
39910
|
+
return await this.client.get(`/invoice/${encodePathSegment(id)}/files/attachments`);
|
|
39911
|
+
}
|
|
39912
|
+
async downloadFile(fileId) {
|
|
39913
|
+
return await this.client.download(`/invoice/file/${encodePathSegment(fileId)}/download`);
|
|
39914
|
+
}
|
|
39915
|
+
async markInvoiceSeen(id) {
|
|
39916
|
+
return await this.client.put(`/invoice/${encodePathSegment(id)}/markAsSeen`);
|
|
39917
|
+
}
|
|
39918
|
+
async getUnseenInvoices(pagination) {
|
|
39919
|
+
return await this.client.get("/invoice/notSeen", {
|
|
39920
|
+
offset: pagination.offset,
|
|
39921
|
+
limit: pagination.limit
|
|
39922
|
+
});
|
|
39923
|
+
}
|
|
39924
|
+
async generateCII(req) {
|
|
39925
|
+
return await this.client.postWithQuery("/tools/cii/generate", normalizeForIopole(req.invoice), {
|
|
39926
|
+
flavor: req.flavor
|
|
39927
|
+
});
|
|
39928
|
+
}
|
|
39929
|
+
async generateUBL(req) {
|
|
39930
|
+
return await this.client.postWithQuery("/tools/ubl/generate", normalizeForIopole(req.invoice), {
|
|
39931
|
+
flavor: req.flavor
|
|
39932
|
+
});
|
|
39933
|
+
}
|
|
39934
|
+
async generateFacturX(req) {
|
|
39935
|
+
return await this.client.postBinary("/tools/facturx/generate", normalizeForIopole(req.invoice), {
|
|
39936
|
+
flavor: req.flavor,
|
|
39937
|
+
language: req.language
|
|
39938
|
+
});
|
|
39939
|
+
}
|
|
39940
|
+
// ─── Directory ────────────────────────────────────────
|
|
39941
|
+
async searchDirectoryFr(filters) {
|
|
39942
|
+
const q = autoWrapDirectoryQuery(filters.q);
|
|
39943
|
+
const raw2 = await this.client.get("/directory/french", {
|
|
39944
|
+
q,
|
|
39945
|
+
offset: filters.offset,
|
|
39946
|
+
limit: filters.limit
|
|
39947
|
+
});
|
|
39948
|
+
const data = raw2.data ?? [];
|
|
39949
|
+
const count = raw2.meta?.count ?? raw2.count ?? data.length;
|
|
39950
|
+
const rows = data.map((row) => {
|
|
39951
|
+
const ci = row.countryIdentifier ?? {};
|
|
39952
|
+
return {
|
|
39953
|
+
entityId: row.businessEntityId ?? "",
|
|
39954
|
+
name: row.name,
|
|
39955
|
+
type: row.type,
|
|
39956
|
+
siren: ci.siren ?? row.siren,
|
|
39957
|
+
siret: ci.siret ?? row.siret,
|
|
39958
|
+
country: ci.country ?? row.country ?? "FR",
|
|
39959
|
+
identifiers: row.identifiers
|
|
39960
|
+
};
|
|
39961
|
+
});
|
|
39962
|
+
return { rows, count };
|
|
39963
|
+
}
|
|
39964
|
+
async searchDirectoryInt(filters) {
|
|
39965
|
+
return await this.client.get("/directory/international", {
|
|
39966
|
+
value: filters.value,
|
|
39967
|
+
offset: filters.offset,
|
|
39968
|
+
limit: filters.limit
|
|
39969
|
+
});
|
|
39970
|
+
}
|
|
39971
|
+
async checkPeppolParticipant(scheme, value) {
|
|
39972
|
+
return await this.client.get(
|
|
39973
|
+
`/directory/international/check/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}`
|
|
39974
|
+
);
|
|
39975
|
+
}
|
|
39976
|
+
// ─── Status ───────────────────────────────────────────
|
|
39977
|
+
async sendStatus(req) {
|
|
39978
|
+
return await this.client.post(`/invoice/${encodePathSegment(req.invoiceId)}/status`, {
|
|
39979
|
+
code: req.code,
|
|
39980
|
+
message: req.message,
|
|
39981
|
+
payment: req.payment
|
|
39982
|
+
});
|
|
39983
|
+
}
|
|
39984
|
+
async getStatusHistory(invoiceId) {
|
|
39985
|
+
const raw2 = await this.client.get(`/invoice/${encodePathSegment(invoiceId)}/status-history`);
|
|
39986
|
+
return normalizeStatusHistory(raw2);
|
|
39987
|
+
}
|
|
39988
|
+
async getUnseenStatuses(pagination) {
|
|
39989
|
+
return await this.client.get("/invoice/status/notSeen", {
|
|
39990
|
+
offset: pagination.offset,
|
|
39991
|
+
limit: pagination.limit
|
|
39992
|
+
});
|
|
39993
|
+
}
|
|
39994
|
+
async markStatusSeen(statusId) {
|
|
39995
|
+
return await this.client.put(`/invoice/status/${encodePathSegment(statusId)}/markAsSeen`);
|
|
39996
|
+
}
|
|
39997
|
+
// ─── Reporting ────────────────────────────────────────
|
|
39998
|
+
async reportInvoiceTransaction(transaction) {
|
|
39999
|
+
return await this.client.post("/reporting/fr/invoice/transaction", transaction);
|
|
40000
|
+
}
|
|
40001
|
+
async reportTransaction(businessEntityId, transaction) {
|
|
40002
|
+
return await this.client.post(
|
|
40003
|
+
`/reporting/fr/transaction/${encodePathSegment(businessEntityId)}`,
|
|
40004
|
+
transaction
|
|
40005
|
+
);
|
|
40006
|
+
}
|
|
40007
|
+
// ─── Webhooks ─────────────────────────────────────────
|
|
40008
|
+
async listWebhooks() {
|
|
40009
|
+
return await this.client.get("/config/webhook");
|
|
40010
|
+
}
|
|
40011
|
+
async getWebhook(id) {
|
|
40012
|
+
return await this.client.get(`/config/webhook/${encodePathSegment(id)}`);
|
|
40013
|
+
}
|
|
40014
|
+
async createWebhook(req) {
|
|
40015
|
+
return await this.client.post("/config/webhook", req);
|
|
40016
|
+
}
|
|
40017
|
+
async updateWebhook(id, req) {
|
|
40018
|
+
return await this.client.put(`/config/webhook/${encodePathSegment(id)}`, req);
|
|
40019
|
+
}
|
|
40020
|
+
async deleteWebhook(id) {
|
|
40021
|
+
return await this.client.delete(`/config/webhook/${encodePathSegment(id)}`);
|
|
40022
|
+
}
|
|
40023
|
+
// ─── Operator Config ───────────────────────────────────
|
|
40024
|
+
async getCustomerId() {
|
|
40025
|
+
return await this.client.get("/config/customer/id");
|
|
40026
|
+
}
|
|
40027
|
+
async listBusinessEntities() {
|
|
40028
|
+
const raw2 = await this.client.get("/config/business/entity");
|
|
40029
|
+
const data = raw2.data ?? [];
|
|
40030
|
+
const rows = data.map((row) => {
|
|
40031
|
+
const ci = row.countryIdentifier ?? {};
|
|
40032
|
+
return {
|
|
40033
|
+
entityId: row.businessEntityId ?? "",
|
|
40034
|
+
name: row.name,
|
|
40035
|
+
type: row.type,
|
|
40036
|
+
siren: ci.siren ?? row.siren,
|
|
40037
|
+
siret: ci.siret ?? row.siret,
|
|
40038
|
+
scope: row.scope,
|
|
40039
|
+
country: ci.country ?? row.country ?? "FR"
|
|
40040
|
+
};
|
|
40041
|
+
});
|
|
40042
|
+
return { rows, count: rows.length };
|
|
40043
|
+
}
|
|
40044
|
+
async getBusinessEntity(id) {
|
|
40045
|
+
return await this.client.get(`/config/business/entity/${encodePathSegment(id)}`);
|
|
40046
|
+
}
|
|
40047
|
+
async createLegalUnit(data) {
|
|
40048
|
+
return await this.client.post("/config/business/entity/legalunit", data);
|
|
40049
|
+
}
|
|
40050
|
+
async createOffice(data) {
|
|
40051
|
+
return await this.client.post("/config/business/entity/office", data);
|
|
40052
|
+
}
|
|
40053
|
+
async deleteBusinessEntity(id) {
|
|
40054
|
+
return await this.client.delete(`/config/business/entity/${encodePathSegment(id)}`);
|
|
40055
|
+
}
|
|
40056
|
+
async configureBusinessEntity(id, data) {
|
|
40057
|
+
return await this.client.post(`/config/business/entity/${encodePathSegment(id)}/configure`, data);
|
|
40058
|
+
}
|
|
40059
|
+
async claimBusinessEntity(id, data) {
|
|
40060
|
+
return await this.client.post(`/config/business/entity/${encodePathSegment(id)}/claim`, data);
|
|
40061
|
+
}
|
|
40062
|
+
async claimBusinessEntityByIdentifier(scheme, value, data) {
|
|
40063
|
+
return await this.client.post(
|
|
40064
|
+
`/config/business/entity/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/claim`,
|
|
40065
|
+
data
|
|
40066
|
+
);
|
|
40067
|
+
}
|
|
40068
|
+
async enrollFrench(data) {
|
|
40069
|
+
return await this.client.put("/config/french/enrollment", data);
|
|
40070
|
+
}
|
|
40071
|
+
async enrollInternational(data) {
|
|
40072
|
+
return await this.client.put("/config/international/enrollment", data);
|
|
40073
|
+
}
|
|
40074
|
+
async registerNetwork(identifierId, network) {
|
|
40075
|
+
return await this.client.post(
|
|
40076
|
+
`/config/business/entity/identifier/${encodePathSegment(identifierId)}/network/${encodePathSegment(network)}`
|
|
40077
|
+
);
|
|
40078
|
+
}
|
|
40079
|
+
async registerNetworkByScheme(scheme, value, network) {
|
|
40080
|
+
return await this.client.post(
|
|
40081
|
+
`/config/business/entity/identifier/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/network/${encodePathSegment(network)}`
|
|
40082
|
+
);
|
|
40083
|
+
}
|
|
40084
|
+
async unregisterNetwork(directoryId) {
|
|
40085
|
+
return await this.client.delete(
|
|
40086
|
+
`/config/business/entity/identifier/directory/${encodePathSegment(directoryId)}`
|
|
40087
|
+
);
|
|
40088
|
+
}
|
|
40089
|
+
// ─── Identifier Management ───────────────────────────────
|
|
40090
|
+
async createIdentifier(entityId, data) {
|
|
40091
|
+
return await this.client.post(`/config/business/entity/${encodePathSegment(entityId)}/identifier`, data);
|
|
40092
|
+
}
|
|
40093
|
+
async createIdentifierByScheme(scheme, value, data) {
|
|
40094
|
+
return await this.client.post(
|
|
40095
|
+
`/config/business/entity/scheme/${encodePathSegment(scheme)}/value/${encodePathSegment(value)}/identifier`,
|
|
40096
|
+
data
|
|
40097
|
+
);
|
|
40098
|
+
}
|
|
40099
|
+
async deleteIdentifier(identifierId) {
|
|
40100
|
+
return await this.client.delete(`/config/business/entity/identifier/${encodePathSegment(identifierId)}`);
|
|
40101
|
+
}
|
|
40102
|
+
// ─── Claim Management ────────────────────────────────────
|
|
40103
|
+
async deleteClaim(entityId) {
|
|
40104
|
+
return await this.client.delete(`/config/business/entity/${encodePathSegment(entityId)}/claim`);
|
|
40105
|
+
}
|
|
40106
|
+
};
|
|
40107
|
+
var normalizeForIopole = (inv) => {
|
|
40108
|
+
const normalized = { ...inv };
|
|
40109
|
+
for (const party of ["seller", "buyer"]) {
|
|
40110
|
+
if (normalized[party] && !normalized[party].postalAddress) {
|
|
40111
|
+
normalized[party] = {
|
|
40112
|
+
...normalized[party],
|
|
40113
|
+
postalAddress: { country: normalized[party].country ?? "FR" }
|
|
40114
|
+
};
|
|
40115
|
+
}
|
|
40116
|
+
}
|
|
40117
|
+
for (const party of ["seller", "buyer"]) {
|
|
40118
|
+
const p = normalized[party];
|
|
40119
|
+
if (p && !p.electronicAddress && p.siren && p.siret) {
|
|
40120
|
+
normalized[party] = {
|
|
40121
|
+
...p,
|
|
40122
|
+
electronicAddress: `0225:${p.siren}_${p.siret}`,
|
|
40123
|
+
identifiers: p.identifiers ?? [
|
|
40124
|
+
{ type: "ELECTRONIC_ADDRESS", value: `${p.siren}_${p.siret}`, scheme: "0225" },
|
|
40125
|
+
{ type: "PARTY_LEGAL_IDENTIFIER", value: p.siren, scheme: "0002" }
|
|
40126
|
+
]
|
|
40127
|
+
};
|
|
40128
|
+
}
|
|
40129
|
+
}
|
|
40130
|
+
if (Array.isArray(normalized.paymentTerms)) {
|
|
40131
|
+
normalized.paymentTerms = normalized.paymentTerms.map((t) => t.description ?? t).join("; ");
|
|
40132
|
+
}
|
|
40133
|
+
if (normalized.monetary) {
|
|
40134
|
+
const m = { ...normalized.monetary };
|
|
40135
|
+
const currency = m.invoiceCurrency ?? "EUR";
|
|
40136
|
+
if (!m.payableAmount) m.payableAmount = m.invoiceAmount;
|
|
40137
|
+
if (m.taxTotalAmount && !m.taxTotalAmount.currency) {
|
|
40138
|
+
m.taxTotalAmount = { ...m.taxTotalAmount, currency };
|
|
40139
|
+
}
|
|
40140
|
+
normalized.monetary = m;
|
|
40141
|
+
}
|
|
40142
|
+
if (Array.isArray(normalized.lines)) {
|
|
40143
|
+
normalized.lines = normalized.lines.map((line) => {
|
|
40144
|
+
if (line.taxDetail && !line.taxDetail.categoryCode) {
|
|
40145
|
+
return { ...line, taxDetail: { ...line.taxDetail, categoryCode: "S" } };
|
|
40146
|
+
}
|
|
40147
|
+
return line;
|
|
40148
|
+
});
|
|
40149
|
+
}
|
|
40150
|
+
return normalized;
|
|
40151
|
+
};
|
|
40152
|
+
function autoWrapDirectoryQuery(q) {
|
|
40153
|
+
const trimmed = q.trim();
|
|
40154
|
+
if (/^\d{14}$/.test(trimmed)) return `siret:"${trimmed}"`;
|
|
40155
|
+
if (/^\d{9}$/.test(trimmed)) return `siren:"${trimmed}"`;
|
|
40156
|
+
if (/^FR\d{11}$/i.test(trimmed)) return `vatNumber:"${trimmed.toUpperCase()}"`;
|
|
40157
|
+
if (trimmed.length >= 3 && !/^\d+$/.test(trimmed) && !trimmed.includes(":")) {
|
|
40158
|
+
return `name:"*${trimmed}*"`;
|
|
40159
|
+
}
|
|
40160
|
+
return trimmed;
|
|
40161
|
+
}
|
|
40162
|
+
function normalizeStatusHistory(raw2) {
|
|
40163
|
+
let entries = [];
|
|
40164
|
+
if (Array.isArray(raw2)) {
|
|
40165
|
+
entries = raw2;
|
|
40166
|
+
} else if (raw2 && typeof raw2 === "object") {
|
|
40167
|
+
const obj = raw2;
|
|
40168
|
+
if (Array.isArray(obj.data)) entries = obj.data;
|
|
40169
|
+
else if (Array.isArray(obj.entries)) entries = obj.entries;
|
|
40170
|
+
else if (Array.isArray(obj.history)) entries = obj.history;
|
|
40171
|
+
}
|
|
40172
|
+
return {
|
|
40173
|
+
entries: entries.map((e) => ({
|
|
40174
|
+
date: e.date ?? e.createdAt ?? "",
|
|
40175
|
+
code: e.status?.code ?? e.code ?? e.statusCode ?? "",
|
|
40176
|
+
message: e.status?.message ?? e.message,
|
|
40177
|
+
destType: e.destType
|
|
40178
|
+
}))
|
|
40179
|
+
};
|
|
40180
|
+
}
|
|
40181
|
+
function createIopoleAdapter() {
|
|
40182
|
+
const baseUrl = requireEnv(
|
|
40183
|
+
"IopoleAdapter",
|
|
40184
|
+
"IOPOLE_API_URL",
|
|
40185
|
+
"Set it to https://api.ppd.iopole.fr/v1 (sandbox) or https://api.iopole.com/v1 (production)."
|
|
40186
|
+
);
|
|
40187
|
+
const clientId = requireEnv(
|
|
40188
|
+
"IopoleAdapter",
|
|
40189
|
+
"IOPOLE_CLIENT_ID",
|
|
40190
|
+
"Get your client ID from the Iopole dashboard or admin console."
|
|
40191
|
+
);
|
|
40192
|
+
const clientSecret = requireEnv(
|
|
40193
|
+
"IopoleAdapter",
|
|
40194
|
+
"IOPOLE_CLIENT_SECRET",
|
|
40195
|
+
"Get your client secret from the Iopole dashboard or admin console."
|
|
40196
|
+
);
|
|
40197
|
+
const customerId = requireEnv(
|
|
40198
|
+
"IopoleAdapter",
|
|
40199
|
+
"IOPOLE_CUSTOMER_ID",
|
|
40200
|
+
"Find it in Settings \u2192 Unique Identifier (sandbox) or admin console."
|
|
40201
|
+
);
|
|
40202
|
+
const authUrl = env2("IOPOLE_AUTH_URL") || IOPOLE_DEFAULT_AUTH_URL;
|
|
40203
|
+
const getToken = createOAuth2TokenProvider({ authUrl, clientId, clientSecret });
|
|
40204
|
+
const client = new IopoleClient({ baseUrl, customerId, getToken });
|
|
40205
|
+
return new IopoleAdapter(client);
|
|
40206
|
+
}
|
|
40207
|
+
|
|
40208
|
+
// src/adapters/shared/http-client.ts
|
|
40209
|
+
var BaseHttpClient = class {
|
|
40210
|
+
config;
|
|
40211
|
+
adapterName;
|
|
40212
|
+
constructor(adapterName, config2) {
|
|
40213
|
+
this.adapterName = adapterName;
|
|
40214
|
+
this.config = config2;
|
|
40215
|
+
}
|
|
40216
|
+
// ─── Generic Request ────────────────────────────────────
|
|
40217
|
+
async request(method, path, options) {
|
|
40218
|
+
const url2 = new URL(`${this.config.baseUrl}${path}`);
|
|
40219
|
+
if (options?.query) {
|
|
40220
|
+
for (const [key, value] of Object.entries(options.query)) {
|
|
40221
|
+
if (value === void 0) continue;
|
|
40222
|
+
if (Array.isArray(value)) {
|
|
40223
|
+
for (const v of value) url2.searchParams.append(key, v);
|
|
40224
|
+
} else {
|
|
40225
|
+
url2.searchParams.set(key, String(value));
|
|
40226
|
+
}
|
|
40227
|
+
}
|
|
40228
|
+
}
|
|
40229
|
+
const authHeaders = await this.getAuthHeaders();
|
|
40230
|
+
const headers = {
|
|
40231
|
+
...authHeaders,
|
|
40232
|
+
Accept: "application/json",
|
|
40233
|
+
...options?.headers
|
|
40234
|
+
};
|
|
40235
|
+
let bodyPayload;
|
|
40236
|
+
if (options?.body) {
|
|
40237
|
+
if (options.contentType && options.body instanceof Uint8Array) {
|
|
40238
|
+
headers["Content-Type"] = options.contentType;
|
|
40239
|
+
bodyPayload = options.body;
|
|
40240
|
+
} else {
|
|
40241
|
+
headers["Content-Type"] = options.contentType ?? "application/json";
|
|
40242
|
+
bodyPayload = JSON.stringify(options.body);
|
|
40243
|
+
}
|
|
40244
|
+
}
|
|
40245
|
+
const controller = new AbortController();
|
|
40246
|
+
const timeout = setTimeout(
|
|
40247
|
+
() => controller.abort(),
|
|
40248
|
+
this.config.timeoutMs ?? 3e4
|
|
40249
|
+
);
|
|
40250
|
+
try {
|
|
40251
|
+
const response = await fetch(url2.toString(), {
|
|
40252
|
+
method,
|
|
40253
|
+
headers,
|
|
40254
|
+
body: bodyPayload,
|
|
40255
|
+
signal: controller.signal
|
|
40256
|
+
});
|
|
40257
|
+
if (!response.ok) {
|
|
40258
|
+
const body = await response.text();
|
|
40259
|
+
throw new AdapterAPIError(
|
|
40260
|
+
this.adapterName,
|
|
40261
|
+
`[${this.adapterName}] ${method} ${path} \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
40262
|
+
response.status,
|
|
40263
|
+
body
|
|
40264
|
+
);
|
|
40265
|
+
}
|
|
40266
|
+
const ct = response.headers.get("content-type") ?? "";
|
|
40267
|
+
if (ct.includes("application/json")) {
|
|
40268
|
+
return await response.json();
|
|
40269
|
+
}
|
|
40270
|
+
return await response.text();
|
|
40271
|
+
} finally {
|
|
40272
|
+
clearTimeout(timeout);
|
|
40273
|
+
}
|
|
40274
|
+
}
|
|
40275
|
+
// ─── Convenience Methods ────────────────────────────────
|
|
40276
|
+
async get(path, query) {
|
|
40277
|
+
return this.request("GET", path, { query });
|
|
40278
|
+
}
|
|
40279
|
+
async post(path, body) {
|
|
40280
|
+
return this.request("POST", path, { body });
|
|
40281
|
+
}
|
|
40282
|
+
async put(path, body) {
|
|
40283
|
+
return this.request("PUT", path, { body });
|
|
40284
|
+
}
|
|
40285
|
+
async patch(path, body) {
|
|
40286
|
+
return this.request("PATCH", path, { body });
|
|
40287
|
+
}
|
|
40288
|
+
async delete(path) {
|
|
40289
|
+
return this.request("DELETE", path);
|
|
40290
|
+
}
|
|
40291
|
+
/**
|
|
40292
|
+
* Download a binary resource. Returns raw bytes + content type.
|
|
40293
|
+
*/
|
|
40294
|
+
async download(path) {
|
|
40295
|
+
const url2 = `${this.config.baseUrl}${path}`;
|
|
40296
|
+
const authHeaders = await this.getAuthHeaders();
|
|
40297
|
+
const controller = new AbortController();
|
|
40298
|
+
const timeout = setTimeout(
|
|
40299
|
+
() => controller.abort(),
|
|
40300
|
+
this.config.timeoutMs ?? 3e4
|
|
40301
|
+
);
|
|
40302
|
+
try {
|
|
40303
|
+
const response = await fetch(url2, {
|
|
40304
|
+
method: "GET",
|
|
40305
|
+
headers: authHeaders,
|
|
40306
|
+
signal: controller.signal
|
|
40307
|
+
});
|
|
40308
|
+
if (!response.ok) {
|
|
40309
|
+
const body = await response.text();
|
|
40310
|
+
throw new AdapterAPIError(
|
|
40311
|
+
this.adapterName,
|
|
40312
|
+
`[${this.adapterName}] GET ${path} \u2192 ${response.status}`,
|
|
40313
|
+
response.status,
|
|
40314
|
+
body
|
|
40315
|
+
);
|
|
40316
|
+
}
|
|
40317
|
+
const data = new Uint8Array(await response.arrayBuffer());
|
|
40318
|
+
const contentType = response.headers.get("content-type") ?? "application/octet-stream";
|
|
40319
|
+
return { data, contentType };
|
|
40320
|
+
} finally {
|
|
40321
|
+
clearTimeout(timeout);
|
|
40322
|
+
}
|
|
40323
|
+
}
|
|
40324
|
+
};
|
|
40325
|
+
|
|
40326
|
+
// src/adapters/storecove/client.ts
|
|
40327
|
+
var StorecoveClient = class extends BaseHttpClient {
|
|
40328
|
+
apiKey;
|
|
40329
|
+
constructor(config2) {
|
|
40330
|
+
super("Storecove", { baseUrl: config2.baseUrl, timeoutMs: config2.timeoutMs });
|
|
40331
|
+
this.apiKey = config2.apiKey;
|
|
40332
|
+
}
|
|
40333
|
+
async getAuthHeaders() {
|
|
40334
|
+
return { Authorization: `Bearer ${this.apiKey}` };
|
|
40335
|
+
}
|
|
40336
|
+
};
|
|
40337
|
+
|
|
40338
|
+
// src/adapters/storecove/adapter.ts
|
|
40339
|
+
var StorecoveAdapter = class extends BaseAdapter {
|
|
40340
|
+
name = "storecove";
|
|
40341
|
+
/** Only methods with real Storecove API mappings. */
|
|
40342
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
40343
|
+
// Invoice
|
|
40344
|
+
"emitInvoice",
|
|
40345
|
+
"getInvoice",
|
|
40346
|
+
"downloadInvoice",
|
|
40347
|
+
// Directory
|
|
40348
|
+
"searchDirectoryFr",
|
|
40349
|
+
"searchDirectoryInt",
|
|
40350
|
+
"checkPeppolParticipant",
|
|
40351
|
+
// Status
|
|
40352
|
+
"getStatusHistory",
|
|
40353
|
+
// Webhooks
|
|
40354
|
+
"listWebhooks",
|
|
40355
|
+
"deleteWebhook",
|
|
40356
|
+
// Config / Entities
|
|
40357
|
+
"getBusinessEntity",
|
|
40358
|
+
"createLegalUnit",
|
|
40359
|
+
"deleteBusinessEntity",
|
|
40360
|
+
"configureBusinessEntity",
|
|
40361
|
+
// Identifiers / Network
|
|
40362
|
+
"enrollInternational",
|
|
40363
|
+
"registerNetwork",
|
|
40364
|
+
"registerNetworkByScheme",
|
|
40365
|
+
"unregisterNetwork",
|
|
40366
|
+
"createIdentifier",
|
|
40367
|
+
"createIdentifierByScheme",
|
|
40368
|
+
"deleteIdentifier"
|
|
40369
|
+
]);
|
|
40370
|
+
client;
|
|
40371
|
+
/** Default legal entity ID for operations that need one. */
|
|
40372
|
+
defaultLegalEntityId;
|
|
40373
|
+
constructor(client, defaultLegalEntityId) {
|
|
40374
|
+
super();
|
|
40375
|
+
this.client = client;
|
|
40376
|
+
this.defaultLegalEntityId = defaultLegalEntityId;
|
|
40377
|
+
}
|
|
40378
|
+
// ─── Invoice Operations ───────────────────────────────
|
|
40379
|
+
async emitInvoice(req) {
|
|
40380
|
+
const base643 = uint8ToBase64(req.file);
|
|
40381
|
+
const isXml = req.filename.toLowerCase().endsWith(".xml");
|
|
40382
|
+
return await this.client.post("/document_submissions", {
|
|
40383
|
+
document: {
|
|
40384
|
+
document_type: "invoice",
|
|
40385
|
+
raw_document: base643,
|
|
40386
|
+
raw_document_content_type: isXml ? "application/xml" : "application/pdf"
|
|
40387
|
+
},
|
|
40388
|
+
...this.defaultLegalEntityId ? { legal_entity_id: Number(this.defaultLegalEntityId) } : {}
|
|
40389
|
+
});
|
|
40390
|
+
}
|
|
40391
|
+
async searchInvoices(_filters) {
|
|
40392
|
+
throw new NotSupportedError(
|
|
40393
|
+
this.name,
|
|
40394
|
+
"searchInvoices",
|
|
40395
|
+
"Storecove delivers invoices via webhooks (push) or pull queue. There is no search endpoint. Use webhook pull mode to retrieve pending documents."
|
|
40396
|
+
);
|
|
40397
|
+
}
|
|
40398
|
+
async getInvoice(id) {
|
|
40399
|
+
const doc = await this.client.get(`/received_documents/${encodePathSegment(id)}/json`);
|
|
40400
|
+
return {
|
|
40401
|
+
id,
|
|
40402
|
+
invoiceNumber: doc.invoiceNumber ?? doc.document?.invoiceNumber,
|
|
40403
|
+
status: doc.status ?? "received",
|
|
40404
|
+
direction: "received",
|
|
40405
|
+
senderName: doc.accountingSupplierParty?.party?.partyName,
|
|
40406
|
+
receiverName: doc.accountingCustomerParty?.party?.partyName,
|
|
40407
|
+
issueDate: doc.issueDate,
|
|
40408
|
+
dueDate: doc.dueDate,
|
|
40409
|
+
currency: doc.documentCurrencyCode ?? "EUR",
|
|
40410
|
+
totalTtc: doc.legalMonetaryTotal?.payableAmount
|
|
40411
|
+
};
|
|
40412
|
+
}
|
|
40413
|
+
async downloadInvoice(id) {
|
|
40414
|
+
return await this.client.download(`/received_documents/${encodePathSegment(id)}/original`);
|
|
40415
|
+
}
|
|
40416
|
+
async downloadReadable(_id) {
|
|
40417
|
+
throw new NotSupportedError(
|
|
40418
|
+
this.name,
|
|
40419
|
+
"downloadReadable",
|
|
40420
|
+
"Storecove does not generate readable PDFs. Use getInvoice for JSON or downloadInvoice for the original document."
|
|
40421
|
+
);
|
|
40422
|
+
}
|
|
40423
|
+
async getInvoiceFiles(_id) {
|
|
40424
|
+
throw new NotSupportedError(
|
|
40425
|
+
this.name,
|
|
40426
|
+
"getInvoiceFiles",
|
|
40427
|
+
"Storecove documents are atomic \u2014 no separate files list. Use getInvoice or downloadInvoice."
|
|
40428
|
+
);
|
|
40429
|
+
}
|
|
40430
|
+
async getAttachments(_id) {
|
|
40431
|
+
throw new NotSupportedError(
|
|
40432
|
+
this.name,
|
|
40433
|
+
"getAttachments",
|
|
40434
|
+
"Attachments are embedded in the Storecove document. Use getInvoice to access them."
|
|
40435
|
+
);
|
|
40436
|
+
}
|
|
40437
|
+
async downloadFile(_fileId) {
|
|
40438
|
+
throw new NotSupportedError(
|
|
40439
|
+
this.name,
|
|
40440
|
+
"downloadFile",
|
|
40441
|
+
"Storecove has no separate file download. Use downloadInvoice for the full document."
|
|
40442
|
+
);
|
|
40443
|
+
}
|
|
40444
|
+
async markInvoiceSeen(_id) {
|
|
40445
|
+
throw new NotSupportedError(
|
|
40446
|
+
this.name,
|
|
40447
|
+
"markInvoiceSeen",
|
|
40448
|
+
"Storecove does not track seen/unseen state via API."
|
|
40449
|
+
);
|
|
40450
|
+
}
|
|
40451
|
+
async getUnseenInvoices(_pagination) {
|
|
40452
|
+
throw new NotSupportedError(
|
|
40453
|
+
this.name,
|
|
40454
|
+
"getUnseenInvoices",
|
|
40455
|
+
"Use Storecove webhook pull mode to poll for new documents."
|
|
40456
|
+
);
|
|
40457
|
+
}
|
|
40458
|
+
async generateCII(_req) {
|
|
40459
|
+
throw new NotSupportedError(
|
|
40460
|
+
this.name,
|
|
40461
|
+
"generateCII",
|
|
40462
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40463
|
+
);
|
|
40464
|
+
}
|
|
40465
|
+
async generateUBL(_req) {
|
|
40466
|
+
throw new NotSupportedError(
|
|
40467
|
+
this.name,
|
|
40468
|
+
"generateUBL",
|
|
40469
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40470
|
+
);
|
|
40471
|
+
}
|
|
40472
|
+
async generateFacturX(_req) {
|
|
40473
|
+
throw new NotSupportedError(
|
|
40474
|
+
this.name,
|
|
40475
|
+
"generateFacturX",
|
|
40476
|
+
"Storecove auto-generates the compliant format on submission. Use emitInvoice with JSON Pure mode instead."
|
|
40477
|
+
);
|
|
40478
|
+
}
|
|
40479
|
+
// ─── Directory ────────────────────────────────────────
|
|
40480
|
+
async searchDirectoryFr(filters) {
|
|
40481
|
+
const raw2 = await this.client.post("/discovery/exists", {
|
|
40482
|
+
identifier: filters.q
|
|
40483
|
+
});
|
|
40484
|
+
const participant = raw2?.participant ?? raw2;
|
|
40485
|
+
if (!participant || !participant.identifier) {
|
|
40486
|
+
return { rows: [], count: 0 };
|
|
40487
|
+
}
|
|
40488
|
+
return {
|
|
40489
|
+
rows: [{
|
|
40490
|
+
entityId: participant.identifier ?? "",
|
|
40491
|
+
name: participant.name ?? participant.partyName,
|
|
40492
|
+
country: participant.country
|
|
40493
|
+
}],
|
|
40494
|
+
count: 1
|
|
40495
|
+
};
|
|
40496
|
+
}
|
|
40497
|
+
async searchDirectoryInt(filters) {
|
|
40498
|
+
return await this.client.post("/discovery/receives", {
|
|
40499
|
+
identifier: filters.value
|
|
40500
|
+
});
|
|
40501
|
+
}
|
|
40502
|
+
async checkPeppolParticipant(scheme, value) {
|
|
40503
|
+
return await this.client.post("/discovery/exists", {
|
|
40504
|
+
identifier: { scheme, identifier: value }
|
|
40505
|
+
});
|
|
40506
|
+
}
|
|
40507
|
+
// ─── Status ───────────────────────────────────────────
|
|
40508
|
+
async sendStatus(_req) {
|
|
40509
|
+
throw new NotSupportedError(
|
|
40510
|
+
this.name,
|
|
40511
|
+
"sendStatus",
|
|
40512
|
+
"Storecove status is managed by the receiving Access Point. The sender receives delivery evidence via the evidence endpoint."
|
|
40513
|
+
);
|
|
40514
|
+
}
|
|
40515
|
+
async getStatusHistory(invoiceId) {
|
|
40516
|
+
const raw2 = await this.client.get(
|
|
40517
|
+
`/document_submissions/${encodePathSegment(invoiceId)}/evidence/delivery`
|
|
40518
|
+
);
|
|
40519
|
+
return {
|
|
40520
|
+
entries: raw2 ? [{
|
|
40521
|
+
date: raw2.timestamp ?? raw2.date ?? "",
|
|
40522
|
+
code: raw2.status ?? "delivered",
|
|
40523
|
+
message: raw2.description
|
|
40524
|
+
}] : []
|
|
40525
|
+
};
|
|
40526
|
+
}
|
|
40527
|
+
async getUnseenStatuses(_pagination) {
|
|
40528
|
+
throw new NotSupportedError(
|
|
40529
|
+
this.name,
|
|
40530
|
+
"getUnseenStatuses",
|
|
40531
|
+
"Storecove delivers status changes via webhooks."
|
|
40532
|
+
);
|
|
40533
|
+
}
|
|
40534
|
+
async markStatusSeen(_statusId) {
|
|
40535
|
+
throw new NotSupportedError(
|
|
40536
|
+
this.name,
|
|
40537
|
+
"markStatusSeen",
|
|
40538
|
+
"Storecove does not track seen/unseen status via API."
|
|
40539
|
+
);
|
|
40540
|
+
}
|
|
40541
|
+
// ─── Reporting ────────────────────────────────────────
|
|
40542
|
+
async reportInvoiceTransaction(_transaction) {
|
|
40543
|
+
throw new NotSupportedError(
|
|
40544
|
+
this.name,
|
|
40545
|
+
"reportInvoiceTransaction",
|
|
40546
|
+
"Storecove handles tax reporting internally based on the destination country."
|
|
40547
|
+
);
|
|
40548
|
+
}
|
|
40549
|
+
async reportTransaction(_businessEntityId, _transaction) {
|
|
40550
|
+
throw new NotSupportedError(
|
|
40551
|
+
this.name,
|
|
40552
|
+
"reportTransaction",
|
|
40553
|
+
"Storecove handles tax reporting internally based on the destination country."
|
|
40554
|
+
);
|
|
40555
|
+
}
|
|
40556
|
+
// ─── Webhooks ─────────────────────────────────────────
|
|
40557
|
+
async listWebhooks() {
|
|
40558
|
+
throw new NotSupportedError(
|
|
40559
|
+
this.name,
|
|
40560
|
+
"listWebhooks",
|
|
40561
|
+
"Storecove /webhook_instances is a pull queue, not webhook config CRUD."
|
|
40562
|
+
);
|
|
40563
|
+
}
|
|
40564
|
+
async getWebhook(_id) {
|
|
40565
|
+
throw new NotSupportedError(
|
|
40566
|
+
this.name,
|
|
40567
|
+
"getWebhook",
|
|
40568
|
+
"Storecove has no get-by-id webhook endpoint. Use listWebhooks instead."
|
|
40569
|
+
);
|
|
40570
|
+
}
|
|
40571
|
+
async createWebhook(_req) {
|
|
40572
|
+
throw new NotSupportedError(
|
|
40573
|
+
this.name,
|
|
40574
|
+
"createWebhook",
|
|
40575
|
+
"Storecove webhooks are configured via the Storecove dashboard UI, not the API."
|
|
40576
|
+
);
|
|
40577
|
+
}
|
|
40578
|
+
async updateWebhook(_id, _req) {
|
|
40579
|
+
throw new NotSupportedError(
|
|
40580
|
+
this.name,
|
|
40581
|
+
"updateWebhook",
|
|
40582
|
+
"Storecove webhooks are configured via the Storecove dashboard UI, not the API."
|
|
40583
|
+
);
|
|
40584
|
+
}
|
|
40585
|
+
async deleteWebhook(_id) {
|
|
40586
|
+
throw new NotSupportedError(
|
|
40587
|
+
this.name,
|
|
40588
|
+
"deleteWebhook",
|
|
40589
|
+
"Storecove /webhook_instances is a pull queue, not webhook config CRUD."
|
|
40590
|
+
);
|
|
40591
|
+
}
|
|
40592
|
+
// ─── Operator Config ───────────────────────────────────
|
|
40593
|
+
async getCustomerId() {
|
|
40594
|
+
throw new NotSupportedError(
|
|
40595
|
+
this.name,
|
|
40596
|
+
"getCustomerId",
|
|
40597
|
+
"Storecove uses API keys, not customer IDs. Your identity is implicit in the API key."
|
|
40598
|
+
);
|
|
40599
|
+
}
|
|
40600
|
+
async listBusinessEntities() {
|
|
40601
|
+
throw new NotSupportedError(
|
|
40602
|
+
this.name,
|
|
40603
|
+
"listBusinessEntities",
|
|
40604
|
+
"Storecove has no list-all endpoint for legal entities. Track entity IDs locally after creation, or use getBusinessEntity with a known ID."
|
|
40605
|
+
);
|
|
40606
|
+
}
|
|
40607
|
+
async getBusinessEntity(id) {
|
|
40608
|
+
return await this.client.get(`/legal_entities/${encodePathSegment(id)}`);
|
|
40609
|
+
}
|
|
40610
|
+
async createLegalUnit(_data) {
|
|
40611
|
+
throw new NotSupportedError(
|
|
40612
|
+
this.name,
|
|
40613
|
+
"createLegalUnit",
|
|
40614
|
+
"Storecove requires address fields (line1, city, zip) not collected by the generic tool schema. Implement normalizeForStorecove when ready."
|
|
40615
|
+
);
|
|
40616
|
+
}
|
|
40617
|
+
async createOffice(_data) {
|
|
40618
|
+
throw new NotSupportedError(
|
|
40619
|
+
this.name,
|
|
40620
|
+
"createOffice",
|
|
40621
|
+
"Storecove has no office/establishment concept. Use createLegalUnit for all entities."
|
|
40622
|
+
);
|
|
40623
|
+
}
|
|
40624
|
+
async deleteBusinessEntity(id) {
|
|
40625
|
+
return await this.client.delete(`/legal_entities/${encodePathSegment(id)}`);
|
|
40626
|
+
}
|
|
40627
|
+
async configureBusinessEntity(_id, _data) {
|
|
40628
|
+
throw new NotSupportedError(
|
|
40629
|
+
this.name,
|
|
40630
|
+
"configureBusinessEntity",
|
|
40631
|
+
"Storecove PATCH /legal_entities uses a different model than the generic tool schema."
|
|
40632
|
+
);
|
|
40633
|
+
}
|
|
40634
|
+
async claimBusinessEntity(_id, _data) {
|
|
40635
|
+
throw new NotSupportedError(
|
|
40636
|
+
this.name,
|
|
40637
|
+
"claimBusinessEntity",
|
|
40638
|
+
"Storecove has no entity claim workflow. Entities are created and owned directly."
|
|
40639
|
+
);
|
|
40640
|
+
}
|
|
40641
|
+
async claimBusinessEntityByIdentifier(_scheme, _value, _data) {
|
|
40642
|
+
throw new NotSupportedError(
|
|
40643
|
+
this.name,
|
|
40644
|
+
"claimBusinessEntityByIdentifier",
|
|
40645
|
+
"Storecove has no entity claim workflow. Entities are created and owned directly."
|
|
40646
|
+
);
|
|
40647
|
+
}
|
|
40648
|
+
async enrollFrench(_data) {
|
|
40649
|
+
throw new NotSupportedError(
|
|
40650
|
+
this.name,
|
|
40651
|
+
"enrollFrench",
|
|
40652
|
+
"Use enrollInternational with Peppol identifiers for French entity registration on Storecove."
|
|
40653
|
+
);
|
|
40654
|
+
}
|
|
40655
|
+
async enrollInternational(data) {
|
|
40656
|
+
const legalEntityId = data.legalEntityId ?? this.defaultLegalEntityId;
|
|
40657
|
+
if (!legalEntityId) {
|
|
40658
|
+
throw new Error("[StorecoveAdapter] enrollInternational requires legalEntityId");
|
|
40659
|
+
}
|
|
40660
|
+
return await this.client.post(
|
|
40661
|
+
`/legal_entities/${encodePathSegment(String(legalEntityId))}/peppol_identifiers`,
|
|
40662
|
+
{
|
|
40663
|
+
superscheme: data.superscheme ?? "iso6523-actorid-upis",
|
|
40664
|
+
scheme: data.scheme,
|
|
40665
|
+
identifier: data.identifier ?? data.value
|
|
40666
|
+
}
|
|
40667
|
+
);
|
|
40668
|
+
}
|
|
40669
|
+
async registerNetwork(identifierId, _network) {
|
|
40670
|
+
return { message: `Peppol identifier ${identifierId} is registered on creation in Storecove.` };
|
|
40671
|
+
}
|
|
40672
|
+
async registerNetworkByScheme(scheme, value, _network) {
|
|
40673
|
+
return {
|
|
40674
|
+
message: `Peppol identifier ${scheme}:${value} is registered on creation in Storecove. Use enrollInternational to create the identifier.`
|
|
40675
|
+
};
|
|
40676
|
+
}
|
|
40677
|
+
async unregisterNetwork(directoryId) {
|
|
40678
|
+
const parts = directoryId.split("/");
|
|
40679
|
+
if (parts.length < 4) {
|
|
40680
|
+
throw new Error(
|
|
40681
|
+
"[StorecoveAdapter] unregisterNetwork expects directoryId as 'legalEntityId/superscheme/scheme/identifier'"
|
|
40682
|
+
);
|
|
40683
|
+
}
|
|
40684
|
+
const [legalEntityId, ...rest] = parts;
|
|
40685
|
+
return await this.client.delete(
|
|
40686
|
+
`/legal_entities/${encodePathSegment(legalEntityId)}/peppol_identifiers/${rest.map(encodePathSegment).join("/")}`
|
|
40687
|
+
);
|
|
40688
|
+
}
|
|
40689
|
+
// ─── Identifier Management ───────────────────────────────
|
|
40690
|
+
async createIdentifier(entityId, data) {
|
|
40691
|
+
if (data.scheme && String(data.scheme).startsWith("0")) {
|
|
40692
|
+
return await this.client.post(
|
|
40693
|
+
`/legal_entities/${encodePathSegment(entityId)}/peppol_identifiers`,
|
|
40694
|
+
{
|
|
40695
|
+
superscheme: data.superscheme ?? "iso6523-actorid-upis",
|
|
40696
|
+
scheme: data.scheme,
|
|
40697
|
+
identifier: data.value
|
|
40698
|
+
}
|
|
40699
|
+
);
|
|
40700
|
+
}
|
|
40701
|
+
return await this.client.post(
|
|
40702
|
+
`/legal_entities/${encodePathSegment(entityId)}/additional_tax_identifiers`,
|
|
40703
|
+
data
|
|
40704
|
+
);
|
|
40705
|
+
}
|
|
40706
|
+
async createIdentifierByScheme(_scheme, _value, _data) {
|
|
40707
|
+
throw new NotSupportedError(
|
|
40708
|
+
this.name,
|
|
40709
|
+
"createIdentifierByScheme",
|
|
40710
|
+
"Storecove cannot look up entities by scheme/value \u2014 requires legalEntityId. Implement when tool schema supports it."
|
|
40711
|
+
);
|
|
40712
|
+
}
|
|
40713
|
+
async deleteIdentifier(identifierId) {
|
|
40714
|
+
if (identifierId.includes("/")) {
|
|
40715
|
+
const segments = identifierId.split("/").map(encodePathSegment).join("/");
|
|
40716
|
+
return await this.client.delete(
|
|
40717
|
+
`/legal_entities/${segments}`
|
|
40718
|
+
);
|
|
40719
|
+
}
|
|
40720
|
+
throw new Error(
|
|
40721
|
+
"[StorecoveAdapter] deleteIdentifier for tax identifiers requires the full path: 'legalEntityId/additional_tax_identifiers/identifierId'"
|
|
40722
|
+
);
|
|
40723
|
+
}
|
|
40724
|
+
// ─── Claim Management ────────────────────────────────────
|
|
40725
|
+
async deleteClaim(_entityId) {
|
|
40726
|
+
throw new NotSupportedError(
|
|
40727
|
+
this.name,
|
|
40728
|
+
"deleteClaim",
|
|
40729
|
+
"Storecove has no entity claim concept. Delete the entity directly with deleteBusinessEntity."
|
|
40730
|
+
);
|
|
40731
|
+
}
|
|
40732
|
+
};
|
|
40733
|
+
function createStorecoveAdapter() {
|
|
40734
|
+
const baseUrl = requireEnv(
|
|
40735
|
+
"StorecoveAdapter",
|
|
40736
|
+
"STORECOVE_API_URL",
|
|
40737
|
+
"Set it to https://api.storecove.com/api/v2 (production/sandbox)."
|
|
40738
|
+
);
|
|
40739
|
+
const apiKey = requireEnv(
|
|
40740
|
+
"StorecoveAdapter",
|
|
40741
|
+
"STORECOVE_API_KEY",
|
|
40742
|
+
"Get your API key from the Storecove dashboard."
|
|
40743
|
+
);
|
|
40744
|
+
const defaultLegalEntityId = env2("STORECOVE_LEGAL_ENTITY_ID") || void 0;
|
|
40745
|
+
const client = new StorecoveClient({ baseUrl, apiKey });
|
|
40746
|
+
return new StorecoveAdapter(client, defaultLegalEntityId);
|
|
40747
|
+
}
|
|
40748
|
+
|
|
40749
|
+
// src/adapters/afnor/base-adapter.ts
|
|
40750
|
+
var AfnorBaseAdapter = class extends BaseAdapter {
|
|
40751
|
+
afnor;
|
|
40752
|
+
constructor(afnor) {
|
|
40753
|
+
super();
|
|
40754
|
+
this.afnor = afnor;
|
|
40755
|
+
}
|
|
40756
|
+
// ─── Invoice Operations (AFNOR: submitFlow, searchFlows, downloadFlow) ───
|
|
40757
|
+
async emitInvoice(req) {
|
|
40758
|
+
const afnor = this.requireAfnor("emitInvoice");
|
|
40759
|
+
const syntax = req.filename.toLowerCase().endsWith(".pdf") ? "Factur-X" : "CII";
|
|
40760
|
+
return await afnor.submitFlow(
|
|
40761
|
+
req.file,
|
|
40762
|
+
{ flowSyntax: syntax, name: req.filename, processingRule: "B2B" }
|
|
40763
|
+
);
|
|
38827
40764
|
}
|
|
38828
|
-
|
|
38829
|
-
|
|
38830
|
-
|
|
38831
|
-
|
|
38832
|
-
|
|
38833
|
-
|
|
38834
|
-
|
|
38835
|
-
|
|
38836
|
-
|
|
38837
|
-
|
|
38838
|
-
|
|
38839
|
-
|
|
40765
|
+
async searchInvoices(filters) {
|
|
40766
|
+
const afnor = this.requireAfnor("searchInvoices");
|
|
40767
|
+
const result = await afnor.searchFlows(
|
|
40768
|
+
{
|
|
40769
|
+
flowType: ["CustomerInvoice", "SupplierInvoice"],
|
|
40770
|
+
...filters.q ? { trackingId: filters.q } : {}
|
|
40771
|
+
},
|
|
40772
|
+
filters.limit
|
|
40773
|
+
);
|
|
40774
|
+
const rows = (result.results ?? []).map((r) => ({
|
|
40775
|
+
id: r.flowId ?? "",
|
|
40776
|
+
status: r.ackStatus,
|
|
40777
|
+
direction: normalizeDirection(r.flowDirection),
|
|
40778
|
+
date: r.updatedAt ?? r.submittedAt
|
|
40779
|
+
}));
|
|
40780
|
+
return { rows, count: rows.length };
|
|
40781
|
+
}
|
|
40782
|
+
async getInvoice(id) {
|
|
40783
|
+
const afnor = this.requireAfnor("getInvoice");
|
|
40784
|
+
const { data, contentType } = await afnor.downloadFlow(id);
|
|
40785
|
+
if (contentType.includes("json")) {
|
|
40786
|
+
const doc = JSON.parse(new TextDecoder().decode(data));
|
|
40787
|
+
return {
|
|
40788
|
+
id,
|
|
40789
|
+
invoiceNumber: doc.invoiceId ?? doc.invoiceNumber,
|
|
40790
|
+
status: doc.ackStatus ?? doc.status,
|
|
40791
|
+
direction: normalizeDirection(doc.flowDirection),
|
|
40792
|
+
senderName: doc.seller?.name,
|
|
40793
|
+
receiverName: doc.buyer?.name,
|
|
40794
|
+
issueDate: doc.invoiceDate,
|
|
40795
|
+
currency: doc.currency ?? "EUR"
|
|
40796
|
+
};
|
|
38840
40797
|
}
|
|
40798
|
+
return { id, status: "UNKNOWN" };
|
|
38841
40799
|
}
|
|
38842
|
-
async
|
|
38843
|
-
|
|
40800
|
+
async downloadInvoice(id) {
|
|
40801
|
+
const afnor = this.requireAfnor("downloadInvoice");
|
|
40802
|
+
return await afnor.downloadFlow(id, "Original");
|
|
38844
40803
|
}
|
|
38845
|
-
|
|
38846
|
-
|
|
40804
|
+
// ─── Status (AFNOR: lifecycle flows) ───────────────────
|
|
40805
|
+
async sendStatus(req) {
|
|
40806
|
+
const afnor = this.requireAfnor("sendStatus");
|
|
40807
|
+
const cdarPayload = JSON.stringify({
|
|
40808
|
+
invoiceId: req.invoiceId,
|
|
40809
|
+
statusCode: req.code,
|
|
40810
|
+
message: req.message,
|
|
40811
|
+
payment: req.payment
|
|
40812
|
+
});
|
|
40813
|
+
return await afnor.submitFlow(
|
|
40814
|
+
new TextEncoder().encode(cdarPayload),
|
|
40815
|
+
{ flowSyntax: "CDAR", name: `status-${req.invoiceId}.json`, processingRule: "B2B" }
|
|
40816
|
+
);
|
|
38847
40817
|
}
|
|
38848
|
-
async
|
|
38849
|
-
|
|
40818
|
+
async getStatusHistory(invoiceId) {
|
|
40819
|
+
const afnor = this.requireAfnor("getStatusHistory");
|
|
40820
|
+
const result = await afnor.searchFlows({
|
|
40821
|
+
flowType: ["CustomerInvoiceLC", "SupplierInvoiceLC"],
|
|
40822
|
+
trackingId: invoiceId
|
|
40823
|
+
});
|
|
40824
|
+
const entries = (result.results ?? []).map((r) => ({
|
|
40825
|
+
date: r.updatedAt ?? r.submittedAt ?? "",
|
|
40826
|
+
code: r.ackStatus ?? "",
|
|
40827
|
+
message: r.flowType,
|
|
40828
|
+
destType: r.flowDirection === "In" ? "PLATFORM" : "OPERATOR"
|
|
40829
|
+
}));
|
|
40830
|
+
return { entries };
|
|
40831
|
+
}
|
|
40832
|
+
// ─── Reporting (AFNOR: e-reporting flows) ──────────────
|
|
40833
|
+
async reportInvoiceTransaction(transaction) {
|
|
40834
|
+
const afnor = this.requireAfnor("reportInvoiceTransaction");
|
|
40835
|
+
const payload = new TextEncoder().encode(JSON.stringify(transaction));
|
|
40836
|
+
return await afnor.submitFlow(
|
|
40837
|
+
payload,
|
|
40838
|
+
{ flowSyntax: "FRR", name: "report.json", processingRule: "B2C" }
|
|
40839
|
+
);
|
|
40840
|
+
}
|
|
40841
|
+
async reportTransaction(businessEntityId, transaction) {
|
|
40842
|
+
const afnor = this.requireAfnor("reportTransaction");
|
|
40843
|
+
const payload = new TextEncoder().encode(JSON.stringify({ businessEntityId, ...transaction }));
|
|
40844
|
+
return await afnor.submitFlow(
|
|
40845
|
+
payload,
|
|
40846
|
+
{ flowSyntax: "FRR", name: "report.json", processingRule: "B2C" }
|
|
40847
|
+
);
|
|
40848
|
+
}
|
|
40849
|
+
// All other methods inherit NotSupportedError stubs from BaseAdapter.
|
|
40850
|
+
// Subclasses override with native API implementations.
|
|
40851
|
+
// ─── Helpers ───────────────────────────────────────────
|
|
40852
|
+
/** Get the AFNOR client or throw if not configured. */
|
|
40853
|
+
requireAfnor(method) {
|
|
40854
|
+
if (!this.afnor) {
|
|
40855
|
+
throw new NotSupportedError(
|
|
40856
|
+
this.name,
|
|
40857
|
+
method,
|
|
40858
|
+
"AFNOR API not configured. Override this method with native implementation."
|
|
40859
|
+
);
|
|
40860
|
+
}
|
|
40861
|
+
return this.afnor;
|
|
40862
|
+
}
|
|
40863
|
+
};
|
|
40864
|
+
|
|
40865
|
+
// src/adapters/afnor/client.ts
|
|
40866
|
+
var AfnorClient = class extends BaseHttpClient {
|
|
40867
|
+
getToken;
|
|
40868
|
+
constructor(config2) {
|
|
40869
|
+
super("AFNOR", { baseUrl: config2.baseUrl, timeoutMs: config2.timeoutMs });
|
|
40870
|
+
this.getToken = config2.getToken;
|
|
40871
|
+
}
|
|
40872
|
+
async getAuthHeaders() {
|
|
40873
|
+
const token = await this.getToken();
|
|
40874
|
+
return { Authorization: `Bearer ${token}` };
|
|
38850
40875
|
}
|
|
40876
|
+
// ─── AFNOR-Specific Methods ──────────────────────────
|
|
38851
40877
|
/**
|
|
38852
|
-
*
|
|
38853
|
-
*
|
|
38854
|
-
* Content-Type: multipart/form-data with a `file` field (binary, PDF or XML).
|
|
40878
|
+
* Submit a new flow (invoice, lifecycle event, or e-reporting).
|
|
40879
|
+
* POST /v1/flows — multipart: flowInfo (JSON) + file (binary).
|
|
38855
40880
|
*/
|
|
38856
|
-
async
|
|
38857
|
-
const url2 = `${this.config.baseUrl}
|
|
38858
|
-
const
|
|
40881
|
+
async submitFlow(file2, flowInfo) {
|
|
40882
|
+
const url2 = new URL(`${this.config.baseUrl}/v1/flows`);
|
|
40883
|
+
const authHeaders = await this.getAuthHeaders();
|
|
38859
40884
|
const controller = new AbortController();
|
|
38860
40885
|
const timeout = setTimeout(
|
|
38861
40886
|
() => controller.abort(),
|
|
@@ -38863,67 +40888,68 @@ var IopoleClient = class {
|
|
|
38863
40888
|
);
|
|
38864
40889
|
try {
|
|
38865
40890
|
const form = new FormData();
|
|
38866
|
-
form.append("
|
|
38867
|
-
|
|
40891
|
+
form.append("flowInfo", JSON.stringify(flowInfo));
|
|
40892
|
+
form.append("file", new Blob([file2]), flowInfo.name ?? "invoice.xml");
|
|
40893
|
+
const response = await fetch(url2.toString(), {
|
|
38868
40894
|
method: "POST",
|
|
38869
|
-
headers: {
|
|
38870
|
-
Authorization: `Bearer ${token}`,
|
|
38871
|
-
"customer-id": this.config.customerId,
|
|
38872
|
-
Accept: "application/json"
|
|
38873
|
-
// Do NOT set Content-Type — fetch sets it with the multipart boundary
|
|
38874
|
-
},
|
|
40895
|
+
headers: { ...authHeaders, Accept: "application/json" },
|
|
38875
40896
|
body: form,
|
|
38876
40897
|
signal: controller.signal
|
|
38877
40898
|
});
|
|
38878
40899
|
if (!response.ok) {
|
|
38879
40900
|
const body = await response.text();
|
|
38880
|
-
throw new
|
|
38881
|
-
|
|
40901
|
+
throw new AdapterAPIError(
|
|
40902
|
+
"AFNOR",
|
|
40903
|
+
`[AFNOR] POST /v1/flows \u2192 ${response.status}: ${body.slice(0, 500)}`,
|
|
38882
40904
|
response.status,
|
|
38883
40905
|
body
|
|
38884
40906
|
);
|
|
38885
40907
|
}
|
|
38886
|
-
|
|
38887
|
-
if (contentType.includes("application/json")) {
|
|
38888
|
-
return await response.json();
|
|
38889
|
-
}
|
|
38890
|
-
return await response.text();
|
|
40908
|
+
return await response.json();
|
|
38891
40909
|
} finally {
|
|
38892
40910
|
clearTimeout(timeout);
|
|
38893
40911
|
}
|
|
38894
40912
|
}
|
|
38895
40913
|
/**
|
|
38896
|
-
*
|
|
38897
|
-
*
|
|
40914
|
+
* Search flows by criteria.
|
|
40915
|
+
* POST /v1/flows/search
|
|
38898
40916
|
*/
|
|
38899
|
-
async
|
|
38900
|
-
return this.request("POST",
|
|
40917
|
+
async searchFlows(filters, limit) {
|
|
40918
|
+
return await this.request("POST", "/v1/flows/search", {
|
|
40919
|
+
body: {
|
|
40920
|
+
where: filters,
|
|
40921
|
+
...limit ? { limit } : {}
|
|
40922
|
+
}
|
|
40923
|
+
});
|
|
38901
40924
|
}
|
|
38902
40925
|
/**
|
|
38903
|
-
* Download a
|
|
38904
|
-
*
|
|
40926
|
+
* Download a flow file.
|
|
40927
|
+
* GET /v1/flows/{flowId}
|
|
38905
40928
|
*/
|
|
38906
|
-
async
|
|
38907
|
-
const
|
|
38908
|
-
const
|
|
40929
|
+
async downloadFlow(flowId, docType) {
|
|
40930
|
+
const query = docType ? { docType } : void 0;
|
|
40931
|
+
const url2 = `${this.config.baseUrl}/v1/flows/${flowId}`;
|
|
40932
|
+
const authHeaders = await this.getAuthHeaders();
|
|
38909
40933
|
const controller = new AbortController();
|
|
38910
40934
|
const timeout = setTimeout(
|
|
38911
40935
|
() => controller.abort(),
|
|
38912
40936
|
this.config.timeoutMs ?? 3e4
|
|
38913
40937
|
);
|
|
38914
40938
|
try {
|
|
38915
|
-
const
|
|
40939
|
+
const fullUrl = new URL(url2);
|
|
40940
|
+
if (query) {
|
|
40941
|
+
for (const [k, v] of Object.entries(query)) fullUrl.searchParams.set(k, v);
|
|
40942
|
+
}
|
|
40943
|
+
const response = await fetch(fullUrl.toString(), {
|
|
38916
40944
|
method: "GET",
|
|
38917
|
-
headers:
|
|
38918
|
-
Authorization: `Bearer ${token}`,
|
|
38919
|
-
"customer-id": this.config.customerId
|
|
38920
|
-
},
|
|
40945
|
+
headers: authHeaders,
|
|
38921
40946
|
signal: controller.signal
|
|
38922
40947
|
});
|
|
38923
40948
|
if (!response.ok) {
|
|
38924
40949
|
const body = await response.text();
|
|
38925
|
-
throw new
|
|
38926
|
-
|
|
40950
|
+
throw new AdapterAPIError(
|
|
40951
|
+
"AFNOR",
|
|
40952
|
+
`[AFNOR] GET /v1/flows/${flowId} \u2192 ${response.status}`,
|
|
38927
40953
|
response.status,
|
|
38928
40954
|
body
|
|
38929
40955
|
);
|
|
@@ -38935,191 +40961,473 @@ var IopoleClient = class {
|
|
|
38935
40961
|
clearTimeout(timeout);
|
|
38936
40962
|
}
|
|
38937
40963
|
}
|
|
40964
|
+
/**
|
|
40965
|
+
* Health check — GET /v1/healthcheck
|
|
40966
|
+
*/
|
|
40967
|
+
async healthcheck() {
|
|
40968
|
+
try {
|
|
40969
|
+
await this.request("GET", "/v1/healthcheck");
|
|
40970
|
+
return true;
|
|
40971
|
+
} catch {
|
|
40972
|
+
return false;
|
|
40973
|
+
}
|
|
40974
|
+
}
|
|
38938
40975
|
};
|
|
38939
40976
|
|
|
38940
|
-
// src/
|
|
38941
|
-
|
|
38942
|
-
|
|
38943
|
-
|
|
38944
|
-
|
|
40977
|
+
// src/adapters/superpdp/client.ts
|
|
40978
|
+
var SuperPDPClient = class extends BaseHttpClient {
|
|
40979
|
+
getToken;
|
|
40980
|
+
constructor(config2) {
|
|
40981
|
+
super("SuperPDP", { baseUrl: config2.baseUrl, timeoutMs: config2.timeoutMs });
|
|
40982
|
+
this.getToken = config2.getToken;
|
|
40983
|
+
}
|
|
40984
|
+
async getAuthHeaders() {
|
|
40985
|
+
const token = await this.getToken();
|
|
40986
|
+
return { Authorization: `Bearer ${token}` };
|
|
40987
|
+
}
|
|
40988
|
+
/**
|
|
40989
|
+
* Submit XML invoice content.
|
|
40990
|
+
* Super PDP accepts raw XML body for invoice creation.
|
|
40991
|
+
*/
|
|
40992
|
+
async postXml(path, xmlData, query) {
|
|
40993
|
+
return this.request("POST", path, {
|
|
40994
|
+
body: xmlData,
|
|
40995
|
+
contentType: "application/xml",
|
|
40996
|
+
query
|
|
40997
|
+
});
|
|
40998
|
+
}
|
|
40999
|
+
/**
|
|
41000
|
+
* Convert invoice format.
|
|
41001
|
+
* POST /invoices/convert with body + from/to query params.
|
|
41002
|
+
*/
|
|
41003
|
+
async convert(data, from, to) {
|
|
41004
|
+
const contentType = from === "en16931" ? "application/json" : "application/xml";
|
|
41005
|
+
return this.request("POST", "/invoices/convert", {
|
|
41006
|
+
body: data,
|
|
41007
|
+
contentType,
|
|
41008
|
+
query: { from, to }
|
|
41009
|
+
});
|
|
41010
|
+
}
|
|
41011
|
+
};
|
|
41012
|
+
|
|
41013
|
+
// src/adapters/superpdp/normalize.ts
|
|
41014
|
+
function toDecimal(v) {
|
|
41015
|
+
if (v == null) return "0.00";
|
|
41016
|
+
const n = typeof v === "string" ? parseFloat(v) : Number(v);
|
|
41017
|
+
if (!Number.isFinite(n)) return "0.00";
|
|
41018
|
+
return n.toFixed(2);
|
|
38945
41019
|
}
|
|
38946
|
-
|
|
38947
|
-
|
|
41020
|
+
function setIfAbsent(obj, key, value) {
|
|
41021
|
+
if (obj[key] == null && value != null) obj[key] = value;
|
|
38948
41022
|
}
|
|
38949
|
-
function
|
|
38950
|
-
|
|
38951
|
-
|
|
38952
|
-
|
|
38953
|
-
} catch {
|
|
38954
|
-
return false;
|
|
41023
|
+
function stripNulls(obj) {
|
|
41024
|
+
const clean = {};
|
|
41025
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
41026
|
+
if (v != null) clean[k] = v;
|
|
38955
41027
|
}
|
|
41028
|
+
return clean;
|
|
38956
41029
|
}
|
|
38957
|
-
|
|
38958
|
-
|
|
38959
|
-
|
|
38960
|
-
|
|
38961
|
-
|
|
41030
|
+
var PARTY_SOURCE_FIELDS = ["country", "address", "siret", "siretNumber", "siren", "sirenNumber", "vatNumber", "vat_number", "vatId"];
|
|
41031
|
+
function normalizeParty(party, requireElectronicAddress) {
|
|
41032
|
+
if (!party || typeof party !== "object") return party;
|
|
41033
|
+
const p = { ...party };
|
|
41034
|
+
if (!p.postal_address) {
|
|
41035
|
+
if (p.address && typeof p.address === "object") {
|
|
41036
|
+
p.postal_address = stripNulls({
|
|
41037
|
+
country_code: p.address.country_code ?? p.address.country ?? p.country ?? "FR",
|
|
41038
|
+
address_line1: p.address.street ?? p.address.address_line1 ?? p.address.line1,
|
|
41039
|
+
city: p.address.city,
|
|
41040
|
+
post_code: p.address.postal_code ?? p.address.post_code ?? p.address.zip
|
|
41041
|
+
});
|
|
41042
|
+
} else if (p.country) {
|
|
41043
|
+
p.postal_address = { country_code: p.country };
|
|
41044
|
+
}
|
|
41045
|
+
}
|
|
41046
|
+
if (!p.electronic_address && requireElectronicAddress) {
|
|
41047
|
+
const siret = p.siret ?? p.siretNumber;
|
|
41048
|
+
if (siret) {
|
|
41049
|
+
p.electronic_address = { scheme: "0009", value: String(siret) };
|
|
41050
|
+
}
|
|
41051
|
+
}
|
|
41052
|
+
if (!p.legal_registration_identifier) {
|
|
41053
|
+
const siren = p.siren ?? p.sirenNumber;
|
|
41054
|
+
const siret = p.siret ?? p.siretNumber;
|
|
41055
|
+
const id = siren ?? siret;
|
|
41056
|
+
if (id) {
|
|
41057
|
+
p.legal_registration_identifier = { scheme: "0002", value: String(id) };
|
|
41058
|
+
}
|
|
41059
|
+
}
|
|
41060
|
+
if (!p.vat_identifier) {
|
|
41061
|
+
const vat = p.vatNumber ?? p.vat_number ?? p.vatId;
|
|
41062
|
+
if (vat) p.vat_identifier = String(vat);
|
|
41063
|
+
}
|
|
41064
|
+
for (const f of PARTY_SOURCE_FIELDS) delete p[f];
|
|
41065
|
+
return p;
|
|
38962
41066
|
}
|
|
38963
|
-
|
|
38964
|
-
|
|
41067
|
+
var TOTALS_SOURCE_FIELDS = ["line_extension_amount", "lineTotalAmount", "tax_exclusive_amount", "taxBasisTotalAmount", "tax_inclusive_amount", "grandTotalAmount", "payableAmount"];
|
|
41068
|
+
function normalizeTotals(totals) {
|
|
41069
|
+
if (!totals || typeof totals !== "object") return totals;
|
|
41070
|
+
const t = { ...totals };
|
|
41071
|
+
setIfAbsent(t, "sum_invoice_lines_amount", t.line_extension_amount ?? t.lineTotalAmount);
|
|
41072
|
+
setIfAbsent(t, "total_without_vat", t.tax_exclusive_amount ?? t.taxBasisTotalAmount);
|
|
41073
|
+
setIfAbsent(t, "total_with_vat", t.tax_inclusive_amount ?? t.grandTotalAmount);
|
|
41074
|
+
setIfAbsent(t, "amount_due_for_payment", t.payableAmount);
|
|
41075
|
+
for (const key of ["sum_invoice_lines_amount", "total_without_vat", "total_with_vat", "amount_due_for_payment"]) {
|
|
41076
|
+
if (t[key] != null) t[key] = toDecimal(t[key]);
|
|
41077
|
+
}
|
|
41078
|
+
if (t.total_vat_amount != null && typeof t.total_vat_amount !== "object") {
|
|
41079
|
+
t.total_vat_amount = { value: toDecimal(t.total_vat_amount) };
|
|
41080
|
+
} else if (t.total_vat_amount?.value != null) {
|
|
41081
|
+
t.total_vat_amount = { ...t.total_vat_amount, value: toDecimal(t.total_vat_amount.value) };
|
|
41082
|
+
}
|
|
41083
|
+
for (const f of TOTALS_SOURCE_FIELDS) delete t[f];
|
|
41084
|
+
return t;
|
|
38965
41085
|
}
|
|
38966
|
-
|
|
38967
|
-
|
|
38968
|
-
|
|
38969
|
-
|
|
38970
|
-
|
|
41086
|
+
var VAT_SOURCE_FIELDS = ["category_code", "categoryCode", "rate", "percent", "vat_rate", "taxable_amount", "taxableAmount", "tax_amount", "taxAmount"];
|
|
41087
|
+
function normalizeVatBreakdown(vat) {
|
|
41088
|
+
if (!vat || typeof vat !== "object") return vat;
|
|
41089
|
+
const v = { ...vat };
|
|
41090
|
+
setIfAbsent(v, "vat_category_code", v.category_code ?? v.categoryCode);
|
|
41091
|
+
setIfAbsent(v, "vat_category_rate", v.rate ?? v.percent ?? v.vat_rate);
|
|
41092
|
+
setIfAbsent(v, "vat_category_taxable_amount", v.taxable_amount ?? v.taxableAmount);
|
|
41093
|
+
setIfAbsent(v, "vat_category_tax_amount", v.tax_amount ?? v.taxAmount);
|
|
41094
|
+
if (v.vat_category_rate != null) v.vat_category_rate = toDecimal(v.vat_category_rate);
|
|
41095
|
+
if (v.vat_category_taxable_amount != null) v.vat_category_taxable_amount = toDecimal(v.vat_category_taxable_amount);
|
|
41096
|
+
if (v.vat_category_tax_amount != null) v.vat_category_tax_amount = toDecimal(v.vat_category_tax_amount);
|
|
41097
|
+
for (const f of VAT_SOURCE_FIELDS) delete v[f];
|
|
41098
|
+
return v;
|
|
41099
|
+
}
|
|
41100
|
+
var LINE_SOURCE_FIELDS = ["id", "name", "item_name", "description", "quantity", "billed_quantity", "unit_code", "unitCode", "net_price", "price", "unit_price", "unitPrice", "line_amount", "line_total_amount", "line_net_amount", "amount", "totalAmount", "tax_category", "vat_category_code", "line_vat_category_code", "vatCategoryCode", "tax_percent", "vat_rate", "line_vat_rate", "vatRate"];
|
|
41101
|
+
function normalizeLine(line) {
|
|
41102
|
+
if (!line || typeof line !== "object") return line;
|
|
41103
|
+
const l = { ...line };
|
|
41104
|
+
setIfAbsent(l, "identifier", l.id);
|
|
41105
|
+
if (!l.item_information) {
|
|
41106
|
+
const name = l.name ?? l.item_name ?? l.description;
|
|
41107
|
+
if (name) l.item_information = { name: String(name) };
|
|
41108
|
+
}
|
|
41109
|
+
setIfAbsent(l, "invoiced_quantity", l.quantity ?? l.billed_quantity);
|
|
41110
|
+
if (l.invoiced_quantity != null) l.invoiced_quantity = toDecimal(l.invoiced_quantity);
|
|
41111
|
+
setIfAbsent(l, "invoiced_quantity_code", l.unit_code ?? l.unitCode ?? "C62");
|
|
41112
|
+
if (!l.price_details) {
|
|
41113
|
+
const price = l.net_price ?? l.price ?? l.unit_price ?? l.unitPrice;
|
|
41114
|
+
if (price != null) l.price_details = { item_net_price: toDecimal(price) };
|
|
41115
|
+
}
|
|
41116
|
+
setIfAbsent(l, "net_amount", l.line_amount ?? l.line_total_amount ?? l.line_net_amount ?? l.amount ?? l.totalAmount);
|
|
41117
|
+
if (l.net_amount != null) l.net_amount = toDecimal(l.net_amount);
|
|
41118
|
+
if (!l.vat_information) {
|
|
41119
|
+
const catCode = l.tax_category ?? l.vat_category_code ?? l.line_vat_category_code ?? l.vatCategoryCode;
|
|
41120
|
+
const rate = l.tax_percent ?? l.vat_rate ?? l.line_vat_rate ?? l.vatRate;
|
|
41121
|
+
if (catCode) {
|
|
41122
|
+
l.vat_information = {
|
|
41123
|
+
invoiced_item_vat_category_code: String(catCode),
|
|
41124
|
+
...rate != null ? { invoiced_item_vat_rate: toDecimal(rate) } : {}
|
|
41125
|
+
};
|
|
41126
|
+
}
|
|
41127
|
+
}
|
|
41128
|
+
for (const f of LINE_SOURCE_FIELDS) delete l[f];
|
|
41129
|
+
return l;
|
|
41130
|
+
}
|
|
41131
|
+
var normalizeForSuperPDP = (inv) => {
|
|
41132
|
+
const n = { ...inv };
|
|
41133
|
+
if (!n.process_control) {
|
|
41134
|
+
n.process_control = { specification_identifier: "urn:cen.eu:en16931:2017" };
|
|
41135
|
+
}
|
|
41136
|
+
if (!n.payment_due_date) {
|
|
41137
|
+
const due = n.due_date ?? n.dueDate ?? n.invoiceDueDate;
|
|
41138
|
+
if (due) n.payment_due_date = String(due);
|
|
41139
|
+
}
|
|
41140
|
+
for (const f of ["due_date", "dueDate", "invoiceDueDate"]) delete n[f];
|
|
41141
|
+
const buyerSiret = n.buyer?.siret ?? n.buyer?.siretNumber;
|
|
41142
|
+
if (n.seller) n.seller = normalizeParty(n.seller, true);
|
|
41143
|
+
if (n.buyer) n.buyer = normalizeParty(n.buyer, false);
|
|
41144
|
+
if (n.totals) n.totals = normalizeTotals(n.totals);
|
|
41145
|
+
let vatSource;
|
|
41146
|
+
if (Array.isArray(n.vat_break_down)) vatSource = n.vat_break_down;
|
|
41147
|
+
else if (Array.isArray(n.taxDetails)) vatSource = n.taxDetails;
|
|
41148
|
+
else if (Array.isArray(n.vatBreakdown)) vatSource = n.vatBreakdown;
|
|
41149
|
+
if (vatSource) {
|
|
41150
|
+
n.vat_break_down = vatSource.map(normalizeVatBreakdown);
|
|
41151
|
+
delete n.taxDetails;
|
|
41152
|
+
delete n.vatBreakdown;
|
|
41153
|
+
}
|
|
41154
|
+
if (Array.isArray(n.lines)) {
|
|
41155
|
+
n.lines = n.lines.map(normalizeLine);
|
|
41156
|
+
}
|
|
41157
|
+
if (n.payment_instructions) {
|
|
41158
|
+
const pi = { ...n.payment_instructions };
|
|
41159
|
+
if (pi.credit_transfer && !pi.credit_transfers) {
|
|
41160
|
+
pi.credit_transfers = Array.isArray(pi.credit_transfer) ? pi.credit_transfer : [pi.credit_transfer];
|
|
41161
|
+
delete pi.credit_transfer;
|
|
41162
|
+
}
|
|
41163
|
+
if (Array.isArray(pi.credit_transfers)) {
|
|
41164
|
+
pi.credit_transfers = pi.credit_transfers.map((ct) => {
|
|
41165
|
+
if (ct?.payment_account_identifier?.scheme?.toUpperCase() === "IBAN") {
|
|
41166
|
+
return { ...ct, payment_account_identifier: { ...ct.payment_account_identifier, scheme: "" } };
|
|
41167
|
+
}
|
|
41168
|
+
return ct;
|
|
41169
|
+
});
|
|
41170
|
+
}
|
|
41171
|
+
n.payment_instructions = pi;
|
|
41172
|
+
}
|
|
41173
|
+
if (!n.delivery_information) {
|
|
41174
|
+
n.delivery_information = { delivery_date: n.issue_date ?? (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) };
|
|
41175
|
+
}
|
|
41176
|
+
if (!n.notes || !Array.isArray(n.notes)) n.notes = [];
|
|
41177
|
+
const noteCodes = new Set(n.notes.map((note) => note?.subject_code));
|
|
41178
|
+
if (!noteCodes.has("PMT")) {
|
|
41179
|
+
n.notes.push({ note: "En cas de retard de paiement, indemnite forfaitaire de 40 euros pour frais de recouvrement (art. L441-10 C.com).", subject_code: "PMT" });
|
|
41180
|
+
}
|
|
41181
|
+
if (!noteCodes.has("PMD")) {
|
|
41182
|
+
n.notes.push({ note: "Penalites de retard : 3 fois le taux d'interet legal (art. L441-10 C.com).", subject_code: "PMD" });
|
|
41183
|
+
}
|
|
41184
|
+
if (!noteCodes.has("AAB")) {
|
|
41185
|
+
n.notes.push({ note: "Pas d'escompte pour paiement anticipe.", subject_code: "AAB" });
|
|
41186
|
+
}
|
|
41187
|
+
const buyerCountry = n.buyer?.postal_address?.country_code ?? n.buyer?.country;
|
|
41188
|
+
if (n.buyer && !n.buyer.electronic_address && buyerCountry === "FR") {
|
|
41189
|
+
const siret = buyerSiret ?? n.buyer.legal_registration_identifier?.value;
|
|
41190
|
+
if (!siret) {
|
|
41191
|
+
throw new Error("BR-FR-12: buyer.electronic_address is required for French buyers. Provide buyer.siret, buyer.electronic_address, or buyer.legal_registration_identifier.");
|
|
41192
|
+
}
|
|
41193
|
+
n.buyer.electronic_address = { scheme: "0009", value: siret };
|
|
41194
|
+
}
|
|
41195
|
+
return n;
|
|
41196
|
+
};
|
|
41197
|
+
|
|
41198
|
+
// src/adapters/superpdp/adapter.ts
|
|
41199
|
+
var SuperPDPAdapter = class extends AfnorBaseAdapter {
|
|
41200
|
+
name = "superpdp";
|
|
41201
|
+
capabilities = /* @__PURE__ */ new Set([
|
|
41202
|
+
// Native overrides
|
|
41203
|
+
"emitInvoice",
|
|
41204
|
+
"searchInvoices",
|
|
41205
|
+
"getInvoice",
|
|
41206
|
+
"downloadInvoice",
|
|
41207
|
+
"generateCII",
|
|
41208
|
+
"generateUBL",
|
|
41209
|
+
"sendStatus",
|
|
41210
|
+
"getStatusHistory",
|
|
41211
|
+
"getCustomerId",
|
|
41212
|
+
"getBusinessEntity",
|
|
41213
|
+
"createOffice",
|
|
41214
|
+
"enrollFrench",
|
|
41215
|
+
"registerNetwork",
|
|
41216
|
+
"registerNetworkByScheme",
|
|
41217
|
+
"unregisterNetwork",
|
|
41218
|
+
"createIdentifier",
|
|
41219
|
+
"createIdentifierByScheme",
|
|
41220
|
+
"deleteIdentifier",
|
|
41221
|
+
"searchDirectoryFr",
|
|
41222
|
+
// Inherited from AFNOR base
|
|
41223
|
+
"reportInvoiceTransaction",
|
|
41224
|
+
"reportTransaction"
|
|
41225
|
+
]);
|
|
38971
41226
|
client;
|
|
38972
|
-
constructor(client) {
|
|
41227
|
+
constructor(client, afnor) {
|
|
41228
|
+
super(afnor);
|
|
38973
41229
|
this.client = client;
|
|
38974
41230
|
}
|
|
38975
|
-
// ─── Invoice Operations
|
|
41231
|
+
// ─── Invoice Operations (native) ──────────────────────
|
|
38976
41232
|
async emitInvoice(req) {
|
|
38977
|
-
return await this.client.
|
|
41233
|
+
return await this.client.postXml("/invoices", req.file, {
|
|
41234
|
+
external_id: req.filename
|
|
41235
|
+
});
|
|
38978
41236
|
}
|
|
38979
41237
|
async searchInvoices(filters) {
|
|
38980
|
-
|
|
38981
|
-
|
|
38982
|
-
expand:
|
|
38983
|
-
|
|
38984
|
-
limit: filters.limit
|
|
41238
|
+
const direction = filters.direction === "received" ? "in" : filters.direction === "sent" ? "out" : filters.q === "in" || filters.q === "out" ? filters.q : void 0;
|
|
41239
|
+
const raw2 = await this.client.get("/invoices", {
|
|
41240
|
+
"expand[]": ["en_invoice", "events"],
|
|
41241
|
+
...direction ? { direction } : {},
|
|
41242
|
+
...filters.limit ? { limit: filters.limit } : {}
|
|
41243
|
+
// Note: SuperPDP uses cursor-based pagination (starting_after_id),
|
|
41244
|
+
// not offset-based. Offset is ignored — use last row ID for next page.
|
|
38985
41245
|
});
|
|
41246
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41247
|
+
const rows = data.map((inv) => ({
|
|
41248
|
+
id: String(inv.id ?? ""),
|
|
41249
|
+
invoiceNumber: inv.en_invoice?.number ?? inv.external_id,
|
|
41250
|
+
status: lastEventCode(inv.events),
|
|
41251
|
+
direction: normalizeDirection(inv.direction),
|
|
41252
|
+
senderName: inv.en_invoice?.seller?.name,
|
|
41253
|
+
receiverName: inv.en_invoice?.buyer?.name,
|
|
41254
|
+
date: inv.en_invoice?.issue_date,
|
|
41255
|
+
amount: inv.en_invoice?.totals?.amount_due_for_payment,
|
|
41256
|
+
currency: inv.en_invoice?.currency_code ?? "EUR"
|
|
41257
|
+
}));
|
|
41258
|
+
return { rows, count: raw2?.count ?? rows.length };
|
|
38986
41259
|
}
|
|
38987
41260
|
async getInvoice(id) {
|
|
38988
|
-
|
|
41261
|
+
const inv = await this.client.get(`/invoices/${encodePathSegment(id)}`);
|
|
41262
|
+
const en = inv.en_invoice;
|
|
41263
|
+
return {
|
|
41264
|
+
id: String(inv.id ?? id),
|
|
41265
|
+
invoiceNumber: en?.number ?? inv.external_id,
|
|
41266
|
+
status: lastEventCode(inv.events),
|
|
41267
|
+
direction: normalizeDirection(inv.direction),
|
|
41268
|
+
senderName: en?.seller?.name,
|
|
41269
|
+
receiverName: en?.buyer?.name,
|
|
41270
|
+
issueDate: en?.issue_date,
|
|
41271
|
+
dueDate: en?.due_date,
|
|
41272
|
+
currency: en?.currency_code ?? "EUR",
|
|
41273
|
+
totalHt: en?.totals?.tax_exclusive_amount,
|
|
41274
|
+
totalTtc: en?.totals?.tax_inclusive_amount
|
|
41275
|
+
};
|
|
38989
41276
|
}
|
|
38990
41277
|
async downloadInvoice(id) {
|
|
38991
|
-
return await this.client.download(`/
|
|
38992
|
-
}
|
|
38993
|
-
async downloadReadable(id) {
|
|
38994
|
-
return await this.client.download(`/invoice/${id}/download/readable`);
|
|
38995
|
-
}
|
|
38996
|
-
async getInvoiceFiles(id) {
|
|
38997
|
-
return await this.client.get(`/invoice/${id}/files`);
|
|
38998
|
-
}
|
|
38999
|
-
async getAttachments(id) {
|
|
39000
|
-
return await this.client.get(`/invoice/${id}/files/attachments`);
|
|
39001
|
-
}
|
|
39002
|
-
async downloadFile(fileId) {
|
|
39003
|
-
return await this.client.download(`/invoice/file/${fileId}/download`);
|
|
39004
|
-
}
|
|
39005
|
-
async markInvoiceSeen(id) {
|
|
39006
|
-
return await this.client.put(`/invoice/${id}/markAsSeen`);
|
|
39007
|
-
}
|
|
39008
|
-
async getUnseenInvoices(pagination) {
|
|
39009
|
-
return await this.client.get("/invoice/notSeen", {
|
|
39010
|
-
offset: pagination.offset,
|
|
39011
|
-
limit: pagination.limit
|
|
39012
|
-
});
|
|
41278
|
+
return await this.client.download(`/invoices/${encodePathSegment(id)}/download`);
|
|
39013
41279
|
}
|
|
41280
|
+
// ─── Format Conversion (native) ───────────────────────
|
|
39014
41281
|
async generateCII(req) {
|
|
39015
|
-
|
|
39016
|
-
|
|
39017
|
-
|
|
41282
|
+
const invoice = normalizeForSuperPDP(req.invoice);
|
|
41283
|
+
const payload = new TextEncoder().encode(JSON.stringify(invoice));
|
|
41284
|
+
return await this.client.convert(payload, "en16931", "cii");
|
|
39018
41285
|
}
|
|
39019
41286
|
async generateUBL(req) {
|
|
39020
|
-
|
|
39021
|
-
|
|
39022
|
-
|
|
39023
|
-
}
|
|
39024
|
-
async generateFacturX(req) {
|
|
39025
|
-
return await this.client.postWithQuery("/tools/facturx/generate", req.invoice, {
|
|
39026
|
-
flavor: req.flavor,
|
|
39027
|
-
language: req.language
|
|
39028
|
-
});
|
|
39029
|
-
}
|
|
39030
|
-
// ─── Directory ────────────────────────────────────────
|
|
39031
|
-
async searchDirectoryFr(filters) {
|
|
39032
|
-
return await this.client.get("/directory/french", {
|
|
39033
|
-
q: filters.q,
|
|
39034
|
-
offset: filters.offset,
|
|
39035
|
-
limit: filters.limit
|
|
39036
|
-
});
|
|
39037
|
-
}
|
|
39038
|
-
async searchDirectoryInt(filters) {
|
|
39039
|
-
return await this.client.get("/directory/international", {
|
|
39040
|
-
value: filters.value,
|
|
39041
|
-
offset: filters.offset,
|
|
39042
|
-
limit: filters.limit
|
|
39043
|
-
});
|
|
39044
|
-
}
|
|
39045
|
-
async checkPeppolParticipant(scheme, value) {
|
|
39046
|
-
return await this.client.get(
|
|
39047
|
-
`/directory/international/check/scheme/${scheme}/value/${value}`
|
|
39048
|
-
);
|
|
41287
|
+
const invoice = normalizeForSuperPDP(req.invoice);
|
|
41288
|
+
const payload = new TextEncoder().encode(JSON.stringify(invoice));
|
|
41289
|
+
return await this.client.convert(payload, "en16931", "ubl");
|
|
39049
41290
|
}
|
|
39050
|
-
// ─── Status
|
|
41291
|
+
// ─── Status / Events (native) ─────────────────────────
|
|
39051
41292
|
async sendStatus(req) {
|
|
39052
|
-
|
|
39053
|
-
|
|
39054
|
-
|
|
39055
|
-
|
|
41293
|
+
const details = [];
|
|
41294
|
+
if (req.message) {
|
|
41295
|
+
details.push({ reason: req.message });
|
|
41296
|
+
}
|
|
41297
|
+
if (req.payment) {
|
|
41298
|
+
const amounts = Array.isArray(req.payment.amounts) ? req.payment.amounts : [req.payment];
|
|
41299
|
+
details.push({ amounts });
|
|
41300
|
+
}
|
|
41301
|
+
return await this.client.post("/invoice_events", {
|
|
41302
|
+
invoice_id: toInvoiceId(req.invoiceId),
|
|
41303
|
+
status_code: req.code,
|
|
41304
|
+
...details.length > 0 ? { details } : {}
|
|
39056
41305
|
});
|
|
39057
41306
|
}
|
|
39058
41307
|
async getStatusHistory(invoiceId) {
|
|
39059
|
-
|
|
39060
|
-
|
|
39061
|
-
async getUnseenStatuses(pagination) {
|
|
39062
|
-
return await this.client.get("/invoice/status/notSeen", {
|
|
39063
|
-
offset: pagination.offset,
|
|
39064
|
-
limit: pagination.limit
|
|
41308
|
+
const raw2 = await this.client.get("/invoice_events", {
|
|
41309
|
+
invoice_id: invoiceId
|
|
39065
41310
|
});
|
|
41311
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41312
|
+
return {
|
|
41313
|
+
// deno-lint-ignore no-explicit-any
|
|
41314
|
+
entries: data.map((e) => ({
|
|
41315
|
+
date: e.created_at ?? e.date ?? "",
|
|
41316
|
+
code: e.status_code ?? e.code ?? "",
|
|
41317
|
+
message: e.message
|
|
41318
|
+
}))
|
|
41319
|
+
};
|
|
41320
|
+
}
|
|
41321
|
+
// ─── Reporting (AFNOR — inherited) ────────────────────
|
|
41322
|
+
// reportInvoiceTransaction → AfnorBaseAdapter.submitFlow (FRR)
|
|
41323
|
+
// reportTransaction → AfnorBaseAdapter.submitFlow (FRR)
|
|
41324
|
+
// ─── Directory (native) ───────────────────────────────
|
|
41325
|
+
async searchDirectoryFr(_filters) {
|
|
41326
|
+
const raw2 = await this.client.get("/directory_entries");
|
|
41327
|
+
const data = Array.isArray(raw2) ? raw2 : raw2?.data ?? [];
|
|
41328
|
+
const rows = data.map((entry) => ({
|
|
41329
|
+
entityId: String(entry.id ?? ""),
|
|
41330
|
+
name: entry.company?.formal_name ?? entry.company?.trade_name ?? entry.name,
|
|
41331
|
+
siret: entry.identifier,
|
|
41332
|
+
country: entry.company?.country ?? "FR",
|
|
41333
|
+
directory: entry.directory,
|
|
41334
|
+
status: entry.status,
|
|
41335
|
+
createdAt: entry.created_at
|
|
41336
|
+
}));
|
|
41337
|
+
return { rows, count: rows.length };
|
|
39066
41338
|
}
|
|
39067
|
-
|
|
39068
|
-
|
|
39069
|
-
|
|
39070
|
-
// ─── Reporting ────────────────────────────────────────
|
|
39071
|
-
async reportInvoiceTransaction(transaction) {
|
|
39072
|
-
return await this.client.post("/reporting/fr/invoice/transaction", transaction);
|
|
39073
|
-
}
|
|
39074
|
-
async reportTransaction(businessEntityId, transaction) {
|
|
39075
|
-
return await this.client.post(`/reporting/fr/transaction/${businessEntityId}`, transaction);
|
|
41339
|
+
// ─── Operator Config (native) ─────────────────────────
|
|
41340
|
+
async getCustomerId() {
|
|
41341
|
+
return await this.client.get("/companies/me");
|
|
39076
41342
|
}
|
|
39077
|
-
|
|
39078
|
-
|
|
39079
|
-
return await this.client.get("/config/webhook");
|
|
41343
|
+
async getBusinessEntity(_id) {
|
|
41344
|
+
return await this.client.get("/companies/me");
|
|
39080
41345
|
}
|
|
39081
|
-
async
|
|
39082
|
-
return await this.client.
|
|
41346
|
+
async createOffice(data) {
|
|
41347
|
+
return await this.client.post("/directory_entries", data);
|
|
39083
41348
|
}
|
|
39084
|
-
async
|
|
39085
|
-
return await this.client.post("/
|
|
41349
|
+
async enrollFrench(data) {
|
|
41350
|
+
return await this.client.post("/directory_entries", data);
|
|
39086
41351
|
}
|
|
39087
|
-
async
|
|
39088
|
-
return await this.client.
|
|
41352
|
+
async registerNetwork(identifierId, network) {
|
|
41353
|
+
return await this.client.post("/directory_entries", {
|
|
41354
|
+
directory: mapNetworkToDirectory(network),
|
|
41355
|
+
identifier: identifierId
|
|
41356
|
+
});
|
|
39089
41357
|
}
|
|
39090
|
-
async
|
|
39091
|
-
return await this.client.
|
|
41358
|
+
async registerNetworkByScheme(scheme, value, network) {
|
|
41359
|
+
return await this.client.post("/directory_entries", {
|
|
41360
|
+
directory: mapNetworkToDirectory(network),
|
|
41361
|
+
identifier: `${scheme}:${value}`
|
|
41362
|
+
});
|
|
39092
41363
|
}
|
|
39093
|
-
|
|
39094
|
-
|
|
39095
|
-
const baseUrl = env2("IOPOLE_API_URL");
|
|
39096
|
-
const clientId = env2("IOPOLE_CLIENT_ID");
|
|
39097
|
-
const clientSecret = env2("IOPOLE_CLIENT_SECRET");
|
|
39098
|
-
const customerId = env2("IOPOLE_CUSTOMER_ID");
|
|
39099
|
-
const authUrl = env2("IOPOLE_AUTH_URL") || IOPOLE_DEFAULT_AUTH_URL;
|
|
39100
|
-
if (!baseUrl) {
|
|
39101
|
-
throw new Error(
|
|
39102
|
-
"[IopoleAdapter] IOPOLE_API_URL is required. Set it to https://api.ppd.iopole.fr/v1 (sandbox) or https://api.iopole.com/v1 (production)."
|
|
39103
|
-
);
|
|
41364
|
+
async unregisterNetwork(directoryId) {
|
|
41365
|
+
return await this.client.delete(`/directory_entries/${encodePathSegment(directoryId)}`);
|
|
39104
41366
|
}
|
|
39105
|
-
|
|
39106
|
-
|
|
39107
|
-
|
|
39108
|
-
|
|
41367
|
+
// ─── Identifier Management (native via directory) ─────
|
|
41368
|
+
async createIdentifier(_entityId, data) {
|
|
41369
|
+
return await this.client.post("/directory_entries", {
|
|
41370
|
+
directory: data.directory ?? "ppf",
|
|
41371
|
+
identifier: data.identifier
|
|
41372
|
+
});
|
|
39109
41373
|
}
|
|
39110
|
-
|
|
39111
|
-
|
|
39112
|
-
|
|
39113
|
-
|
|
41374
|
+
async createIdentifierByScheme(scheme, value, data) {
|
|
41375
|
+
return await this.client.post("/directory_entries", {
|
|
41376
|
+
directory: data.directory ?? "ppf",
|
|
41377
|
+
identifier: `${scheme}:${value}`
|
|
41378
|
+
});
|
|
39114
41379
|
}
|
|
39115
|
-
|
|
39116
|
-
|
|
39117
|
-
|
|
39118
|
-
|
|
41380
|
+
async deleteIdentifier(identifierId) {
|
|
41381
|
+
return await this.client.delete(`/directory_entries/${encodePathSegment(identifierId)}`);
|
|
41382
|
+
}
|
|
41383
|
+
// ─── Stubs (21 methods — inherited from AfnorBaseAdapter) ─
|
|
41384
|
+
// downloadReadable, getInvoiceFiles, getAttachments, downloadFile,
|
|
41385
|
+
// markInvoiceSeen, getUnseenInvoices, getUnseenStatuses, markStatusSeen,
|
|
41386
|
+
// listWebhooks, getWebhook, createWebhook, updateWebhook, deleteWebhook,
|
|
41387
|
+
// listBusinessEntities, createLegalUnit, deleteBusinessEntity,
|
|
41388
|
+
// configureBusinessEntity, claimBusinessEntity, claimBusinessEntityByIdentifier,
|
|
41389
|
+
// enrollInternational, searchDirectoryInt, checkPeppolParticipant, deleteClaim
|
|
41390
|
+
};
|
|
41391
|
+
function lastEventCode(events) {
|
|
41392
|
+
if (!events?.length) return void 0;
|
|
41393
|
+
return events[events.length - 1].status_code;
|
|
41394
|
+
}
|
|
41395
|
+
function mapNetworkToDirectory(network) {
|
|
41396
|
+
if (network === "DOMESTIC_FR" || network === "ppf") return "ppf";
|
|
41397
|
+
if (network === "PEPPOL_INTERNATIONAL" || network === "peppol") return "peppol";
|
|
41398
|
+
throw new Error(
|
|
41399
|
+
`[SuperPDP] Unknown network "${network}". Supported: "DOMESTIC_FR"/"ppf", "PEPPOL_INTERNATIONAL"/"peppol".`
|
|
41400
|
+
);
|
|
41401
|
+
}
|
|
41402
|
+
function toInvoiceId(id) {
|
|
41403
|
+
const n = Number(id);
|
|
41404
|
+
if (!Number.isFinite(n)) {
|
|
41405
|
+
throw new Error(`[SuperPDP] invoice_id must be numeric, got "${id}".`);
|
|
39119
41406
|
}
|
|
41407
|
+
return n;
|
|
41408
|
+
}
|
|
41409
|
+
function createSuperPDPAdapter() {
|
|
41410
|
+
const baseUrl = requireEnv(
|
|
41411
|
+
"SuperPDPAdapter",
|
|
41412
|
+
"SUPERPDP_API_URL",
|
|
41413
|
+
"Set it to https://api.superpdp.tech/v1.beta"
|
|
41414
|
+
);
|
|
41415
|
+
const clientId = requireEnv(
|
|
41416
|
+
"SuperPDPAdapter",
|
|
41417
|
+
"SUPERPDP_CLIENT_ID",
|
|
41418
|
+
"Get your client ID from the Super PDP dashboard."
|
|
41419
|
+
);
|
|
41420
|
+
const clientSecret = requireEnv(
|
|
41421
|
+
"SuperPDPAdapter",
|
|
41422
|
+
"SUPERPDP_CLIENT_SECRET",
|
|
41423
|
+
"Get your client secret from the Super PDP dashboard."
|
|
41424
|
+
);
|
|
41425
|
+
const authUrl = env2("SUPERPDP_AUTH_URL") || "https://api.superpdp.tech/oauth2/token";
|
|
41426
|
+
const afnorUrl = env2("SUPERPDP_AFNOR_URL") || "https://api.superpdp.tech/afnor-flow";
|
|
39120
41427
|
const getToken = createOAuth2TokenProvider({ authUrl, clientId, clientSecret });
|
|
39121
|
-
const client = new
|
|
39122
|
-
|
|
41428
|
+
const client = new SuperPDPClient({ baseUrl, getToken });
|
|
41429
|
+
const afnor = new AfnorClient({ baseUrl: afnorUrl, getToken });
|
|
41430
|
+
return new SuperPDPAdapter(client, afnor);
|
|
39123
41431
|
}
|
|
39124
41432
|
|
|
39125
41433
|
// server.ts
|
|
@@ -39129,14 +41437,22 @@ function createAdapter(adapterName) {
|
|
|
39129
41437
|
switch (adapterName) {
|
|
39130
41438
|
case "iopole":
|
|
39131
41439
|
return createIopoleAdapter();
|
|
41440
|
+
case "storecove":
|
|
41441
|
+
return createStorecoveAdapter();
|
|
41442
|
+
case "superpdp":
|
|
41443
|
+
return createSuperPDPAdapter();
|
|
39132
41444
|
default:
|
|
39133
41445
|
throw new Error(
|
|
39134
|
-
`${LOG_PREFIX} Unknown adapter: "${adapterName}". Available adapters: iopole`
|
|
41446
|
+
`${LOG_PREFIX} Unknown adapter: "${adapterName}". Available adapters: iopole, storecove, superpdp`
|
|
39135
41447
|
);
|
|
39136
41448
|
}
|
|
39137
41449
|
}
|
|
39138
41450
|
async function main() {
|
|
39139
41451
|
const args = getArgs();
|
|
41452
|
+
if (args.includes("--inspect")) {
|
|
41453
|
+
await launchInspector("deno", ["run", "--allow-all", import.meta.filename]);
|
|
41454
|
+
return;
|
|
41455
|
+
}
|
|
39140
41456
|
const adapterArg = args.find((arg) => arg.startsWith("--adapter="));
|
|
39141
41457
|
const adapterName = adapterArg ? adapterArg.split("=")[1] : env2("EINVOICE_ADAPTER") || "iopole";
|
|
39142
41458
|
const categoriesArg = args.find((arg) => arg.startsWith("--categories="));
|
|
@@ -39145,26 +41461,27 @@ async function main() {
|
|
|
39145
41461
|
const portArg = args.find((arg) => arg.startsWith("--port="));
|
|
39146
41462
|
const httpPort = portArg ? parseInt(portArg.split("=")[1], 10) : DEFAULT_HTTP_PORT;
|
|
39147
41463
|
const hostnameArg = args.find((arg) => arg.startsWith("--hostname="));
|
|
39148
|
-
const hostname3 = hostnameArg ? hostnameArg.split("=")[1] : "
|
|
41464
|
+
const hostname3 = hostnameArg ? hostnameArg.split("=")[1] : "localhost";
|
|
39149
41465
|
const adapter = createAdapter(adapterName);
|
|
39150
41466
|
const toolsClient = new EInvoiceToolsClient(
|
|
39151
41467
|
categories ? { categories } : void 0
|
|
39152
41468
|
);
|
|
39153
41469
|
const server = new ConcurrentMCPServer({
|
|
39154
41470
|
name: "mcp-einvoice",
|
|
39155
|
-
version: "0.1.
|
|
41471
|
+
version: "0.1.1",
|
|
39156
41472
|
maxConcurrent: 10,
|
|
39157
41473
|
backpressureStrategy: "queue",
|
|
39158
41474
|
validateSchema: true,
|
|
41475
|
+
toolErrorMapper: einvoiceErrorMapper,
|
|
39159
41476
|
logger: (msg) => console.error(`${LOG_PREFIX} ${msg}`)
|
|
39160
41477
|
});
|
|
39161
|
-
const mcpTools = toolsClient.toMCPFormat();
|
|
41478
|
+
const mcpTools = toolsClient.toMCPFormat(adapter);
|
|
39162
41479
|
const handlers = toolsClient.buildHandlersMap(adapter);
|
|
39163
41480
|
server.registerTools(mcpTools, handlers);
|
|
39164
41481
|
server.registerViewers({
|
|
39165
41482
|
prefix: "mcp-einvoice",
|
|
39166
41483
|
moduleUrl: import.meta.url,
|
|
39167
|
-
viewers: ["invoice-viewer", "doclist-viewer", "status-timeline", "directory-card"],
|
|
41484
|
+
viewers: ["invoice-viewer", "doclist-viewer", "status-timeline", "directory-card", "directory-list", "action-result"],
|
|
39168
41485
|
exists: statSync,
|
|
39169
41486
|
readFile: readTextFile2
|
|
39170
41487
|
});
|