ReplaceWarehouseUseCase.java

package com.fulfilment.application.monolith.warehouses.domain.usecases;

import com.fulfilment.application.monolith.warehouses.domain.models.Warehouse;
import com.fulfilment.application.monolith.warehouses.domain.ports.ReplaceWarehouseOperation;
import com.fulfilment.application.monolith.warehouses.domain.ports.WarehouseStore;
import jakarta.enterprise.context.ApplicationScoped;
import com.fulfilment.application.monolith.exception.HttpStatus;
import jakarta.ws.rs.NotFoundException;
import jakarta.ws.rs.WebApplicationException;
import java.time.LocalDateTime;
import org.jboss.logging.Logger;

/**
 * Replaces an active warehouse with a new one using the same Business Unit Code.
 *
 * <p>The replacement process:
 * <ol>
 *   <li>Find the existing active warehouse by businessUnitCode</li>
 *   <li>Validate the new warehouse can accommodate the old stock (Capacity Accommodation)</li>
 *   <li>Validate the new warehouse stock matches the old warehouse stock (Stock Matching)</li>
 *   <li>Archive the old warehouse (soft delete via archivedAt timestamp)</li>
 *   <li>Create the new warehouse with the same businessUnitCode</li>
 * </ol>
 *
 * <p>Both archive and create happen within the same transaction (managed by the caller)
 * to guarantee atomicity — if create fails, the archive is rolled back.
 */
@ApplicationScoped
public class ReplaceWarehouseUseCase implements ReplaceWarehouseOperation {

  private static final Logger LOGGER = Logger.getLogger(ReplaceWarehouseUseCase.class.getName());

  private final WarehouseStore warehouseStore;

  public ReplaceWarehouseUseCase(WarehouseStore warehouseStore) {
    this.warehouseStore = warehouseStore;
  }

  @Override
  public void replace(Warehouse newWarehouse) {
    LOGGER.debugf("Attempting to replace warehouse with businessUnitCode: '%s'",
        newWarehouse.businessUnitCode);

    // 1. Find the existing active warehouse by businessUnitCode
    Warehouse oldWarehouse = warehouseStore.findByBusinessUnitCode(newWarehouse.businessUnitCode);
    if (oldWarehouse == null) {
      LOGGER.warnf("No active warehouse found with businessUnitCode '%s'",
          newWarehouse.businessUnitCode);
      throw new NotFoundException(
          "Warehouse with business unit code '" + newWarehouse.businessUnitCode + "' not found.");
    }

    // 2. Capacity Accommodation — new warehouse must be able to hold the old stock
    if (newWarehouse.capacity < oldWarehouse.stock) {
      LOGGER.warnf(
          "New warehouse capacity %d cannot accommodate old warehouse stock %d",
          newWarehouse.capacity, oldWarehouse.stock);
      throw new WebApplicationException(
          "New warehouse capacity (" + newWarehouse.capacity
              + ") cannot accommodate the stock of the warehouse being replaced ("
              + oldWarehouse.stock + ").", HttpStatus.UNPROCESSABLE_ENTITY);
    }

    // 3. Stock Matching — new warehouse stock must equal old warehouse stock
    if (!newWarehouse.stock.equals(oldWarehouse.stock)) {
      LOGGER.warnf(
          "Stock mismatch — new warehouse stock %d does not match old warehouse stock %d",
          newWarehouse.stock, oldWarehouse.stock);
      throw new WebApplicationException(
          "New warehouse stock (" + newWarehouse.stock
              + ") must match the stock of the warehouse being replaced ("
              + oldWarehouse.stock + ").", HttpStatus.UNPROCESSABLE_ENTITY);
    }

    // 4. Archive the old warehouse (soft delete)
    oldWarehouse.archivedAt = LocalDateTime.now();
    warehouseStore.update(oldWarehouse);
    LOGGER.infof("Warehouse '%s' archived at %s", oldWarehouse.businessUnitCode, oldWarehouse.archivedAt);

    // 5. Create the new warehouse with the same businessUnitCode
    newWarehouse.createdAt = LocalDateTime.now();
    warehouseStore.create(newWarehouse);
    LOGGER.infof("Replacement warehouse '%s' created successfully at location '%s'",
        newWarehouse.businessUnitCode, newWarehouse.location);
  }
}