fix(overseas): restore dispatch delivery returns
This commit is contained in:
+45
@@ -14,6 +14,14 @@ import com.baiyue.lastleg.delivery.entity.OutboundPdaPick;
|
|||||||
import com.baiyue.lastleg.delivery.mapper.OutboundOrderMapper;
|
import com.baiyue.lastleg.delivery.mapper.OutboundOrderMapper;
|
||||||
import com.baiyue.lastleg.delivery.mapper.OutboundPalletItemMapper;
|
import com.baiyue.lastleg.delivery.mapper.OutboundPalletItemMapper;
|
||||||
import com.baiyue.lastleg.delivery.mapper.OutboundPdaPickMapper;
|
import com.baiyue.lastleg.delivery.mapper.OutboundPdaPickMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.entity.DispatchItem;
|
||||||
|
import com.baiyue.lastleg.dispatch.mapper.DispatchItemMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaPick;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaPickOrder;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaTask;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaPickMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaPickOrderMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaTaskMapper;
|
||||||
import com.baiyue.lastleg.delivery.model.DeliveryStatusPolicy;
|
import com.baiyue.lastleg.delivery.model.DeliveryStatusPolicy;
|
||||||
import com.baiyue.lastleg.entity.LastlegInboundOrder;
|
import com.baiyue.lastleg.entity.LastlegInboundOrder;
|
||||||
import com.baiyue.lastleg.entity.LastlegOrder;
|
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||||
@@ -66,6 +74,10 @@ public class ExceptionOrderService {
|
|||||||
@Resource private OutboundOrderMapper outboundOrders;
|
@Resource private OutboundOrderMapper outboundOrders;
|
||||||
@Resource private OutboundPalletItemMapper outboundPalletItems;
|
@Resource private OutboundPalletItemMapper outboundPalletItems;
|
||||||
@Resource private OutboundPdaPickMapper outboundPdaPicks;
|
@Resource private OutboundPdaPickMapper outboundPdaPicks;
|
||||||
|
@Resource private DispatchItemMapper dispatchItems;
|
||||||
|
@Resource private DispatchPdaTaskMapper dispatchPdaTasks;
|
||||||
|
@Resource private DispatchPdaPickMapper dispatchPdaPicks;
|
||||||
|
@Resource private DispatchPdaPickOrderMapper dispatchPdaPickOrders;
|
||||||
@Resource private ExceptionReturnItemMapper returnItems;
|
@Resource private ExceptionReturnItemMapper returnItems;
|
||||||
@Resource private ExceptionOrderCommandMapper commands;
|
@Resource private ExceptionOrderCommandMapper commands;
|
||||||
@Resource private InventoryCommandService inventoryCommands;
|
@Resource private InventoryCommandService inventoryCommands;
|
||||||
@@ -424,9 +436,42 @@ public class ExceptionOrderService {
|
|||||||
item.getSkuCode(), item.getInboundCode(), Objects.requireNonNullElse(pick.getPackages(), 0L)),
|
item.getSkuCode(), item.getInboundCode(), Objects.requireNonNullElse(pick.getPackages(), 0L)),
|
||||||
(left, right) -> left.withPackages(left.maxReturnPackages() + right.maxReturnPackages()));
|
(left, right) -> left.withPackages(left.maxReturnPackages() + right.maxReturnPackages()));
|
||||||
}
|
}
|
||||||
|
appendDispatchPickSources(delivery, outbound, itemById, aggregated);
|
||||||
return new ArrayList<>(aggregated.values());
|
return new ArrayList<>(aggregated.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void appendDispatchPickSources(DeliveryOrder delivery, OutboundOrder outbound,
|
||||||
|
Map<Long, OutboundPalletItem> outboundItemById,
|
||||||
|
Map<String, Source> aggregated) {
|
||||||
|
if (outbound.getDispatchOrderId() == null) return;
|
||||||
|
String customerId = delivery.getCustomerId();
|
||||||
|
DispatchPdaTask task = dispatchPdaTasks.findByDispatch(customerId, outbound.getDispatchOrderId());
|
||||||
|
if (task == null) return;
|
||||||
|
Map<Long, DispatchItem> dispatchItemById = new LinkedHashMap<>();
|
||||||
|
dispatchItems.findByDispatchOrder(customerId, outbound.getDispatchOrderId())
|
||||||
|
.forEach(item -> dispatchItemById.put(item.getId(), item));
|
||||||
|
for (DispatchPdaPick pick : dispatchPdaPicks.listTask(customerId, task.getId())) {
|
||||||
|
DispatchItem dispatchItem = dispatchItemById.get(pick.getDispatchItemId());
|
||||||
|
if (dispatchItem == null || pick.getInventoryPositionId() == null) continue;
|
||||||
|
List<DispatchPdaPickOrder> attributions = dispatchPdaPickOrders.listPick(customerId, pick.getId());
|
||||||
|
for (DispatchPdaPickOrder attribution : attributions) {
|
||||||
|
LastlegOrder tail = lastMileOrders.selectById(attribution.getOrderId());
|
||||||
|
if (tail == null || !customerId.equals(tail.getCustomerId())) continue;
|
||||||
|
OutboundPalletItem outboundItem = outboundItemById.values().stream()
|
||||||
|
.filter(item -> Objects.equals(item.getWarehousePalletId(), dispatchItem.getWarehousePalletId()))
|
||||||
|
.filter(item -> !StringUtils.hasText(item.getInboundCode())
|
||||||
|
|| Objects.equals(item.getInboundCode(), tail.getInboundCode()))
|
||||||
|
.findFirst().orElse(null);
|
||||||
|
if (outboundItem == null) continue;
|
||||||
|
String key = outboundItem.getId() + ":" + pick.getInventoryPositionId() + ":" + tail.getInboundCode();
|
||||||
|
long packages = Objects.requireNonNullElse(attribution.getPackages(), 0L);
|
||||||
|
aggregated.merge(key, new Source(outboundItem.getId(), pick.getInventoryPositionId(),
|
||||||
|
outboundItem.getPalletNo(), outboundItem.getSkuCode(), tail.getInboundCode(), packages),
|
||||||
|
(left, right) -> left.withPackages(left.maxReturnPackages() + right.maxReturnPackages()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<ExceptionOrderResponses.ReturnSource> storedReturnSources(ExceptionOrder order) {
|
private List<ExceptionOrderResponses.ReturnSource> storedReturnSources(ExceptionOrder order) {
|
||||||
return returnItems.findByException(order.getCustomerId(), order.getId()).stream()
|
return returnItems.findByException(order.getCustomerId(), order.getId()).stream()
|
||||||
.map(item -> new ExceptionOrderResponses.ReturnSource(item.getOutboundPalletItemId(),
|
.map(item -> new ExceptionOrderResponses.ReturnSource(item.getOutboundPalletItemId(),
|
||||||
|
|||||||
+113
@@ -0,0 +1,113 @@
|
|||||||
|
package com.baiyue.lastleg.exceptionorder.service;
|
||||||
|
|
||||||
|
import com.baiyue.lastleg.delivery.entity.DeliveryOrder;
|
||||||
|
import com.baiyue.lastleg.delivery.entity.OutboundOrder;
|
||||||
|
import com.baiyue.lastleg.delivery.entity.OutboundPalletItem;
|
||||||
|
import com.baiyue.lastleg.delivery.mapper.DeliveryOrderMapper;
|
||||||
|
import com.baiyue.lastleg.delivery.mapper.OutboundOrderMapper;
|
||||||
|
import com.baiyue.lastleg.delivery.mapper.OutboundPalletItemMapper;
|
||||||
|
import com.baiyue.lastleg.delivery.mapper.OutboundPdaPickMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.entity.DispatchItem;
|
||||||
|
import com.baiyue.lastleg.dispatch.mapper.DispatchItemMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaPick;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaPickOrder;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.entity.DispatchPdaTask;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaPickMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaPickOrderMapper;
|
||||||
|
import com.baiyue.lastleg.dispatch.pda.mapper.DispatchPdaTaskMapper;
|
||||||
|
import com.baiyue.lastleg.entity.LastlegInboundOrder;
|
||||||
|
import com.baiyue.lastleg.entity.LastlegOrder;
|
||||||
|
import com.baiyue.lastleg.mapper.LastlegInboundOrderMapper;
|
||||||
|
import com.baiyue.lastleg.mapper.LastlegOrderMapper;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class ExceptionOrderDispatchReturnSourceTest {
|
||||||
|
@Mock private DeliveryOrderMapper deliveryOrders;
|
||||||
|
@Mock private OutboundOrderMapper outboundOrders;
|
||||||
|
@Mock private OutboundPalletItemMapper outboundPalletItems;
|
||||||
|
@Mock private OutboundPdaPickMapper outboundPdaPicks;
|
||||||
|
@Mock private DispatchItemMapper dispatchItems;
|
||||||
|
@Mock private DispatchPdaTaskMapper dispatchPdaTasks;
|
||||||
|
@Mock private DispatchPdaPickMapper dispatchPdaPicks;
|
||||||
|
@Mock private DispatchPdaPickOrderMapper dispatchPdaPickOrders;
|
||||||
|
@Mock private LastlegOrderMapper lastMileOrders;
|
||||||
|
@Mock private LastlegInboundOrderMapper inboundOrders;
|
||||||
|
private ExceptionOrderService service;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void setUp() {
|
||||||
|
service = new ExceptionOrderService();
|
||||||
|
ReflectionTestUtils.setField(service, "deliveryOrders", deliveryOrders);
|
||||||
|
ReflectionTestUtils.setField(service, "outboundOrders", outboundOrders);
|
||||||
|
ReflectionTestUtils.setField(service, "outboundPalletItems", outboundPalletItems);
|
||||||
|
ReflectionTestUtils.setField(service, "outboundPdaPicks", outboundPdaPicks);
|
||||||
|
ReflectionTestUtils.setField(service, "dispatchItems", dispatchItems);
|
||||||
|
ReflectionTestUtils.setField(service, "dispatchPdaTasks", dispatchPdaTasks);
|
||||||
|
ReflectionTestUtils.setField(service, "dispatchPdaPicks", dispatchPdaPicks);
|
||||||
|
ReflectionTestUtils.setField(service, "dispatchPdaPickOrders", dispatchPdaPickOrders);
|
||||||
|
ReflectionTestUtils.setField(service, "lastMileOrders", lastMileOrders);
|
||||||
|
ReflectionTestUtils.setField(service, "inboundOrders", inboundOrders);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void returnsTheAttributedInventorySourceForDispatchDeliveryPicks() {
|
||||||
|
DeliveryOrder delivery = new DeliveryOrder();
|
||||||
|
delivery.setId(400L); delivery.setCustomerId("1001"); delivery.setDeliveryNo("DL-1");
|
||||||
|
delivery.setOutboundNo("OUT-1"); delivery.setStatus("DELIVERED");
|
||||||
|
OutboundOrder outbound = new OutboundOrder();
|
||||||
|
outbound.setId(300L); outbound.setCustomerId("1001"); outbound.setOutboundNo("OUT-1");
|
||||||
|
outbound.setDispatchOrderId(200L);
|
||||||
|
OutboundPalletItem outboundItem = new OutboundPalletItem();
|
||||||
|
outboundItem.setId(301L); outboundItem.setCustomerId("1001"); outboundItem.setOutboundOrderId(300L);
|
||||||
|
outboundItem.setWarehousePalletId(501L); outboundItem.setPalletNo("PLT-1"); outboundItem.setInboundCode("IN-1");
|
||||||
|
DispatchPdaTask task = new DispatchPdaTask();
|
||||||
|
task.setId(201L); task.setCustomerId("1001"); task.setDispatchOrderId(200L);
|
||||||
|
DispatchItem dispatchItem = new DispatchItem();
|
||||||
|
dispatchItem.setId(202L); dispatchItem.setCustomerId("1001"); dispatchItem.setDispatchOrderId(200L);
|
||||||
|
dispatchItem.setWarehousePalletId(501L);
|
||||||
|
DispatchPdaPick pick = new DispatchPdaPick();
|
||||||
|
pick.setId(203L); pick.setCustomerId("1001"); pick.setTaskId(201L);
|
||||||
|
pick.setDispatchItemId(202L); pick.setInventoryPositionId(601L); pick.setPackages(2L);
|
||||||
|
DispatchPdaPickOrder attribution = new DispatchPdaPickOrder();
|
||||||
|
attribution.setId(204L); attribution.setCustomerId("1001"); attribution.setPickId(203L);
|
||||||
|
attribution.setOrderId(101L); attribution.setPackages(2L);
|
||||||
|
LastlegOrder tail = new LastlegOrder();
|
||||||
|
tail.setId(101L); tail.setCustomerId("1001"); tail.setOrderNo("LM-1"); tail.setInboundCode("IN-1");
|
||||||
|
tail.setDeliveryOrderNo("DL-1"); tail.setOutboundOrderNo("OUT-1"); tail.setPackageCount(2);
|
||||||
|
LastlegInboundOrder inbound = new LastlegInboundOrder();
|
||||||
|
inbound.setId(102L); inbound.setCustomerId("1001"); inbound.setInboundCode("IN-1"); inbound.setPackageCount(2);
|
||||||
|
|
||||||
|
when(deliveryOrders.findByNo("1001", "DL-1")).thenReturn(delivery);
|
||||||
|
when(lastMileOrders.selectList(any())).thenReturn(List.of(tail));
|
||||||
|
when(inboundOrders.selectList(any())).thenReturn(List.of(inbound));
|
||||||
|
when(outboundOrders.findByNo("1001", "OUT-1")).thenReturn(outbound);
|
||||||
|
when(outboundPalletItems.findByOutbound("1001", 300L)).thenReturn(List.of(outboundItem));
|
||||||
|
when(outboundPdaPicks.findByOutbound("1001", 300L)).thenReturn(List.of());
|
||||||
|
when(dispatchPdaTasks.findByDispatch("1001", 200L)).thenReturn(task);
|
||||||
|
when(dispatchItems.findByDispatchOrder("1001", 200L)).thenReturn(List.of(dispatchItem));
|
||||||
|
when(dispatchPdaPicks.listTask("1001", 201L)).thenReturn(List.of(pick));
|
||||||
|
when(dispatchPdaPickOrders.listPick("1001", 203L)).thenReturn(List.of(attribution));
|
||||||
|
when(lastMileOrders.selectById(101L)).thenReturn(tail);
|
||||||
|
|
||||||
|
var candidates = service.returnCandidates("1001", "DL-1");
|
||||||
|
|
||||||
|
assertThat(candidates).hasSize(1);
|
||||||
|
assertThat(candidates.get(0).returnSources()).singleElement().satisfies(source -> {
|
||||||
|
assertThat(source.outboundPalletItemId()).isEqualTo(301L);
|
||||||
|
assertThat(source.inventoryPositionId()).isEqualTo(601L);
|
||||||
|
assertThat(source.maxReturnPackages()).isEqualTo(2L);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -50,7 +50,8 @@ test('P2 to P5 cover all frozen endpoints, replays and conflict boundaries witho
|
|||||||
'chargeAmount: 0.01', 'cartonMark:', 'fbaCartonMark:',
|
'chargeAmount: 0.01', 'cartonMark:', 'fbaCartonMark:',
|
||||||
'inventory-candidates', 'sourceInventoryBalanceId:', 'sourceInventoryPositionId:',
|
'inventory-candidates', 'sourceInventoryBalanceId:', 'sourceInventoryPositionId:',
|
||||||
'sourceInboundOrderId:', 'operationWarehouseId:', 'relabelItemForUnit', 'sourceLines.map',
|
'sourceInboundOrderId:', 'operationWarehouseId:', 'relabelItemForUnit', 'sourceLines.map',
|
||||||
'const packageCount = 1', 'oldLabel: item.cartonMark',
|
'const packageCount = 1', 'line.packageCount += additionalPackages', 'oldLabel: item.cartonMark',
|
||||||
|
'id(candidate.itemId) !== id(item.itemId)', 'oldLabel: null',
|
||||||
'newLabel: item.fbaCartonMark || item.newSkuCode',
|
'newLabel: item.fbaCartonMark || item.newSkuCode',
|
||||||
"operationType: changesCartonLabel ? 'CARTON_AND_SKU' : 'SKU_LABEL'", 'skuItems:',
|
"operationType: changesCartonLabel ? 'CARTON_AND_SKU' : 'SKU_LABEL'", 'skuItems:',
|
||||||
'P5换标费用冻结后开放PDA作业', 'P5换标完成后钱包结算收敛']) {
|
'P5换标费用冻结后开放PDA作业', 'P5换标完成后钱包结算收敛']) {
|
||||||
|
|||||||
@@ -371,6 +371,14 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|||||||
}
|
}
|
||||||
if (requiredPackages === 0) break
|
if (requiredPackages === 0) break
|
||||||
}
|
}
|
||||||
|
for (const line of sourceLines) {
|
||||||
|
if (requiredPackages === 0) break
|
||||||
|
const remainingCapacity = Number(line.position.availablePackages) - line.packageCount
|
||||||
|
if (remainingCapacity <= 0) continue
|
||||||
|
const additionalPackages = Math.min(requiredPackages, remainingCapacity)
|
||||||
|
line.packageCount += additionalPackages
|
||||||
|
requiredPackages -= additionalPackages
|
||||||
|
}
|
||||||
context.addStep(MODULE, 'P5客户库存候选返回换标权威来源', inventoryCandidatesResponse,
|
context.addStep(MODULE, 'P5客户库存候选返回换标权威来源', inventoryCandidatesResponse,
|
||||||
requiredPackages === 0,
|
requiredPackages === 0,
|
||||||
`sourceLines=${sourceLines.length}, selectedPackages=${3 - requiredPackages}; ${trace(inventoryCandidatesResponse)}`, OPTIONS)
|
`sourceLines=${sourceLines.length}, selectedPackages=${3 - requiredPackages}; ${trace(inventoryCandidatesResponse)}`, OPTIONS)
|
||||||
@@ -456,7 +464,8 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|||||||
await expectConflict(context, token, 'POST', 'P5正常换标旧版本A0409', unitPath,
|
await expectConflict(context, token, 'POST', 'P5正常换标旧版本A0409', unitPath,
|
||||||
{ ...scanBody, oldLabel: `${context.marker}-OLD-STALE`, idempotencyKey: key(context, 'RELABEL-STALE') })
|
{ ...scanBody, oldLabel: `${context.marker}-OLD-STALE`, idempotencyKey: key(context, 'RELABEL-STALE') })
|
||||||
detail = scanned.data
|
detail = scanned.data
|
||||||
const skipItem = relabelItemForUnit(detail, 1)
|
const skipItem = Array.isArray(detail?.items)
|
||||||
|
? detail.items.find((candidate) => id(candidate?.itemId) && id(candidate.itemId) !== id(item.itemId)) : null
|
||||||
const skipBody = { itemId: id(skipItem?.itemId), oldLabel: skipItem?.cartonMark,
|
const skipBody = { itemId: id(skipItem?.itemId), oldLabel: skipItem?.cartonMark,
|
||||||
reason: `${context.marker}-skip`, expectedVersion: Number(detail.version),
|
reason: `${context.marker}-skip`, expectedVersion: Number(detail.version),
|
||||||
idempotencyKey: key(context, 'RELABEL-SKIP') }
|
idempotencyKey: key(context, 'RELABEL-SKIP') }
|
||||||
@@ -465,8 +474,8 @@ async function verifyRelabel(context, token, merchantToken, fixture) {
|
|||||||
context.assert(MODULE, 'P5跳过换标重放不重复计数', skipReplay.response,
|
context.assert(MODULE, 'P5跳过换标重放不重复计数', skipReplay.response,
|
||||||
Number(skipReplay.data?.skippedPackages) === 1, trace(skipReplay.response), OPTIONS)
|
Number(skipReplay.data?.skippedPackages) === 1, trace(skipReplay.response), OPTIONS)
|
||||||
detail = skipped.data
|
detail = skipped.data
|
||||||
const exceptionItem = relabelItemForUnit(detail, 2)
|
const exceptionItem = item
|
||||||
const exceptionBody = { itemId: id(exceptionItem?.itemId), oldLabel: exceptionItem?.cartonMark,
|
const exceptionBody = { itemId: id(exceptionItem?.itemId), oldLabel: null,
|
||||||
exceptionCode: 'DAMAGED', reason: `${context.marker}-damaged`, expectedVersion: Number(detail.version),
|
exceptionCode: 'DAMAGED', reason: `${context.marker}-damaged`, expectedVersion: Number(detail.version),
|
||||||
idempotencyKey: key(context, 'RELABEL-EXCEPTION') }
|
idempotencyKey: key(context, 'RELABEL-EXCEPTION') }
|
||||||
const exception = await request(context, token, 'POST', 'P5登记换标异常一箱',
|
const exception = await request(context, token, 'POST', 'P5登记换标异常一箱',
|
||||||
|
|||||||
Reference in New Issue
Block a user