fix(prd): reverse 102/262 with original posting date and restore S after 261
Cross-month corrections must reverse on the old BUDAT and post the new date; posted consume repost now writes status S after both SAP calls succeed. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
6da7c08e91
commit
069d766d1f
@ -99,7 +99,7 @@ public class ZLotMaterialCompleteCorrectionServiceImpl implements ZLotMaterialCo
|
||||
JSONObject sapRequest = new JSONObject();
|
||||
sapRequest.put("transaction", source);
|
||||
sapRequest.put("reverseSid", reverseSid);
|
||||
sapRequest.put("postingDate", request.getPostingDate().toString());
|
||||
sapRequest.put("postingDate", requireOriginalPostingDate(source.getSyncPostingDate()).toString());
|
||||
JSONObject response = remoteInvokeService.reverseComplete102(sapRequest, SecurityConstants.FROM_IN);
|
||||
if (response == null || !"S".equals(response.getString("RESULT"))) {
|
||||
return result(source.getSid(), "SAP", "FAILED",
|
||||
@ -197,7 +197,7 @@ public class ZLotMaterialCompleteCorrectionServiceImpl implements ZLotMaterialCo
|
||||
JSONObject sapRequest = new JSONObject();
|
||||
sapRequest.put("transaction", source);
|
||||
sapRequest.put("reverseSid", reverseSid);
|
||||
sapRequest.put("postingDate", request.getPostingDate().toString());
|
||||
sapRequest.put("postingDate", requireOriginalPostingDate(source.getSyncPostingDate()).toString());
|
||||
JSONObject response = remoteInvokeService.reverseComplete102(sapRequest, SecurityConstants.FROM_IN);
|
||||
if (response == null || !"S".equals(response.getString("RESULT"))) {
|
||||
throw BusinessException.build(response == null ? "SAP未返回结果" : response.getString("MESSAGE"));
|
||||
@ -487,6 +487,13 @@ public class ZLotMaterialCompleteCorrectionServiceImpl implements ZLotMaterialCo
|
||||
return current != null && next != null && !current.equals(next);
|
||||
}
|
||||
|
||||
private LocalDate requireOriginalPostingDate(LocalDate original) {
|
||||
if (original == null) {
|
||||
throw BusinessException.build("原过账日期为空,不能反冲");
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
private boolean zeroQty(CompleteCorrectionRequestDTO request) {
|
||||
return request.getMainQty() != null && request.getMainQty().compareTo(BigDecimal.ZERO) == 0;
|
||||
}
|
||||
|
||||
@ -128,9 +128,10 @@ public class ZLotMaterialConsumptionRepostServiceImpl implements ZLotMaterialCon
|
||||
if (newQty.compareTo(BigDecimal.ZERO) < 0) {
|
||||
throw BusinessException.build("目标数量不能小于0");
|
||||
}
|
||||
LocalDate reverseDate = requireOriginalPostingDate(source.getSyncPostingDate());
|
||||
String cancelSid = StrUtil.uuid();
|
||||
call262(source, oldQty, request.getPostingDate(), cancelSid);
|
||||
saveCancel(source, cancelSid, oldQty, request.getPostingDate());
|
||||
call262(source, oldQty, reverseDate, cancelSid);
|
||||
saveCancel(source, cancelSid, oldQty, reverseDate);
|
||||
if (oldQty.compareTo(BigDecimal.ZERO) > 0) {
|
||||
adjustWms(source, oldQty, BigDecimal.ZERO, source.getSid() + ":rp262:" + oldQty.toPlainString(),
|
||||
source.getShopOrder() + "已过账消耗262冲销退回线边库", "MATERIAL_CONSUMPTION_REPOST_262");
|
||||
@ -142,7 +143,7 @@ public class ZLotMaterialConsumptionRepostServiceImpl implements ZLotMaterialCon
|
||||
adjustWms(source, newQty.negate(), newQty, source.getSid() + ":rp261:" + newQty.toPlainString(),
|
||||
source.getShopOrder() + "已过账消耗按新数量261", "MATERIAL_CONSUMPTION_REPOST_261");
|
||||
call261(source, newQty, request.getPostingDate(), source.getReservedNo(), source.getReservedLine());
|
||||
persistQty(source.getSid(), newQty);
|
||||
persistPosted(source.getSid(), newQty, request.getPostingDate());
|
||||
}
|
||||
|
||||
private void persistUnposted(String transactionSid, BigDecimal newQty) {
|
||||
@ -229,13 +230,14 @@ public class ZLotMaterialConsumptionRepostServiceImpl implements ZLotMaterialCon
|
||||
if (StrUtil.isBlank(source.getReservedNo()) || StrUtil.isBlank(source.getReservedLine())) {
|
||||
throw BusinessException.build("原消耗缺少预留号或预留行号,不能冲销262");
|
||||
}
|
||||
LocalDate reverseDate = requireOriginalPostingDate(source.getSyncPostingDate());
|
||||
String cancelSid = StrUtil.uuid();
|
||||
call262(source, request.getTransferQty(), request.getPostingDate(), cancelSid);
|
||||
saveCancel(source, cancelSid, request.getTransferQty(), request.getPostingDate());
|
||||
call262(source, request.getTransferQty(), reverseDate, cancelSid);
|
||||
saveCancel(source, cancelSid, request.getTransferQty(), reverseDate);
|
||||
closeOrReduce(source, remainQty);
|
||||
ZLotMaterialConsumptionTransaction target = createTarget(source, targetOrder, reserved, request.getTransferQty());
|
||||
call261(target, request.getTransferQty(), request.getPostingDate(), reserved.getReserved(), reserved.getReservedSeq());
|
||||
persistQty(target.getSid(), request.getTransferQty());
|
||||
persistPosted(target.getSid(), request.getTransferQty(), request.getPostingDate());
|
||||
return;
|
||||
}
|
||||
closeOrReduce(source, remainQty);
|
||||
@ -418,11 +420,26 @@ public class ZLotMaterialConsumptionRepostServiceImpl implements ZLotMaterialCon
|
||||
}
|
||||
|
||||
private void persistQty(String transactionSid, BigDecimal newQty) {
|
||||
persistQty(transactionSid, newQty, null, null);
|
||||
}
|
||||
|
||||
private void persistPosted(String transactionSid, BigDecimal newQty, LocalDate postingDate) {
|
||||
persistQty(transactionSid, newQty, "S", postingDate);
|
||||
}
|
||||
|
||||
private void persistQty(String transactionSid, BigDecimal newQty, String syncStatus, LocalDate postingDate) {
|
||||
ZLotMaterialConsumptionTransaction latest = consumptionService.getById(transactionSid);
|
||||
if (latest == null) {
|
||||
throw BusinessException.build("物料消耗事务不存在");
|
||||
}
|
||||
latest.setMainQty(newQty);
|
||||
if (syncStatus != null) {
|
||||
latest.setSyncStatus(syncStatus);
|
||||
latest.setErpFlag("Y");
|
||||
}
|
||||
if (postingDate != null) {
|
||||
latest.setSyncPostingDate(postingDate);
|
||||
}
|
||||
latest.setUpdater(currentUser());
|
||||
latest.setDateUpdate(LocalDateTime.now());
|
||||
consumptionService.updateById(latest);
|
||||
@ -435,9 +452,21 @@ public class ZLotMaterialConsumptionRepostServiceImpl implements ZLotMaterialCon
|
||||
}
|
||||
lotMaterial.setConsumeQty(newQty);
|
||||
lotMaterial.setQty(newQty);
|
||||
if ("S".equals(syncStatus)) {
|
||||
lotMaterial.setErpFlag("Y");
|
||||
lotMaterial.setRemoved(null);
|
||||
lotMaterial.setRemovedDate(null);
|
||||
}
|
||||
lotMaterialService.updateById(lotMaterial);
|
||||
}
|
||||
|
||||
private LocalDate requireOriginalPostingDate(LocalDate original) {
|
||||
if (original == null) {
|
||||
throw BusinessException.build("原过账日期为空,不能反冲");
|
||||
}
|
||||
return original;
|
||||
}
|
||||
|
||||
private void adjustWms(ZLotMaterialConsumptionTransaction source, BigDecimal delta, BigDecimal newQty,
|
||||
String requestId, String comments, String changeType) {
|
||||
LotMaterial lotMaterial = source.getLotMaterialSid() == null
|
||||
|
||||
@ -149,6 +149,7 @@ class ZLotMaterialCompleteCorrectionServiceImplTest {
|
||||
ZLotMaterialCompleteTransaction source = source();
|
||||
source.setSite("2031");
|
||||
source.setSyncStatus("S");
|
||||
source.setSyncPostingDate(LocalDate.of(2026, 8, 1));
|
||||
when(completeService.getById("sid-1")).thenReturn(source);
|
||||
Inventory inventory = new Inventory();
|
||||
inventory.setHandle("InventoryBO:2031,3601180036A005");
|
||||
@ -167,13 +168,53 @@ class ZLotMaterialCompleteCorrectionServiceImplTest {
|
||||
request.setPostingDate(LocalDate.of(2026, 8, 28));
|
||||
service.correct(request);
|
||||
|
||||
verify(remoteInvokeService).reverseComplete102(any(), anyString());
|
||||
ArgumentCaptor<JSONObject> reverseCaptor = ArgumentCaptor.forClass(JSONObject.class);
|
||||
verify(remoteInvokeService).reverseComplete102(reverseCaptor.capture(), anyString());
|
||||
assertEquals("2026-08-01", reverseCaptor.getValue().getString("postingDate"));
|
||||
verify(remoteInvokeService, never()).repostComplete101(any(), anyString());
|
||||
assertEquals(0, BigDecimal.ZERO.compareTo(source.getMainQty()));
|
||||
assertEquals("Y", source.getErpReverse());
|
||||
assertEquals("C", source.getSyncStatus());
|
||||
}
|
||||
|
||||
@Test
|
||||
void syncedPostingDateChangeReversesWithOldDateThenRepostsNew() {
|
||||
ZLotMaterialCompleteTransactionService completeService = mock(ZLotMaterialCompleteTransactionService.class);
|
||||
RemoteInvokeService remoteInvokeService = mock(RemoteInvokeService.class);
|
||||
RemoteWmsInventoryService remoteWmsInventoryService = mock(RemoteWmsInventoryService.class);
|
||||
ZLotMaterialCompleteCorrectionServiceImpl service = new ZLotMaterialCompleteCorrectionServiceImpl(
|
||||
completeService,
|
||||
mock(ZLotMaterialCompleteTransactionOffService.class),
|
||||
remoteInvokeService,
|
||||
remoteWmsInventoryService,
|
||||
mock(ItemService.class),
|
||||
mock(NlvLotStartService.class),
|
||||
mock(RemoteGetSapBatchNoService.class),
|
||||
mock(ShopOrderService.class),
|
||||
mock(LotBasisService.class));
|
||||
|
||||
ZLotMaterialCompleteTransaction source = source();
|
||||
source.setSite("2031");
|
||||
source.setSyncStatus("S");
|
||||
source.setSyncPostingDate(LocalDate.of(2026, 8, 1));
|
||||
when(completeService.getById("sid-1")).thenReturn(source);
|
||||
JSONObject sapOk = new JSONObject();
|
||||
sapOk.put("RESULT", "S");
|
||||
when(remoteInvokeService.reverseComplete102(any(), anyString())).thenReturn(sapOk);
|
||||
when(remoteInvokeService.repostComplete101(any(), anyString())).thenReturn(sapOk);
|
||||
|
||||
CompleteCorrectionRequestDTO request = requestFrom(source);
|
||||
request.setPostingDate(LocalDate.of(2026, 9, 1));
|
||||
request.setForceSap(true);
|
||||
service.correct(request);
|
||||
|
||||
ArgumentCaptor<JSONObject> reverseCaptor = ArgumentCaptor.forClass(JSONObject.class);
|
||||
verify(remoteInvokeService).reverseComplete102(reverseCaptor.capture(), anyString());
|
||||
assertEquals("2026-08-01", reverseCaptor.getValue().getString("postingDate"));
|
||||
verify(remoteInvokeService).repostComplete101(any(), anyString());
|
||||
assertEquals(LocalDate.of(2026, 9, 1), source.getSyncPostingDate());
|
||||
}
|
||||
|
||||
@Test
|
||||
void zeroQtyOnUnpostedRowSkipsSap101() {
|
||||
ZLotMaterialCompleteTransactionService completeService = mock(ZLotMaterialCompleteTransactionService.class);
|
||||
|
||||
@ -2,6 +2,7 @@ package com.foreverwin.iep.prd.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.foreverwin.iep.integration.domain.MaterialQtySapRequest;
|
||||
import com.foreverwin.iep.integration.feign.RemoteInvokeService;
|
||||
import com.foreverwin.iep.prd.dto.ConsumptionCancelRequestDTO;
|
||||
import com.foreverwin.iep.prd.dto.ConsumptionRepostRequestDTO;
|
||||
@ -137,6 +138,7 @@ class ZLotMaterialConsumptionRepostServiceImplTest {
|
||||
ZLotMaterialConsumptionTransaction source = unposted("sid-s", "S", new BigDecimal("55"));
|
||||
source.setReservedNo("90001");
|
||||
source.setReservedLine("10");
|
||||
source.setSyncPostingDate(LocalDate.of(2026, 8, 1));
|
||||
when(consumptionService.getById("sid-s")).thenReturn(source);
|
||||
when(qtyAdjustmentService.hasSuccessAdjustment("sid-s")).thenReturn(false);
|
||||
LotMaterial lotMaterial = new LotMaterial();
|
||||
@ -153,12 +155,21 @@ class ZLotMaterialConsumptionRepostServiceImplTest {
|
||||
request.setPostingDate(LocalDate.of(2026, 9, 1));
|
||||
service.repost261(Collections.singletonList(request));
|
||||
|
||||
verify(remoteInvokeService).adjustMaterialQty(any(), anyString());
|
||||
verify(remoteInvokeService).callErpMaterial(any(), anyString());
|
||||
ArgumentCaptor<MaterialQtySapRequest> reverseCaptor = ArgumentCaptor.forClass(MaterialQtySapRequest.class);
|
||||
verify(remoteInvokeService).adjustMaterialQty(reverseCaptor.capture(), anyString());
|
||||
assertEquals("262", reverseCaptor.getValue().getMovementType());
|
||||
assertEquals("2026-08-01", reverseCaptor.getValue().getPostingDate());
|
||||
ArgumentCaptor<JSONArray> sapCaptor = ArgumentCaptor.forClass(JSONArray.class);
|
||||
verify(remoteInvokeService).callErpMaterial(sapCaptor.capture(), anyString());
|
||||
assertEquals("2026-09-01", sapCaptor.getValue().getJSONObject(0).getString("syncPostingDate"));
|
||||
ArgumentCaptor<InventoryQtyAdjustRequest> captor = ArgumentCaptor.forClass(InventoryQtyAdjustRequest.class);
|
||||
verify(remoteWmsInventoryService, org.mockito.Mockito.times(2)).adjustQty(captor.capture());
|
||||
assertEquals(0, new BigDecimal("55").compareTo(captor.getAllValues().get(0).getDeltaQty()));
|
||||
assertEquals(0, new BigDecimal("-50").compareTo(captor.getAllValues().get(1).getDeltaQty()));
|
||||
assertEquals("S", source.getSyncStatus());
|
||||
assertEquals(LocalDate.of(2026, 9, 1), source.getSyncPostingDate());
|
||||
assertEquals(0, new BigDecimal("50").compareTo(source.getMainQty()));
|
||||
assertEquals("Y", lotMaterial.getErpFlag());
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -166,6 +177,7 @@ class ZLotMaterialConsumptionRepostServiceImplTest {
|
||||
ZLotMaterialConsumptionTransaction source = unposted("sid-0", "S", new BigDecimal("55"));
|
||||
source.setReservedNo("90001");
|
||||
source.setReservedLine("10");
|
||||
source.setSyncPostingDate(LocalDate.of(2026, 8, 1));
|
||||
when(consumptionService.getById("sid-0")).thenReturn(source);
|
||||
when(qtyAdjustmentService.hasSuccessAdjustment("sid-0")).thenReturn(false);
|
||||
LotMaterial lotMaterial = new LotMaterial();
|
||||
@ -180,7 +192,9 @@ class ZLotMaterialConsumptionRepostServiceImplTest {
|
||||
request.setPostingDate(LocalDate.of(2026, 9, 1));
|
||||
service.repost261(Collections.singletonList(request));
|
||||
|
||||
verify(remoteInvokeService).adjustMaterialQty(any(), anyString());
|
||||
ArgumentCaptor<MaterialQtySapRequest> reverseCaptor = ArgumentCaptor.forClass(MaterialQtySapRequest.class);
|
||||
verify(remoteInvokeService).adjustMaterialQty(reverseCaptor.capture(), anyString());
|
||||
assertEquals("2026-08-01", reverseCaptor.getValue().getPostingDate());
|
||||
verify(remoteInvokeService, never()).callErpMaterial(any(), anyString());
|
||||
assertEquals("N", source.getSyncStatus());
|
||||
assertEquals(0, BigDecimal.ZERO.compareTo(source.getMainQty()));
|
||||
|
||||
Loading…
Reference in New Issue
Block a user