feat(appointments): unify pre-inbound planning and confirmed FBA bookings
This commit is contained in:
+45
@@ -14,6 +14,51 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class DispatchCandidateMapperContractTest {
|
||||
@Test
|
||||
void confirmedAppointmentUsesOriginalCustomerOrderAndDestinationAndNeverInventsLoadingTime() throws Exception {
|
||||
try (var connection = DriverManager.getConnection("jdbc:h2:mem:dispatch-confirmed-appointment;MODE=MySQL")) {
|
||||
var statement = connection.createStatement();
|
||||
statement.execute("CREATE TABLE ll_inbound_plan(id BIGINT, customer_id VARCHAR)");
|
||||
statement.execute("CREATE TABLE ll_inbound_plan_pallet(id BIGINT, plan_id BIGINT,destination_warehouse_code VARCHAR)");
|
||||
statement.execute("CREATE TABLE ll_inbound_plan_line(id BIGINT,plan_id BIGINT,customer_id VARCHAR,order_id BIGINT,fba_warehouse_code VARCHAR)");
|
||||
statement.execute("CREATE TABLE ll_inbound_plan_pallet_allocation(pallet_id BIGINT,line_id BIGINT,customer_id VARCHAR)");
|
||||
statement.execute("CREATE TABLE ll_inbound_fba_appointment(id BIGINT,appointment_at TIMESTAMP,planned_at TIMESTAMP,appointment_status VARCHAR,fba_warehouse_code VARCHAR)");
|
||||
statement.execute("CREATE TABLE ll_inbound_fba_appointment_item(appointment_id BIGINT,order_id BIGINT,customer_id VARCHAR,active INT,fba_warehouse_code VARCHAR)");
|
||||
for (int i = 1; i <= 8; i++) {
|
||||
statement.execute("INSERT INTO ll_inbound_plan VALUES ("+i+",'9')");
|
||||
statement.execute("INSERT INTO ll_inbound_plan_pallet VALUES ("+i+","+i+",'ACY2')");
|
||||
statement.execute("INSERT INTO ll_inbound_plan_line VALUES ("+i+","+i+",'9',"+i+",'OLD')");
|
||||
statement.execute("INSERT INTO ll_inbound_plan_pallet_allocation VALUES ("+i+","+i+",'9')");
|
||||
statement.execute("INSERT INTO ll_inbound_fba_appointment VALUES ("+i+",'2026-10-12 10:00:00','2026-10-11 09:00:00','SUCCESS','ACY2')");
|
||||
statement.execute("INSERT INTO ll_inbound_fba_appointment_item VALUES ("+i+","+i+",'9',1,'ACY2')");
|
||||
}
|
||||
statement.execute("UPDATE ll_inbound_fba_appointment SET appointment_status='PENDING' WHERE id=2");
|
||||
statement.execute("UPDATE ll_inbound_fba_appointment_item SET active=0 WHERE appointment_id=3");
|
||||
statement.execute("UPDATE ll_inbound_fba_appointment_item SET customer_id='OTHER' WHERE appointment_id=4");
|
||||
statement.execute("UPDATE ll_inbound_plan_pallet SET destination_warehouse_code='ONT8' WHERE id=5");
|
||||
statement.execute("UPDATE ll_inbound_plan_line SET customer_id='OTHER' WHERE id=6");
|
||||
statement.execute("UPDATE ll_inbound_fba_appointment SET appointment_at=NULL WHERE id=7");
|
||||
statement.execute("UPDATE ll_inbound_fba_appointment_item SET fba_warehouse_code='ONT8' WHERE appointment_id=8");
|
||||
try (var rows = statement.executeQuery("SELECT p.id," + DispatchCandidateMapper.CONFIRMED_APPOINTMENT_AT
|
||||
+ " AS appointment_at FROM ll_inbound_plan_pallet p JOIN ll_inbound_plan ip ON ip.id=p.plan_id ORDER BY p.id")) {
|
||||
int count = 0;
|
||||
while (rows.next()) {
|
||||
count++;
|
||||
assertEquals(count == 1 ? java.sql.Timestamp.valueOf("2026-10-12 10:00:00") : null, rows.getTimestamp("appointment_at"));
|
||||
}
|
||||
assertEquals(8, count);
|
||||
}
|
||||
for (Method method : List.of(
|
||||
DispatchCandidateMapper.class.getMethod("available", String.class, List.class, String.class, String.class, String.class, String.class),
|
||||
DispatchCandidateMapper.class.getMethod("lockSelected", String.class, List.class))) {
|
||||
String sql = String.join(" ", method.getAnnotation(Select.class).value());
|
||||
assertTrue(sql.contains(DispatchCandidateMapper.CONFIRMED_APPOINTMENT_AT + " AS appointmentAt,NULL AS expectedLoadingAt"));
|
||||
assertFalse(sql.contains("expected_loading_at"));
|
||||
assertFalse(sql.contains("fi.line_id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void historicalTransferCreationModeDoesNotGrantReservationOwnership() throws Exception {
|
||||
for (Method method : List.of(
|
||||
|
||||
+49
@@ -47,5 +47,54 @@ class FleetPlanningReadPersistenceTest {
|
||||
assertThat(sources.stream().filter(s->s.bookingNo().equals("FB-5")).findFirst().orElseThrow().packageCount()).isNull();
|
||||
assertThat(sources.stream().filter(s->s.bookingNo().equals("FB-6")).findFirst().orElseThrow().packageCount()).isEqualTo(10);
|
||||
assertThat(sources).allMatch(s->s.lastlegOrderId()==null&&s.expectedPalletCount()==null);
|
||||
jdbc.execute("CREATE TABLE ll_order(id BIGINT PRIMARY KEY,customer_id VARCHAR(64))");
|
||||
jdbc.update("INSERT INTO ll_order VALUES(501,'9'),(601,'9'),(701,'OTHER'),(801,'9')");
|
||||
jdbc.update("INSERT INTO ll_fleet_pool_order_link VALUES(5,'9',501,'LB-5','WAITING_INBOUND'),(6,'9',601,'LB-6','RECEIVED'),(6,'9',701,'INVALID','RECEIVED')");
|
||||
var adapter = new FleetAppointmentSourceAdapter(resolver);
|
||||
var bookingRefs = List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.SourceRef("9", "FB-6"),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.SourceRef("9", "FB-5"));
|
||||
assertThatThrownBy(() -> adapter.lockForAppointment(bookingRefs)).isInstanceOf(IllegalStateException.class);
|
||||
var transaction = new org.springframework.transaction.support.TransactionTemplate(new org.springframework.jdbc.datasource.DataSourceTransactionManager(ds));
|
||||
transaction.executeWithoutResult(status -> {
|
||||
adapter.lockForAppointment(bookingRefs);
|
||||
adapter.lockForAppointment(List.of(bookingRefs.get(0), bookingRefs.get(0)));
|
||||
adapter.lockForAppointment(List.of());
|
||||
assertThatThrownBy(() -> adapter.lockForAppointment(List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.SourceRef("OTHER", "FB-5"))))
|
||||
.isInstanceOf(org.springframework.web.server.ResponseStatusException.class);
|
||||
assertThatThrownBy(() -> adapter.lockForAppointment(List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.SourceRef("9", "MISSING"))))
|
||||
.isInstanceOf(org.springframework.web.server.ResponseStatusException.class);
|
||||
assertThatThrownBy(() -> adapter.lockForAppointment(java.util.Collections.nCopies(101, bookingRefs.get(0))))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> adapter.lockForAppointment(List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.SourceRef(" ", "FB-5"))))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
});
|
||||
var lockingMapper = org.mockito.Mockito.mock(FleetPoolOrderMapper.class);
|
||||
org.mockito.Mockito.when(lockingMapper.lockAppointmentSource("FB-5")).thenReturn(new FleetPoolSourceResolver.SourceRef("9", "FB-5"));
|
||||
org.mockito.Mockito.when(lockingMapper.lockAppointmentSource("FB-6")).thenReturn(new FleetPoolSourceResolver.SourceRef("9", "FB-6"));
|
||||
var lockingAdapter = new FleetAppointmentSourceAdapter(new FleetPoolSourceResolver(lockingMapper, new ObjectMapper()));
|
||||
transaction.executeWithoutResult(status -> lockingAdapter.lockForAppointment(List.of(bookingRefs.get(0), bookingRefs.get(1), bookingRefs.get(0))));
|
||||
var lockOrder = org.mockito.Mockito.inOrder(lockingMapper);
|
||||
lockOrder.verify(lockingMapper).lockAppointmentSource("FB-5");
|
||||
lockOrder.verify(lockingMapper).lockAppointmentSource("FB-6");
|
||||
lockOrder.verifyNoMoreInteractions();
|
||||
var refs = List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 501L),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 601L),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 501L),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("OTHER", 601L),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 701L),
|
||||
new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 801L));
|
||||
var resolved = adapter.resolveOrders(refs);
|
||||
assertThat(resolved).extracting(s -> s.bookingNo()).containsExactlyInAnyOrder("FB-5", "FB-6");
|
||||
assertThat(resolved).filteredOn(s -> s.bookingNo().equals("FB-5")).singleElement().satisfies(s -> {
|
||||
assertThat(s.packageCount()).isNull(); assertThat(s.orderId()).isEqualTo(501L);
|
||||
assertThat(s.customerId()).isEqualTo("9"); assertThat(s.destinationKey()).isEqualTo("FBA:ACY2");
|
||||
});
|
||||
assertThat(adapter.resolveOrders(List.of())).isEmpty();
|
||||
assertThat(adapter.resolveOrders(null)).isEmpty();
|
||||
assertThatThrownBy(() -> adapter.resolveOrders(java.util.Collections.nCopies(101, refs.get(0))))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
assertThatThrownBy(() -> adapter.resolveOrders(List.of(new com.baiyue.lastleg.appointmentplanning.AppointmentExternalSourceGateway.OrderRef("9", 0L))))
|
||||
.isInstanceOf(IllegalArgumentException.class);
|
||||
assertThat(jdbc.queryForObject("SELECT COUNT(*) FROM ll_fleet_external_booking", Integer.class)).isEqualTo(2);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user