Skip to content

Commit 045e12e

Browse files
chore(multiInvoices): add new methods for multiinvoice receipts
1 parent 815a520 commit 045e12e

6 files changed

Lines changed: 101 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [6.4.0]
9+
### Added
10+
- Added `facturapi.Receipt.MultiInvoiceAsync(Dictionary<string, object> data)` to call `POST /receipts/multi-invoice`.
11+
- Added `facturapi.Receipt.PreviewMultiInvoicePdfAsync(Dictionary<string, object> data)` to call `POST /receipts/multi-invoice/preview/pdf`.
12+
813
## [6.3.0] - 2026-04-23
914
### Added
1015
- Added Carta Porte constants and description catalogs: customs regimes, transport keys, station types, SCT permits, COFEPRIS sectors, pharmaceutical forms, special transport conditions, material types, customs document types, transport unit/figure types, Istmo records, loading keys, maritime configuration, rail traffic, container types, maritime container types, rail car/service types, transfer motives, incoterms, and customs units.

FacturapiTest/WrapperBehaviorTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
using Facturapi;
22
using Facturapi.Wrappers;
3+
using Newtonsoft.Json.Linq;
34
using System;
45
using System.Collections.Generic;
56
using System.IO;
@@ -54,6 +55,60 @@ public async Task ReceiptCancelAsync_UsesReceiptDeleteRoute()
5455
Assert.Equal("rcp_123", result.Id);
5556
}
5657

58+
[Fact]
59+
public async Task ReceiptMultiInvoiceAsync_UsesMultiInvoiceRoute()
60+
{
61+
var handler = new RecordingHandler(async (request, cancellationToken) =>
62+
{
63+
Assert.Equal(HttpMethod.Post, request.Method);
64+
Assert.NotNull(request.RequestUri);
65+
Assert.Equal("/v2/receipts/multi-invoice", request.RequestUri.PathAndQuery);
66+
Assert.NotNull(request.Content);
67+
var body = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
68+
Assert.Contains("\"keys\":[\"rcp_1\",\"rcp_2\"]", body);
69+
return JsonResponse("{\"id\":\"inv_123\"}");
70+
});
71+
72+
var wrapper = new ReceiptWrapper("test_key", "v2", CreateHttpClient(handler));
73+
var result = await wrapper.MultiInvoiceAsync(new Dictionary<string, object>
74+
{
75+
["keys"] = new[] { "rcp_1", "rcp_2" }
76+
});
77+
78+
Assert.Equal(JTokenType.Object, result.Type);
79+
Assert.Equal("inv_123", result["id"]?.Value<string>());
80+
}
81+
82+
[Fact]
83+
public async Task ReceiptPreviewMultiInvoicePdfAsync_UsesPreviewPdfRoute()
84+
{
85+
var payload = Encoding.UTF8.GetBytes("pdf-bytes");
86+
var handler = new RecordingHandler(async (request, cancellationToken) =>
87+
{
88+
Assert.Equal(HttpMethod.Post, request.Method);
89+
Assert.NotNull(request.RequestUri);
90+
Assert.Equal("/v2/receipts/multi-invoice/preview/pdf", request.RequestUri.PathAndQuery);
91+
Assert.NotNull(request.Content);
92+
var body = await request.Content.ReadAsStringAsync().ConfigureAwait(false);
93+
Assert.Contains("\"keys\":[\"rcp_1\"]", body);
94+
return new HttpResponseMessage(HttpStatusCode.OK)
95+
{
96+
Content = new ByteArrayContent(payload)
97+
};
98+
});
99+
100+
var wrapper = new ReceiptWrapper("test_key", "v2", CreateHttpClient(handler));
101+
using var stream = await wrapper.PreviewMultiInvoicePdfAsync(new Dictionary<string, object>
102+
{
103+
["keys"] = new[] { "rcp_1" }
104+
});
105+
106+
Assert.Equal(0, stream.Position);
107+
using var reader = new StreamReader(stream, Encoding.UTF8, false, 1024, leaveOpen: true);
108+
var text = await reader.ReadToEndAsync();
109+
Assert.Equal("pdf-bytes", text);
110+
}
111+
57112
[Fact]
58113
public async Task OrganizationDeleteSeriesAsync_UsesDeleteSeriesRoute()
59114
{

Router/ReceiptRouter.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,16 @@ public static string CreateGlobalInvoice()
3838
return $"receipts/global-invoice";
3939
}
4040

41+
public static string MultiInvoice()
42+
{
43+
return "receipts/multi-invoice";
44+
}
45+
46+
public static string PreviewMultiInvoicePdf()
47+
{
48+
return "receipts/multi-invoice/preview/pdf";
49+
}
50+
4151
public static string DownloadReceiptPdf(string id)
4252
{
4353
return $"receipts/{id}/pdf";

Wrappers/IReceiptWrapper.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.IO;
33
using System.Threading;
44
using System.Threading.Tasks;
5+
using Newtonsoft.Json.Linq;
56

67
namespace Facturapi.Wrappers
78
{
@@ -13,6 +14,8 @@ public interface IReceiptWrapper
1314
Task<Receipt> CancelAsync(string id, CancellationToken cancellationToken = default);
1415
Task InvoiceAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default);
1516
Task CreateGlobalInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
17+
Task<JToken> MultiInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
18+
Task<Stream> PreviewMultiInvoicePdfAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
1619
Task SendByEmailAsync(string id, Dictionary<string, object> data = null, CancellationToken cancellationToken = default);
1720
Task<Stream> DownloadPdfAsync(string id, CancellationToken cancellationToken = default);
1821
}

Wrappers/ReceiptWrapper.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Newtonsoft.Json;
2+
using Newtonsoft.Json.Linq;
23
using System.Collections.Generic;
34
using System.IO;
45
using System.Net.Http;
@@ -78,6 +79,32 @@ public async Task CreateGlobalInvoiceAsync(Dictionary<string, object> data, Canc
7879
}
7980
}
8081

82+
public async Task<JToken> MultiInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
83+
{
84+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
85+
using (var response = await client.PostAsync(Router.MultiInvoice(), content, cancellationToken).ConfigureAwait(false))
86+
{
87+
await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false);
88+
var resultString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
89+
var result = JsonConvert.DeserializeObject<JToken>(resultString, this.jsonSettings);
90+
return result;
91+
}
92+
}
93+
94+
public async Task<Stream> PreviewMultiInvoicePdfAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
95+
{
96+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
97+
using (var response = await client.PostAsync(Router.PreviewMultiInvoicePdf(), content, cancellationToken).ConfigureAwait(false))
98+
{
99+
await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false);
100+
var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
101+
var memory = new MemoryStream();
102+
await responseStream.CopyToAsync(memory, 81920, cancellationToken).ConfigureAwait(false);
103+
memory.Position = 0;
104+
return memory;
105+
}
106+
}
107+
81108
public async Task SendByEmailAsync(string id, Dictionary<string, object> data = null, CancellationToken cancellationToken = default)
82109
{
83110
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))

facturapi-net.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<Summary>SDK oficial de Facturapi para .NET para facturación electrónica en México (CFDI), envío de documentos, búsqueda y trazabilidad.</Summary>
1212
<PackageTags>factura factura-electronica facturacion cfdi cfdi40 sat invoice invoicing facturapi mexico</PackageTags>
1313
<Title>Facturapi</Title>
14-
<Version>6.3.0</Version>
14+
<Version>6.4.0</Version>
1515
<PackageVersion>$(Version)</PackageVersion>
1616
<PackageLicenseExpression>MIT</PackageLicenseExpression>
1717
<PackageRequireLicenseAcceptance>false</PackageRequireLicenseAcceptance>

0 commit comments

Comments
 (0)