@deuriib/dgii-ecf 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +68 -59
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +68 -59
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -399,6 +399,14 @@ declare class ECF {
|
|
|
399
399
|
private readonly inFlight;
|
|
400
400
|
constructor(credentials: P12ReaderData, environment?: Environment, options?: EcfOptions);
|
|
401
401
|
private buildIdempotencyKey;
|
|
402
|
+
/**
|
|
403
|
+
* Run a submission through the persist-before-transmit idempotency flow.
|
|
404
|
+
*
|
|
405
|
+
* The pending marker is written first so an interrupted send can be
|
|
406
|
+
* reconciled instead of re-issued; a successful response replaces it, and
|
|
407
|
+
* a failure clears it so the caller can retry.
|
|
408
|
+
*/
|
|
409
|
+
private submitIdempotent;
|
|
402
410
|
private singleFlight;
|
|
403
411
|
/** Authenticate against DGII (or a buyer host) and store the bearer token. */
|
|
404
412
|
authenticate(buyerHost?: string): Promise<Result<AuthToken, DgiiError>>;
|
|
@@ -418,7 +426,15 @@ declare class ECF {
|
|
|
418
426
|
getCustomerDirectory(rnc: string): Promise<Result<ServiceDirectoryResponse[] | undefined, DgiiError>>;
|
|
419
427
|
/** RFCE inquiry — DGII production only. */
|
|
420
428
|
getSummaryInvoiceInquiry(rnc: string, encf: string, codSeguridad: string): Promise<Result<InquiryInvoiceSummary, DgiiError>>;
|
|
429
|
+
/**
|
|
430
|
+
* Submit a signed commercial approval (ACECF). Idempotent like submissions,
|
|
431
|
+
* keyed on `RNC + eNCF + SHA-256(signed XML)`.
|
|
432
|
+
*/
|
|
421
433
|
sendCommercialApproval(signedXml: string, fileName: string): Promise<Result<CommercialApprovalResponse, DgiiError>>;
|
|
434
|
+
/**
|
|
435
|
+
* Submit a signed range-voiding request (ANECF). Idempotent like
|
|
436
|
+
* submissions, keyed on `RNC + RangoNCF + SHA-256(signed XML)`.
|
|
437
|
+
*/
|
|
422
438
|
voidENCF(signedXml: string, fileName: string): Promise<Result<VoidNCFResponse, DgiiError>>;
|
|
423
439
|
}
|
|
424
440
|
|
package/dist/index.d.ts
CHANGED
|
@@ -399,6 +399,14 @@ declare class ECF {
|
|
|
399
399
|
private readonly inFlight;
|
|
400
400
|
constructor(credentials: P12ReaderData, environment?: Environment, options?: EcfOptions);
|
|
401
401
|
private buildIdempotencyKey;
|
|
402
|
+
/**
|
|
403
|
+
* Run a submission through the persist-before-transmit idempotency flow.
|
|
404
|
+
*
|
|
405
|
+
* The pending marker is written first so an interrupted send can be
|
|
406
|
+
* reconciled instead of re-issued; a successful response replaces it, and
|
|
407
|
+
* a failure clears it so the caller can retry.
|
|
408
|
+
*/
|
|
409
|
+
private submitIdempotent;
|
|
402
410
|
private singleFlight;
|
|
403
411
|
/** Authenticate against DGII (or a buyer host) and store the bearer token. */
|
|
404
412
|
authenticate(buyerHost?: string): Promise<Result<AuthToken, DgiiError>>;
|
|
@@ -418,7 +426,15 @@ declare class ECF {
|
|
|
418
426
|
getCustomerDirectory(rnc: string): Promise<Result<ServiceDirectoryResponse[] | undefined, DgiiError>>;
|
|
419
427
|
/** RFCE inquiry — DGII production only. */
|
|
420
428
|
getSummaryInvoiceInquiry(rnc: string, encf: string, codSeguridad: string): Promise<Result<InquiryInvoiceSummary, DgiiError>>;
|
|
429
|
+
/**
|
|
430
|
+
* Submit a signed commercial approval (ACECF). Idempotent like submissions,
|
|
431
|
+
* keyed on `RNC + eNCF + SHA-256(signed XML)`.
|
|
432
|
+
*/
|
|
421
433
|
sendCommercialApproval(signedXml: string, fileName: string): Promise<Result<CommercialApprovalResponse, DgiiError>>;
|
|
434
|
+
/**
|
|
435
|
+
* Submit a signed range-voiding request (ANECF). Idempotent like
|
|
436
|
+
* submissions, keyed on `RNC + RangoNCF + SHA-256(signed XML)`.
|
|
437
|
+
*/
|
|
422
438
|
voidENCF(signedXml: string, fileName: string): Promise<Result<VoidNCFResponse, DgiiError>>;
|
|
423
439
|
}
|
|
424
440
|
|
package/dist/index.js
CHANGED
|
@@ -1034,8 +1034,39 @@ var ECF = class {
|
|
|
1034
1034
|
}
|
|
1035
1035
|
buildIdempotencyKey(signedXml) {
|
|
1036
1036
|
const rnc = extractElementText(signedXml, "RNCEmisor") ?? "unknown-rnc";
|
|
1037
|
-
const
|
|
1038
|
-
return `${rnc}:${
|
|
1037
|
+
const ncf = extractElementText(signedXml, "eNCF") ?? extractElementText(signedXml, "RangoNCF") ?? "unknown-ncf";
|
|
1038
|
+
return `${rnc}:${ncf}:${sha256Hex(signedXml)}`;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Run a submission through the persist-before-transmit idempotency flow.
|
|
1042
|
+
*
|
|
1043
|
+
* The pending marker is written first so an interrupted send can be
|
|
1044
|
+
* reconciled instead of re-issued; a successful response replaces it, and
|
|
1045
|
+
* a failure clears it so the caller can retry.
|
|
1046
|
+
*/
|
|
1047
|
+
async submitIdempotent(key, send) {
|
|
1048
|
+
const cached = await this.idempotency.get(key);
|
|
1049
|
+
if (cached !== void 0) {
|
|
1050
|
+
if (isPendingSubmission(cached)) {
|
|
1051
|
+
return err(
|
|
1052
|
+
new DgiiError(
|
|
1053
|
+
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1054
|
+
{ code: "IDEMPOTENCY" }
|
|
1055
|
+
)
|
|
1056
|
+
);
|
|
1057
|
+
}
|
|
1058
|
+
return ok(cached);
|
|
1059
|
+
}
|
|
1060
|
+
return this.singleFlight(key, async () => {
|
|
1061
|
+
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1062
|
+
const result = await send();
|
|
1063
|
+
if (result.isOk()) {
|
|
1064
|
+
await this.idempotency.set(key, result.value);
|
|
1065
|
+
} else {
|
|
1066
|
+
await this.idempotency.delete(key);
|
|
1067
|
+
}
|
|
1068
|
+
return result;
|
|
1069
|
+
});
|
|
1039
1070
|
}
|
|
1040
1071
|
async singleFlight(key, task) {
|
|
1041
1072
|
const existing = this.inFlight.get(key);
|
|
@@ -1108,66 +1139,28 @@ var ECF = class {
|
|
|
1108
1139
|
*/
|
|
1109
1140
|
async sendElectronicDocument(signedXml, fileName, buyerHost) {
|
|
1110
1141
|
const key = this.buildIdempotencyKey(signedXml);
|
|
1111
|
-
|
|
1112
|
-
if (cached !== void 0) {
|
|
1113
|
-
if (isPendingSubmission(cached)) {
|
|
1114
|
-
return err(
|
|
1115
|
-
new DgiiError(
|
|
1116
|
-
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1117
|
-
{ code: "IDEMPOTENCY" }
|
|
1118
|
-
)
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1121
|
-
return ok(cached);
|
|
1122
|
-
}
|
|
1123
|
-
return this.singleFlight(key, async () => {
|
|
1124
|
-
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1142
|
+
return this.submitIdempotent(key, () => {
|
|
1125
1143
|
const url = buyerHost ? buildSenderReceiverUrl(buyerHost, "fe/recepcion/api/ecf" /* SendInvoice */) : buildUrl(this.environment, "recepcion/api/FacturasElectronicas" /* SendInvoice */);
|
|
1126
|
-
|
|
1144
|
+
return this.http.postMultipart(
|
|
1127
1145
|
url,
|
|
1128
1146
|
fileName,
|
|
1129
1147
|
signedXml,
|
|
1130
1148
|
{ absoluteUrl: true, resource: "recepcion/api/FacturasElectronicas" /* SendInvoice */ }
|
|
1131
1149
|
);
|
|
1132
|
-
if (result.isOk()) {
|
|
1133
|
-
await this.idempotency.set(key, result.value);
|
|
1134
|
-
} else {
|
|
1135
|
-
await this.idempotency.delete(key);
|
|
1136
|
-
}
|
|
1137
|
-
return result;
|
|
1138
1150
|
});
|
|
1139
1151
|
}
|
|
1140
1152
|
/** Submit a signed summary invoice (RFCE). Idempotent like submissions. */
|
|
1141
1153
|
async sendSummary(signedXml, fileName) {
|
|
1142
1154
|
const key = this.buildIdempotencyKey(signedXml);
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
new DgiiError(
|
|
1148
|
-
"A submission with this key is already pending; reconcile via a status lookup before retrying",
|
|
1149
|
-
{ code: "IDEMPOTENCY" }
|
|
1150
|
-
)
|
|
1151
|
-
);
|
|
1152
|
-
}
|
|
1153
|
-
return ok(cached);
|
|
1154
|
-
}
|
|
1155
|
-
return this.singleFlight(key, async () => {
|
|
1156
|
-
await this.idempotency.set(key, PENDING_SUBMISSION);
|
|
1157
|
-
const url = buildUrl(this.environment, "recepcionfc/api/recepcion/ecf" /* SendSummary */, BaseUrl.CF);
|
|
1158
|
-
const result = await this.http.postMultipart(
|
|
1159
|
-
url,
|
|
1155
|
+
return this.submitIdempotent(
|
|
1156
|
+
key,
|
|
1157
|
+
() => this.http.postMultipart(
|
|
1158
|
+
buildUrl(this.environment, "recepcionfc/api/recepcion/ecf" /* SendSummary */, BaseUrl.CF),
|
|
1160
1159
|
fileName,
|
|
1161
1160
|
signedXml,
|
|
1162
1161
|
{ absoluteUrl: true, resource: "recepcionfc/api/recepcion/ecf" /* SendSummary */ }
|
|
1163
|
-
)
|
|
1164
|
-
|
|
1165
|
-
await this.idempotency.set(key, result.value);
|
|
1166
|
-
} else {
|
|
1167
|
-
await this.idempotency.delete(key);
|
|
1168
|
-
}
|
|
1169
|
-
return result;
|
|
1170
|
-
});
|
|
1162
|
+
)
|
|
1163
|
+
);
|
|
1171
1164
|
}
|
|
1172
1165
|
statusTrackId(trackId) {
|
|
1173
1166
|
const url = buildUrl(this.environment, "consultaresultado/api/Consultas/Estado" /* TrackResultStatus */);
|
|
@@ -1227,21 +1220,37 @@ var ECF = class {
|
|
|
1227
1220
|
{ absoluteUrl: true, resource: "consultarfce/api/Consultas/Consulta" /* InquiryInvoiceSummary */ }
|
|
1228
1221
|
);
|
|
1229
1222
|
}
|
|
1223
|
+
/**
|
|
1224
|
+
* Submit a signed commercial approval (ACECF). Idempotent like submissions,
|
|
1225
|
+
* keyed on `RNC + eNCF + SHA-256(signed XML)`.
|
|
1226
|
+
*/
|
|
1230
1227
|
sendCommercialApproval(signedXml, fileName) {
|
|
1231
|
-
const
|
|
1232
|
-
return this.
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1228
|
+
const key = this.buildIdempotencyKey(signedXml);
|
|
1229
|
+
return this.submitIdempotent(
|
|
1230
|
+
key,
|
|
1231
|
+
() => this.http.postMultipart(
|
|
1232
|
+
buildUrl(this.environment, "aprobacionComercial/api/AprobacionComercial" /* CommercialApproval */),
|
|
1233
|
+
fileName,
|
|
1234
|
+
signedXml,
|
|
1235
|
+
{ absoluteUrl: true, resource: "aprobacionComercial/api/AprobacionComercial" /* CommercialApproval */ }
|
|
1236
|
+
)
|
|
1237
1237
|
);
|
|
1238
1238
|
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Submit a signed range-voiding request (ANECF). Idempotent like
|
|
1241
|
+
* submissions, keyed on `RNC + RangoNCF + SHA-256(signed XML)`.
|
|
1242
|
+
*/
|
|
1239
1243
|
voidENCF(signedXml, fileName) {
|
|
1240
|
-
const
|
|
1241
|
-
return this.
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1244
|
+
const key = this.buildIdempotencyKey(signedXml);
|
|
1245
|
+
return this.submitIdempotent(
|
|
1246
|
+
key,
|
|
1247
|
+
() => this.http.postMultipart(
|
|
1248
|
+
buildUrl(this.environment, "anulacionrangos/api/operaciones/anularrango" /* Void */),
|
|
1249
|
+
fileName,
|
|
1250
|
+
signedXml,
|
|
1251
|
+
{ absoluteUrl: true, resource: "anulacionrangos/api/operaciones/anularrango" /* Void */ }
|
|
1252
|
+
)
|
|
1253
|
+
);
|
|
1245
1254
|
}
|
|
1246
1255
|
};
|
|
1247
1256
|
export {
|