InvoiceClosedListener.java

package com.stocks.stockease.movement.internal;

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import com.stocks.stockease.invoice.InvoiceClosedEvent;
import com.stocks.stockease.invoice.InvoiceType;
import com.stocks.stockease.movement.MovementReason;
import com.stocks.stockease.movement.RecordMovementCommand;
import com.stocks.stockease.movement.StockMovementService;

import lombok.RequiredArgsConstructor;

/**
 * Books one stock movement per line when an invoice is closed.
 * Deliberately a synchronous {@code @EventListener}, NOT {@code @ApplicationModuleListener} - the async
 * variant runs after commit, which would let a close succeed while its stock booking fails.
 */
@Component
@RequiredArgsConstructor
public class InvoiceClosedListener {

    private final StockMovementService stockMovementService;

    /**
     * Records the stock movement each closed line implies.
     *
     * @param event the close that was published inside the closing transaction
     */
    @EventListener
    public void onInvoiceClosed(InvoiceClosedEvent event) {
        MovementReason reason = event.type() == InvoiceType.PURCHASE ? MovementReason.PURCHASE : MovementReason.SOLD;
        for (InvoiceClosedEvent.Line line : event.lines()) {
            stockMovementService.recordMovement(
                    new RecordMovementCommand(line.productId(), reason, line.quantity(), line.invoiceItemId(), null),
                    event.closedBy());
        }
    }
}