fix: fetch all Dolibarr products without tms sqlfilter

Incremental sync via t.tms filter breaks when products were created before
the service started running: their tms is older than lastSyncStart so they
are never returned. Since the sync is idempotent (ProductMapping handles
create-vs-update), always fetching all products is safe and simpler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
luklpz 2026-05-14 21:04:09 +02:00
parent 359590b7d8
commit 806497b24d
2 changed files with 9 additions and 26 deletions

View File

@ -7,9 +7,6 @@ import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import java.time.Instant;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -24,9 +21,6 @@ import java.util.Map;
@Component @Component
public class DolibarrClient { public class DolibarrClient {
private static final DateTimeFormatter DOLIBARR_DATE_FORMAT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneOffset.UTC);
private final RestClient restClient; private final RestClient restClient;
public DolibarrClient(@Qualifier("dolibarrRestClient") RestClient restClient) { public DolibarrClient(@Qualifier("dolibarrRestClient") RestClient restClient) {
@ -36,20 +30,15 @@ public class DolibarrClient {
// ── Products ────────────────────────────────────────────────────────── // ── Products ──────────────────────────────────────────────────────────
/** /**
* Returns all products, optionally filtered by last modification date. * Returns all active products (up to 500).
* *
* @param modifiedSince if non-null, only products with {@code tms >= modifiedSince} are returned * <p>Full fetch on every run keeps sync idempotent and avoids timezone issues
* with Dolibarr's {@code tms} column filter. The service handles create-vs-update
* via {@code ProductMapping}, so re-processing unchanged products is safe.</p>
*/ */
public List<DolibarrProductDto> getProducts(Instant modifiedSince) { public List<DolibarrProductDto> getProducts() {
return restClient.get() return restClient.get()
.uri(u -> { .uri(u -> u.path("/products").queryParam("limit", 500).build())
u.path("/products").queryParam("limit", 500);
if (modifiedSince != null) {
u.queryParam("sqlfilters",
"(t.tms:>=:'%s')".formatted(DOLIBARR_DATE_FORMAT.format(modifiedSince)));
}
return u.build();
})
.retrieve() .retrieve()
.body(new ParameterizedTypeReference<List<DolibarrProductDto>>() {}); .body(new ParameterizedTypeReference<List<DolibarrProductDto>>() {});
} }

View File

@ -40,15 +40,9 @@ public class ProductSyncService {
private final IntegrationProperties integrationProperties; private final IntegrationProperties integrationProperties;
/** /**
* Executes a full incremental product push and returns a summary. * Fetches all Dolibarr products and pushes them to PrestaShop (create or update by SKU).
*/ */
public SyncResult synchronize() { public SyncResult synchronize() {
// Determine cutoff BEFORE saving current log to avoid self-reference
Instant lastSyncStart = syncLogRepository
.findFirstBySyncTypeOrderByStartedAtDesc(SyncType.PRODUCT_PUSH)
.map(SyncLog::getStartedAt)
.orElse(null);
SyncLog syncLog = syncLogRepository.save(SyncLog.builder() SyncLog syncLog = syncLogRepository.save(SyncLog.builder()
.syncType(SyncType.PRODUCT_PUSH) .syncType(SyncType.PRODUCT_PUSH)
.startedAt(Instant.now()) .startedAt(Instant.now())
@ -59,8 +53,8 @@ public class ProductSyncService {
List<String> errors = new ArrayList<>(); List<String> errors = new ArrayList<>();
try { try {
List<DolibarrProductDto> products = dolibarrClient.getProducts(lastSyncStart); List<DolibarrProductDto> products = dolibarrClient.getProducts();
log.info("ProductSync: {} products to process (since {})", products.size(), lastSyncStart); log.info("ProductSync: {} products to process", products.size());
for (DolibarrProductDto product : products) { for (DolibarrProductDto product : products) {
if (product.ref() == null || product.ref().isBlank()) { if (product.ref() == null || product.ref().isBlank()) {