fix(sync): push stock immediately on new product creation
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>
This commit is contained in:
parent
a5feff6a1a
commit
e501d83c73
|
|
@ -156,21 +156,30 @@ public class PrestashopClient {
|
|||
* @param productId PrestaShop product internal ID
|
||||
*/
|
||||
public Optional<PrestashopStockAvailableDto> getStockAvailableForProduct(Integer productId) {
|
||||
PrestashopStockAvailableDto.ListResponse response = restClient.get()
|
||||
.uri(u -> u.path("/stock_availables")
|
||||
.queryParam("ws_key", wsKey)
|
||||
.queryParam("output_format", "JSON")
|
||||
.queryParam("display", "full")
|
||||
.queryParam("filter[id_product]", productId)
|
||||
.queryParam("filter[id_product_attribute]", "0")
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(PrestashopStockAvailableDto.ListResponse.class);
|
||||
try {
|
||||
PrestashopStockAvailableDto.ListResponse response = restClient.get()
|
||||
.uri(u -> u.path("/stock_availables")
|
||||
.queryParam("ws_key", wsKey)
|
||||
.queryParam("output_format", "JSON")
|
||||
.queryParam("display", "full")
|
||||
.queryParam("filter[id_product]", productId)
|
||||
.queryParam("filter[id_product_attribute]", "0")
|
||||
.build())
|
||||
.retrieve()
|
||||
.body(PrestashopStockAvailableDto.ListResponse.class);
|
||||
|
||||
if (response == null || response.stockAvailables() == null || response.stockAvailables().isEmpty()) {
|
||||
if (response == null || response.stockAvailables() == null || response.stockAvailables().isEmpty()) {
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(response.stockAvailables().get(0));
|
||||
} catch (PrestashopApiException e) {
|
||||
if (e.getStatusCode() != null && e.getStatusCode().value() == 404) return Optional.empty();
|
||||
throw e;
|
||||
} catch (HttpMessageConversionException | RestClientException e) {
|
||||
// PS 8.x returns {"stock_availables": false} instead of [] when filter has no results
|
||||
log.debug("PS returned unparseable body for stock_available productId={}, treating as not found: {}", productId, e.getMessage());
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(response.stockAvailables().get(0));
|
||||
}
|
||||
|
||||
/** Updates the stock quantity for a stock_available record. */
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.teterialosjuanjos.tfg.sync_service.integration.dolibarr.dto.DolibarrP
|
|||
import com.teterialosjuanjos.tfg.sync_service.integration.dolibarr.exception.DolibarrApiException;
|
||||
import com.teterialosjuanjos.tfg.sync_service.integration.prestashop.PrestashopClient;
|
||||
import com.teterialosjuanjos.tfg.sync_service.integration.prestashop.dto.PrestashopProductDto;
|
||||
import com.teterialosjuanjos.tfg.sync_service.integration.prestashop.dto.PrestashopStockAvailableDto;
|
||||
import com.teterialosjuanjos.tfg.sync_service.integration.prestashop.exception.PrestashopApiException;
|
||||
import com.teterialosjuanjos.tfg.sync_service.mapping.*;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
|
@ -110,10 +111,37 @@ public class ProductSyncService {
|
|||
"Product created in PS but not found via GET (reference=" + sku + ")");
|
||||
}
|
||||
upsertMapping(sku, dolProduct.id(), created.id());
|
||||
pushInitialStock(dolProduct, created.id(), sku);
|
||||
log.debug("ProductSync: created PS product reference={} id={}", sku, created.id());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pushes Dolibarr stock to the PS stock_available record immediately after product creation.
|
||||
* Skipped when stockReel is null or zero (PS default is already 0).
|
||||
* Failures are logged as warnings but do not abort the product sync — StockSyncService
|
||||
* will correct any discrepancy on its next run.
|
||||
*/
|
||||
private void pushInitialStock(DolibarrProductDto dolProduct, Integer psProductId, String sku) {
|
||||
if (dolProduct.stockReel() == null || dolProduct.stockReel() == 0) return;
|
||||
int qty = dolProduct.stockReel().intValue();
|
||||
try {
|
||||
Optional<PrestashopStockAvailableDto> stockOpt =
|
||||
prestashopClient.getStockAvailableForProduct(psProductId);
|
||||
if (stockOpt.isEmpty()) {
|
||||
log.warn("ProductSync: no stock_available found for new product SKU={}, stock will sync on next StockSync run", sku);
|
||||
return;
|
||||
}
|
||||
PrestashopStockAvailableDto current = stockOpt.get();
|
||||
prestashopClient.updateStockAvailable(current.id(),
|
||||
new PrestashopStockAvailableDto(current.id(), current.idProduct(),
|
||||
current.idProductAttribute(), String.valueOf(qty)));
|
||||
log.debug("ProductSync: initial stock pushed SKU={} qty={}", sku, qty);
|
||||
} catch (PrestashopApiException | RestClientException | HttpMessageConversionException e) {
|
||||
log.warn("ProductSync: could not push initial stock for SKU={}: {}", sku, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void upsertMapping(String sku, Integer dolibarrId, Integer prestashopId) {
|
||||
ProductMapping mapping = productMappingRepository.findBySku(sku)
|
||||
.orElseGet(() -> ProductMapping.builder().sku(sku).build());
|
||||
|
|
|
|||
Loading…
Reference in New Issue