165 lines
10 KiB
Java
165 lines
10 KiB
Java
package com.baiyue.lastleg.discard;
|
|||
|
|
|
||
|
|
import com.baiyue.lastleg.discard.dto.CargoDiscardRequests;
|
||
|
|
import com.baiyue.lastleg.discard.service.CargoDiscardInventoryAdjustmentService;
|
||
|
|
import com.baiyue.lastleg.discard.service.CargoDiscardService;
|
||
|
|
import org.mybatis.spring.annotation.MapperScan;
|
||
|
|
import org.junit.jupiter.api.Test;
|
||
|
|
import org.junit.jupiter.api.condition.EnabledIfSystemProperty;
|
||
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
||
|
|
import org.springframework.boot.SpringBootConfiguration;
|
||
|
|
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||
|
|
import org.springframework.boot.test.context.SpringBootTest;
|
||
|
|
import org.springframework.context.annotation.Import;
|
||
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||
|
|
import org.springframework.web.server.ResponseStatusException;
|
||
|
|
|
||
|
|
import java.util.List;
|
||
|
|
|
||
|
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||
|
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||
|
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||
|
|
|
||
|
|
@SpringBootTest(classes = CargoDiscardMysqlAcceptanceTest.AcceptanceApplication.class, properties = {
|
||
|
|
"spring.sql.init.mode=never",
|
||
|
|
"spring.cloud.nacos.discovery.enabled=false",
|
||
|
|
"spring.cloud.nacos.discovery.register-enabled=false"
|
||
|
|
})
|
||
|
|
@EnabledIfSystemProperty(named = "cargo.discard.acceptance", matches = "true")
|
||
|
|
class CargoDiscardMysqlAcceptanceTest {
|
||
|
|
private static final String CUSTOMER = "990001";
|
||
|
|
|
||
|
|
@Autowired private JdbcTemplate jdbc;
|
||
|
|
@Autowired private CargoDiscardService service;
|
||
|
|
|
||
|
|
@SpringBootConfiguration
|
||
|
|
@EnableAutoConfiguration
|
||
|
|
@Import({CargoDiscardService.class, CargoDiscardInventoryAdjustmentService.class})
|
||
|
|
@MapperScan({
|
||
|
|
"com.baiyue.lastleg.mapper",
|
||
|
|
"com.baiyue.lastleg.discard.mapper",
|
||
|
|
"com.baiyue.lastleg.dropship.mapper"
|
||
|
|
})
|
||
|
|
static class AcceptanceApplication {
|
||
|
|
}
|
||
|
|
|
||
|
|
@Test
|
||
|
|
void createApprovalRejectionPendingAndInsufficientStockScenarios() {
|
||
|
|
resetAcceptanceTenant();
|
||
|
|
seedOrder(990001001L, "RK-ACCEPT-APPROVE", "JF-ACCEPT-001", 20);
|
||
|
|
seedProfile(990001101L, 990001001L, "PALLET-A");
|
||
|
|
seedSku(990001201L, 990001001L, 990001101L, "SKU-BOX-RED", "红色折叠收纳箱", 12);
|
||
|
|
seedSku(990001202L, 990001001L, 990001101L, "SKU-RACK-KITCHEN", "厨房置物架", 8);
|
||
|
|
seedCartons(990001001L, 990001101L, 990001301L, "PALLET-A", "SKU-BOX-RED", "RED", 12);
|
||
|
|
seedCartons(990001001L, 990001101L, 990001401L, "PALLET-A", "SKU-RACK-KITCHEN", "RACK", 8);
|
||
|
|
|
||
|
|
seedOrder(990001002L, "RK-ACCEPT-REJECT", "JF-ACCEPT-002", 10);
|
||
|
|
seedProfile(990001102L, 990001002L, "PALLET-B");
|
||
|
|
seedSku(990001203L, 990001002L, 990001102L, "SKU-LAMP-WHITE", "白色台灯", 10);
|
||
|
|
seedCartons(990001002L, 990001102L, 990001501L, "PALLET-B", "SKU-LAMP-WHITE", "LAMP", 10);
|
||
|
|
|
||
|
|
seedOrder(990001003L, "RK-ACCEPT-PENDING", "JF-ACCEPT-003", 7);
|
||
|
|
seedProfile(990001103L, 990001003L, "PALLET-C");
|
||
|
|
seedSku(990001204L, 990001003L, 990001103L, "SKU-MAT-BLUE", "蓝色瑜伽垫", 7);
|
||
|
|
seedCartons(990001003L, 990001103L, 990001601L, "PALLET-C", "SKU-MAT-BLUE", "MAT", 7);
|
||
|
|
|
||
|
|
var approveRequest = new CargoDiscardRequests.Create(List.of(
|
||
|
|
new CargoDiscardRequests.Item(990001001L, "SKU-BOX-RED", 3),
|
||
|
|
new CargoDiscardRequests.Item(990001001L, "SKU-RACK-KITCHEN", 2)),
|
||
|
|
"ACCEPT-DISCARD-APPROVE", "验收:破损货物,同意废弃");
|
||
|
|
var approved = service.create(CUSTOMER, "acceptance-creator", approveRequest);
|
||
|
|
assertEquals(20L, skuPackages(990001001L));
|
||
|
|
approved = service.review(CUSTOMER, approved.summary().discardNo(), "acceptance-reviewer",
|
||
|
|
new CargoDiscardRequests.Review("APPROVE", "仓库复核破损属实"));
|
||
|
|
assertEquals(CargoDiscardService.APPROVED, approved.summary().status());
|
||
|
|
assertEquals(15L, skuPackages(990001001L));
|
||
|
|
|
||
|
|
var rejected = service.create(CUSTOMER, "acceptance-creator", new CargoDiscardRequests.Create(
|
||
|
|
List.of(new CargoDiscardRequests.Item(990001002L, "SKU-LAMP-WHITE", 4)),
|
||
|
|
"ACCEPT-DISCARD-REJECT", "验收:外箱可修复"));
|
||
|
|
assertEquals(10L, skuPackages(990001002L));
|
||
|
|
rejected = service.review(CUSTOMER, rejected.summary().discardNo(), "acceptance-reviewer",
|
||
|
|
new CargoDiscardRequests.Review("REJECT", "复核后无需废弃"));
|
||
|
|
assertEquals(CargoDiscardService.REJECTED, rejected.summary().status());
|
||
|
|
assertEquals(10L, skuPackages(990001002L));
|
||
|
|
|
||
|
|
var pending = service.create(CUSTOMER, "acceptance-creator", new CargoDiscardRequests.Create(
|
||
|
|
List.of(new CargoDiscardRequests.Item(990001003L, "SKU-MAT-BLUE", 2)),
|
||
|
|
"ACCEPT-DISCARD-PENDING", "验收:等待仓库审核"));
|
||
|
|
assertEquals(CargoDiscardService.PENDING_REVIEW, pending.summary().status());
|
||
|
|
assertEquals(7L, skuPackages(990001003L));
|
||
|
|
|
||
|
|
assertThrows(ResponseStatusException.class, () -> service.create(CUSTOMER, "acceptance-creator",
|
||
|
|
new CargoDiscardRequests.Create(List.of(
|
||
|
|
new CargoDiscardRequests.Item(990001003L, "SKU-MAT-BLUE", 99)),
|
||
|
|
"ACCEPT-DISCARD-INSUFFICIENT", "验收:库存不足应失败")));
|
||
|
|
|
||
|
|
assertEquals(1, count("SELECT COUNT(*) FROM ll_cargo_discard_application WHERE customer_id=? AND status='APPROVED'"));
|
||
|
|
assertEquals(1, count("SELECT COUNT(*) FROM ll_cargo_discard_application WHERE customer_id=? AND status='REJECTED'"));
|
||
|
|
assertEquals(1, count("SELECT COUNT(*) FROM ll_cargo_discard_application WHERE customer_id=? AND status='PENDING_REVIEW'"));
|
||
|
|
assertEquals(5, count("SELECT COUNT(*) FROM ll_dropship_carton WHERE customer_id=? AND order_id=990001001 AND status='DISCARDED'"));
|
||
|
|
assertEquals(0, count("SELECT COUNT(*) FROM ll_dropship_carton WHERE customer_id=? AND order_id=990001002 AND status='DISCARD_PENDING'"));
|
||
|
|
assertEquals(2, count("SELECT COUNT(*) FROM ll_dropship_carton WHERE customer_id=? AND order_id=990001003 AND status='DISCARD_PENDING'"));
|
||
|
|
assertEquals(5, count("SELECT COUNT(*) FROM ll_dropship_carton_ledger WHERE customer_id=? AND operation_type='DISCARD'"));
|
||
|
|
assertEquals(5, count("SELECT COUNT(DISTINCT idempotency_key) FROM ll_dropship_carton_ledger WHERE customer_id=? AND operation_type='DISCARD'"));
|
||
|
|
String audit = jdbc.queryForObject("SELECT remark FROM ll_cargo_discard_status_history WHERE customer_id=? " +
|
||
|
|
"AND to_status='APPROVED'", String.class, CUSTOMER);
|
||
|
|
assertTrue(audit.contains("inventoryBefore=20, discarded=5, inventoryAfter=15"));
|
||
|
|
}
|
||
|
|
|
||
|
|
private void resetAcceptanceTenant() {
|
||
|
|
jdbc.update("DELETE FROM ll_dropship_carton_ledger WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_cargo_discard_status_history WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_cargo_discard_carton WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_cargo_discard_item WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_cargo_discard_application WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_dropship_carton WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_dropship_sku_inventory WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_dropship_order_profile WHERE customer_id=?", CUSTOMER);
|
||
|
|
jdbc.update("DELETE FROM ll_order WHERE customer_id=?", CUSTOMER);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void seedOrder(long id, String inboundCode, String orderNo, int packages) {
|
||
|
|
jdbc.update("INSERT INTO ll_order(id,order_no,customer_id,order_type,inbound_code,source_region," +
|
||
|
|
"source_inbound_id,customer_name,overseas_warehouse_id,overseas_warehouse_name,pallet_count," +
|
||
|
|
"package_count,product_quantity,current_status,source_event_id,source_loading_plan_id,source_payload_hash," +
|
||
|
|
"inbound_order_no,created_at,updated_at) VALUES(?,?,?,'ONE_CLICK_DROPSHIP',?,'ACCEPTANCE',?," +
|
||
|
|
"'废弃验收客户','WH-US-LAX','美西洛杉矶仓',1,?,?, 'WAREHOUSED',?,?,?, ?,NOW(3),NOW(3))",
|
||
|
|
id, orderNo, CUSTOMER, inboundCode, "ACCEPT-" + id, packages, packages * 10,
|
||
|
|
"ACCEPT-EVENT-" + id, "ACCEPT-PLAN-" + id, "sha256:acceptance-" + id, orderNo);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void seedProfile(long id, long orderId, String palletNo) {
|
||
|
|
jdbc.update("INSERT INTO ll_dropship_order_profile(id,customer_id,order_id,warehouse_pallet_id,pallet_no," +
|
||
|
|
"source_pallet_no,status,idempotency_key,version,created_at,updated_at) " +
|
||
|
|
"VALUES(?,?,?, ?,?,?,'RECEIVED',?,0,NOW(3),NOW(3))",
|
||
|
|
id, CUSTOMER, orderId, id, palletNo, "SRC-" + palletNo, "ACCEPT-PROFILE-" + id);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void seedSku(long id, long orderId, long profileId, String sku, String name, int packages) {
|
||
|
|
jdbc.update("INSERT INTO ll_dropship_sku_inventory(id,customer_id,order_id,profile_id,sku_code,product_name," +
|
||
|
|
"package_count,product_quantity,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,NOW(3),NOW(3))",
|
||
|
|
id, CUSTOMER, orderId, profileId, sku, name, packages, packages * 10);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void seedCartons(long orderId, long profileId, long firstId, String palletNo,
|
||
|
|
String sku, String markPrefix, int packages) {
|
||
|
|
for (int index = 1; index <= packages; index++) {
|
||
|
|
jdbc.update("INSERT INTO ll_dropship_carton(id,customer_id,order_id,profile_id,warehouse_pallet_id," +
|
||
|
|
"pallet_no,sku_code,carton_mark,carton_sequence,status,version,created_at,updated_at) " +
|
||
|
|
"VALUES(?,?,?,?,?,?,?,?,?,'AVAILABLE',0,NOW(3),NOW(3))",
|
||
|
|
firstId + index - 1, CUSTOMER, orderId, profileId, profileId, palletNo, sku,
|
||
|
|
"ACCEPT-" + markPrefix + "-" + String.format("%03d", index), index);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private long skuPackages(long orderId) {
|
||
|
|
return jdbc.queryForObject("SELECT SUM(package_count) FROM ll_dropship_sku_inventory WHERE customer_id=? AND order_id=?",
|
||
|
|
Long.class, CUSTOMER, orderId);
|
||
|
|
}
|
||
|
|
|
||
|
|
private int count(String sql) {
|
||
|
|
return jdbc.queryForObject(sql, Integer.class, CUSTOMER);
|
||
|
|
}
|
||
|
|
}
|