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 <noreply@anthropic.com>
POST /invoices with only fk_commande+socid creates an empty invoice.
Lines must be included in the request body explicitly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProductSyncService now calls pushInitialStock() after creating a new
product in PrestaShop, so stock is correct without waiting for the next
StockSyncService run. Failures are caught and logged as warnings — the
product mapping is saved as SYNCED regardless, and StockSyncService will
correct any discrepancy on its next scheduled run.
Also applies the PS 8.2.5 false-instead-of-empty-array workaround to
getStockAvailableForProduct(), consistent with getProductByReference().
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PS 8 introduced a state column (0=draft, 1=published). Products created
via Webservice without state default to 0, making them invisible in the
admin product list (which filters state=1 only). The storefront does not
check state, so products appeared there but not in admin.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PS Webservice creates stock_available records with id_shop=0 when
products are created via API. The PS frontend reads id_shop=1 records
for stock display in a single-shop setup, causing OUT OF STOCK to appear
even when quantity > 0. Adding id_shop=1 to the XML ensures stock updates
target the correct shop-specific record.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Including <associations><categories> in the product XML triggers
Product::addToCategories() in PS, which may also invoke the shop
association logic needed to populate ps_product_shop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Spring 6 RestClient wraps HttpMessageNotReadableException (thrown when
deserializing {"products":false}) into RestClientException with message
"Error while extracting response for type [...]". The previous fix only
caught HttpMessageConversionException directly, missing the wrapper.
PrestashopApiException is not a RestClientException, so 4xx/5xx error
handling is unaffected.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PS 8.2.5 returns {"products": false} instead of {"products": []}
when a reference filter finds no matching products. Jackson cannot
deserialize false as a List, throwing HttpMessageConversionException.
Catch it and return Optional.empty() so pushProduct takes the CREATE
path correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Deletes the PS product via Webservice and removes the local mapping.
Use this to clean up incomplete PS products (missing ps_product_shop
entry) that block re-creation on next sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Products created via PS Webservice were missing the ps_product_shop
table entry, causing them to be invisible in PS admin (which queries
products with INNER JOIN ps_product_shop). Adding id_shop_default=1
forces PS to create the shop association on product create/update.
Also add available_for_order, show_price, and visibility=both to
ensure products are fully configured for catalog display.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
PS soft-deletes products (sets active=0) when deleted from admin,
keeping them in the DB. The previous filter returned those inactive
products, causing pushProduct to take the UPDATE path and silently
re-update a soft-deleted product that remains invisible.
Adding filter[active]=[1] ensures only visible products are matched.
Products deleted in PS are now correctly re-created on next sync.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ProductSyncService now queries PS by reference before deciding
create vs update, preventing stale local mappings from causing
silent no-ops when PS products are deleted externally.
Also fix active field default: tosell=null now maps to active=1
instead of active=0, so products without an explicit sell flag
are created as visible in PrestaShop.
Add server.forward-headers-strategy=framework so Spring reads
X-Forwarded-Proto from Railway's reverse proxy; fixes Swagger UI
generating http:// URLs instead of https://.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dolibarr GET /orders/{id} returns date fields as ISO strings, not
Unix timestamps, causing Jackson Long deserialization to fail.
String handles both: sends epoch string in POST body (Dolibarr accepts
it); ignores the date_commande string in GET via @JsonIgnoreProperties.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Dolibarr POST /orders requires field named "date" (not "date_commande").
Add @JsonAlias("date_commande") so GET responses are still deserialized
correctly when the response uses the alternative field name.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both APIs return 404 (not 200+empty) when a filter finds no results.
Catch DolibarrApiException(404) in getOrCreateThirdparty and
getProductByRef; catch PrestashopApiException(404) in
getProductByReference — treat as empty/not-found instead of error.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- PrestashopClient.toStockAvailableXml: add out_of_stock=2 (use shop
default) — PS 8.2.5 returns 400 code 41 without this field
- PrestashopClient.getOrdersSince: remove filter[date_add] — PS 8.2.5
does not expose date_add as a filterable field for orders (400 code 32)
Re-import prevention handled by OrderMapping.existsByPrestashopOrderId
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DolibarrClient.getProductByRef: use sqlfilters=(t.ref:=:'x') instead
of unsupported ref query param (caused StockSync to push wrong stock)
- PrestashopClient.getProductByReference: wrap value in [x] for PS
exact-match filter syntax (root cause of createProduct returning null)
- OrderSyncService.resolveCustomer: skip getCustomer when id_customer
is 0 or null (guest orders returned 404 and failed import)
- StockSyncService: parse stock quantity as Double to handle "5.000"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
If PS accepts the POST but the subsequent GET by reference finds nothing,
the null return from createProduct now throws PrestashopApiException, which
is caught by the per-item handler and recorded in SyncLog without aborting
the rest of the sync loop.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>