diff --git a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostService.java b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostService.java index 946ded305..e491cbd24 100644 --- a/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostService.java +++ b/lastleg-service/src/main/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostService.java @@ -22,7 +22,9 @@ import java.util.Map; @Service @RequiredArgsConstructor public class DestuffingPalletCostService { + static final String DESTUFF_BUSINESS_TYPE = "OVERSEAS_CONTAINER"; static final String READY_BOOKINGS_SQL = "SELECT o.source_container_booking_id FROM ll_inbound_plan p JOIN ll_order o ON o.id=p.source_order_id JOIN ll_inbound_pda_task t ON t.plan_id=p.id AND t.customer_id=p.customer_id LEFT JOIN ll_destuff_pallet_cost c ON c.source_container_booking_id=o.source_container_booking_id WHERE o.source_container_booking_id IS NOT NULL AND c.id IS NULL GROUP BY o.source_container_booking_id HAVING COUNT(*)=SUM(CASE WHEN t.status='COMPLETED' THEN 1 ELSE 0 END) ORDER BY MAX(t.completed_at) DESC LIMIT 50"; + static final String RECEIVED_PALLET_IDS_SQL = "SELECT DISTINCT pp.warehouse_pallet_id FROM ll_inbound_plan_pallet pp JOIN ll_inbound_plan p ON p.id=pp.plan_id JOIN ll_order o ON o.id=p.source_order_id WHERE o.source_container_booking_id=? AND pp.warehouse_pallet_id IS NOT NULL AND pp.actual_package_count>0 AND pp.actual_volume_cbm>0 ORDER BY pp.warehouse_pallet_id"; private final JdbcTemplate jdbc; private final PalletPhysicalGateway pallets; private final OverseasFeeSourceMapper feeSources; @@ -38,7 +40,7 @@ public class DestuffingPalletCostService { if (exists != null && exists > 0) return; List> plans = jdbc.queryForList("SELECT p.id,p.customer_id,p.container_no,p.overseas_warehouse_id,t.status FROM ll_inbound_plan p JOIN ll_order o ON o.id=p.source_order_id LEFT JOIN ll_inbound_pda_task t ON t.plan_id=p.id AND t.customer_id=p.customer_id WHERE o.source_container_booking_id=? ORDER BY p.customer_id,p.id", bookingId); if (plans.isEmpty() || plans.stream().anyMatch(row -> !"COMPLETED".equals(row.get("status")))) return; - List palletIds = jdbc.queryForList("SELECT DISTINCT pp.warehouse_pallet_id FROM ll_inbound_plan_pallet pp JOIN ll_inbound_plan p ON p.id=pp.plan_id JOIN ll_order o ON o.id=p.source_order_id WHERE o.source_container_booking_id=? AND pp.warehouse_pallet_id IS NOT NULL ORDER BY pp.warehouse_pallet_id", Long.class, bookingId); + List palletIds = jdbc.queryForList(RECEIVED_PALLET_IDS_SQL, Long.class, bookingId); BigDecimal totalCost = BigDecimal.ZERO; for (Long palletId : palletIds) totalCost = totalCost.add(pallets.resolve(palletId).unitPrice()); totalCost = totalCost.setScale(2, RoundingMode.HALF_UP); @@ -79,7 +81,7 @@ public class DestuffingPalletCostService { private OverseasFeeSource fee(long costId, String bookingId, String containerNo, Long warehouseId, CustomerVolume volume, BigDecimal amount, String operator) { LocalDateTime now=LocalDateTime.now(ZoneOffset.UTC); OverseasFeeSource v=new OverseasFeeSource(); v.setId(IdWorker.getId()); v.setSourceFeeId(IdWorker.getId()); v.setSourceVersion(1); v.setCustomerId(volume.customerId()); - v.setSourceType("OVERSEAS_CONTAINER_FEE"); v.setSourceNo("DESTUFF-"+bookingId); v.setBusinessType("CONTAINER_DESTUFFING"); + v.setSourceType("OVERSEAS_CONTAINER_FEE"); v.setSourceNo("DESTUFF-"+bookingId); v.setBusinessType(DESTUFF_BUSINESS_TYPE); v.setBusinessId(costId); v.setBusinessNo("DESTUFF-"+bookingId); v.setContainerBookingId(parseLong(bookingId)); v.setContainerNo(containerNo); v.setWarehouseId(warehouseId); v.setFeeItemCode("DESTUFF_PALLET"); v.setFeeItemName("拆柜打托费"); v.setDirection("RECEIVABLE"); v.setPaymentVoucherFileIds(List.of()); v.setBusinessDate(LocalDate.now()); v.setCurrency("CNY"); v.setAmount(amount); diff --git a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostServiceTest.java b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostServiceTest.java index 11eceedb6..1d6ae96db 100644 --- a/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostServiceTest.java +++ b/lastleg-service/src/test/java/com/baiyue/lastleg/inboundplan/service/DestuffingPalletCostServiceTest.java @@ -7,11 +7,19 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; class DestuffingPalletCostServiceTest { + @Test void publishesDestuffingReceivableAsOverseasContainerBusiness() { + assertThat(DestuffingPalletCostService.DESTUFF_BUSINESS_TYPE).isEqualTo("OVERSEAS_CONTAINER"); + } @Test void newestCompletedBookingsAreNotStarvedByOldUnbillableRows() { assertThat(DestuffingPalletCostService.READY_BOOKINGS_SQL) .contains("ORDER BY MAX(t.completed_at) DESC LIMIT 50") .doesNotContain("ORDER BY MIN(t.completed_at)"); } + @Test void chargesOnlyPalletsWithPositiveReceivedPackagesAndVolume() { + assertThat(DestuffingPalletCostService.RECEIVED_PALLET_IDS_SQL) + .contains("pp.actual_package_count>0") + .contains("pp.actual_volume_cbm>0"); + } @Test void preservesTotalAndLetsFinalCustomerAbsorbRoundingRemainder() { List result = DestuffingPalletCostService.allocate(new BigDecimal("10.00"), List.of(new BigDecimal("1"), new BigDecimal("1"), new BigDecimal("1"))); diff --git a/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost-contract.test.mjs b/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost-contract.test.mjs index e774e5218..c88834fbb 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost-contract.test.mjs @@ -51,7 +51,7 @@ test('托盘汇总和拆柜成本验证总数、分摊及跨区金额守恒', () '/api/platform/v1/pallets/summary', '/api/platform/v1/warehouse-admin/pallets/summary', '/pallets/summary?page=1&size=20', - 'CONTAINER_DESTUFFING', + 'OVERSEAS_CONTAINER', 'DESTUFF_PALLET', 'expectedPalletCost', 'sourceFeeId', diff --git a/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost.mjs b/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost.mjs index f5b32e584..ab094501e 100644 --- a/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost.mjs +++ b/scripts/admin-test-env-modules/phase4-overseas-wallet-and-pallet-cost.mjs @@ -283,7 +283,7 @@ async function verifyDestuffCosts(context, completedFacts) { for (const fact of completedFacts) { const businessNo = `DESTUFF-${fact.containerBookingId}` const lastleg = await poll(async () => context.requestOverseas('GET', query('/api/platform/v1/last-leg/finance-fee-sources', { - businessType: 'CONTAINER_DESTUFFING', businessNo, + businessType: 'OVERSEAS_CONTAINER', businessNo, })), (response) => success(response) && rows(response.body.data).length > 0, 75) const sources = success(lastleg.result) ? rows(lastleg.result.body.data) : [] const allocated = money(sources.reduce((sum, item) => sum + Number(item.amount || 0), 0))