| | |
| | | wkType: "wkType", |
| | | anfme: "anfme", |
| | | qty: "qty", |
| | | palletQty: "Pallet Qty", |
| | | logisNo: "logisNo", |
| | | arrTime: "Arrived", |
| | | rleStatus: "Release", |
| | |
| | | purUnit: "purUnit", |
| | | unit: 'Unit', |
| | | qty: "qty", |
| | | palletQty: "Pallet Qty", |
| | | safeQty: 'Safe Qty', |
| | | disQty: 'Def Qty', |
| | | splrCode: "splrCode", |
| | |
| | | wkType: "业务类型", |
| | | anfme: "计划数量", |
| | | qty: "完成数量", |
| | | palletQty: "组托数量", |
| | | logisNo: "物流单号", |
| | | arrTime: "预计到达时间", |
| | | rleStatus: "释放状态", |
| | |
| | | purUnit: "采购单位", |
| | | unit: '单位', |
| | | qty: "完成数", |
| | | palletQty: "组托数量", |
| | | safeQty: '合格数', |
| | | disQty: '不合格数', |
| | | splrBatch: "批次", |
| | |
| | | readOnly |
| | | parse={v => v} |
| | | /> |
| | | <TextInput |
| | | label="table.field.asnOrder.palletQty" |
| | | source="palletQty" |
| | | readOnly |
| | | parse={v => v} |
| | | /> |
| | | <DateInput |
| | | label="table.field.asnOrder.arrTime" |
| | | source="arrTime" |
| | |
| | | <TextField source="model" label="table.field.asnOrderItem.model" />, |
| | | <NumberField source="anfme" label="table.field.asnOrderItem.anfme" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} />, |
| | | <NumberField source="qty" label="table.field.asnOrderItem.qty" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} />, |
| | | <NumberField source="palletQty" label="table.field.asnOrderItem.palletQty" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} />, |
| | | <TextField source="stockUnit" label="table.field.asnOrderItem.stockUnit" />, |
| | | <NumberField source="purQty" label="table.field.asnOrderItem.purQty" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} />, |
| | | <TextField source="purUnit" label="table.field.asnOrderItem.purUnit" />, |
| | |
| | | <TextField source="trackCode" label="table.field.asnOrderItem.barcode" />, |
| | | // <TextField source="packName" label="table.field.asnOrderItem.packName" />, // 包装已注释 |
| | | ] |
| | | const fields = data.map(el => <TextField key={el.fields} source={`extendFields.[${el.fields}]`} label={el.fieldsAlise} />) |
| | | const fields = data |
| | | .filter(el => el.fields !== 'crushNo' && el.fieldsAlise !== '现品票号') |
| | | .map(el => <TextField key={el.fields} source={`extendFields.[${el.fields}]`} label={el.fieldsAlise} />) |
| | | const lastArr = [ |
| | | <DateField source="updateTime" label="common.field.updateTime" showTime />, |
| | | <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}> |
| | |
| | | preferenceKey='asnOrderItem' |
| | | bulkActionButtons={false} |
| | | rowClick={(id, resource, record) => false} |
| | | omit={['id', 'orderId', 'orderCode', 'poDetlId', 'poDetlCode', 'matnrId', 'spec', 'model', 'purQty', 'purUnit', 'qrcode', 'trackCode', 'splrCode', 'platWorkCode', 'projectCode', 'platItemId', 'isptResult$', 'packName']} |
| | | omit={['id', 'orderId', 'orderCode', 'poDetlId', 'poDetlCode', 'matnrId', 'spec', 'model', 'purQty', 'purUnit', 'qrcode', 'trackCode', 'splrCode', 'platWorkCode', 'projectCode', 'platItemId', 'fieldsIndex', 'isptResult$', 'packName']} |
| | | > |
| | | {columns.map((column) => column)} |
| | | </StyledDatagrid>} |
| | |
| | | <TextField cellClassName="wkType" source="wkType$" label="table.field.asnOrder.wkType" /> |
| | | <NumberField source="anfme" label="table.field.asnOrder.anfme" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} /> |
| | | <NumberField source="qty" label="table.field.asnOrder.qty" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} /> |
| | | <NumberField source="palletQty" label="table.field.asnOrder.palletQty" options={{ minimumFractionDigits: 2, maximumFractionDigits: 2 }} /> |
| | | <DateField source="arrTime" label="table.field.asnOrder.arrTime" showTime /> |
| | | <TextField source="rleStatus$" label="table.field.asnOrder.rleStatus" sortable={false} /> |
| | | <TextField source="logisNo" label="table.field.asnOrder.logisNo" /> |
| | |
| | | import com.vincent.rsf.server.system.entity.*; |
| | | import com.vincent.rsf.server.system.mapper.FieldsMapper; |
| | | import com.vincent.rsf.server.system.mapper.TenantMapper; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.service.FieldsItemService; |
| | | import com.vincent.rsf.server.system.service.UserLoginService; |
| | |
| | | } |
| | | //TODO /**收货数量累加,1. 会出超收情况 2. 会有收货不足情况*/ |
| | | Double rcptedQty = QuantityUtils.add(wkOrder.getQty(), receiptQty); |
| | | // 新顺序:未执行(组托)→任务执行中→已完成,不再设置执行中/收货完成 |
| | | wkOrder.setQty(rcptedQty); // .setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_EXCE_ING.val) |
| | | wkOrder.setQty(rcptedQty); |
| | | if (AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val.equals(wkOrder.getExceStatus())) { |
| | | wkOrder.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_EXCE_ING.val); |
| | | } |
| | | if (!asnOrderMapper.updateById(wkOrder)) { |
| | | throw new CoolException("已收货数量修改失败!!"); |
| | | } |
| | |
| | | if (asnOrderItemMapper.updateById(orderItem) < 1) { |
| | | throw new CoolException("通知单明细数量修改失败!!"); |
| | | } |
| | | // 收货区已停用,不再保存至收货区 |
| | | // extracted(loginUserId, dto, areasItem, orderItem, wkOrder, matnr); |
| | | // DirectWaitPakin 启用时保存至收货区,形成闭环;未启用时不写收货区 |
| | | Config directPakinConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | if (directPakinConfig != null && Boolean.parseBoolean(directPakinConfig.getVal())) { |
| | | extracted(loginUserId, dto, areasItem, orderItem, wkOrder, matnr); |
| | | } |
| | | } |
| | | |
| | | // 新顺序:未执行(组托)→任务执行中→已完成,不再设置收货完成 |
| | | // WkOrder order = asnOrderMapper.getOne(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getCode, asnCode)); |
| | | // if (order.getQty().compareTo(order.getAnfme()) >= 0.00) { |
| | | // order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_RECEIPT_DONE.val).setRleStatus(Short.valueOf("1")); |
| | | // if (!asnOrderMapper.updateById(order)) { |
| | | // throw new CoolException("订单状态修改失败!!"); |
| | | // } |
| | | // } |
| | | // 已收数量 >= 计划数量时置为收货完成(2) |
| | | WkOrder order = asnOrderMapper.getOne(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getCode, asnCode)); |
| | | if (order != null && order.getAnfme() != null && QuantityUtils.compare(order.getQty(), order.getAnfme()) >= 0) { |
| | | order.setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_RECEIPT_DONE.val).setRleStatus(Short.valueOf("1")); |
| | | if (!asnOrderMapper.updateById(order)) { |
| | | throw new CoolException("订单状态修改失败!!"); |
| | | } |
| | | } |
| | | |
| | | return R.ok("收货成功!!"); |
| | | } |
| | |
| | | * @description 获取收货区 |
| | | * @time 2025/3/11 10:12 |
| | | */ |
| | | /** 收货区已停用,返回空列表 */ |
| | | /** DirectWaitPakin 启用时返回收货区列表,未启用时返回空列表(不影响组托与云仓入库流程) */ |
| | | @Override |
| | | public R getReceiptAreas() { |
| | | // List<WarehouseAreas> areas = warehouseAreasService.list(new LambdaQueryWrapper<WarehouseAreas>() |
| | | // .eq(WarehouseAreas::getType, WarehouseAreaType.WAREHOUSE_AREA_RECEIPT.type)); |
| | | // return R.ok(areas); |
| | | return R.ok(Collections.emptyList()); |
| | | Config directPakinConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | if (directPakinConfig == null || !Boolean.parseBoolean(directPakinConfig.getVal())) { |
| | | return R.ok(Collections.emptyList()); |
| | | } |
| | | List<WarehouseAreas> areas = warehouseAreasService.list(new LambdaQueryWrapper<WarehouseAreas>() |
| | | .eq(WarehouseAreas::getType, WarehouseAreaType.WAREHOUSE_AREA_RECEIPT.type)); |
| | | return R.ok(areas); |
| | | } |
| | | |
| | | /** |
| | |
| | | return R.ok(resultList); |
| | | } |
| | | |
| | | // 收货区已停用:有ASN单号时从订单明细查可组托物料;可组盘数量 = 计划数量 - 已组托数量 - 已上架数量 |
| | | // 有ASN单号时:DirectWaitPakin 启用则从收货区查可组托物料,未启用则从订单明细查(可组盘数量 = 计划 - 已组托 - 已上架) |
| | | if (!Cools.isEmpty(asnCode)) { |
| | | Config directPakinConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | if (directPakinConfig != null && Boolean.parseBoolean(directPakinConfig.getVal())) { |
| | | LambdaQueryWrapper<WarehouseAreasItem> receiptWrapper = new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getAsnCode, asnCode); |
| | | if (!Cools.isEmpty(matnrCode)) receiptWrapper.eq(WarehouseAreasItem::getMatnrCode, matnrCode); |
| | | if (!Cools.isEmpty(batch)) receiptWrapper.eq(WarehouseAreasItem::getSplrBatch, batch); |
| | | if (!Objects.isNull(fieldIndex)) receiptWrapper.eq(WarehouseAreasItem::getFieldsIndex, fieldIndex); |
| | | if (!Cools.isEmpty(code)) receiptWrapper.eq(WarehouseAreasItem::getTrackCode, code); |
| | | List<WarehouseAreasItem> receiptList = warehouseAreasItemService.list(receiptWrapper); |
| | | // 组托通知档(WaitPakinItem)的已组托数量也要扣减:收货区 workQty 仅反映从收货区组托的部分,从订单直接组托的在此汇总 |
| | | Map<Long, Double> waitPakinSumByItemId = new java.util.HashMap<>(); |
| | | if (!receiptList.isEmpty() && receiptList.stream().anyMatch(e -> e.getAsnId() != null && e.getAsnItemId() != null)) { |
| | | List<Long> asnItemIds = receiptList.stream().map(WarehouseAreasItem::getAsnItemId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | Long asnId = receiptList.get(0).getAsnId(); |
| | | if (asnId != null && !asnItemIds.isEmpty()) { |
| | | List<WaitPakinItem> wpItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnId, asnId).in(WaitPakinItem::getAsnItemId, asnItemIds).eq(WaitPakinItem::getDeleted, 0)); |
| | | wpItems.forEach(w -> waitPakinSumByItemId.merge(w.getAsnItemId(), w.getAnfme() != null ? w.getAnfme() : 0.0, (a, b) -> (a != null ? a : 0.0) + (b != null ? b : 0.0))); |
| | | waitPakinSumByItemId.replaceAll((k, v) -> QuantityUtils.roundToScale(v)); |
| | | } |
| | | } |
| | | // 可组托 = 计划(anfme) - 已组托(waitPakinSumByItemId),不叠加 areaWorkQty 避免与组托档汇总重复计算 |
| | | receiptList.removeIf(e -> { |
| | | Double anfme = e.getAnfme() != null ? e.getAnfme() : 0.0; |
| | | Double qty = e.getQty() != null ? e.getQty() : 0.0; |
| | | Double alreadyInWaitPakin = e.getAsnItemId() != null ? waitPakinSumByItemId.getOrDefault(e.getAsnItemId(), 0.0) : 0.0; |
| | | Double totalAlready = QuantityUtils.roundToScale(alreadyInWaitPakin); |
| | | Double available = QuantityUtils.roundToScale(QuantityUtils.subtract(QuantityUtils.subtract(anfme, totalAlready), qty)); |
| | | e.setWorkQty(totalAlready); |
| | | e.setAvailablePalletQty(available); |
| | | return QuantityUtils.compare(available, 0.0) <= 0; |
| | | }); |
| | | logger.info("=== 从收货区查询可组托物料(DirectWaitPakin)asnCode: {} 返回 {} 条", asnCode, receiptList.size()); |
| | | return R.ok(receiptList); |
| | | } |
| | | WkOrder order = asnOrderMapper.getOne(new LambdaQueryWrapper<WkOrder>().eq(WkOrder::getCode, asnCode)); |
| | | if (order == null) { |
| | | logger.info("未找到ASN单号: {}", asnCode); |
| | | return R.ok(Collections.emptyList()); |
| | | } |
| | | // 按明细汇总已组托数量(组托数量不会因改单而变) |
| | | List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnId, order.getId())); |
| | | List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnId, order.getId()).eq(WaitPakinItem::getDeleted, 0)); |
| | | Map<Long, Double> palletizedByItemId = pakinItems.stream() |
| | | .collect(Collectors.groupingBy(WaitPakinItem::getAsnItemId, Collectors.summingDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0))); |
| | | palletizedByItemId.replaceAll((k, v) -> QuantityUtils.roundToScale(v)); |
| | |
| | | for (WkOrderItem oi : orderItems) { |
| | | Double anfme = QuantityUtils.roundToScale(oi.getAnfme() != null ? oi.getAnfme() : 0.0); |
| | | Double qty = QuantityUtils.roundToScale(oi.getQty() != null ? oi.getQty() : 0.0); |
| | | Double workQty = palletizedByItemId.getOrDefault(oi.getId(), 0.0); // 已组托数量 |
| | | if (QuantityUtils.compare(QuantityUtils.subtract(QuantityUtils.subtract(anfme, workQty), qty), 0.0) <= 0) continue; // 可组盘数量<=0 不返回 |
| | | Double workQty = palletizedByItemId.getOrDefault(oi.getId(), 0.0); |
| | | // 可组盘数量 = 计划 - 已组托(不扣减明细完成数量 qty,避免云仓同步完成数量=计划后导致可组托被算成 0) |
| | | Double availablePalletQty = QuantityUtils.roundToScale(QuantityUtils.subtract(anfme, workQty)); |
| | | if (QuantityUtils.compare(availablePalletQty, 0.0) <= 0) continue; |
| | | WarehouseAreasItem v = new WarehouseAreasItem(); |
| | | v.setId(oi.getId()); |
| | | v.setAsnItemId(oi.getId()); |
| | |
| | | v.setAnfme(anfme); |
| | | v.setQty(qty); |
| | | v.setWorkQty(QuantityUtils.roundToScale(workQty)); |
| | | v.setAvailablePalletQty(availablePalletQty); |
| | | v.setMatnrCode(oi.getMatnrCode()); |
| | | v.setMaktx(oi.getMaktx()); |
| | | v.setSplrBatch(oi.getSplrBatch()); |
| | |
| | | } |
| | | list.add(v); |
| | | } |
| | | logger.info("=== 从订单明细查询可组托物料(收货区已停用)asnCode: {} 返回 {} 条", asnCode, list.size()); |
| | | logger.info("=== 从订单明细查询可组托物料 asnCode: {} 返回 {} 条", asnCode, list.size()); |
| | | return R.ok(list); |
| | | } |
| | | |
| | |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.vincent.rsf.server.manager.service.impl.MatnrServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.DictTypeCode; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.entity.DictData; |
| | | import com.vincent.rsf.server.system.entity.DictType; |
| | | import com.vincent.rsf.server.system.entity.Fields; |
| | |
| | | private WaitPakinItemService waitPakinItemService; |
| | | @Autowired |
| | | private WarehouseAreasItemService warehouseAreasItemService; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | |
| | | |
| | | /** |
| | |
| | | .eq(WkOrder::getPoCode, syncOrder.getOrderInternalCode())); |
| | | if (!Objects.isNull(order)) { |
| | | // 仅未执行状态可被 order/add 修改(入库未执行、出库任务初始) |
| | | List<Short> editableStatus = Arrays.asList(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val); |
| | | List<Short> editableStatus = Arrays.asList(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val |
| | | ,AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val |
| | | ,AsnExceStatus.ASN_EXCE_STATUS_RECEIPT_DONE.val |
| | | , AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val); |
| | | if (!editableStatus.contains(order.getExceStatus())) { |
| | | throw new CoolException("仅未执行状态的单据可修改!!"); |
| | | } |
| | | // 存在则修改(1 和 2 均走此处),组托校验在 mergeOrderWithPakin/updateOrderNoPakin 内 |
| | | long pakinCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>() |
| | | .eq(WaitPakinItem::getAsnId, order.getId())); |
| | | .eq(WaitPakinItem::getAsnId, order.getId()).eq(WaitPakinItem::getDeleted, 0)); |
| | | if (pakinCount > 0) { |
| | | // 已组托:按 lineId(platItemId)合并,校验数量与删除 |
| | | mergeOrderWithPakin(order, syncOrder, resolvedOrderType, typeCode, loginUserId); |
| | | // 收货区已停用 // syncReceiptAreaByOrder(order.getId()); |
| | | return; // 本单已处理,跳过下方“新建主单+明细” |
| | | if (isDirectWaitPakin()) { |
| | | syncReceiptAreaByOrder(order.getId()); |
| | | } |
| | | return; |
| | | } |
| | | // 未组托:在原单上更新主单+明细,保留 exceStatus/qty/workQty,避免再次触发定时任务导致重复收货 |
| | | updateOrderNoPakin(order, syncOrder, loginUserId); |
| | | // 收货区已停用 // syncReceiptAreaByOrder(order.getId()); |
| | | if (isDirectWaitPakin()) { |
| | | syncReceiptAreaByOrder(order.getId()); |
| | | } |
| | | return; |
| | | } else if (Integer.valueOf(2).equals(syncOrder.getOperateType())) { |
| | | // 仅 operateType=2 时要求单据必须存在 |
| | |
| | | } |
| | | } |
| | | List<WkOrderItem> existingItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())); |
| | | List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnId, order.getId())); |
| | | List<WaitPakinItem> pakinItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnId, order.getId()).eq(WaitPakinItem::getDeleted, 0)); |
| | | Map<Long, Double> palletizedByItemId = pakinItems.stream() |
| | | .collect(Collectors.groupingBy(WaitPakinItem::getAsnItemId, Collectors.summingDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0))); |
| | | palletizedByItemId.replaceAll((k, v) -> QuantityUtils.roundToScale(v)); |
| | |
| | | .map(e -> StringUtils.isNotBlank(e.getPlatItemId()) ? e.getPlatItemId().trim() : null) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | // 已组托单据修改只能增加数量不能减少数量:云仓下发的总数量不得小于已组托总量 |
| | | double totalPalletized = QuantityUtils.roundToScale(pakinItems.stream().mapToDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0).sum()); |
| | | double incomingTotal = syncOrder.getOrderItems() == null ? 0.0 : syncOrder.getOrderItems().stream() |
| | | .mapToDouble(it -> it.getAnfme() != null ? it.getAnfme() : 0.0).sum(); |
| | | incomingTotal = QuantityUtils.roundToScale(incomingTotal); |
| | | if (QuantityUtils.compare(incomingTotal, totalPalletized) < 0) { |
| | | throw new CoolException("已组托单据修改只能增加数量不能减少数量(当前已组托总量 " + totalPalletized + ",下发总量 " + incomingTotal + ")!!"); |
| | | } |
| | | |
| | | for (WkOrderItem existing : existingItems) { |
| | | String lineId = StringUtils.isNotBlank(existing.getPlatItemId()) ? existing.getPlatItemId().trim() : null; |
| | |
| | | continue; |
| | | } |
| | | SyncOrdersItem inc = incomingByLineId.get(lineId); |
| | | existing.setAnfme(QuantityUtils.roundToScale(inc.getAnfme() != null ? inc.getAnfme() : existing.getAnfme())); |
| | | Double newAnfme = QuantityUtils.roundToScale(inc.getAnfme() != null ? inc.getAnfme() : existing.getAnfme()); |
| | | existing.setAnfme(newAnfme); |
| | | if (inc.getQty() != null) { |
| | | existing.setQty(QuantityUtils.roundToScale(inc.getQty())); |
| | | } else { |
| | | Double curQty = existing.getQty() != null ? existing.getQty() : 0.0; |
| | | if (QuantityUtils.compare(curQty, 0.0) != 0) { |
| | | existing.setQty(QuantityUtils.roundToScale(newAnfme)); |
| | | } |
| | | } |
| | | existing.setMaktx(inc.getMaktx()); |
| | | existing.setSpec(inc.getSpec()); |
| | | existing.setModel(inc.getModel()); |
| | |
| | | Double sum = QuantityUtils.roundToScale(asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())) |
| | | .stream().mapToDouble(WkOrderItem::getAnfme).sum()); |
| | | order.setAnfme(sum); |
| | | List<WkOrderItem> afterItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())); |
| | | double qtySum = afterItems.stream().mapToDouble(oi -> oi.getQty() != null ? oi.getQty() : 0.0).sum(); |
| | | order.setQty(QuantityUtils.roundToScale(qtySum)); |
| | | asnOrderService.updateById(order); |
| | | } |
| | | |
| | |
| | | for (WkOrderItem existing : existingItems) { |
| | | String lineId = StringUtils.isNotBlank(existing.getPlatItemId()) ? existing.getPlatItemId().trim() : null; |
| | | if (lineId == null || !incomingByLineId.containsKey(lineId)) { |
| | | // 收货区已停用 // warehouseAreasItemService.remove(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getAsnItemId, existing.getId())); |
| | | if (isDirectWaitPakin()) { |
| | | warehouseAreasItemService.remove(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getAsnItemId, existing.getId())); |
| | | } |
| | | asnOrderItemService.removeById(existing.getId()); |
| | | continue; |
| | | } |
| | | SyncOrdersItem inc = incomingByLineId.get(lineId); |
| | | existing.setAnfme(QuantityUtils.roundToScale(inc.getAnfme() != null ? inc.getAnfme() : existing.getAnfme())); |
| | | Double newAnfme = QuantityUtils.roundToScale(inc.getAnfme() != null ? inc.getAnfme() : existing.getAnfme()); |
| | | existing.setAnfme(newAnfme); |
| | | if (inc.getQty() != null) { |
| | | existing.setQty(QuantityUtils.roundToScale(inc.getQty())); |
| | | } else { |
| | | Double curQty = existing.getQty() != null ? existing.getQty() : 0.0; |
| | | if (QuantityUtils.compare(curQty, 0.0) != 0) { |
| | | existing.setQty(QuantityUtils.roundToScale(newAnfme)); |
| | | } |
| | | } |
| | | existing.setMaktx(inc.getMaktx()); |
| | | existing.setSpec(inc.getSpec()); |
| | | existing.setModel(inc.getModel()); |
| | |
| | | Double sum = QuantityUtils.roundToScale(asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())) |
| | | .stream().mapToDouble(WkOrderItem::getAnfme).sum()); |
| | | order.setAnfme(sum); |
| | | List<WkOrderItem> afterItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())); |
| | | double qtySum = afterItems.stream().mapToDouble(oi -> oi.getQty() != null ? oi.getQty() : 0.0).sum(); |
| | | order.setQty(QuantityUtils.roundToScale(qtySum)); |
| | | asnOrderService.updateById(order); |
| | | } |
| | | |
| | | /** 收货区已停用,方法整体注释 |
| | | * 订单修改后同步收货区:按 asnItemId 将收货区记录的 anfme 更新为订单明细的 anfme。 |
| | | */ |
| | | // private void syncReceiptAreaByOrder(Long orderId) { |
| | | // List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, orderId)); |
| | | // if (orderItems.isEmpty()) { |
| | | // return; |
| | | // } |
| | | // List<WarehouseAreasItem> areaItems = warehouseAreasItemService.list(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getAsnId, orderId)); |
| | | // Map<Long, Double> itemAnfme = orderItems.stream().collect(Collectors.toMap(WkOrderItem::getId, i -> i.getAnfme() != null ? i.getAnfme() : 0.0, (a, b) -> b)); |
| | | // for (WarehouseAreasItem area : areaItems) { |
| | | // if (area.getAsnItemId() == null || !itemAnfme.containsKey(area.getAsnItemId())) { |
| | | // continue; |
| | | // } |
| | | // Double anfme = itemAnfme.get(area.getAsnItemId()); |
| | | // if (area.getAnfme() != null && area.getAnfme().equals(anfme)) { |
| | | // continue; |
| | | // } |
| | | // area.setAnfme(anfme); |
| | | // warehouseAreasItemService.updateById(area); |
| | | // } |
| | | // } |
| | | /** DirectWaitPakin 启用时:订单修改后同步收货区,按 asnItemId 将收货区记录的 anfme 更新为订单明细的 anfme。 */ |
| | | private void syncReceiptAreaByOrder(Long orderId) { |
| | | List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, orderId)); |
| | | if (orderItems.isEmpty()) { |
| | | return; |
| | | } |
| | | List<WarehouseAreasItem> areaItems = warehouseAreasItemService.list(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getAsnId, orderId)); |
| | | Map<Long, Double> itemAnfme = orderItems.stream().collect(Collectors.toMap(WkOrderItem::getId, i -> i.getAnfme() != null ? i.getAnfme() : 0.0, (a, b) -> b)); |
| | | for (WarehouseAreasItem area : areaItems) { |
| | | if (area.getAsnItemId() == null || !itemAnfme.containsKey(area.getAsnItemId())) { |
| | | continue; |
| | | } |
| | | Double anfme = itemAnfme.get(area.getAsnItemId()); |
| | | if (area.getAnfme() != null && area.getAnfme().equals(anfme)) { |
| | | continue; |
| | | } |
| | | area.setAnfme(anfme); |
| | | warehouseAreasItemService.updateById(area); |
| | | } |
| | | } |
| | | |
| | | private boolean isDirectWaitPakin() { |
| | | Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | return config != null && Boolean.parseBoolean(config.getVal()); |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | |
| | | import com.vincent.rsf.server.api.utils.SlaveProperties; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.vincent.rsf.server.manager.controller.params.PakinItem; |
| | | import com.vincent.rsf.server.manager.controller.params.WaitPakinParam; |
| | | import com.vincent.rsf.server.manager.service.impl.LocServiceImpl; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.utils.SystemAuthUtils; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.manager.enums.LocStsType; |
| | |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | import org.springframework.web.client.RestTemplate; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.Collections; |
| | | import java.util.Date; |
| | |
| | | private RestTemplate restTemplate; |
| | | @Autowired |
| | | private RemotesInfoProperties.RcsApi rcsApi; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private MatnrService matnrService; |
| | | @Autowired |
| | | private AsnOrderService asnOrderService; |
| | | @Autowired |
| | | private AsnOrderItemService asnOrderItemService; |
| | | @Autowired |
| | | private com.vincent.rsf.server.api.service.MobileService mobileService; |
| | | |
| | | |
| | | @Override |
| | |
| | | |
| | | // 2. 若未命中拣料/盘点入库,再校验组托并继续其他入库逻辑 |
| | | if (pickInTask == null && checkInTask == null) { |
| | | waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, param.getBarcode()) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | // 空托盘无组托时:若配置启用则按 AUTO_FULL_OUT_MATNR_CODE 自动组托并生成入库单,再继续入库任务逻辑 |
| | | if (waitPakin == null) { |
| | | tryAutoPakinForBarcode(param.getBarcode()); |
| | | waitPakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, param.getBarcode()) |
| | | .in(WaitPakin::getIoStatus, PakinIOStatus.PAKIN_IO_STATUS_DONE.val, PakinIOStatus.PAKIN_IO_STATUS_TASK_EXCE.val)); |
| | | } |
| | | waitPakin = validateWaitPakin(param.getBarcode()); |
| | | waitPakinItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, waitPakin.getId())); |
| | |
| | | throw new RuntimeException(e); |
| | | } |
| | | |
| | | if (waitPakin == null) { |
| | | throw new CoolException("请检查组拖状态是否完成!!"); |
| | | } |
| | | // 创建并保存任务 |
| | | Task task = createTask(ruleCode, locNo.getLocNo(), waitPakin.getBarcode(), |
| | | deviceSite.getDeviceSite(), param.getSourceStaNo().toString(), param.getUser()); |
| | |
| | | |
| | | |
| | | /** |
| | | * RCS 入库申请时若 barcode 无组托且配置启用:按 AUTO_FULL_OUT_MATNR_CODE 无订单组托并生成入库单,便于后续生成入库任务。 |
| | | */ |
| | | private void tryAutoPakinForBarcode(String barcode) { |
| | | Config enabledConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_ON_ASN_ENABLED)); |
| | | if (enabledConfig == null || !Boolean.parseBoolean(enabledConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config matnrConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_MATNR_CODE)); |
| | | if (matnrConfig == null || StringUtils.isBlank(matnrConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config qtyConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_QTY)); |
| | | double autoQty = 1.0; |
| | | if (qtyConfig != null && StringUtils.isNotBlank(qtyConfig.getVal())) { |
| | | try { |
| | | autoQty = Double.parseDouble(qtyConfig.getVal().trim()); |
| | | if (autoQty <= 0) autoQty = 1.0; |
| | | } catch (NumberFormatException e) { |
| | | // ignore |
| | | } |
| | | } |
| | | String matnrCode = matnrConfig.getVal().trim(); |
| | | Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, matnrCode)); |
| | | if (matnr == null) { |
| | | log.warn("[RCS入库申请-自动组托] 物料不存在: {}", matnrCode); |
| | | return; |
| | | } |
| | | List<PakinItem> pakinItems = new ArrayList<>(); |
| | | PakinItem pi = new PakinItem(); |
| | | pi.setMatnrId(matnr.getId()); |
| | | pi.setReceiptQty(autoQty); |
| | | pi.setAsnCode(null); |
| | | pi.setId(null); |
| | | pakinItems.add(pi); |
| | | WaitPakinParam param = new WaitPakinParam(); |
| | | param.setBarcode(barcode); |
| | | param.setItems(pakinItems); |
| | | WaitPakin waitPakin; |
| | | try { |
| | | waitPakin = mobileService.mergeItems(param, 1L); |
| | | } catch (Exception e) { |
| | | log.warn("[RCS入库申请-自动组托] 组托失败, barcode={}: {}", barcode, e.getMessage()); |
| | | return; |
| | | } |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_ASN_ORDER, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | log.warn("[RCS入库申请-自动组托] 入库单编码规则未配置"); |
| | | return; |
| | | } |
| | | WkOrder order = new WkOrder(); |
| | | order.setCode(ruleCode) |
| | | .setType(OrderType.ORDER_IN.type) |
| | | .setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setCreateBy(1L) |
| | | .setUpdateBy(1L); |
| | | if (!asnOrderService.save(order)) { |
| | | throw new CoolException("入库主单保存失败"); |
| | | } |
| | | WkOrderItem orderItem = new WkOrderItem(); |
| | | orderItem.setOrderId(order.getId()) |
| | | .setOrderCode(order.getCode()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setMatnrCode(matnr.getCode()) |
| | | .setMaktx(matnr.getName()) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setStockUnit(matnr.getStockUnit() != null ? matnr.getStockUnit() : "个") |
| | | .setPurUnit(matnr.getPurUnit() != null ? matnr.getPurUnit() : "个") |
| | | .setFieldsIndex(matnr.getFieldsIndex()) |
| | | .setCreateBy(1L) |
| | | .setUpdateBy(1L); |
| | | if (!asnOrderItemService.save(orderItem)) { |
| | | throw new CoolException("入库明细保存失败"); |
| | | } |
| | | waitPakinItemService.update(new LambdaUpdateWrapper<WaitPakinItem>() |
| | | .eq(WaitPakinItem::getPakinId, waitPakin.getId()) |
| | | .set(WaitPakinItem::getAsnId, order.getId()) |
| | | .set(WaitPakinItem::getAsnCode, order.getCode()) |
| | | .set(WaitPakinItem::getAsnItemId, orderItem.getId())); |
| | | log.info("[RCS入库申请-自动组托] 已组托并生成入库单: {}, barcode: {}, 物料: {}, 数量: {}", order.getCode(), barcode, matnrCode, autoQty); |
| | | } |
| | | |
| | | /** |
| | | * 验证设备站点 |
| | | */ |
| | | private DeviceSite validateDeviceSite(TaskInParam param) { |
| | |
| | | QueryWrapper<WkOrder> queryWrapper = pageParam.buildWrapper(true); |
| | | List<String> asList = Arrays.asList(OrderType.ORDER_IN.type); |
| | | queryWrapper.in("type", asList); |
| | | return R.ok().add(asnOrderService.page(pageParam, queryWrapper)); |
| | | Page<WkOrder> pageResult = asnOrderService.page(pageParam, queryWrapper); |
| | | asnOrderService.fillPalletQty(pageResult.getRecords()); |
| | | return R.ok().add(pageResult); |
| | | } |
| | | |
| | | @ApiOperation("获取首页表头数据") |
| | |
| | | @PreAuthorize("hasAuthority('manager:asnOrder:list')") |
| | | @PostMapping({"/asnOrder/many/{ids}", "/asnOrders/many/{ids}"}) |
| | | public R many(@PathVariable Long[] ids) { |
| | | return R.ok().add(asnOrderService.listByIds(Arrays.asList(ids))); |
| | | List<WkOrder> list = asnOrderService.listByIds(Arrays.asList(ids)); |
| | | asnOrderService.fillPalletQty(list); |
| | | return R.ok().add(list); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('manager:asnOrder:list')") |
| | | @OperationLog("表单查询") |
| | | @GetMapping("/asnOrder/{id}") |
| | | public R get(@PathVariable("id") Long id) { |
| | | return R.ok().add(asnOrderService.getById(id)); |
| | | WkOrder order = asnOrderService.getById(id); |
| | | if (order != null) { |
| | | asnOrderService.fillPalletQty(Collections.singletonList(order)); |
| | | } |
| | | return R.ok().add(order); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('manager:asnOrder:save')") |
| | |
| | | record.setExtendFields(fields); |
| | | } |
| | | } |
| | | asnOrderItemService.fillPalletQty(records); |
| | | page.setRecords(records); |
| | | return R.ok(page); |
| | | } |
| | |
| | | @ApiModelProperty("组拖类型{null: 组拖, defective: 不良品}") |
| | | private String type; |
| | | |
| | | @ApiModelProperty("是否半箱:true 时同一料箱码可继续组托追加,false 时料箱码已组托则不可再用") |
| | | private Boolean isHalf; |
| | | |
| | | } |
| | | |
| | |
| | | @TableField(exist = false) |
| | | private Map<String, String> extendFields; |
| | | |
| | | /** 可组盘数量 = 计划(anfme) - 已组托(workQty) - 已上架(qty),仅组盘明细接口返回时填充,供前端直接展示 */ |
| | | @ApiModelProperty(value = "可组盘数量(计划-已组托-已上架)") |
| | | @TableField(exist = false) |
| | | private Double availablePalletQty; |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | |
| | | @ApiModelProperty(value = "已收数量") |
| | | private Double qty; |
| | | |
| | | /** 组托数量(非持久化,列表展示用:该单下 WaitPakinItem.anfme 汇总) */ |
| | | @ApiModelProperty(value = "组托数量") |
| | | @com.baomidou.mybatisplus.annotation.TableField(exist = false) |
| | | private Double palletQty; |
| | | |
| | | /** |
| | | * 物流单号 |
| | | */ |
| | |
| | | @ApiModelProperty(value= "送货数量") |
| | | private Double anfme; |
| | | |
| | | /** 组托数量(非持久化,列表展示:该明细 WaitPakinItem.anfme 汇总) */ |
| | | @ApiModelProperty(value = "组托数量") |
| | | @TableField(exist = false) |
| | | private Double palletQty; |
| | | |
| | | /** |
| | | * 库存单位 |
| | | */ |
| | |
| | | */ |
| | | public enum AsnExceStatus { |
| | | |
| | | // ASN/入库单执行状态(新顺序):未执行(组托) → 任务执行中 → 已完成 |
| | | // ASN/入库单执行状态:未执行(组托) → 任务执行中 → 已完成 |
| | | ASN_EXCE_STATUS_UN_EXCE("0", "未执行"), |
| | | /** @deprecated 已停用,新流程不再使用 */ |
| | | |
| | | ASN_EXCE_STATUS_EXCE_ING("1", "执行中"), |
| | | /** @deprecated 已停用,新流程不再使用 */ |
| | | ASN_EXCE_STATUS_RECEIPT_DONE("2", "收货完成"), |
| | | |
| | | ASN_EXCE_STATUS_TASK_EXCE("3", "任务执行中"), |
| | | /** 已完成:整单全流程结束(收货+组托+上架/入库任务等),可归档历史、上报 ERP;由组托/任务定时或移历史时设置 */ |
| | | ASN_EXCE_STATUS_TASK_DONE("4", "已完成"), |
| New file |
| | |
| | | package com.vincent.rsf.server.manager.schedules; |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.manager.controller.params.OutStockToTaskParams; |
| | | import com.vincent.rsf.server.manager.controller.params.PakinItem; |
| | | import com.vincent.rsf.server.manager.controller.params.WaitPakinParam; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.AsnExceStatus; |
| | | import com.vincent.rsf.server.manager.enums.OrderType; |
| | | import com.vincent.rsf.server.manager.enums.OrderWorkType; |
| | | import com.vincent.rsf.server.manager.enums.TaskStsType; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.vincent.rsf.server.manager.utils.LocManageUtil; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.scheduling.annotation.Scheduled; |
| | | import org.springframework.stereotype.Component; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * 指定物料自动化定时任务:可配置物料编码后, |
| | | * 1)有库存时自动生成全版出库单; |
| | | * 2)该物料出库单自动下发任务; |
| | | * 3)RCS 入库通知时(可选)自动组托,数量可配置。 |
| | | */ |
| | | @Slf4j |
| | | @Component |
| | | public class MaterialAutoSchedules { |
| | | |
| | | private static final Long SYSTEM_USER_ID = 1L; |
| | | private static final String DEFAULT_SITE_NO = "1001"; |
| | | |
| | | @Autowired |
| | | private ConfigService configService; |
| | | @Autowired |
| | | private OutStockService outStockService; |
| | | @Autowired |
| | | private AsnOrderItemService asnOrderItemService; |
| | | @Autowired |
| | | private LocItemService locItemService; |
| | | @Autowired |
| | | private LocService locService; |
| | | @Autowired |
| | | private MatnrService matnrService; |
| | | @Autowired |
| | | private TaskService taskService; |
| | | @Autowired |
| | | private TaskItemService taskItemService; |
| | | @Autowired |
| | | private com.vincent.rsf.server.api.service.MobileService mobileService; |
| | | @Autowired |
| | | private AsnOrderService asnOrderService; |
| | | @Autowired |
| | | private WaitPakinItemService waitPakinItemService; |
| | | |
| | | /** |
| | | * 定时任务1:指定物料有库存时自动生成全版出库单(每 2 分钟) |
| | | * 配置:AUTO_FULL_OUT_MATNR_CODE(物料编码)、AUTO_FULL_OUT_ENABLED(true 启用) |
| | | */ |
| | | @Scheduled(cron = "0 0/2 * * * ?") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void autoCreateFullOutOrder() { |
| | | Config enabledConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_ENABLED)); |
| | | if (enabledConfig == null || !Boolean.parseBoolean(enabledConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config matnrConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_MATNR_CODE)); |
| | | if (matnrConfig == null || StringUtils.isBlank(matnrConfig.getVal())) { |
| | | return; |
| | | } |
| | | String matnrCode = matnrConfig.getVal().trim(); |
| | | Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, matnrCode)); |
| | | if (matnr == null) { |
| | | log.warn("[自动全版出库单] 物料不存在: {}", matnrCode); |
| | | return; |
| | | } |
| | | // 已有该物料未下发的出库单则本轮不再生成,等下发完后再生成(避免重复) |
| | | List<Long> initOrderIds = outStockService.list(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getType, OrderType.ORDER_OUT.type) |
| | | .eq(WkOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)) |
| | | .stream().map(WkOrder::getId).collect(Collectors.toList()); |
| | | if (!initOrderIds.isEmpty()) { |
| | | long hasMatnr = asnOrderItemService.count(new LambdaQueryWrapper<WkOrderItem>() |
| | | .in(WkOrderItem::getOrderId, initOrderIds) |
| | | .eq(WkOrderItem::getMatnrCode, matnrCode)); |
| | | if (hasMatnr > 0) { |
| | | return; |
| | | } |
| | | } |
| | | // 按库位分组:该物料、库位状态为 F 的在库;每个库位各生成一张出库单 |
| | | List<LocItem> items = locItemService.list(new LambdaQueryWrapper<LocItem>() |
| | | .eq(LocItem::getMatnrCode, matnrCode) |
| | | .gt(LocItem::getAnfme, 0)); |
| | | if (items.isEmpty()) { |
| | | return; |
| | | } |
| | | Map<Long, List<LocItem>> byLocId = items.stream().collect(Collectors.groupingBy(LocItem::getLocId)); |
| | | for (Long locId : byLocId.keySet()) { |
| | | Loc loc = locService.getById(locId); |
| | | if (loc == null || !"F".equals(loc.getUseStatus())) { |
| | | continue; |
| | | } |
| | | List<LocItem> locItems = byLocId.get(locId); |
| | | double sumQty = locItems.stream().mapToDouble(li -> li.getAnfme() != null ? li.getAnfme() : 0).sum(); |
| | | if (sumQty <= 0) continue; |
| | | try { |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_OUT_STOCK_CODE, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | log.warn("[自动全版出库单] 出库单编码规则未配置"); |
| | | break; |
| | | } |
| | | WkOrder order = new WkOrder(); |
| | | order.setCode(ruleCode) |
| | | .setType(OrderType.ORDER_OUT.type) |
| | | .setWkType(OrderWorkType.ORDER_WORK_TYPE_STOCK_OUT.type) |
| | | .setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val) |
| | | .setAnfme(sumQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setCreateBy(SYSTEM_USER_ID) |
| | | .setUpdateBy(SYSTEM_USER_ID); |
| | | if (!outStockService.save(order)) { |
| | | throw new CoolException("出库主单保存失败"); |
| | | } |
| | | LocItem first = locItems.get(0); |
| | | WkOrderItem orderItem = new WkOrderItem(); |
| | | orderItem.setOrderId(order.getId()) |
| | | .setOrderCode(order.getCode()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setMatnrCode(matnr.getCode()) |
| | | .setMaktx(matnr.getName()) |
| | | .setAnfme(sumQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setStockUnit(first.getUnit() != null ? first.getUnit() : "个") |
| | | .setPurUnit(first.getUnit() != null ? first.getUnit() : "个") |
| | | .setSplrBatch(first.getSplrBatch()) |
| | | .setBatch(first.getBatch()) |
| | | .setFieldsIndex(first.getFieldsIndex()) |
| | | .setCreateBy(SYSTEM_USER_ID) |
| | | .setUpdateBy(SYSTEM_USER_ID); |
| | | if (!asnOrderItemService.save(orderItem)) { |
| | | throw new CoolException("出库明细保存失败"); |
| | | } |
| | | log.info("[自动全版出库单] 已生成出库单: {}, 库位: {}, 物料: {}, 数量: {}", order.getCode(), loc.getCode(), matnrCode, sumQty); |
| | | } catch (Exception e) { |
| | | log.error("[自动全版出库单] 生成失败, 库位: {}, 物料: {}", loc.getCode(), matnrCode, e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 定时任务2:该物料出库单自动下发任务(每 1 分钟) |
| | | * 配置:AUTO_FULL_OUT_DISPATCH_ENABLED(true 启用)、AUTO_FULL_OUT_MATNR_CODE |
| | | */ |
| | | @Scheduled(cron = "0 0/1 * * * ?") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void autoDispatchOutTask() { |
| | | Config enabledConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_DISPATCH_ENABLED)); |
| | | if (enabledConfig == null || !Boolean.parseBoolean(enabledConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config matnrConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_MATNR_CODE)); |
| | | if (matnrConfig == null || StringUtils.isBlank(matnrConfig.getVal())) { |
| | | return; |
| | | } |
| | | String matnrCode = matnrConfig.getVal().trim(); |
| | | List<WkOrder> orders = outStockService.list(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getType, OrderType.ORDER_OUT.type) |
| | | .eq(WkOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_INIT.val)); |
| | | for (WkOrder order : orders) { |
| | | List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>() |
| | | .eq(WkOrderItem::getOrderId, order.getId()) |
| | | .eq(WkOrderItem::getMatnrCode, matnrCode)); |
| | | if (orderItems.isEmpty()) continue; |
| | | List<OutStockToTaskParams> paramsList = new ArrayList<>(); |
| | | for (WkOrderItem oi : orderItems) { |
| | | double remaining = (oi.getAnfme() != null ? oi.getAnfme() : 0) - (oi.getWorkQty() != null ? oi.getWorkQty() : 0); |
| | | if (remaining <= 0) continue; |
| | | String batch = oi.getSplrBatch() != null ? oi.getSplrBatch() : oi.getBatch(); |
| | | List<LocItem> locItems = LocManageUtil.getFirstInFirstOutItemList(oi.getMatnrCode(), batch, remaining); |
| | | for (LocItem locItem : locItems) { |
| | | if (remaining <= 0) break; |
| | | Loc loc = locService.getById(locItem.getLocId()); |
| | | if (loc == null || !"F".equals(loc.getUseStatus())) continue; |
| | | double outQty = Math.min(remaining, locItem.getAnfme() != null ? locItem.getAnfme() : 0); |
| | | if (outQty <= 0) continue; |
| | | OutStockToTaskParams param = new OutStockToTaskParams(); |
| | | param.setId(locItem.getId()); |
| | | param.setLocCode(loc.getCode()); |
| | | param.setOutQty(outQty); |
| | | param.setSiteNo(DEFAULT_SITE_NO); |
| | | param.setBatch(locItem.getBatch()); |
| | | paramsList.add(param); |
| | | remaining -= outQty; |
| | | } |
| | | } |
| | | if (paramsList.isEmpty()) continue; |
| | | try { |
| | | outStockService.genOutStockTask(paramsList, SYSTEM_USER_ID, order.getId()); |
| | | List<TaskItem> taskItems = taskItemService.list(new LambdaQueryWrapper<TaskItem>().eq(TaskItem::getSourceId, order.getId())); |
| | | if (!taskItems.isEmpty()) { |
| | | Set<Long> taskIds = taskItems.stream().map(TaskItem::getTaskId).collect(Collectors.toSet()); |
| | | List<Task> tasks = taskService.listByIds(taskIds).stream() |
| | | .filter(t -> TaskStsType.GENERATE_OUT.id.equals(t.getTaskStatus())) |
| | | .collect(Collectors.toList()); |
| | | if (!tasks.isEmpty()) { |
| | | taskService.pubTaskToWcs(tasks); |
| | | log.info("[自动下发任务] 出库单: {} 已下发任务", order.getCode()); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | | log.error("[自动下发任务] 出库单: {} 下发失败", order.getCode(), e); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 定时任务3:无订单组托 + 自动生成入库单(仅针对配置物料,每 2 分钟) |
| | | * 先按配置物料与数量做无订单组托,再生成入库单并关联组托明细,便于 RCS 入库闭环。 |
| | | * 配置:AUTO_PAKIN_ON_ASN_ENABLED(true)、AUTO_FULL_OUT_MATNR_CODE、AUTO_PAKIN_QTY(数量) |
| | | */ |
| | | @Scheduled(cron = "0 0/2 * * * ?") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void autoPakinOnInbound() { |
| | | Config enabledConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_ON_ASN_ENABLED)); |
| | | if (enabledConfig == null || !Boolean.parseBoolean(enabledConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config matnrConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_FULL_OUT_MATNR_CODE)); |
| | | if (matnrConfig == null || StringUtils.isBlank(matnrConfig.getVal())) { |
| | | return; |
| | | } |
| | | Config qtyConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.AUTO_PAKIN_QTY)); |
| | | double autoQty = 1.0; |
| | | if (qtyConfig != null && StringUtils.isNotBlank(qtyConfig.getVal())) { |
| | | try { |
| | | autoQty = Double.parseDouble(qtyConfig.getVal().trim()); |
| | | if (autoQty <= 0) autoQty = 1.0; |
| | | } catch (NumberFormatException e) { |
| | | // ignore |
| | | } |
| | | } |
| | | String matnrCode = matnrConfig.getVal().trim(); |
| | | Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getCode, matnrCode)); |
| | | if (matnr == null) { |
| | | log.warn("[无订单自动组托] 物料不存在: {}", matnrCode); |
| | | return; |
| | | } |
| | | // 已有该物料未执行入库单则本轮不继续生成,避免堆积 |
| | | List<Long> initAsnIds = asnOrderService.list(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getType, OrderType.ORDER_IN.type) |
| | | .eq(WkOrder::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)) |
| | | .stream().map(WkOrder::getId).collect(Collectors.toList()); |
| | | if (!initAsnIds.isEmpty()) { |
| | | long hasMatnr = asnOrderItemService.count(new LambdaQueryWrapper<WkOrderItem>() |
| | | .in(WkOrderItem::getOrderId, initAsnIds) |
| | | .eq(WkOrderItem::getMatnrCode, matnrCode)); |
| | | if (hasMatnr > 0) { |
| | | return; |
| | | } |
| | | } |
| | | // 1)无订单组托:仅物料 + 数量,不传 asnCode/id |
| | | List<PakinItem> pakinItems = new ArrayList<>(); |
| | | PakinItem pi = new PakinItem(); |
| | | pi.setMatnrId(matnr.getId()); |
| | | pi.setReceiptQty(autoQty); |
| | | pi.setAsnCode(null); |
| | | pi.setId(null); |
| | | pakinItems.add(pi); |
| | | String barcode = "AUTO-PAKIN-" + System.currentTimeMillis(); |
| | | WaitPakinParam param = new WaitPakinParam(); |
| | | param.setBarcode(barcode); |
| | | param.setItems(pakinItems); |
| | | WaitPakin waitPakin; |
| | | try { |
| | | waitPakin = mobileService.mergeItems(param, SYSTEM_USER_ID); |
| | | } catch (Exception e) { |
| | | log.warn("[无订单自动组托] 组托失败: {}", e.getMessage()); |
| | | return; |
| | | } |
| | | // 2)自动生成入库单(一条明细,配置物料 + 数量) |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_ASN_ORDER, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | log.warn("[无订单自动组托] 入库单编码规则未配置"); |
| | | return; |
| | | } |
| | | WkOrder order = new WkOrder(); |
| | | order.setCode(ruleCode) |
| | | .setType(OrderType.ORDER_IN.type) |
| | | .setExceStatus(AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setCreateBy(SYSTEM_USER_ID) |
| | | .setUpdateBy(SYSTEM_USER_ID); |
| | | if (!asnOrderService.save(order)) { |
| | | throw new CoolException("入库主单保存失败"); |
| | | } |
| | | WkOrderItem orderItem = new WkOrderItem(); |
| | | orderItem.setOrderId(order.getId()) |
| | | .setOrderCode(order.getCode()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setMatnrCode(matnr.getCode()) |
| | | .setMaktx(matnr.getName()) |
| | | .setAnfme(autoQty) |
| | | .setWorkQty(0.0) |
| | | .setQty(0.0) |
| | | .setStockUnit(matnr.getStockUnit() != null ? matnr.getStockUnit() : "个") |
| | | .setPurUnit(matnr.getPurUnit() != null ? matnr.getPurUnit() : "个") |
| | | .setFieldsIndex(matnr.getFieldsIndex()) |
| | | .setCreateBy(SYSTEM_USER_ID) |
| | | .setUpdateBy(SYSTEM_USER_ID); |
| | | if (!asnOrderItemService.save(orderItem)) { |
| | | throw new CoolException("入库明细保存失败"); |
| | | } |
| | | // 3)关联组托明细到入库单(asnId / asnCode / asnItemId) |
| | | boolean updated = waitPakinItemService.update(new LambdaUpdateWrapper<WaitPakinItem>() |
| | | .eq(WaitPakinItem::getPakinId, waitPakin.getId()) |
| | | .set(WaitPakinItem::getAsnId, order.getId()) |
| | | .set(WaitPakinItem::getAsnCode, order.getCode()) |
| | | .set(WaitPakinItem::getAsnItemId, orderItem.getId())); |
| | | if (!updated) { |
| | | log.warn("[无订单自动组托] 组托明细关联入库单失败, pakinId={}", waitPakin.getId()); |
| | | } |
| | | log.info("[无订单自动组托] 已组托并生成入库单: {}, 物料: {}, 数量: {}", order.getCode(), matnrCode, autoQty); |
| | | } |
| | | } |
| | |
| | | @Autowired |
| | | private FieldsItemService fieldsItemService; |
| | | |
| | | // /** |
| | | // * @author Ryan |
| | | // * @date 2025/5/9 |
| | | // * @description: 直接组托开关为true,将收货单直接加入临时库存 |
| | | // * @version 1.0 |
| | | // */ |
| | | // @Scheduled(cron = "0/25 * * * * ?") |
| | | // @Transactional(rollbackFor = Exception.class) |
| | | // public synchronized void IgnoreReceipt() { |
| | | // Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | // if (Objects.isNull(config)) { |
| | | // return; |
| | | // } |
| | | // if (!Boolean.parseBoolean(config.getVal())) { |
| | | // return; |
| | | // } |
| | | // //自动收货单 |
| | | // List<WkOrder> orders = asnOrderService.list(new LambdaQueryWrapper<WkOrder>() |
| | | // .eq(WkOrder::getType, OrderType.ORDER_IN.type) |
| | | // .eq(WkOrder::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)); |
| | | // if (!orders.isEmpty()) { |
| | | // for (WkOrder order : orders) { |
| | | // if (order.getWkType().equals(OrderWorkType.ORDER_WORK_TYPE_OTHER_TERANSFER.type)) { |
| | | // WkOrder one = outStockService.getOne(new LambdaQueryWrapper<WkOrder>() |
| | | // .eq(WkOrder::getPoCode, order.getPoCode()) |
| | | // .eq(WkOrder::getWkType, OrderWorkType.ORDER_WORK_TYPE_STOCK_TERANSFER.type)); |
| | | // if (Objects.isNull(one)) { |
| | | // throw new CoolException("数据错误"); |
| | | // } |
| | | // if (!one.getExceStatus().equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val)) { |
| | | // continue; |
| | | // } |
| | | // } |
| | | // |
| | | // List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())); |
| | | // if (orderItems.isEmpty()) { |
| | | // return; |
| | | // } |
| | | // // 收货区已停用:不再写入收货区,仅更新单据状态与数量 |
| | | //// List<WarehouseAreas> receiptAreas = warehouseAreasService.list(new LambdaQueryWrapper<WarehouseAreas>() |
| | | //// .eq(WarehouseAreas::getType, WarehouseAreasType.WAREHOUSE_AREAS_TYPE_RECEIPT.type)); |
| | | //// WarehouseAreas receiptArea = receiptAreas.isEmpty() ? null : receiptAreas.get(0); |
| | | //// List<WarehouseAreasItem> items = new ArrayList<>(); |
| | | // for (WkOrderItem item : orderItems) { |
| | | //// Long areaId = receiptArea == null ? null : receiptArea.getId(); |
| | | //// String areaName = receiptArea == null ? null : receiptArea.getName(); |
| | | //// WarehouseAreasItem param = new WarehouseAreasItem(); |
| | | //// BeanUtils.copyProperties(item, param); |
| | | //// param.setAsnCode(order.getCode()).setAsnId(order.getId()); |
| | | //// if (areaId != null) { |
| | | //// param.setAreaId(areaId).setAreaName(areaName); |
| | | //// } |
| | | //// items.add(param); |
| | | //// Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getId, item.getMatnrId())); |
| | | //// if (Objects.isNull(matnr)) { |
| | | //// throw new CoolException("物料不存在!!"); |
| | | //// } |
| | | //// if (receiptArea != null) { |
| | | //// try { |
| | | //// updateReceipt(receiptArea, item, order, matnr); |
| | | //// } catch (Exception e) { |
| | | //// throw new CoolException(e.getMessage()); |
| | | //// } |
| | | //// } |
| | | // if (!asnOrderItemService.update(new LambdaUpdateWrapper<WkOrderItem>().set(WkOrderItem::getQty, item.getAnfme()).eq(WkOrderItem::getId, item.getId()))) { |
| | | // throw new CoolException("收货单明细完成数量修改失败!!"); |
| | | // } |
| | | // } |
| | | //// if (!warehouseAreasItemService.saveBatch(items)) { |
| | | //// throw new CoolException("收货单保存至收货区执行失败!!"); |
| | | //// } |
| | | // |
| | | // if (!asnOrderService.update(new LambdaUpdateWrapper<WkOrder>() |
| | | // .set(WkOrder::getQty, order.getAnfme()) |
| | | // .set(WkOrder::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_RECEIPT_DONE.val) |
| | | // .eq(WkOrder::getId, order.getId()))) { |
| | | // throw new CoolException("收货单状态修改失败!!"); |
| | | // } |
| | | // } |
| | | // } |
| | | // } |
| | | |
| | | |
| | | /** 收货区已停用,方法整体注释 |
| | | * @author Ryan |
| | | * @date 2025/5/12 |
| | | * @description: 收货区库存更新 |
| | | /** |
| | | * 自动收货:仅当 DirectWaitPakin 配置为 true 时执行,将未执行入库单直接收货并写入收货区,更新单据状态为收货完成。 |
| | | * 不影响云仓WMS入库接口(8.3 同步单)与组托流程;关闭时不做任何操作。 |
| | | */ |
| | | // @Transactional(rollbackFor = Exception.class) |
| | | // public void updateReceipt(WarehouseAreas areasItem, WkOrderItem orderItem, WkOrder wkOrder, Matnr matnr) throws Exception { |
| | | // Companys companys = new Companys(); |
| | | // if (StringUtils.isNoneBlank(orderItem.getSplrCode())) { |
| | | // companys = companysService.getOne(new LambdaQueryWrapper<Companys>().eq(Companys::getCode, orderItem.getSplrCode())); |
| | | // } |
| | | // WarehouseAreasItem item = new WarehouseAreasItem(); |
| | | // item.setTrackCode(orderItem.getBarcode()) |
| | | // .setAreaName(areasItem.getName()) |
| | | // .setAreaId(areasItem.getId()) |
| | | // .setAsnItemId(orderItem.getId()) |
| | | // .setAsnCode(wkOrder.getCode()) |
| | | // .setAsnId(wkOrder.getId()) |
| | | // .setProdTime(orderItem.getProdTime()) |
| | | // .setPlatItemId(orderItem.getPlatItemId()) |
| | | // .setPlatOrderCode(orderItem.getPlatOrderCode()) |
| | | // .setPlatWorkCode(orderItem.getPlatWorkCode()) |
| | | // .setProjectCode(orderItem.getProjectCode()) |
| | | // .setSplrId(companys.getId()) |
| | | // .setUnit(orderItem.getStockUnit()) |
| | | // .setStockUnit(orderItem.getStockUnit()) |
| | | // .setMatnrCode(matnr.getCode()) |
| | | // .setAnfme(orderItem.getAnfme()) |
| | | // .setMatnrId(matnr.getId()) |
| | | // .setIsptResult(orderItem.getIsptResult()) |
| | | // .setMaktx(matnr.getName()) |
| | | // .setSplrBatch(orderItem.getSplrBatch()) |
| | | // .setWeight(matnr.getWeight()) |
| | | // .setFieldsIndex(orderItem.getFieldsIndex()) |
| | | // .setShipperId(matnr.getShipperId()); |
| | | // List<WarehouseAreasItem> warehousList = StringUtils.isNotBlank(orderItem.getFieldsIndex()) |
| | | // ? warehouseAreasItemService.list(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getFieldsIndex, orderItem.getFieldsIndex())) |
| | | // : Collections.emptyList(); |
| | | // WarehouseAreasItem warehousItem = warehousList.isEmpty() ? null : warehousList.get(0); |
| | | // if (!Objects.isNull(warehousItem)) { |
| | | // List<FieldsItem> fieldsList = fieldsItemService.list(new LambdaQueryWrapper<FieldsItem>() |
| | | // .eq(FieldsItem::getUuid, orderItem.getFieldsIndex()).last("LIMIT 1")); |
| | | // FieldsItem fieldsItem = fieldsList.isEmpty() ? null : fieldsList.get(0); |
| | | // if (!Objects.isNull(fieldsItem)) { |
| | | // throw new CoolException("票号:" + fieldsItem.getValue() + "已在收货区,不可推送相当票号数据。请联系管理员!!" ); |
| | | // } |
| | | // } |
| | | // LambdaQueryWrapper<WarehouseAreasItem> queryWrapper = new LambdaQueryWrapper<WarehouseAreasItem>() |
| | | // .eq(WarehouseAreasItem::getMatnrCode, orderItem.getMatnrCode()) |
| | | // .eq(!Cools.isEmpty(orderItem.getFieldsIndex()), WarehouseAreasItem::getFieldsIndex, orderItem.getFieldsIndex()) |
| | | // .eq(WarehouseAreasItem::getAsnCode, orderItem.getOrderCode()) |
| | | // .eq(StringUtils.isNotBlank(orderItem.getSplrBatch()), WarehouseAreasItem::getSplrBatch, orderItem.getSplrBatch()); |
| | | // if (Objects.isNull(orderItem.getIsptResult())) { |
| | | // queryWrapper.isNull(WarehouseAreasItem::getIsptResult); |
| | | // } else { |
| | | // queryWrapper.eq(WarehouseAreasItem::getIsptResult, orderItem.getIsptResult()); |
| | | // } |
| | | // List<WarehouseAreasItem> serviceList = warehouseAreasItemService.list(queryWrapper); |
| | | // WarehouseAreasItem serviceOne = serviceList.isEmpty() ? null : serviceList.get(0); |
| | | // if (!Objects.isNull(serviceOne)) { |
| | | // item.setId(serviceOne.getId()); |
| | | // item.setAnfme(orderItem.getAnfme()); |
| | | // } |
| | | // if (!warehouseAreasItemService.saveOrUpdate(item)) { |
| | | // throw new CoolException("收货失败!!"); |
| | | // } |
| | | // } |
| | | @Scheduled(cron = "0/25 * * * * ?") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public synchronized void IgnoreReceipt() { |
| | | Config config = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | if (Objects.isNull(config) || !Boolean.parseBoolean(config.getVal())) { |
| | | return; |
| | | } |
| | | List<WkOrder> orders = asnOrderService.list(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getType, OrderType.ORDER_IN.type) |
| | | .eq(WkOrder::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_UN_EXCE.val)); |
| | | if (orders.isEmpty()) { |
| | | return; |
| | | } |
| | | WarehouseAreas receiptArea = warehouseAreasService.getOne(new LambdaQueryWrapper<WarehouseAreas>() |
| | | .eq(WarehouseAreas::getType, WarehouseAreasType.WAREHOUSE_AREAS_TYPE_RECEIPT.type), false); |
| | | for (WkOrder order : orders) { |
| | | if (OrderWorkType.ORDER_WORK_TYPE_OTHER_TERANSFER.type.equals(order.getWkType())) { |
| | | WkOrder one = outStockService.getOne(new LambdaQueryWrapper<WkOrder>() |
| | | .eq(WkOrder::getPoId, order.getPoId()) |
| | | .eq(WkOrder::getWkType, OrderWorkType.ORDER_WORK_TYPE_STOCK_TERANSFER.type)); |
| | | if (Objects.isNull(one)) { |
| | | throw new CoolException("数据错误"); |
| | | } |
| | | if (!one.getExceStatus().equals(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val)) { |
| | | continue; |
| | | } |
| | | } |
| | | List<WkOrderItem> orderItems = asnOrderItemService.list(new LambdaQueryWrapper<WkOrderItem>().eq(WkOrderItem::getOrderId, order.getId())); |
| | | if (orderItems.isEmpty()) { |
| | | continue; |
| | | } |
| | | for (WkOrderItem item : orderItems) { |
| | | if (receiptArea != null) { |
| | | Matnr matnr = matnrService.getOne(new LambdaQueryWrapper<Matnr>().eq(Matnr::getId, item.getMatnrId())); |
| | | if (Objects.isNull(matnr)) { |
| | | throw new CoolException("物料不存在!!"); |
| | | } |
| | | try { |
| | | updateReceipt(receiptArea, item, order, matnr); |
| | | } catch (Exception e) { |
| | | throw new CoolException(e.getMessage()); |
| | | } |
| | | } |
| | | if (!asnOrderItemService.update(new LambdaUpdateWrapper<WkOrderItem>().set(WkOrderItem::getQty, item.getAnfme()).eq(WkOrderItem::getId, item.getId()))) { |
| | | throw new CoolException("收货单明细完成数量修改失败!!"); |
| | | } |
| | | } |
| | | if (!asnOrderService.update(new LambdaUpdateWrapper<WkOrder>() |
| | | .set(WkOrder::getQty, order.getAnfme()) |
| | | .set(WkOrder::getExceStatus, AsnExceStatus.ASN_EXCE_STATUS_RECEIPT_DONE.val) |
| | | .eq(WkOrder::getId, order.getId()))) { |
| | | throw new CoolException("收货单状态修改失败!!"); |
| | | } |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 收货区库存更新(仅 DirectWaitPakin 启用时由 IgnoreReceipt 调用) |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void updateReceipt(WarehouseAreas areasItem, WkOrderItem orderItem, WkOrder wkOrder, Matnr matnr) throws Exception { |
| | | Companys companys = new Companys(); |
| | | if (StringUtils.isNoneBlank(orderItem.getSplrCode())) { |
| | | companys = companysService.getOne(new LambdaQueryWrapper<Companys>().eq(Companys::getCode, orderItem.getSplrCode())); |
| | | } |
| | | if (Objects.isNull(companys)) { |
| | | companys = new Companys(); |
| | | } |
| | | WarehouseAreasItem item = new WarehouseAreasItem(); |
| | | item.setTrackCode(orderItem.getBarcode()) |
| | | .setAreaName(areasItem.getName()) |
| | | .setAreaId(areasItem.getId()) |
| | | .setAsnItemId(orderItem.getId()) |
| | | .setAsnCode(wkOrder.getCode()) |
| | | .setAsnId(wkOrder.getId()) |
| | | .setProdTime(orderItem.getProdTime()) |
| | | .setPlatItemId(orderItem.getPlatItemId()) |
| | | .setPlatOrderCode(orderItem.getPlatOrderCode()) |
| | | .setPlatWorkCode(orderItem.getPlatWorkCode()) |
| | | .setProjectCode(orderItem.getProjectCode()) |
| | | .setSplrId(companys.getId()) |
| | | .setUnit(orderItem.getStockUnit()) |
| | | .setStockUnit(orderItem.getStockUnit()) |
| | | .setMatnrCode(matnr.getCode()) |
| | | .setAnfme(orderItem.getAnfme()) |
| | | .setMatnrId(matnr.getId()) |
| | | .setIsptResult(orderItem.getIsptResult()) |
| | | .setMaktx(matnr.getName()) |
| | | .setSplrBatch(orderItem.getSplrBatch()) |
| | | .setWeight(matnr.getWeight()) |
| | | .setFieldsIndex(orderItem.getFieldsIndex()) |
| | | .setShipperId(matnr.getShipperId()); |
| | | if (StringUtils.isNotBlank(orderItem.getFieldsIndex())) { |
| | | List<WarehouseAreasItem> warehousList = warehouseAreasItemService.list(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getFieldsIndex, orderItem.getFieldsIndex())); |
| | | WarehouseAreasItem warehousItem = warehousList.isEmpty() ? null : warehousList.get(0); |
| | | if (Objects.nonNull(warehousItem)) { |
| | | List<FieldsItem> fieldsList = fieldsItemService.list(new LambdaQueryWrapper<FieldsItem>().eq(FieldsItem::getUuid, orderItem.getFieldsIndex()).last("LIMIT 1")); |
| | | FieldsItem fieldsItem = fieldsList.isEmpty() ? null : fieldsList.get(0); |
| | | if (Objects.nonNull(fieldsItem)) { |
| | | throw new CoolException("票号:" + fieldsItem.getValue() + "已在收货区,不可推送相当票号数据。请联系管理员!!"); |
| | | } |
| | | } |
| | | } |
| | | LambdaQueryWrapper<WarehouseAreasItem> queryWrapper = new LambdaQueryWrapper<WarehouseAreasItem>() |
| | | .eq(WarehouseAreasItem::getMatnrCode, orderItem.getMatnrCode()) |
| | | .eq(!Cools.isEmpty(orderItem.getFieldsIndex()), WarehouseAreasItem::getFieldsIndex, orderItem.getFieldsIndex()) |
| | | .eq(WarehouseAreasItem::getAsnCode, orderItem.getOrderCode()) |
| | | .eq(StringUtils.isNotBlank(orderItem.getSplrBatch()), WarehouseAreasItem::getSplrBatch, orderItem.getSplrBatch()); |
| | | if (Objects.isNull(orderItem.getIsptResult())) { |
| | | queryWrapper.isNull(WarehouseAreasItem::getIsptResult); |
| | | } else { |
| | | queryWrapper.eq(WarehouseAreasItem::getIsptResult, orderItem.getIsptResult()); |
| | | } |
| | | WarehouseAreasItem serviceOne = warehouseAreasItemService.getOne(queryWrapper); |
| | | if (Objects.nonNull(serviceOne)) { |
| | | item.setId(serviceOne.getId()); |
| | | Double anfme = Math.round((item.getAnfme() + serviceOne.getAnfme()) * 10000) / 10000.0; |
| | | item.setAnfme(anfme); |
| | | } |
| | | if (!warehouseAreasItemService.saveOrUpdate(item)) { |
| | | throw new CoolException("收货失败!!"); |
| | | } |
| | | } |
| | | |
| | | |
| | | /** |
| | |
| | | @Scheduled(cron = "0/35 * * * * ? ") |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public void pubTaskToWcs() { |
| | | log.info("定时任务开始执行:任务下发到RCS"); |
| | | log.debug("定时任务开始执行:任务下发到RCS"); |
| | | Long loginUserId = SystemAuthUtils.getLoginUserId(); |
| | | List<Integer> list = Arrays.asList( |
| | | TaskType.TASK_TYPE_LOC_MOVE.type |
| | |
| | | .in(Task::getTaskType, list) |
| | | .in(Task::getTaskStatus, integers) |
| | | .orderByDesc(Task::getSort)); |
| | | log.info("查询到待下发任务数量:{}", tasks.size()); |
| | | log.debug("查询到待下发任务数量:{}", tasks.size()); |
| | | if (tasks.isEmpty()) { |
| | | log.debug("没有待下发的任务,定时任务结束"); |
| | | return; |
| | |
| | | // } |
| | | // } |
| | | /**下发普通站点任务,报错回滚,不再往下执行*/ |
| | | log.info("开始下发{}个任务到RCS", tasks.size()); |
| | | log.debug("开始下发{}个任务到RCS", tasks.size()); |
| | | taskService.pubTaskToWcs(tasks); |
| | | log.info("定时任务执行完成:任务下发到RCS"); |
| | | log.debug("定时任务执行完成:任务下发到RCS"); |
| | | } |
| | | |
| | | /** |
| | |
| | | } |
| | | } else if ((task.getTaskType() >= TaskType.TASK_TYPE_OUT.type && task.getTaskType() <= TaskType.TASK_TYPE_EMPITY_OUT.type) |
| | | || task.getTaskType().equals(TaskType.TASK_TYPE_PICK_IN.type)) { |
| | | /**判断单据是否完成**/ |
| | | // 只有波次类型的任务才需要查询波次关联单 |
| | | /**判断单据是否完成:波次下发、按单下发(点击下发任务)完成后均将出库单置为完结*/ |
| | | Set<Long> orderIdsToDone = new HashSet<>(); |
| | | if (task.getResource() != null && task.getResource().equals(TaskResouceType.TASK_RESOUCE_WAVE_TYPE.val)) { |
| | | Set<Long> longSet = taskItems.stream() |
| | | .map(TaskItem::getSourceId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | |
| | | if (longSet.isEmpty()) { |
| | | logger.warn("任务{}的任务明细中没有有效的sourceId,跳过波次关联单查询。任务编码:{},任务类型:{}", |
| | | task.getId(), task.getTaskCode(), task.getTaskType()); |
| | | } else { |
| | | if (!longSet.isEmpty()) { |
| | | List<WaveOrderRela> waveOrderRelas = waveOrderRelaService.list(new LambdaQueryWrapper<WaveOrderRela>() |
| | | .in(WaveOrderRela::getWaveId, longSet)); |
| | | if (Cools.isEmpty(waveOrderRelas)) { |
| | | logger.warn("任务{}的波次对应关联单未找到,可能是数据不一致或任务不是通过波次创建的。任务编码:{},sourceIds:{}", |
| | | task.getId(), task.getTaskCode(), longSet); |
| | | } else { |
| | | Set<Long> orderIds = waveOrderRelas.stream().map(WaveOrderRela::getOrderId).collect(Collectors.toSet()); |
| | | List<WkOrder> wkOrders = asnOrderService.listByIds(orderIds); |
| | | if (wkOrders.isEmpty()) { |
| | | logger.warn("任务{}的关联单据不存在。任务编码:{},orderIds:{}", |
| | | task.getId(), task.getTaskCode(), orderIds); |
| | | } else { |
| | | Config allowChang = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE)); |
| | | |
| | | wkOrders.forEach(order -> { |
| | | //判断是否允许超收,不允许超收添加拒收判断 |
| | | if (!Objects.isNull(allowChang)) { |
| | | if (!Boolean.parseBoolean(allowChang.getVal())) { |
| | | if (order.getAnfme().compareTo(order.getQty()) == 0) { |
| | | order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val); |
| | | if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) { |
| | | order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0); |
| | | } |
| | | if (!asnOrderService.updateById(order)) { |
| | | logger.error("出库单更新状态失败。订单ID:{},订单编码:{}", order.getId(), order.getCode()); |
| | | } |
| | | } |
| | | } else { |
| | | if (order.getAnfme().compareTo(order.getQty()) <= 0) { |
| | | order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val); |
| | | if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) { |
| | | order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0); |
| | | } |
| | | if (!asnOrderService.updateById(order)) { |
| | | logger.error("出库单更新状态失败。订单ID:{},订单编码:{}", order.getId(), order.getCode()); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | //检查单据是否完成 |
| | | |
| | | }); |
| | | } |
| | | if (!Cools.isEmpty(waveOrderRelas)) { |
| | | orderIdsToDone.addAll(waveOrderRelas.stream().map(WaveOrderRela::getOrderId).collect(Collectors.toSet())); |
| | | } |
| | | } |
| | | } else { |
| | | logger.debug("任务{}不是波次类型任务(资源类型:{}),跳过波次关联单查询。任务编码:{}", |
| | | task.getId(), task.getResource(), task.getTaskCode()); |
| | | } else if (task.getResource() != null && task.getResource().equals(TaskResouceType.TASK_RESOUCE_ORDER_TYPE.val)) { |
| | | // 按单下发:任务明细 sourceId 为出库单ID |
| | | Set<Long> ids = taskItems.stream() |
| | | .map(TaskItem::getSourceId) |
| | | .filter(Objects::nonNull) |
| | | .collect(Collectors.toSet()); |
| | | orderIdsToDone.addAll(ids); |
| | | } |
| | | if (!orderIdsToDone.isEmpty()) { |
| | | List<WkOrder> wkOrders = asnOrderService.listByIds(orderIdsToDone); |
| | | if (!wkOrders.isEmpty()) { |
| | | Config allowChang = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.ALLOW_OVER_CHANGE)); |
| | | wkOrders.forEach(order -> { |
| | | if (order.getAnfme() == null) return; |
| | | boolean canDone = Boolean.TRUE.equals(allowChang != null && Boolean.parseBoolean(allowChang.getVal())) |
| | | ? (order.getQty() != null && order.getAnfme().compareTo(order.getQty()) <= 0) |
| | | : (order.getQty() != null && order.getAnfme().compareTo(order.getQty()) == 0); |
| | | if (canDone) { |
| | | order.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_DONE.val); |
| | | if (order.getQty() == null || order.getQty().compareTo(0.0) == 0) { |
| | | order.setQty(order.getWorkQty() != null ? order.getWorkQty() : 0.0); |
| | | } |
| | | if (!asnOrderService.updateById(order)) { |
| | | logger.error("出库单更新状态失败。订单ID:{},订单编码:{}", order.getId(), order.getCode()); |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | } |
| | | |
| | | //出库单上报RCS修改库位状态 |
| | |
| | | |
| | | IPage<Map<String, Object>> listByAsnId(PageParam<WkOrderItem, BaseParam> pageParam, QueryWrapper<WkOrderItem> buildWrapper); |
| | | |
| | | /** 为收货明细列表填充组托数量(palletQty),来源于组托档 WaitPakinItem 按 asnItemId 汇总 */ |
| | | void fillPalletQty(List<WkOrderItem> items); |
| | | |
| | | } |
| | |
| | | R getDashbord(); |
| | | |
| | | R getStockTrand(); |
| | | |
| | | /** 为入库通知单列表填充组托数量(palletQty),用于列表展示 */ |
| | | void fillPalletQty(List<WkOrder> orders); |
| | | } |
| | |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.common.utils.QuantityUtils; |
| | | import com.vincent.rsf.server.manager.entity.WaitPakinItem; |
| | | import com.vincent.rsf.server.manager.entity.WkOrder; |
| | | import com.vincent.rsf.server.manager.entity.WkOrderItem; |
| | | import com.vincent.rsf.server.manager.enums.OrderType; |
| | |
| | | import com.vincent.rsf.server.manager.service.AsnOrderItemService; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.server.manager.service.AsnOrderService; |
| | | import com.vincent.rsf.server.manager.service.WaitPakinItemService; |
| | | import com.vincent.rsf.server.manager.service.CompanysService; |
| | | import com.vincent.rsf.server.manager.service.MatnrService; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | |
| | | private DictDataService dictDataService; |
| | | @Autowired |
| | | private AsnOrderService asnOrderService; |
| | | |
| | | @Autowired |
| | | private WaitPakinItemService waitPakinItemService; |
| | | |
| | | @Override |
| | | public R generateBarcode(List<Long> orders) { |
| | |
| | | return hsahMap; |
| | | } |
| | | |
| | | @Override |
| | | public void fillPalletQty(List<WkOrderItem> items) { |
| | | if (items == null || items.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> itemIds = items.stream().map(WkOrderItem::getId).filter(Objects::nonNull).distinct().collect(Collectors.toList()); |
| | | if (itemIds.isEmpty()) { |
| | | return; |
| | | } |
| | | List<WaitPakinItem> allItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().in(WaitPakinItem::getAsnItemId, itemIds).eq(WaitPakinItem::getDeleted, 0)); |
| | | Map<Long, Double> sumByAsnItemId = allItems.stream() |
| | | .collect(Collectors.groupingBy(WaitPakinItem::getAsnItemId, |
| | | Collectors.summingDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0))); |
| | | sumByAsnItemId.replaceAll((k, v) -> QuantityUtils.roundToScale(v)); |
| | | for (WkOrderItem item : items) { |
| | | if (item.getId() != null) { |
| | | item.setPalletQty(sumByAsnItemId.getOrDefault(item.getId(), 0.0)); |
| | | } |
| | | } |
| | | } |
| | | |
| | | } |
| | |
| | | import com.vincent.rsf.server.manager.controller.params.BatchUpdateParam; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.*; |
| | | import com.vincent.rsf.server.common.utils.QuantityUtils; |
| | | import com.vincent.rsf.server.manager.mapper.AsnOrderMapper; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | |
| | | public R removeOrders(List<Long> ids) { |
| | | // 已组托不可删除,需先解除组托 |
| | | long palletizedCount = waitPakinItemService.count(new LambdaQueryWrapper<WaitPakinItem>() |
| | | .in(WaitPakinItem::getAsnId, ids)); |
| | | .in(WaitPakinItem::getAsnId, ids).eq(WaitPakinItem::getDeleted, 0)); |
| | | if (palletizedCount > 0) { |
| | | throw new CoolException("单据已组托,请先解除组托后再删除!!"); |
| | | } |
| | |
| | | throw new CoolException("原单据删除失败!!"); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void fillPalletQty(List<WkOrder> orders) { |
| | | if (orders == null || orders.isEmpty()) { |
| | | return; |
| | | } |
| | | List<Long> orderIds = orders.stream().map(WkOrder::getId).filter(Objects::nonNull).collect(Collectors.toList()); |
| | | if (orderIds.isEmpty()) { |
| | | return; |
| | | } |
| | | List<WaitPakinItem> allItems = waitPakinItemService.list( |
| | | new LambdaQueryWrapper<WaitPakinItem>().in(WaitPakinItem::getAsnId, orderIds).eq(WaitPakinItem::getDeleted, 0)); |
| | | Map<Long, Double> sumByAsnId = allItems.stream() |
| | | .collect(Collectors.groupingBy(WaitPakinItem::getAsnId, |
| | | Collectors.summingDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0))); |
| | | sumByAsnId.replaceAll((k, v) -> QuantityUtils.roundToScale(v)); |
| | | for (WkOrder order : orders) { |
| | | if (order.getId() != null) { |
| | | order.setPalletQty(sumByAsnId.getOrDefault(order.getId(), 0.0)); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | if (Objects.isNull(waveItem)) { |
| | | throw new CoolException("波次明细不存在!!"); |
| | | } |
| | | // try { |
| | | // saveOutStockItem(maps.get(key), null, waveItem, null, loginUserId); |
| | | // } catch (Exception e) { |
| | | // throw new CoolException(e.getMessage()); |
| | | // } |
| | | try { |
| | | saveOutStockItem(maps.get(key), null, waveItem, null, loginUserId); |
| | | } catch (Exception e) { |
| | | throw new CoolException(e.getMessage()); |
| | | } |
| | | } else if (task.getResource().equals(TaskResouceType.TASK_RESOUCE_ORDER_TYPE.val)) { |
| | | WkOrderItem orderItem = asnOrderItemService.getById(key); |
| | | if (Objects.isNull(orderItem)) { |
| | |
| | | .setUpdateTime(new Date()) |
| | | .setAnfme(sum); |
| | | if (!Objects.isNull(waveItem)) { |
| | | //TODO 生成波次时需要将波次号写入单据,通过物料,批次,动态字段等唯一值反查单据信息 |
| | | // 波次出库:更新出库单完成数量及出库单明细执行数量 |
| | | stock.setSourceId(waveItem.getId()).setType(OrderType.ORDER_OUT.type); |
| | | Long orderId = waveItem.getOrderId(); |
| | | if (orderId != null) { |
| | | WkOrder wkOrder = asnOrderService.getById(orderId); |
| | | if (wkOrder != null) { |
| | | Double curQty = wkOrder.getQty() != null ? wkOrder.getQty() : 0.0; |
| | | Double newQty = QuantityUtils.roundToScale(curQty + sum); |
| | | wkOrder.setExceStatus(AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val).setQty(newQty); |
| | | if (!asnOrderService.updateById(wkOrder)) { |
| | | throw new CoolException("出库单完成数量更新失败!!"); |
| | | } |
| | | } |
| | | // 按出库单明细汇总本次任务数量,更新各明细 workQty |
| | | Map<Long, Double> sumByOrderItemId = taskItems.stream() |
| | | .filter(ti -> ti.getOrderItemId() != null) |
| | | .collect(Collectors.groupingBy(TaskItem::getOrderItemId, |
| | | Collectors.summingDouble(ti -> ti.getAnfme() != null ? ti.getAnfme() : 0.0))); |
| | | for (Map.Entry<Long, Double> e : sumByOrderItemId.entrySet()) { |
| | | WkOrderItem oi = outStockItemService.getById(e.getKey()); |
| | | if (oi != null) { |
| | | Double wq = oi.getWorkQty() != null ? oi.getWorkQty() : 0.0; |
| | | oi.setWorkQty(QuantityUtils.roundToScale(wq + e.getValue())); |
| | | if (!outStockItemService.updateById(oi)) { |
| | | throw new CoolException("出库单明细执行数量更新失败!!"); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } else if (!Objects.isNull(orderItem) && StringUtils.isNotBlank(orderItem.getId() + "")) { |
| | | WkOrder wkOrder = asnOrderService.getById(orderItem.getOrderId()); |
| | | Double qty = Math.round((wkOrder.getQty() + sum) * 1000000) / 1000000.0; |
| | |
| | | List<TaskItem> items = orderMap.get(key); |
| | | //保存入出库明细 |
| | | saveStockItems(items, task, pakinItem.getId(), pakinItem.getAsnCode(), pakinItem.getWkType(), pakinItem.getType(), loginUserId); |
| | | // 收货区已停用,不再移出收货区库存 |
| | | // if (Objects.nonNull(pakinItem.getSource())) { |
| | | // removeReceiptStock(pakinItem, loginUserId); |
| | | // } |
| | | // DirectWaitPakin 启用时组托来自收货区,入库完成后移出收货区库存;未启用时 source 为 null 不执行 |
| | | if (Objects.nonNull(pakinItem.getSource())) { |
| | | removeReceiptStock(pakinItem, loginUserId); |
| | | } |
| | | }); |
| | | |
| | | Set<Long> pkinItemIds = taskItems.stream().map(TaskItem::getSource).collect(Collectors.toSet()); |
| | |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.server.common.utils.QuantityUtils; |
| | | import com.vincent.rsf.server.system.constant.GlobalConfigCode; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | import com.vincent.rsf.server.system.entity.Config; |
| | | import com.vincent.rsf.server.system.service.ConfigService; |
| | | import com.vincent.rsf.server.system.utils.SerialRuleUtils; |
| | | import org.apache.commons.lang3.StringUtils; |
| | | import org.springframework.beans.BeanUtils; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | |
| | | private TaskItemService taskItemService; |
| | | @Autowired |
| | | private MatnrMapper matnrMapper; |
| | | @Autowired |
| | | private ConfigService configService; |
| | | |
| | | |
| | | /** |
| | |
| | | WaitPakin pakin = waitPakinService.getOne(new LambdaQueryWrapper<WaitPakin>() |
| | | .eq(WaitPakin::getBarcode, waitPakin.getBarcode())); |
| | | |
| | | // 如果料箱码已经组托过,提示已组托,请更换料箱码 |
| | | if (!Objects.isNull(pakin)) { |
| | | // 半箱时允许同一料箱码继续组托追加;非半箱时料箱码已组托则不可再用 |
| | | if (!Objects.isNull(pakin) && !Boolean.TRUE.equals(waitPakin.getIsHalf())) { |
| | | throw new CoolException("已组托,请更换料箱码"); |
| | | } |
| | | |
| | |
| | | |
| | | Double sum = QuantityUtils.roundToScale(waitPakin.getItems().stream().mapToDouble(PakinItem::getReceiptQty).sum()); |
| | | |
| | | WaitPakin waitPakin1 = new WaitPakin(); |
| | | Config directPakinConfig = configService.getOne(new LambdaQueryWrapper<Config>().eq(Config::getFlag, GlobalConfigCode.DIRECT_WAIT_PAKIN)); |
| | | boolean directWaitPakin = directPakinConfig != null && Boolean.parseBoolean(directPakinConfig.getVal()); |
| | | |
| | | WaitPakin waitPakin1; |
| | | if (Objects.isNull(pakin)) { |
| | | waitPakin1 = new WaitPakin(); |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_WAIT_PAKIN_CODE, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | throw new CoolException("编码规则错误: 编码规则「SYS_WAIT_PAKIN_CODE」规则是不存在"); |
| | | } |
| | | waitPakin1.setCode(ruleCode) |
| | | //状态修改为入库中 |
| | | .setIoStatus(PakinIOStatus.PAKIN_IO_STATUS_DONE.val) |
| | | .setAnfme(sum) |
| | | .setUpdateBy(userId) |
| | |
| | | throw new CoolException("主单保存失败!!"); |
| | | } |
| | | } else { |
| | | BeanUtils.copyProperties(pakin, waitPakin1); |
| | | // 半箱追加:沿用已有组托单,只追加明细 |
| | | waitPakin1 = pakin; |
| | | } |
| | | |
| | | List<WaitPakinItem> items = new ArrayList<>(); |
| | | if (directWaitPakin) { |
| | | // DirectWaitPakin 启用:组托来自收货区,param 中 id 为 WarehouseAreasItem.id |
| | | for (PakinItem pakinItem1 : waitPakin.getItems()) { |
| | | WarehouseAreasItem areaItem = warehouseAreasItemService.getById(pakinItem1.getId()); |
| | | if (areaItem == null) { |
| | | throw new CoolException("物料未送至收货区!!"); |
| | | } |
| | | WaitPakinItem pakinItem = new WaitPakinItem(); |
| | | pakinItem.setPakinId(waitPakin1.getId()) |
| | | .setSource(areaItem.getId()) |
| | | .setAsnId(areaItem.getAsnId()) |
| | | .setAsnCode(areaItem.getAsnCode()) |
| | | .setAsnItemId(areaItem.getAsnItemId()) |
| | | .setIsptResult(areaItem.getIsptResult()) |
| | | .setPlatItemId(areaItem.getPlatItemId()) |
| | | .setPlatOrderCode(areaItem.getPlatOrderCode()) |
| | | .setPlatWorkCode(areaItem.getPlatWorkCode()) |
| | | .setProjectCode(areaItem.getProjectCode()) |
| | | .setBatch(areaItem.getSplrBatch()) |
| | | .setUnit(areaItem.getStockUnit()) |
| | | .setFieldsIndex(areaItem.getFieldsIndex()) |
| | | .setMatnrId(areaItem.getMatnrId()) |
| | | .setMaktx(areaItem.getMaktx()) |
| | | .setUpdateBy(userId) |
| | | .setCreateBy(userId) |
| | | .setMatnrCode(areaItem.getMatnrCode()); |
| | | WkOrder order = asnOrderService.getById(areaItem.getAsnId()); |
| | | if (order != null) { |
| | | pakinItem.setType(order.getType()).setWkType(StringUtils.isNotBlank(order.getWkType()) ? Short.parseShort(order.getWkType()) : null); |
| | | } |
| | | if (pakinItem1.getReceiptQty() == null || pakinItem1.getReceiptQty().compareTo(0.0) <= 0) { |
| | | throw new CoolException("组托数量不能小于等于零!!"); |
| | | } |
| | | if (QuantityUtils.compare(pakinItem1.getReceiptQty(), areaItem.getAnfme()) > 0) { |
| | | throw new CoolException("组托数量不能大于收货数量!!"); |
| | | } |
| | | pakinItem.setAnfme(QuantityUtils.roundToScale(pakinItem1.getReceiptQty())).setTrackCode(pakinItem1.getTrackCode()); |
| | | items.add(pakinItem); |
| | | } |
| | | if (!waitPakinItemService.saveBatch(items)) { |
| | | throw new CoolException("组托明细保存失败!!"); |
| | | } |
| | | for (WaitPakinItem pakinItem : items) { |
| | | WarehouseAreasItem one = warehouseAreasItemService.getOne(new LambdaQueryWrapper<WarehouseAreasItem>().eq(WarehouseAreasItem::getId, pakinItem.getSource())); |
| | | if (one == null) { |
| | | throw new CoolException("收货区数据错误!!"); |
| | | } |
| | | Double workQty = QuantityUtils.roundToScale(QuantityUtils.add(one.getWorkQty() != null ? one.getWorkQty() : 0.0, pakinItem.getAnfme())); |
| | | Double qty = QuantityUtils.roundToScale(QuantityUtils.add(workQty, one.getQty() != null ? one.getQty() : 0.0)); |
| | | one.setWorkQty(workQty); |
| | | if (QuantityUtils.compare(qty, one.getAnfme() != null ? one.getAnfme() : 0.0) > 0) { |
| | | throw new CoolException("组托数量不能大于收货数量!!"); |
| | | } |
| | | if (!warehouseAreasItemService.saveOrUpdate(one)) { |
| | | throw new CoolException("收货区执行数量修改失败!!"); |
| | | } |
| | | } |
| | | // 半箱追加时需按该组托单下全部明细重算总数量 |
| | | List<WaitPakinItem> allItems = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getPakinId, waitPakin1.getId())); |
| | | waitPakin1.setAnfme(QuantityUtils.roundToScale(allItems.stream().mapToDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0).sum())); |
| | | if (!this.updateById(waitPakin1)) { |
| | | throw new CoolException("组托数量修改失败!!"); |
| | | } |
| | | return waitPakin1; |
| | | } |
| | | |
| | | // 有ASN时:按订单明细汇总已组托数量,校验 已组托+本次组盘 ≤ 计划数量 |
| | | Map<Long, Double> alreadyPalletizedByItemId = new java.util.HashMap<>(); |
| | | for (PakinItem p : waitPakin.getItems()) { |
| | | if (StringUtils.isNotBlank(p.getAsnCode()) && p.getId() != null) { |
| | | alreadyPalletizedByItemId.putIfAbsent(p.getId(), null); |
| | | } |
| | | } |
| | | if (!alreadyPalletizedByItemId.isEmpty()) { |
| | | for (Long asnItemId : alreadyPalletizedByItemId.keySet()) { |
| | | double alreadySum = waitPakinItemService.list(new LambdaQueryWrapper<WaitPakinItem>().eq(WaitPakinItem::getAsnItemId, asnItemId).eq(WaitPakinItem::getDeleted, 0)) |
| | | .stream().mapToDouble(w -> w.getAnfme() != null ? w.getAnfme() : 0.0).sum(); |
| | | alreadyPalletizedByItemId.put(asnItemId, QuantityUtils.roundToScale(alreadySum)); |
| | | } |
| | | } |
| | | |
| | | for (PakinItem pakinItem1 : waitPakin.getItems()) { |
| | | WaitPakinItem pakinItem = new WaitPakinItem(); |
| | | |
| | | // 如果ASN单号为空,从物料信息表获取物料信息,不查询收货区 |
| | | if (StringUtils.isBlank(pakinItem1.getAsnCode())) { |
| | | if (Objects.isNull(pakinItem1.getMatnrId())) { |
| | | throw new CoolException("物料ID不能为空!!"); |
| | |
| | | if (waitPakinItem.getReceiptQty() == null || waitPakinItem.getReceiptQty().compareTo(0.0) <= 0) { |
| | | throw new CoolException("组托数量不能小于等于零!!"); |
| | | } |
| | | if (QuantityUtils.compare(waitPakinItem.getReceiptQty(), orderItem.getAnfme()) > 0) { |
| | | throw new CoolException("组托数量不能大于计划数量!!"); |
| | | Double anfme = orderItem.getAnfme() != null ? orderItem.getAnfme() : 0.0; |
| | | Double already = alreadyPalletizedByItemId.getOrDefault(orderItem.getId(), 0.0); |
| | | if (QuantityUtils.compare(QuantityUtils.add(already, waitPakinItem.getReceiptQty()), anfme) > 0) { |
| | | throw new CoolException("组托数量不能超过可组盘数量(计划" + anfme + ",已组托" + already + ",本次最多" + QuantityUtils.subtract(anfme, already) + ")!!"); |
| | | } |
| | | pakinItem.setAnfme(QuantityUtils.roundToScale(waitPakinItem.getReceiptQty())) |
| | | .setTrackCode(waitPakinItem.getTrackCode()); |
| | |
| | | throw new CoolException("组托明细删除失败!!"); |
| | | } |
| | | } |
| | | // 收货区已停用,不再回写收货区 |
| | | // for (int i = 0; i < warehouseAreasItems.size(); i++) { |
| | | // if (warehouseAreasItems.get(i).getId().equals(pakinItems.get(i1).getSource())) { |
| | | // double v = Math.round((warehouseAreasItems.get(i).getWorkQty() - item.getReceiptQty()) * 1000000) / 1000000.0; |
| | | // warehouseAreasItems.get(i).setWorkQty(v); |
| | | // if (!warehouseAreasItemService.updateById(warehouseAreasItems.get(i))) { |
| | | // throw new CoolException("收货区数量修改失败!!"); |
| | | // } |
| | | // } |
| | | // } |
| | | // DirectWaitPakin 启用时组托来自收货区,解绑需回写收货区 workQty |
| | | if (Objects.nonNull(pakinItems.get(i1).getSource())) { |
| | | for (WarehouseAreasItem areaItem : warehouseAreasItems) { |
| | | if (areaItem.getId().equals(pakinItems.get(i1).getSource())) { |
| | | Double v = QuantityUtils.roundToScale(QuantityUtils.subtract(areaItem.getWorkQty() != null ? areaItem.getWorkQty() : 0.0, item.getReceiptQty())); |
| | | areaItem.setWorkQty(v); |
| | | if (!warehouseAreasItemService.updateById(areaItem)) { |
| | | throw new CoolException("收货区数量修改失败!!"); |
| | | } |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | /** 云仓通知状态:失败 */ |
| | | public final static String CLOUD_WMS_NOTIFY_STATUS_FAIL = "CLOUD_WMS_NOTIFY_STATUS_FAIL"; |
| | | |
| | | /** 指定物料自动全版出库:物料编码(val=物料编码时启用按物料自动生成全版出库单) */ |
| | | public final static String AUTO_FULL_OUT_MATNR_CODE = "AUTO_FULL_OUT_MATNR_CODE"; |
| | | /** 是否启用:有库存时自动生成全版出库单 */ |
| | | public final static String AUTO_FULL_OUT_ENABLED = "AUTO_FULL_OUT_ENABLED"; |
| | | /** 是否启用:该物料出库单自动下发任务 */ |
| | | public final static String AUTO_FULL_OUT_DISPATCH_ENABLED = "AUTO_FULL_OUT_DISPATCH_ENABLED"; |
| | | /** 是否启用:RCS 入库通知时自动组托 */ |
| | | public final static String AUTO_PAKIN_ON_ASN_ENABLED = "AUTO_PAKIN_ON_ASN_ENABLED"; |
| | | /** 自动组托数量(与 AUTO_PAKIN_ON_ASN_ENABLED 配合,每条明细组托数量) */ |
| | | public final static String AUTO_PAKIN_QTY = "AUTO_PAKIN_QTY"; |
| | | |
| | | } |
| New file |
| | |
| | | -- 指定物料自动化定时任务配置(物料编码、自动全版出库、自动下发、入库自动组托及数量) |
| | | -- 使用方式:在系统配置中维护以下 flag,val 填物料编码或 true/false/数字 |
| | | INSERT INTO `sys_config` (`uuid`, `name`, `flag`, `type`, `val`, `content`, `status`, `deleted`, `tenant_id`, `create_by`, `create_time`, `update_by`, `update_time`, `memo`) |
| | | VALUES |
| | | (UPPER(UUID()), '指定物料编码(自动全版出库/自动组托)', 'AUTO_FULL_OUT_MATNR_CODE', 3, '', '填物料编码时参与自动全版出库与自动组托', 1, 0, 1, NULL, NOW(), NULL, NOW(), '与 AUTO_FULL_OUT_ENABLED 等配合'), |
| | | (UPPER(UUID()), '启用:有库存时自动生成全版出库单', 'AUTO_FULL_OUT_ENABLED', 1, 'false', 'true/false', 1, 0, 1, NULL, NOW(), NULL, NOW(), '需配置 AUTO_FULL_OUT_MATNR_CODE'), |
| | | (UPPER(UUID()), '启用:该物料出库单自动下发任务', 'AUTO_FULL_OUT_DISPATCH_ENABLED', 1, 'false', 'true/false', 1, 0, 1, NULL, NOW(), NULL, NOW(), '需配置 AUTO_FULL_OUT_MATNR_CODE'), |
| | | (UPPER(UUID()), '启用:RCS入库通知时自动组托', 'AUTO_PAKIN_ON_ASN_ENABLED', 1, 'false', 'true/false', 1, 0, 1, NULL, NOW(), NULL, NOW(), '需配置 AUTO_FULL_OUT_MATNR_CODE、AUTO_PAKIN_QTY'), |
| | | (UPPER(UUID()), '自动组托数量', 'AUTO_PAKIN_QTY', 2, '1', '每条入库明细自动组托数量', 1, 0, 1, NULL, NOW(), NULL, NOW(), '与 AUTO_PAKIN_ON_ASN_ENABLED 配合') |
| | | ; |