From df3775d96b6f85ac295e03f99ba971f82559826c Mon Sep 17 00:00:00 2001 From: luklpz Date: Thu, 28 May 2026 18:47:55 +0200 Subject: [PATCH] fix: map Dolibarr tva_tx to PS id_tax_rules_group on product sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Price with VAT was showing the same value as price without VAT because id_tax_rules_group was hardcoded to 1 (no-tax group in this PS instance). Now reads tva_tx from Dolibarr product, resolves the PS tax rule group via a configurable map (integration.prestashop.tax-rule-groups), and sets the correct group in the product XML. Defaults to default-tax-rules-group-id when no mapping is found. ES groups configured: 21% → 53, 10% → 54, 4% → 55. Co-Authored-By: Claude Sonnet 4.6 --- .../config/IntegrationProperties.java | 22 ++++++++++++++----- .../dolibarr/dto/DolibarrProductDto.java | 4 +++- .../prestashop/PrestashopClient.java | 3 ++- .../prestashop/dto/PrestashopProductDto.java | 4 +++- .../sync_service/sync/ProductSyncService.java | 19 +++++++++++++++- .../resources/application-dev.yml.example | 5 +++++ .../src/main/resources/application.yml | 5 +++++ 7 files changed, 52 insertions(+), 10 deletions(-) diff --git a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/config/IntegrationProperties.java b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/config/IntegrationProperties.java index 519e388..387ec15 100644 --- a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/config/IntegrationProperties.java +++ b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/config/IntegrationProperties.java @@ -3,8 +3,11 @@ package com.teterialosjuanjos.tfg.sync_service.config; import jakarta.validation.Valid; import jakarta.validation.constraints.NotBlank; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.bind.DefaultValue; import org.springframework.validation.annotation.Validated; +import java.util.Map; + /** * Externalized configuration for Dolibarr and PrestaShop integration endpoints. * Bound from {@code integration.*} properties in application.yml; validated at startup. @@ -28,16 +31,23 @@ public record IntegrationProperties( ) {} /** - * @param baseUrl full API base URL, e.g. {@code https://host/tienda/api} - * @param apiKey webservice key sent as {@code ws_key} query param - * @param defaultCategoryId PrestaShop category ID assigned to new products (default: 2 = Home) - * @param languageId PrestaShop language ID used for all multilingual fields (name, description, etc.) - * Check yours at GET /api/languages. Default 1 is usually English; set to Spanish ID. + * @param baseUrl full API base URL, e.g. {@code https://host/tienda/api} + * @param apiKey webservice key sent as {@code ws_key} query param + * @param defaultCategoryId PrestaShop category ID assigned to new products (default: 2 = Home) + * @param languageId PrestaShop language ID used for all multilingual fields (name, description, etc.) + * Check yours at GET /api/languages. Default 1 is usually English; set to Spanish ID. + * @param defaultTaxRulesGroupId Fallback PS {@code id_tax_rules_group} when the product's Dolibarr {@code tva_tx} + * has no entry in {@code taxRuleGroups}. Defaults to 1. + * Check your PS tax groups at GET /api/tax_rule_groups. + * @param taxRuleGroups Map of Dolibarr VAT rate (integer key, e.g. "21") to PS {@code id_tax_rules_group}. + * Configure in application-dev/prod.yml. If empty, {@code defaultTaxRulesGroupId} is used. */ public record Prestashop( @NotBlank String baseUrl, @NotBlank String apiKey, int defaultCategoryId, - int languageId + int languageId, + @DefaultValue("1") int defaultTaxRulesGroupId, + Map taxRuleGroups ) {} } diff --git a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/dolibarr/dto/DolibarrProductDto.java b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/dolibarr/dto/DolibarrProductDto.java index 29a9cf5..a90f19f 100644 --- a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/dolibarr/dto/DolibarrProductDto.java +++ b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/dolibarr/dto/DolibarrProductDto.java @@ -26,5 +26,7 @@ public record DolibarrProductDto( @JsonProperty("tosell") Integer toSell, /** 0 = physical product, 1 = service */ Integer type, - Double weight + Double weight, + /** VAT rate in percent, e.g. 21.0 for 21% IVA */ + @JsonProperty("tva_tx") Double tvaTx ) {} diff --git a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/PrestashopClient.java b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/PrestashopClient.java index c526a00..79021de 100644 --- a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/PrestashopClient.java +++ b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/PrestashopClient.java @@ -371,7 +371,8 @@ public class PrestashopClient { sb.append("").append(escapeXml(dto.idCategoryDefault())).append(""); } sb.append("1"); - sb.append("1"); + int taxRulesGroupId = dto.taxRulesGroupId() != null ? dto.taxRulesGroupId() : 1; + sb.append("").append(taxRulesGroupId).append(""); sb.append("1"); sb.append("1"); sb.append("1"); diff --git a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/dto/PrestashopProductDto.java b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/dto/PrestashopProductDto.java index 923c694..0dd45bd 100644 --- a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/dto/PrestashopProductDto.java +++ b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/integration/prestashop/dto/PrestashopProductDto.java @@ -34,7 +34,9 @@ public record PrestashopProductDto( @JsonProperty("description_short") @JsonDeserialize(using = MultilangStringDeserializer.class) String descriptionShort, /** PS category IDs to set on write; not present in PS JSON responses (ignored on read). */ - @JsonIgnore List categoryIds + @JsonIgnore List categoryIds, + /** PS tax rule group ID; resolved from Dolibarr tva_tx at sync time, write-only. */ + @JsonIgnore Integer taxRulesGroupId ) { /** Envelope for {@code GET /api/products?display=full} */ diff --git a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/sync/ProductSyncService.java b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/sync/ProductSyncService.java index 5c411ba..ec0b803 100644 --- a/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/sync/ProductSyncService.java +++ b/sync-service/src/main/java/com/teterialosjuanjos/tfg/sync_service/sync/ProductSyncService.java @@ -19,6 +19,7 @@ import org.springframework.web.client.RestClientException; import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -249,6 +250,7 @@ public class ProductSyncService { String description = src.description() != null ? src.description() : ""; String descriptionShort = src.notePublic() != null ? src.notePublic() : ""; String active = (src.toSell() != null && src.toSell() == 0) ? "0" : "1"; + Integer taxRulesGroupId = resolveTaxRulesGroup(src.tvaTx()); return new PrestashopProductDto( prestashopId, @@ -259,7 +261,22 @@ public class ProductSyncService { label, description, descriptionShort, - allCategoryIds + allCategoryIds, + taxRulesGroupId ); } + + /** + * Resolves the PrestaShop {@code id_tax_rules_group} from the Dolibarr VAT rate. + * Looks up {@code tvaTx} (truncated to int, e.g. 21.0 → "21") in the configured map. + * Falls back to {@code defaultTaxRulesGroupId} when no mapping exists. + */ + private Integer resolveTaxRulesGroup(Double tvaTx) { + Map map = integrationProperties.prestashop().taxRuleGroups(); + if (tvaTx != null && map != null && !map.isEmpty()) { + Integer groupId = map.get(String.valueOf(tvaTx.intValue())); + if (groupId != null) return groupId; + } + return integrationProperties.prestashop().defaultTaxRulesGroupId(); + } } diff --git a/sync-service/src/main/resources/application-dev.yml.example b/sync-service/src/main/resources/application-dev.yml.example index 58d8353..50be201 100644 --- a/sync-service/src/main/resources/application-dev.yml.example +++ b/sync-service/src/main/resources/application-dev.yml.example @@ -29,6 +29,11 @@ integration: api-key: DOLIBARR_API_KEY_AQUI prestashop: api-key: PRESTASHOP_API_KEY_AQUI + default-tax-rules-group-id: 53 # ES Standard rate (21%) — fallback + tax-rule-groups: + 21: 53 # ES Standard rate (21%) + 10: 54 # ES Reduced Rate (10%) + 4: 55 # ES Super Reduced Rate (4%) logging: level: diff --git a/sync-service/src/main/resources/application.yml b/sync-service/src/main/resources/application.yml index 3598a3a..3c02695 100644 --- a/sync-service/src/main/resources/application.yml +++ b/sync-service/src/main/resources/application.yml @@ -17,6 +17,11 @@ integration: api-key: ${PRESTASHOP_API_KEY} default-category-id: 2 language-id: ${PRESTASHOP_LANGUAGE_ID:1} # set to your Spanish language ID (check GET /api/languages) + default-tax-rules-group-id: 53 # ES Standard rate (21%) — fallback + tax-rule-groups: + 21: 53 # ES Standard rate (21%) + 10: 54 # ES Reduced Rate (10%) + 4: 55 # ES Super Reduced Rate (4%) api: security: