From efd94365d1ad83a6fba88f4dddad979dee5ff910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Garc=C3=ADa?= <141654545+raul-facturapi@users.noreply.github.com> Date: Mon, 24 Aug 2026 09:07:07 -0600 Subject: [PATCH 1/7] =?UTF-8?q?feat(retentions):=20agregar=20m=C3=A9todos?= =?UTF-8?q?=20para=20borradores=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(retentions): add new retentions method for support drafts * chore: keep retention draft methods unreleased --------- Co-authored-by: javorosas --- CHANGELOG.md | 6 ++ README.es.md | 4 +- README.md | 4 +- .../resources/RetentionsResource.java | 47 +++++++++- .../io/facturapi/FacturapiResourcesTest.java | 94 +++++++++++++++++++ 5 files changed, 150 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba72fd2..2c392a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Added + +- Add draft support methods for retentions: `retentions.updateDraft(String id, Map data)`, `retentions.copyToDraft(String id)`, `retentions.stampDraft(String id)`, and `retentions.cancel(String id)`. + ## [1.3.0] - 2026-06-07 ### Added diff --git a/README.es.md b/README.es.md index 0e1dc20..2920846 100644 --- a/README.es.md +++ b/README.es.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.1.0 + 1.3.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.1.0") +implementation("io.facturapi:facturapi-java:1.3.0") ``` ## Inicio rápido diff --git a/README.md b/README.md index fe0f67b..6915bd6 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.1.0 + 1.3.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.1.0") +implementation("io.facturapi:facturapi-java:1.3.0") ``` ## Quickstart diff --git a/src/main/java/io/facturapi/resources/RetentionsResource.java b/src/main/java/io/facturapi/resources/RetentionsResource.java index 4fd50fc..5eafbd7 100644 --- a/src/main/java/io/facturapi/resources/RetentionsResource.java +++ b/src/main/java/io/facturapi/resources/RetentionsResource.java @@ -19,7 +19,7 @@ public RetentionsResource(FacturapiHttpClient client) { } /** - * Creates a new valid retention (CFDI). + * Creates a new retention (CFDI). * * @param data Retention payload. * @return Created retention. @@ -63,6 +63,51 @@ public Retention cancel(String id, Map params) { return delete("/retentions/" + id, params, Retention.class); } + /** + * Cancels a retention, or deletes it directly when it is a draft. + * + * @param id Retention id. + * @return Canceled or deleted retention. + * @see API reference + */ + public Retention cancel(String id) { + return delete("/retentions/" + id, null, Retention.class); + } + + /** + * Updates a draft retention. + * + * @param id Retention id. + * @param data Retention updates. + * @return Updated draft retention. + * @see API reference + */ + public Retention updateDraft(String id, Map data) { + return put("/retentions/" + id, data, null, Retention.class); + } + + /** + * Creates a draft copy from an existing retention. + * + * @param id Retention id. + * @return Draft retention. + * @see API reference + */ + public Retention copyToDraft(String id) { + return post("/retentions/" + id + "/copy", null, null, Retention.class); + } + + /** + * Stamps an existing draft retention. + * + * @param id Retention id. + * @return Stamped retention. + * @see API reference + */ + public Retention stampDraft(String id) { + return post("/retentions/" + id + "/stamp", null, null, Retention.class); + } + /** * Sends the retention to the customer's email. * diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java index b523184..30fdeab 100644 --- a/src/test/java/io/facturapi/FacturapiResourcesTest.java +++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java @@ -112,6 +112,100 @@ void invoicePdfCanBeStreamed() throws Exception { assertEquals("/v2/invoices/inv_1/pdf", request.uri().getPath()); } + @Test + void retentionDraftCreateSupportsDraftStatus() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"ret_draft_1\",\"status\":\"draft\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + Map payload = new java.util.HashMap<>(); + payload.put("status", "draft"); + payload.put("customer", null); + + var response = sdk.retentions().create(payload); + + assertEquals("ret_draft_1", response.getId()); + assertEquals("draft", response.getStatus()); + var request = httpClient.requests().get(0); + assertEquals("POST", request.method()); + assertEquals("/v2/retentions", request.uri().getPath()); + assertTrue(request.bodyUtf8().contains("\"status\":\"draft\"")); + assertTrue(request.bodyUtf8().contains("\"customer\":null")); + } + + @Test + void retentionDraftUpdateUsesExpectedPath() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"ret_1\",\"folio_int\":\"R-2026-001\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var response = sdk.retentions().updateDraft("ret_1", Map.of("folio_int", "R-2026-001")); + + assertEquals("ret_1", response.getId()); + assertEquals("R-2026-001", response.getFolioInt()); + var request = httpClient.requests().get(0); + assertEquals("PUT", request.method()); + assertEquals("/v2/retentions/ret_1", request.uri().getPath()); + assertTrue(request.bodyUtf8().contains("\"folio_int\":\"R-2026-001\"")); + } + + @Test + void retentionDraftCopyAndStampUseExpectedPaths() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"ret_copy_1\",\"status\":\"draft\"}"); + httpClient.enqueueJson(200, "{\"id\":\"ret_1\",\"status\":\"valid\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var draft = sdk.retentions().copyToDraft("ret_1"); + var stamped = sdk.retentions().stampDraft("ret_copy_1"); + + assertEquals("ret_copy_1", draft.getId()); + assertEquals("ret_1", stamped.getId()); + assertEquals("POST", httpClient.requests().get(0).method()); + assertEquals("/v2/retentions/ret_1/copy", httpClient.requests().get(0).uri().getPath()); + assertEquals("POST", httpClient.requests().get(1).method()); + assertEquals("/v2/retentions/ret_copy_1/stamp", httpClient.requests().get(1).uri().getPath()); + } + + @Test + void retentionDraftCancelAndListUseExpectedPaths() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"ret_draft_1\",\"status\":\"canceled\"}"); + httpClient.enqueueJson(200, "{\"data\":[{\"id\":\"ret_draft_2\",\"status\":\"draft\"}]}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var deleted = sdk.retentions().cancel("ret_draft_1"); + var drafts = sdk.retentions().list(Map.of("status", "draft")); + + assertEquals("ret_draft_1", deleted.getId()); + assertEquals(1, drafts.getData().size()); + assertEquals("DELETE", httpClient.requests().get(0).method()); + assertEquals("/v2/retentions/ret_draft_1", httpClient.requests().get(0).uri().getPath()); + var listRequest = httpClient.requests().get(1); + assertEquals("GET", listRequest.method()); + assertEquals("/v2/retentions?status=draft", listRequest.uri().getPath() + "?" + listRequest.uri().getQuery()); + } + @Test void organizationUploadsAcceptBytes() { StubHttpClient httpClient = new StubHttpClient(); From 575f2cf64ff5ac4db798056c1b4f8a4a2deed429 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Garc=C3=ADa?= <141654545+raul-facturapi@users.noreply.github.com> Date: Mon, 24 Aug 2026 09:32:33 -0600 Subject: [PATCH 2/7] =?UTF-8?q?feat(invoices):=20agregar=20m=C3=A9todos=20?= =?UTF-8?q?ZIP=20(#11)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add invoice ZIP request methods * fix: align ZIP download method naming --------- Co-authored-by: javorosas --- CHANGELOG.md | 5 + .../facturapi/resources/InvoicesResource.java | 57 ++++++++++++ .../io/facturapi/FacturapiResourcesTest.java | 92 +++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2c392a8..59e5f2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add draft support methods for retentions: `retentions.updateDraft(String id, Map data)`, `retentions.copyToDraft(String id)`, `retentions.stampDraft(String id)`, and `retentions.cancel(String id)`. +- Add `invoices.createZipRequest(Map data)` for `POST /invoices/zip-requests`. +- Add `invoices.listZipRequests(Map params)` for `GET /invoices/zip-requests`. +- Add `invoices.retrieveZipRequest(String id)` for `GET /invoices/zip-requests/{id}`. +- Add `invoices.downloadZipRequestStream(String id)` for streaming `GET /invoices/zip-requests/{id}/zip`. + ## [1.3.0] - 2026-06-07 ### Added diff --git a/src/main/java/io/facturapi/resources/InvoicesResource.java b/src/main/java/io/facturapi/resources/InvoicesResource.java index f0d2911..6dac015 100644 --- a/src/main/java/io/facturapi/resources/InvoicesResource.java +++ b/src/main/java/io/facturapi/resources/InvoicesResource.java @@ -142,6 +142,63 @@ public InputStream downloadZipStream(String id) { return client.getStream("/invoices/" + id + "/zip"); } + /** + * Creates a ZIP request or returns the existing request for the same criteria. + * + * @param data ZIP request criteria. + * @return ZIP request data. + * @see API reference + */ + public Map createZipRequest(Map data) { + return post( + "/invoices/zip-requests", + data, + null, + new TypeReference>() {} + ); + } + + /** + * Gets a paginated list of ZIP requests. + * + * @param params Search and pagination parameters. + * @return Paginated ZIP request result. + * @see API reference + */ + public SearchResult> listZipRequests(Map params) { + return get( + "/invoices/zip-requests", + params, + new TypeReference>>() {} + ); + } + + /** + * Gets a ZIP request by id. + * + * @param id ZIP request id. + * @return ZIP request data. + * @see API reference + */ + public Map retrieveZipRequest(String id) { + return get( + "/invoices/zip-requests/" + id, + null, + new TypeReference>() {} + ); + } + + /** + * Downloads the ZIP generated for a request. + * + * @param id ZIP request id. + * @return ZIP stream. Caller owns closing it. + * @see API reference + */ + public InputStream downloadZipRequestStream(String id) { + return client.getStream("/invoices/zip-requests/" + id + "/zip"); + } + /** * Downloads the cancellation receipt XML file. * diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java index 30fdeab..8443312 100644 --- a/src/test/java/io/facturapi/FacturapiResourcesTest.java +++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java @@ -206,6 +206,98 @@ void retentionDraftCancelAndListUseExpectedPaths() { assertEquals("/v2/retentions?status=draft", listRequest.uri().getPath() + "?" + listRequest.uri().getQuery()); } + @Test + void invoiceZipRequestCanBeCreated() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"zip_1\",\"status\":\"pending\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var response = sdk.invoices().createZipRequest( + Map.of( + "year", 2025, + "month", 3, + "issuer_type", "issuing", + "invoice_types", java.util.List.of("I", "E") + ) + ); + + assertEquals("zip_1", response.get("id")); + var request = httpClient.requests().get(0); + assertEquals("POST", request.method()); + assertEquals("/v2/invoices/zip-requests", request.uri().getPath()); + assertTrue(request.bodyUtf8().contains("\"issuer_type\":\"issuing\"")); + } + + @Test + void invoiceZipRequestsCanBeListed() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson( + 200, + "{\"page\":1,\"total_pages\":1,\"total_results\":1,\"data\":[{\"id\":\"zip_1\"}]}" + ); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var response = sdk.invoices().listZipRequests( + Map.of("year", 2025, "month", 3, "status", "finished", "limit", 20, "page", 1) + ); + + assertEquals("zip_1", response.getData().get(0).get("id")); + var request = httpClient.requests().get(0); + assertEquals("GET", request.method()); + assertEquals("/v2/invoices/zip-requests", request.uri().getPath()); + assertTrue(request.uri().getQuery().contains("year=2025")); + assertTrue(request.uri().getQuery().contains("status=finished")); + } + + @Test + void invoiceZipRequestCanBeRetrieved() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(200, "{\"id\":\"zip_1\",\"status\":\"finished\"}"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + var response = sdk.invoices().retrieveZipRequest("zip_1"); + + assertEquals("finished", response.get("status")); + var request = httpClient.requests().get(0); + assertEquals("GET", request.method()); + assertEquals("/v2/invoices/zip-requests/zip_1", request.uri().getPath()); + } + + @Test + void invoiceZipRequestCanBeDownloaded() throws Exception { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueBinary(200, "ZIP-CONTENT".getBytes(StandardCharsets.UTF_8), "application/zip"); + + Facturapi sdk = new Facturapi( + FacturapiConfig.builder("sk_test") + .httpClient(httpClient.client()) + .build() + ); + + try (InputStream stream = sdk.invoices().downloadZipRequestStream("zip_1")) { + assertEquals("ZIP-CONTENT", new String(stream.readAllBytes(), StandardCharsets.UTF_8)); + } + + var request = httpClient.requests().get(0); + assertEquals("GET", request.method()); + assertEquals("/v2/invoices/zip-requests/zip_1/zip", request.uri().getPath()); + } + @Test void organizationUploadsAcceptBytes() { StubHttpClient httpClient = new StubHttpClient(); From 91eccbf0b79c28bc61df2b199c7b9ebc51ce88cb Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:18:44 +0200 Subject: [PATCH 3/7] fix!: return Java API error codes as strings --- CHANGELOG.md | 6 +++++- pom.xml | 2 +- .../java/io/facturapi/FacturapiException.java | 8 ++++---- .../facturapi/http/FacturapiHttpClient.java | 14 ++------------ .../io/facturapi/FacturapiHttpClientTest.java | 19 +++++++++++++++++++ 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59e5f2a..7f549b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## Unreleased +## [2.0.0] - 2026-08-21 ### Added @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add `invoices.retrieveZipRequest(String id)` for `GET /invoices/zip-requests/{id}`. - Add `invoices.downloadZipRequestStream(String id)` for streaming `GET /invoices/zip-requests/{id}/zip`. +### Changed + +- Change `FacturapiException.getErrorCode()` to return strings, including converted legacy numeric codes. + ## [1.3.0] - 2026-06-07 ### Added diff --git a/pom.xml b/pom.xml index 4235f4a..a69187f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ io.facturapi facturapi-java - 1.3.0 + 2.0.0 facturapi-java Official Java SDK for Facturapi https://github.com/facturapi/facturapi-java diff --git a/src/main/java/io/facturapi/FacturapiException.java b/src/main/java/io/facturapi/FacturapiException.java index 0e0839c..482baed 100644 --- a/src/main/java/io/facturapi/FacturapiException.java +++ b/src/main/java/io/facturapi/FacturapiException.java @@ -7,7 +7,7 @@ public class FacturapiException extends RuntimeException { private final int statusCode; - private final Object errorCode; + private final String errorCode; private final String errorPath; private final String errorLocation; private final JsonNode errors; @@ -29,14 +29,14 @@ public FacturapiException(String message, Throwable cause) { this.headers = Collections.emptyMap(); } - public FacturapiException(String message, int statusCode, Object errorCode, String errorPath) { + public FacturapiException(String message, int statusCode, String errorCode, String errorPath) { this(message, statusCode, errorCode, errorPath, null, null, null, Collections.emptyMap()); } public FacturapiException( String message, int statusCode, - Object errorCode, + String errorCode, String errorPath, String errorLocation, JsonNode errors, @@ -57,7 +57,7 @@ public int getStatusCode() { return statusCode; } - public Object getErrorCode() { + public String getErrorCode() { return errorCode; } diff --git a/src/main/java/io/facturapi/http/FacturapiHttpClient.java b/src/main/java/io/facturapi/http/FacturapiHttpClient.java index 0fee0d7..b0febcb 100644 --- a/src/main/java/io/facturapi/http/FacturapiHttpClient.java +++ b/src/main/java/io/facturapi/http/FacturapiHttpClient.java @@ -210,7 +210,7 @@ private static JsonNode firstDefined(JsonNode node, String... keys) { private FacturapiException buildApiException(String bodyText, Response response) { int statusCode = response.code(); int resolvedStatus = statusCode; - Object errorCode = null; + String errorCode = null; String errorPath = null; String errorLocation = null; JsonNode errors = null; @@ -246,17 +246,7 @@ private FacturapiException buildApiException(String bodyText, Response response) JsonNode codeNode = firstDefined(root, "code"); if (codeNode != null && !codeNode.isNull()) { - if (codeNode.isTextual()) { - errorCode = codeNode.asText(); - } else if (codeNode.isIntegralNumber()) { - errorCode = codeNode.intValue(); - } else if (codeNode.isNumber()) { - errorCode = codeNode.numberValue(); - } else if (codeNode.isBoolean()) { - errorCode = codeNode.asBoolean(); - } else { - errorCode = codeNode.toString(); - } + errorCode = codeNode.isValueNode() ? codeNode.asText() : codeNode.toString(); } JsonNode pathNode = firstDefined(root, "path"); diff --git a/src/test/java/io/facturapi/FacturapiHttpClientTest.java b/src/test/java/io/facturapi/FacturapiHttpClientTest.java index 4917970..e8d6eac 100644 --- a/src/test/java/io/facturapi/FacturapiHttpClientTest.java +++ b/src/test/java/io/facturapi/FacturapiHttpClientTest.java @@ -77,4 +77,23 @@ void throwsFacturapiExceptionWithApiMessage() { assertEquals("3", ex.getHeaders().get("Retry-After").get(0)); assertEquals("log_123", ex.getHeaders().get("x-facturapi-log-id").get(0)); } + + @Test + void convertsNumericApiErrorCodesToStrings() { + StubHttpClient httpClient = new StubHttpClient(); + httpClient.enqueueJson(400, "{\"message\":\"Invalid customer\",\"code\":400}"); + + FacturapiHttpClient client = new FacturapiHttpClient( + FacturapiConfig.builder("sk_test_123") + .httpClient(httpClient.client()) + .build() + ); + + FacturapiException ex = assertThrows( + FacturapiException.class, + () -> client.get("/customers/cus_1", null, GenericResponse.class) + ); + + assertEquals("400", ex.getErrorCode()); + } } From 59234b28f6792d9b425616dd304617288c68f648 Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:21:26 +0200 Subject: [PATCH 4/7] fix: only normalize numeric API error codes --- src/main/java/io/facturapi/http/FacturapiHttpClient.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/facturapi/http/FacturapiHttpClient.java b/src/main/java/io/facturapi/http/FacturapiHttpClient.java index b0febcb..a25c3b4 100644 --- a/src/main/java/io/facturapi/http/FacturapiHttpClient.java +++ b/src/main/java/io/facturapi/http/FacturapiHttpClient.java @@ -245,8 +245,8 @@ private FacturapiException buildApiException(String bodyText, Response response) } JsonNode codeNode = firstDefined(root, "code"); - if (codeNode != null && !codeNode.isNull()) { - errorCode = codeNode.isValueNode() ? codeNode.asText() : codeNode.toString(); + if (codeNode != null && (codeNode.isTextual() || codeNode.isNumber())) { + errorCode = codeNode.asText(); } JsonNode pathNode = firstDefined(root, "path"); From 8c5414f56bc649802a11076648aac31de6d17565 Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 17 Jul 2026 16:45:35 +0200 Subject: [PATCH 5/7] fix: model property tax accounts as arrays --- .../java/io/facturapi/models/InvoiceItem.java | 9 ++++++--- .../io/facturapi/FacturapiResourcesTest.java | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/src/main/java/io/facturapi/models/InvoiceItem.java b/src/main/java/io/facturapi/models/InvoiceItem.java index adcb675..dd35db8 100644 --- a/src/main/java/io/facturapi/models/InvoiceItem.java +++ b/src/main/java/io/facturapi/models/InvoiceItem.java @@ -14,7 +14,8 @@ public class InvoiceItem { private InvoiceItemThirdParty thirdParty; private String complement; private List parts = new ArrayList<>(); - private String propertyTaxAccount; + @JsonProperty("property_tax_account") + private List propertyTaxAccounts = new ArrayList<>(); public Double getQuantity() { return quantity; } public void setQuantity(Double quantity) { this.quantity = quantity; } @@ -30,6 +31,8 @@ public class InvoiceItem { public void setComplement(String complement) { this.complement = complement; } public List getParts() { return parts; } public void setParts(List parts) { this.parts = parts; } - public String getPropertyTaxAccount() { return propertyTaxAccount; } - public void setPropertyTaxAccount(String propertyTaxAccount) { this.propertyTaxAccount = propertyTaxAccount; } + @JsonProperty("property_tax_account") + public List getPropertyTaxAccounts() { return propertyTaxAccounts; } + @JsonProperty("property_tax_account") + public void setPropertyTaxAccounts(List propertyTaxAccounts) { this.propertyTaxAccounts = propertyTaxAccounts; } } diff --git a/src/test/java/io/facturapi/FacturapiResourcesTest.java b/src/test/java/io/facturapi/FacturapiResourcesTest.java index 8443312..ef43828 100644 --- a/src/test/java/io/facturapi/FacturapiResourcesTest.java +++ b/src/test/java/io/facturapi/FacturapiResourcesTest.java @@ -16,10 +16,12 @@ import io.facturapi.enums.Taxability; import io.facturapi.http.FacturapiConfig; import io.facturapi.models.Customer; +import io.facturapi.models.InvoiceItem; import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.time.LocalDate; +import java.util.List; import java.util.Map; import org.junit.jupiter.api.Test; @@ -506,4 +508,18 @@ void objectMapperDeserializesCodeEnums() throws Exception { assertEquals(TaxType.IEPS, tax.getType()); assertEquals(TaxFactor.EXENTO, tax.getFactor()); } + + @Test + void objectMapperDeserializesPropertyTaxAccountsAsArrays() throws Exception { + var mapper = FacturapiConfig.builder("sk_test").build().getObjectMapper(); + + var empty = mapper.readValue("{\"property_tax_account\":[]}", InvoiceItem.class); + var accounts = mapper.readValue( + "{\"property_tax_account\":[\"0102030405\"]}", + InvoiceItem.class + ); + + assertEquals(List.of(), empty.getPropertyTaxAccounts()); + assertEquals(List.of("0102030405"), accounts.getPropertyTaxAccounts()); + } } From 1e1b1f49dbd8f549bded16b073bcbc2aa5e74294 Mon Sep 17 00:00:00 2001 From: javorosas Date: Fri, 21 Aug 2026 18:16:20 +0200 Subject: [PATCH 6/7] docs: update Java dependency snippets for 2.0.0 --- README.es.md | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.es.md b/README.es.md index 2920846..d5ba3bd 100644 --- a/README.es.md +++ b/README.es.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.3.0 + 2.0.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.3.0") +implementation("io.facturapi:facturapi-java:2.0.0") ``` ## Inicio rápido diff --git a/README.md b/README.md index 6915bd6..b2ca541 100644 --- a/README.md +++ b/README.md @@ -29,14 +29,14 @@ Maven: io.facturapi facturapi-java - 1.3.0 + 2.0.0 ``` Gradle: ```gradle -implementation("io.facturapi:facturapi-java:1.3.0") +implementation("io.facturapi:facturapi-java:2.0.0") ``` ## Quickstart From 38104041692884481407b75851822e3367e6d8ea Mon Sep 17 00:00:00 2001 From: javorosas Date: Mon, 24 Aug 2026 17:37:09 +0200 Subject: [PATCH 7/7] docs: complete Java 2.0.0 changelog --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f549b9..f13e13c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [2.0.0] - 2026-08-21 +## [2.0.0] - 2026-08-24 ### Added @@ -19,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Change `FacturapiException.getErrorCode()` to return strings, including converted legacy numeric codes. +- Change `InvoiceItem.property_tax_account` to a list of property tax account numbers. ## [1.3.0] - 2026-06-07