OpenInvoiceDeletionVeto.java
package com.stocks.stockease.invoice.internal;
import org.springframework.context.event.EventListener;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import com.stocks.stockease.invoice.InvoiceStatus;
import com.stocks.stockease.product.ProductChangedEvent;
import com.stocks.stockease.supplier.SupplierDeletedEvent;
import lombok.RequiredArgsConstructor;
/**
* Open invoices pin the parties they reference: neither the supplier billed nor a product on a line
* may disappear while the invoice is unsettled. These synchronous listeners veto supplier and product
* deletions inside the deleting transaction.
*/
// vetoes are validations and run before any other listener does work - fail fast; ordering is pinned
// so behavior cannot shift with classpath or JVM changes
@Order(Ordered.HIGHEST_PRECEDENCE)
@Component
@RequiredArgsConstructor
public class OpenInvoiceDeletionVeto {
private final InvoiceRepository invoiceRepository;
private final InvoiceItemRepository invoiceItemRepository;
/**
* Vetoes deleting a supplier that still has open invoices.
*
* @param event the pending supplier deletion
* @throws IllegalStateException if any open invoice references the supplier
*/
@EventListener
public void onSupplierDeleted(SupplierDeletedEvent event) {
if (invoiceRepository.existsBySupplierIdAndStatus(event.supplierId(), InvoiceStatus.OPEN)) {
throw new IllegalStateException(
"Cannot delete supplier '" + event.supplierName() + "': open invoices exist.");
}
}
/**
* Vetoes deleting a product that appears on an open invoice; other product changes are ignored.
*
* @param event the product change being recorded
* @throws IllegalStateException if the product is a line on any open invoice
*/
@EventListener
public void onProductChanged(ProductChangedEvent event) {
if (event.field() != ProductChangedEvent.Field.DELETED) {
return;
}
if (invoiceItemRepository.existsByProductIdAndInvoiceStatus(
event.product().getId(), InvoiceStatus.OPEN)) {
throw new IllegalStateException("Cannot delete product '" + event.product().getName()
+ "': it appears on an open invoice.");
}
}
}