fix(orders): copy order lines into invoice on creation

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>
This commit is contained in:
luklpz 2026-05-23 15:39:10 +02:00
parent 312829e5f4
commit c02e771f0a
3 changed files with 57 additions and 15 deletions

View File

@ -9,7 +9,6 @@ import org.springframework.stereotype.Component;
import org.springframework.web.client.RestClient; import org.springframework.web.client.RestClient;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Typed HTTP client for the Dolibarr REST API. * Typed HTTP client for the Dolibarr REST API.
@ -182,17 +181,38 @@ public class DolibarrClient {
// ── Invoices ───────────────────────────────────────────────────────── // ── Invoices ─────────────────────────────────────────────────────────
/** /**
* Creates an invoice linked to an existing order. * Creates an invoice linked to an existing Dolibarr order, copying its lines.
* *
* @param orderId Dolibarr internal order ID * <p>Dolibarr's {@code POST /invoices} does not copy order lines automatically when
* @param socid customer (thirdparty) internal ID — required by Dolibarr even with fk_commande * only {@code fk_commande + socid} are sent — lines must be included in the body.</p>
*
* @param order the created order (must have {@code id}, {@code socid}, and {@code lines})
*/ */
public DolibarrInvoiceDto createInvoiceFromOrder(Integer orderId, Integer socid) { public DolibarrInvoiceDto createInvoiceFromOrder(DolibarrOrderDto order) {
log.debug("Creating Dolibarr invoice from orderId={}", orderId); log.debug("Creating Dolibarr invoice from orderId={}", order.id());
Map<String, Object> body = Map.of("fk_commande", orderId, "socid", socid);
List<DolibarrInvoiceDto.InvoiceLine> invoiceLines = order.lines() == null
? List.of()
: order.lines().stream()
.map(l -> DolibarrInvoiceDto.InvoiceLine.builder()
.fkProduct(l.fkProduct())
.productRef(l.productRef())
.desc(l.desc())
.subprice(l.subprice())
.qty(l.qty())
.tvaTx(l.tvaTx())
.build())
.toList();
DolibarrInvoiceDto invoiceBody = DolibarrInvoiceDto.builder()
.fkCommande(order.id())
.socid(order.socid())
.lines(invoiceLines)
.build();
Integer newId = restClient.post() Integer newId = restClient.post()
.uri("/invoices") .uri("/invoices")
.body(body) .body(invoiceBody)
.retrieve() .retrieve()
.body(Integer.class); .body(Integer.class);

View File

@ -3,18 +3,41 @@ package com.teterialosjuanjos.tfg.sync_service.integration.dolibarr.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import java.util.List;
/** /**
* Minimal representation of a Dolibarr invoice. * Represents a Dolibarr invoice.
* Used for {@code GET /invoices/{id}} responses after creation via {@code POST /invoices}. * Used for {@code POST /invoices} request bodies (with lines) and {@code GET /invoices/{id}} responses.
*/ */
@Builder
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL) @JsonInclude(JsonInclude.Include.NON_NULL)
public record DolibarrInvoiceDto( public record DolibarrInvoiceDto(
Integer id, Integer id,
String ref, String ref,
Integer socid,
/** Linked order internal ID */ /** Linked order internal ID */
@JsonProperty("fk_commande") Integer fkCommande, @JsonProperty("fk_commande") Integer fkCommande,
/** 0 = draft, 1 = validated, 2 = paid, 3 = abandoned */ /** 0 = draft, 1 = validated, 2 = paid, 3 = abandoned */
Integer statut Integer statut,
List<InvoiceLine> lines
) {
/**
* One line of an invoice. Mirrors {@link DolibarrOrderDto.OrderLine} fields
* so order lines can be copied directly into the invoice on creation.
*/
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record InvoiceLine(
@JsonProperty("fk_product") Integer fkProduct,
@JsonProperty("product_ref") String productRef,
String desc,
Double subprice,
Double qty,
@JsonProperty("tva_tx") Double tvaTx
) {} ) {}
}

View File

@ -130,9 +130,8 @@ public class OrderSyncService {
DolibarrOrderDto createdOrder = dolibarrClient.createOrder(orderDto); DolibarrOrderDto createdOrder = dolibarrClient.createOrder(orderDto);
// 3. Create linked invoice // 3. Create linked invoice with lines copied from the order
DolibarrInvoiceDto invoice = dolibarrClient.createInvoiceFromOrder( DolibarrInvoiceDto invoice = dolibarrClient.createInvoiceFromOrder(createdOrder);
createdOrder.id(), customer.id());
// 4. Persist mapping to prevent re-import // 4. Persist mapping to prevent re-import
orderMappingRepository.save(OrderMapping.builder() orderMappingRepository.save(OrderMapping.builder()