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 java.util.List;
import java.util.Map;
/**
* Typed HTTP client for the Dolibarr REST API.
@ -182,17 +181,38 @@ public class DolibarrClient {
// ── 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
* @param socid customer (thirdparty) internal ID — required by Dolibarr even with fk_commande
* <p>Dolibarr's {@code POST /invoices} does not copy order lines automatically when
* 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) {
log.debug("Creating Dolibarr invoice from orderId={}", orderId);
Map<String, Object> body = Map.of("fk_commande", orderId, "socid", socid);
public DolibarrInvoiceDto createInvoiceFromOrder(DolibarrOrderDto order) {
log.debug("Creating Dolibarr invoice from orderId={}", order.id());
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()
.uri("/invoices")
.body(body)
.body(invoiceBody)
.retrieve()
.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.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Builder;
import java.util.List;
/**
* Minimal representation of a Dolibarr invoice.
* Used for {@code GET /invoices/{id}} responses after creation via {@code POST /invoices}.
* Represents a Dolibarr invoice.
* Used for {@code POST /invoices} request bodies (with lines) and {@code GET /invoices/{id}} responses.
*/
@Builder
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record DolibarrInvoiceDto(
Integer id,
String ref,
Integer socid,
/** Linked order internal ID */
@JsonProperty("fk_commande") Integer fkCommande,
/** 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);
// 3. Create linked invoice
DolibarrInvoiceDto invoice = dolibarrClient.createInvoiceFromOrder(
createdOrder.id(), customer.id());
// 3. Create linked invoice with lines copied from the order
DolibarrInvoiceDto invoice = dolibarrClient.createInvoiceFromOrder(createdOrder);
// 4. Persist mapping to prevent re-import
orderMappingRepository.save(OrderMapping.builder()