Skip to content

Commit 91ca9a0

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

6 files changed

Lines changed: 92 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.ToInvoiceAsync(Dictionary<string, object> data)` to call `POST /receipts/to-invoice`.
11+
- Added `facturapi.Receipt.PreviewToInvoicePdfAsync(Dictionary<string, object> data)` to call `POST /receipts/to-invoice/preview`.
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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,57 @@ public async Task ReceiptCancelAsync_UsesReceiptDeleteRoute()
5454
Assert.Equal("rcp_123", result.Id);
5555
}
5656

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

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 ToInvoice()
42+
{
43+
return "receipts/to-invoice";
44+
}
45+
46+
public static string PreviewToInvoicePdf()
47+
{
48+
return "receipts/to-invoice/preview";
49+
}
50+
4151
public static string DownloadReceiptPdf(string id)
4252
{
4353
return $"receipts/{id}/pdf";

Wrappers/IReceiptWrapper.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public interface IReceiptWrapper
1313
Task<Receipt> CancelAsync(string id, CancellationToken cancellationToken = default);
1414
Task InvoiceAsync(string id, Dictionary<string, object> data, CancellationToken cancellationToken = default);
1515
Task CreateGlobalInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
16+
Task ToInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
17+
Task<Stream> PreviewToInvoicePdfAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default);
1618
Task SendByEmailAsync(string id, Dictionary<string, object> data = null, CancellationToken cancellationToken = default);
1719
Task<Stream> DownloadPdfAsync(string id, CancellationToken cancellationToken = default);
1820
}

Wrappers/ReceiptWrapper.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,29 @@ public async Task CreateGlobalInvoiceAsync(Dictionary<string, object> data, Canc
7878
}
7979
}
8080

81+
public async Task ToInvoiceAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
82+
{
83+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
84+
using (var response = await client.PostAsync(Router.ToInvoice(), content, cancellationToken).ConfigureAwait(false))
85+
{
86+
await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false);
87+
}
88+
}
89+
90+
public async Task<Stream> PreviewToInvoicePdfAsync(Dictionary<string, object> data, CancellationToken cancellationToken = default)
91+
{
92+
using (var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json"))
93+
using (var response = await client.PostAsync(Router.PreviewToInvoicePdf(), content, cancellationToken).ConfigureAwait(false))
94+
{
95+
await this.ThrowIfErrorAsync(response, cancellationToken).ConfigureAwait(false);
96+
var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
97+
var memory = new MemoryStream();
98+
await responseStream.CopyToAsync(memory, 81920, cancellationToken).ConfigureAwait(false);
99+
memory.Position = 0;
100+
return memory;
101+
}
102+
}
103+
81104
public async Task SendByEmailAsync(string id, Dictionary<string, object> data = null, CancellationToken cancellationToken = default)
82105
{
83106
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)