原材料入库
This commit is contained in:
@@ -10,7 +10,7 @@ CREATE TABLE IF NOT EXISTS `mes_mixer_material` (
|
||||
`minor_category_id` varchar(32) DEFAULT NULL COMMENT '物料小类',
|
||||
`material_desc` varchar(500) DEFAULT NULL COMMENT '物料描述',
|
||||
`alias_name` varchar(200) DEFAULT NULL COMMENT '物料别名',
|
||||
`feed_manage_status` int DEFAULT NULL COMMENT '投管状态:1在投管 0未投管',
|
||||
`feed_manage_status` int DEFAULT NULL COMMENT '投罐状态:1在投管 0未投管',
|
||||
`use_status` int DEFAULT NULL COMMENT '使用状态:1使用中 0停用',
|
||||
`specific_gravity` decimal(18,6) DEFAULT NULL COMMENT '比重',
|
||||
`shelf_life_days` int DEFAULT NULL COMMENT '保质期(天)',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
@@ -10,7 +11,6 @@ import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
|
||||
import org.jeecg.modules.xslmes.service.MesXslStompNotifyService;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -84,15 +84,17 @@ public class MesXslRawMaterialEntryController extends JeecgController<MesXslRawM
|
||||
}
|
||||
|
||||
@AutoLog(value = "原料入场记录-批量结存入库")
|
||||
@Operation(summary = "原料入场记录-批量结存入库:将选中记录的入库结存改为「是」")
|
||||
@Operation(summary = "原料入场记录-批量结存入库:将选中记录结存并自动汇总原材料库存")
|
||||
@RequiresPermissions("xslmes:mes_xsl_raw_material_entry:stockIn")
|
||||
@PutMapping(value = "/batchStockIn")
|
||||
public Result<String> batchStockIn(@RequestParam(name = "ids", required = true) String ids) {
|
||||
UpdateWrapper<MesXslRawMaterialEntry> uw = new UpdateWrapper<>();
|
||||
uw.in("id", Arrays.asList(ids.split(","))).set("stock_balance", "1");
|
||||
mesXslRawMaterialEntryService.update(uw);
|
||||
List<String> idList = Arrays.stream(ids.split(","))
|
||||
.map(String::trim)
|
||||
.filter(s -> !s.isEmpty())
|
||||
.toList();
|
||||
mesXslRawMaterialEntryService.batchStockInAndSyncInventory(idList);
|
||||
stompNotify.publishRawMaterialEntryChanged("batchStockIn", ids);
|
||||
return Result.OK("结存入库成功!");
|
||||
return Result.OK("结存入库成功,库存已自动汇总!");
|
||||
}
|
||||
|
||||
@AutoLog(value = "原料入场记录-批量删除")
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package org.jeecg.modules.xslmes.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
import org.jeecg.common.api.vo.Result;
|
||||
import org.jeecg.common.system.base.controller.JeecgController;
|
||||
import org.jeecg.common.system.query.QueryGenerator;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 原材料库存
|
||||
*/
|
||||
@Tag(name = "原材料库存")
|
||||
@RestController
|
||||
@RequestMapping("/xslmes/mesXslRawMaterialInventory")
|
||||
@Slf4j
|
||||
public class MesXslRawMaterialInventoryController extends JeecgController<MesXslRawMaterialInventory, IMesXslRawMaterialInventoryService> {
|
||||
|
||||
@Autowired
|
||||
private IMesXslRawMaterialInventoryService mesXslRawMaterialInventoryService;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Operation(summary = "原材料库存-分页列表查询")
|
||||
@GetMapping(value = "/list")
|
||||
public Result<IPage<MesXslRawMaterialInventory>> queryPageList(
|
||||
MesXslRawMaterialInventory mesXslRawMaterialInventory,
|
||||
@RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
|
||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize,
|
||||
HttpServletRequest req) {
|
||||
QueryWrapper<MesXslRawMaterialInventory> queryWrapper = QueryGenerator.initQueryWrapper(mesXslRawMaterialInventory, req.getParameterMap());
|
||||
Page<MesXslRawMaterialInventory> page = new Page<>(pageNo, pageSize);
|
||||
IPage<MesXslRawMaterialInventory> pageList = mesXslRawMaterialInventoryService.page(page, queryWrapper);
|
||||
fillMaterialDisplay(pageList.getRecords());
|
||||
return Result.OK(pageList);
|
||||
}
|
||||
|
||||
@Operation(summary = "原材料库存-通过id查询")
|
||||
@GetMapping(value = "/queryById")
|
||||
public Result<MesXslRawMaterialInventory> queryById(@RequestParam(name = "id", required = true) String id) {
|
||||
MesXslRawMaterialInventory entity = mesXslRawMaterialInventoryService.getById(id);
|
||||
if (entity == null) {
|
||||
return Result.error("未找到对应数据");
|
||||
}
|
||||
fillMaterialDisplay(Collections.singletonList(entity));
|
||||
return Result.OK(entity);
|
||||
}
|
||||
|
||||
@RequiresPermissions("xslmes:mes_xsl_raw_material_inventory:exportXls")
|
||||
@RequestMapping(value = "/exportXls")
|
||||
public ModelAndView exportXls(HttpServletRequest request, MesXslRawMaterialInventory mesXslRawMaterialInventory) {
|
||||
return super.exportXls(request, mesXslRawMaterialInventory, MesXslRawMaterialInventory.class, "原材料库存");
|
||||
}
|
||||
|
||||
private void fillMaterialDisplay(List<MesXslRawMaterialInventory> rows) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<String> materialIds = rows.stream()
|
||||
.map(MesXslRawMaterialInventory::getMaterialId)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
if (materialIds.isEmpty()) {
|
||||
fillWarehouseDisplay(rows);
|
||||
return;
|
||||
}
|
||||
List<String> idList = materialIds.stream().toList();
|
||||
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
|
||||
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE id IN (" + placeholders + ") "
|
||||
+ "AND (del_flag = 0 OR del_flag IS NULL)";
|
||||
Map<String, String> codeMap = new HashMap<>();
|
||||
Map<String, String> nameMap = new HashMap<>();
|
||||
jdbcTemplate.query(sql, rs -> {
|
||||
String id = trim(rs.getString("id"));
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
codeMap.put(id, trim(rs.getString("material_code")));
|
||||
nameMap.put(id, trim(rs.getString("material_name")));
|
||||
}, idList.toArray());
|
||||
|
||||
for (MesXslRawMaterialInventory row : rows) {
|
||||
String id = trim(row.getMaterialId());
|
||||
if (id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String code = codeMap.get(id);
|
||||
String name = nameMap.get(id);
|
||||
if (code != null && !code.isBlank()) {
|
||||
row.setMaterialCode(code);
|
||||
}
|
||||
if (name != null && !name.isBlank()) {
|
||||
row.setMaterialName(name);
|
||||
}
|
||||
}
|
||||
fillWarehouseDisplay(rows);
|
||||
}
|
||||
|
||||
private void fillWarehouseDisplay(List<MesXslRawMaterialInventory> rows) {
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<String> warehouseIds = rows.stream()
|
||||
.map(MesXslRawMaterialInventory::getWarehouseId)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
if (warehouseIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<String> idList = warehouseIds.stream().toList();
|
||||
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
|
||||
String sql = "SELECT id, warehouse_name FROM mes_xsl_warehouse WHERE id IN (" + placeholders + ") "
|
||||
+ "AND (del_flag = 0 OR del_flag IS NULL)";
|
||||
Map<String, String> nameMap = new HashMap<>();
|
||||
jdbcTemplate.query(sql, rs -> {
|
||||
String id = trim(rs.getString("id"));
|
||||
if (id.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
nameMap.put(id, trim(rs.getString("warehouse_name")));
|
||||
}, idList.toArray());
|
||||
for (MesXslRawMaterialInventory row : rows) {
|
||||
String id = trim(row.getWarehouseId());
|
||||
if (id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String name = nameMap.get(id);
|
||||
if (name != null && !name.isBlank()) {
|
||||
row.setWarehouseName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String trim(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package org.jeecg.modules.xslmes.entity;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.experimental.Accessors;
|
||||
import org.jeecg.common.aspect.annotation.Dict;
|
||||
import org.jeecg.common.system.base.entity.JeecgEntity;
|
||||
import org.jeecgframework.poi.excel.annotation.Excel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 原材料库存
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = false)
|
||||
@Accessors(chain = true)
|
||||
@TableName("mes_xsl_raw_material_inventory")
|
||||
@Schema(description = "原材料库存")
|
||||
public class MesXslRawMaterialInventory extends JeecgEntity {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
@Excel(name = "所在仓库", width = 20)
|
||||
@Schema(description = "所在仓库")
|
||||
private String warehouseName;
|
||||
|
||||
@Schema(description = "所在仓库ID(关联 mes_xsl_warehouse.id)")
|
||||
private String warehouseId;
|
||||
|
||||
@Schema(description = "物料ID(关联 mes_mixer_material.id)")
|
||||
private String materialId;
|
||||
|
||||
@Excel(name = "物料名称", width = 20)
|
||||
@Schema(description = "物料名称")
|
||||
private String materialName;
|
||||
|
||||
@Excel(name = "物料编码", width = 20)
|
||||
@Schema(description = "物料编码")
|
||||
private String materialCode;
|
||||
|
||||
@Excel(name = "状态检验", width = 12, dicCode = "xslmes_test_result")
|
||||
@Dict(dicCode = "xslmes_test_result")
|
||||
@Schema(description = "状态检验(字典 xslmes_test_result)")
|
||||
private String testResult;
|
||||
|
||||
@Excel(name = "总包数", width = 12)
|
||||
@Schema(description = "总包数")
|
||||
private BigDecimal totalPackages;
|
||||
|
||||
@Excel(name = "总重量", width = 12)
|
||||
@Schema(description = "总重量")
|
||||
private BigDecimal totalWeight;
|
||||
|
||||
@Schema(description = "租户ID")
|
||||
private Integer tenantId;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.jeecg.modules.xslmes.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
|
||||
|
||||
/**
|
||||
* 原材料库存 Mapper
|
||||
*/
|
||||
@Mapper
|
||||
public interface MesXslRawMaterialInventoryMapper extends BaseMapper<MesXslRawMaterialInventory> {
|
||||
}
|
||||
@@ -34,4 +34,15 @@ public interface IMesXslRawMaterialEntryService extends IService<MesXslRawMateri
|
||||
* @return billNo -> 累计已入场重量;查不到的 billNo 不会出现在 map 中
|
||||
*/
|
||||
Map<String, BigDecimal> sumEnteredWeightByBillNos(Collection<String> billNos);
|
||||
|
||||
/**
|
||||
* 结存入库并汇总原材料库存。
|
||||
* <p>
|
||||
* 对本次选中的入场记录(仅处理 stock_balance != 1)按
|
||||
* 「所在仓库 + 物料编码 + 检验状态」汇总总包数/总重量并累加到库存表,
|
||||
* 同时将入场记录 stock_balance 更新为 1。
|
||||
*
|
||||
* @param ids 入场记录主键集合
|
||||
*/
|
||||
void batchStockInAndSyncInventory(Collection<String> ids);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package org.jeecg.modules.xslmes.service;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
|
||||
|
||||
/**
|
||||
* 原材料库存
|
||||
*/
|
||||
public interface IMesXslRawMaterialInventoryService extends IService<MesXslRawMaterialInventory> {
|
||||
}
|
||||
@@ -1,14 +1,25 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialCard;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialEntry;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslWarehouseArea;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialEntryMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialCardService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialEntryService;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslWarehouseAreaService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -30,6 +41,15 @@ public class MesXslRawMaterialEntryServiceImpl
|
||||
extends ServiceImpl<MesXslRawMaterialEntryMapper, MesXslRawMaterialEntry>
|
||||
implements IMesXslRawMaterialEntryService {
|
||||
|
||||
@Autowired
|
||||
private IMesXslRawMaterialInventoryService mesXslRawMaterialInventoryService;
|
||||
@Autowired
|
||||
private IMesXslRawMaterialCardService mesXslRawMaterialCardService;
|
||||
@Autowired
|
||||
private IMesXslWarehouseAreaService mesXslWarehouseAreaService;
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public String generateBarcode(String materialCode) {
|
||||
if (materialCode == null) materialCode = "";
|
||||
@@ -76,6 +96,166 @@ public class MesXslRawMaterialEntryServiceImpl
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void batchStockInAndSyncInventory(Collection<String> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Set<String> distinctIds = ids.stream()
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toCollection(HashSet::new));
|
||||
if (distinctIds.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 仅处理未结存记录,避免重复点击导致库存重复累加。
|
||||
List<MesXslRawMaterialEntry> entries = this.lambdaQuery()
|
||||
.in(MesXslRawMaterialEntry::getId, distinctIds)
|
||||
.and(w -> w.ne(MesXslRawMaterialEntry::getStockBalance, "1")
|
||||
.or()
|
||||
.isNull(MesXslRawMaterialEntry::getStockBalance))
|
||||
.list();
|
||||
if (entries.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
Map<String, WarehouseRef> warehouseByEntryId = resolveWarehouseByEntry(entries);
|
||||
Set<String> materialIds = entries.stream()
|
||||
.map(MesXslRawMaterialEntry::getMaterialId)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> materialCodes = entries.stream()
|
||||
.map(e -> {
|
||||
String code = normalizeText(e.getMaterialCode());
|
||||
return code.isEmpty() ? parseMaterialCodeFromBarcode(e.getBarcode()) : code;
|
||||
})
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
Map<String, MaterialBaseInfo> materialInfoById = loadMaterialInfoByIds(materialIds);
|
||||
Map<String, MaterialBaseInfo> materialInfoByCode = loadMaterialInfoByCodes(materialCodes);
|
||||
|
||||
Map<String, InventoryAgg> aggMap = new HashMap<>();
|
||||
for (MesXslRawMaterialEntry entry : entries) {
|
||||
WarehouseRef warehouseRef = warehouseByEntryId.get(entry.getId());
|
||||
String warehouseId = warehouseRef == null ? "" : normalizeText(warehouseRef.id);
|
||||
String warehouseName = warehouseRef == null ? "" : normalizeText(warehouseRef.name);
|
||||
if (warehouseName.isEmpty()) {
|
||||
warehouseName = normalizeText(entry.getWarehouseLocation());
|
||||
}
|
||||
String materialId = normalizeText(entry.getMaterialId());
|
||||
String materialCode = normalizeText(entry.getMaterialCode());
|
||||
if (materialCode.isEmpty()) {
|
||||
materialCode = parseMaterialCodeFromBarcode(entry.getBarcode());
|
||||
}
|
||||
MaterialBaseInfo materialInfo = null;
|
||||
if (!materialId.isEmpty()) {
|
||||
materialInfo = materialInfoById.get(materialId);
|
||||
}
|
||||
if (materialInfo == null && !materialCode.isEmpty()) {
|
||||
materialInfo = materialInfoByCode.get(materialCode);
|
||||
if (materialInfo != null && materialId.isEmpty()) {
|
||||
materialId = normalizeText(materialInfo.id);
|
||||
}
|
||||
}
|
||||
if (materialInfo != null) {
|
||||
if (!normalizeText(materialInfo.materialCode).isEmpty()) {
|
||||
materialCode = normalizeText(materialInfo.materialCode);
|
||||
}
|
||||
}
|
||||
String materialName = materialInfo == null ? normalizeText(entry.getMaterialName()) : normalizeText(materialInfo.materialName);
|
||||
String testResult = normalizeText(entry.getTestResult());
|
||||
String materialKey = materialId.isEmpty() ? ("CODE:" + materialCode) : ("ID:" + materialId);
|
||||
String warehouseKey = warehouseId.isEmpty() ? ("NAME:" + warehouseName) : ("ID:" + warehouseId);
|
||||
String key = warehouseKey + "|" + materialKey;
|
||||
|
||||
BigDecimal packageCount = resolvePackageCount(entry);
|
||||
BigDecimal weight = resolveWeight(entry);
|
||||
|
||||
InventoryAgg agg = aggMap.computeIfAbsent(key, k -> new InventoryAgg());
|
||||
if (agg.warehouseId == null || agg.warehouseId.isBlank()) {
|
||||
agg.warehouseId = warehouseId;
|
||||
}
|
||||
agg.warehouseName = warehouseName;
|
||||
if (agg.materialId == null || agg.materialId.isBlank()) {
|
||||
agg.materialId = materialId;
|
||||
}
|
||||
agg.materialCode = materialCode;
|
||||
agg.testResult = testResult;
|
||||
if (agg.materialName == null || agg.materialName.isBlank()) {
|
||||
agg.materialName = materialName;
|
||||
}
|
||||
agg.totalPackages = agg.totalPackages.add(packageCount);
|
||||
agg.totalWeight = agg.totalWeight.add(weight);
|
||||
}
|
||||
|
||||
List<MesXslRawMaterialInventory> toSave = new ArrayList<>();
|
||||
for (InventoryAgg agg : aggMap.values()) {
|
||||
MesXslRawMaterialInventory existing = mesXslRawMaterialInventoryService.lambdaQuery()
|
||||
.eq(agg.warehouseId != null && !agg.warehouseId.isBlank(), MesXslRawMaterialInventory::getWarehouseId, agg.warehouseId)
|
||||
.eq(agg.warehouseId == null || agg.warehouseId.isBlank(), MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
|
||||
.eq(agg.materialId != null && !agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialId, agg.materialId)
|
||||
.eq(agg.materialId == null || agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
|
||||
.one();
|
||||
if (existing == null && agg.warehouseId != null && !agg.warehouseId.isBlank()) {
|
||||
existing = mesXslRawMaterialInventoryService.lambdaQuery()
|
||||
.eq(MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
|
||||
.eq(agg.materialId != null && !agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialId, agg.materialId)
|
||||
.eq(agg.materialId == null || agg.materialId.isBlank(), MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
|
||||
.and(w -> w.isNull(MesXslRawMaterialInventory::getWarehouseId)
|
||||
.or()
|
||||
.eq(MesXslRawMaterialInventory::getWarehouseId, ""))
|
||||
.one();
|
||||
}
|
||||
if (existing == null && agg.materialId != null && !agg.materialId.isBlank()) {
|
||||
existing = mesXslRawMaterialInventoryService.lambdaQuery()
|
||||
.eq(MesXslRawMaterialInventory::getWarehouseName, agg.warehouseName)
|
||||
.eq(MesXslRawMaterialInventory::getTestResult, agg.testResult)
|
||||
.eq(MesXslRawMaterialInventory::getMaterialCode, agg.materialCode)
|
||||
.and(w -> w.isNull(MesXslRawMaterialInventory::getMaterialId)
|
||||
.or()
|
||||
.eq(MesXslRawMaterialInventory::getMaterialId, ""))
|
||||
.one();
|
||||
}
|
||||
if (existing == null) {
|
||||
MesXslRawMaterialInventory inventory = new MesXslRawMaterialInventory();
|
||||
inventory.setWarehouseName(agg.warehouseName);
|
||||
inventory.setWarehouseId(agg.warehouseId);
|
||||
inventory.setMaterialId(agg.materialId);
|
||||
inventory.setMaterialName(agg.materialName);
|
||||
inventory.setMaterialCode(agg.materialCode);
|
||||
inventory.setTestResult(agg.testResult);
|
||||
inventory.setTotalPackages(agg.totalPackages);
|
||||
inventory.setTotalWeight(agg.totalWeight);
|
||||
toSave.add(inventory);
|
||||
continue;
|
||||
}
|
||||
if (normalizeText(existing.getWarehouseId()).isEmpty() && agg.warehouseId != null && !agg.warehouseId.isBlank()) {
|
||||
existing.setWarehouseId(agg.warehouseId);
|
||||
}
|
||||
existing.setWarehouseName((agg.warehouseName == null || agg.warehouseName.isBlank()) ? existing.getWarehouseName() : agg.warehouseName);
|
||||
if (normalizeText(existing.getMaterialId()).isEmpty() && agg.materialId != null && !agg.materialId.isBlank()) {
|
||||
existing.setMaterialId(agg.materialId);
|
||||
}
|
||||
existing.setMaterialCode((agg.materialCode == null || agg.materialCode.isBlank()) ? existing.getMaterialCode() : agg.materialCode);
|
||||
existing.setMaterialName((agg.materialName == null || agg.materialName.isBlank()) ? existing.getMaterialName() : agg.materialName);
|
||||
if (normalizeText(existing.getTestResult()).isEmpty() && agg.testResult != null && !agg.testResult.isBlank()) {
|
||||
existing.setTestResult(agg.testResult);
|
||||
}
|
||||
existing.setTotalPackages(nullSafe(existing.getTotalPackages()).add(agg.totalPackages));
|
||||
existing.setTotalWeight(nullSafe(existing.getTotalWeight()).add(agg.totalWeight));
|
||||
mesXslRawMaterialInventoryService.updateById(existing);
|
||||
}
|
||||
if (!toSave.isEmpty()) {
|
||||
mesXslRawMaterialInventoryService.saveBatch(toSave);
|
||||
}
|
||||
|
||||
List<String> processedIds = entries.stream().map(MesXslRawMaterialEntry::getId).toList();
|
||||
LambdaUpdateWrapper<MesXslRawMaterialEntry> uw = new LambdaUpdateWrapper<>();
|
||||
uw.in(MesXslRawMaterialEntry::getId, processedIds)
|
||||
.set(MesXslRawMaterialEntry::getStockBalance, "1");
|
||||
this.update(uw);
|
||||
}
|
||||
|
||||
/**
|
||||
* 把一条入场记录的 totalPortions/portionWeight 按 "x/y/z/" 解析后,逐位 (份数 × 每份重量) 累加。
|
||||
* 任一位置解析失败或缺失,跳过该位置(不抛异常,确保前端展示降级可用)。
|
||||
@@ -114,4 +294,295 @@ public class MesXslRawMaterialEntryServiceImpl
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BigDecimal resolvePackageCount(MesXslRawMaterialEntry entry) {
|
||||
BigDecimal fromSplit = sumByPortion(entry.getTotalPortions(), entry.getPortionPackages());
|
||||
if (fromSplit.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return fromSplit;
|
||||
}
|
||||
BigDecimal fallback = tryParseBigDecimal(entry.getTotalPortions());
|
||||
return fallback == null ? BigDecimal.ZERO : fallback;
|
||||
}
|
||||
|
||||
private static BigDecimal resolveWeight(MesXslRawMaterialEntry entry) {
|
||||
BigDecimal fromSplit = sumByPortion(entry.getTotalPortions(), entry.getPortionWeight());
|
||||
if (fromSplit.compareTo(BigDecimal.ZERO) > 0) {
|
||||
return fromSplit;
|
||||
}
|
||||
return nullSafe(entry.getTotalWeight());
|
||||
}
|
||||
|
||||
private static BigDecimal sumByPortion(String portionsRaw, String valueRaw) {
|
||||
String[] portionsArr = splitJoined(portionsRaw);
|
||||
String[] valuesArr = splitJoined(valueRaw);
|
||||
if (portionsArr.length == 0 || valuesArr.length == 0) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
int n = Math.min(portionsArr.length, valuesArr.length);
|
||||
BigDecimal sum = BigDecimal.ZERO;
|
||||
for (int i = 0; i < n; i++) {
|
||||
BigDecimal portions = tryParseBigDecimal(portionsArr[i]);
|
||||
BigDecimal val = tryParseBigDecimal(valuesArr[i]);
|
||||
if (portions == null || val == null) {
|
||||
continue;
|
||||
}
|
||||
sum = sum.add(portions.multiply(val));
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
private static String normalizeText(String value) {
|
||||
return value == null ? "" : value.trim();
|
||||
}
|
||||
|
||||
private static BigDecimal nullSafe(BigDecimal value) {
|
||||
return value == null ? BigDecimal.ZERO : value;
|
||||
}
|
||||
|
||||
private static class InventoryAgg {
|
||||
private String warehouseId;
|
||||
private String warehouseName;
|
||||
private String materialId;
|
||||
private String materialName;
|
||||
private String materialCode;
|
||||
private String testResult;
|
||||
private BigDecimal totalPackages = BigDecimal.ZERO;
|
||||
private BigDecimal totalWeight = BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从入场记录关联的「原材料卡片 -> 库区 -> 所属仓库」反查库存仓库名称。
|
||||
* 优先通过 splitDetailId 关联;若无 splitDetailId,再降级按 barcode+batchNo 取卡片。
|
||||
*/
|
||||
private Map<String, WarehouseRef> resolveWarehouseByEntry(List<MesXslRawMaterialEntry> entries) {
|
||||
Map<String, WarehouseRef> result = new HashMap<>();
|
||||
if (entries == null || entries.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Map<String, List<String>> splitIdsByEntryId = new HashMap<>();
|
||||
Set<String> allSplitIds = new HashSet<>();
|
||||
for (MesXslRawMaterialEntry entry : entries) {
|
||||
String[] splitIds = splitJoined(entry.getPortionDetailIds());
|
||||
if (splitIds.length == 0) {
|
||||
continue;
|
||||
}
|
||||
List<String> list = java.util.Arrays.stream(splitIds).toList();
|
||||
splitIdsByEntryId.put(entry.getId(), list);
|
||||
allSplitIds.addAll(list);
|
||||
}
|
||||
|
||||
Map<String, WarehouseRef> splitIdToWarehouse = new HashMap<>();
|
||||
if (!allSplitIds.isEmpty()) {
|
||||
List<MesXslRawMaterialCard> cards = mesXslRawMaterialCardService.lambdaQuery()
|
||||
.in(MesXslRawMaterialCard::getSplitDetailId, allSplitIds)
|
||||
.select(MesXslRawMaterialCard::getSplitDetailId, MesXslRawMaterialCard::getWarehouseArea)
|
||||
.list();
|
||||
Map<String, WarehouseRef> areaToWarehouse = resolveAreaToWarehouseMap(cards.stream()
|
||||
.map(MesXslRawMaterialCard::getWarehouseArea)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet()));
|
||||
for (MesXslRawMaterialCard card : cards) {
|
||||
String splitId = normalizeText(card.getSplitDetailId());
|
||||
if (splitId.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
WarehouseRef warehouse = resolveWarehouse(card.getWarehouseArea(), areaToWarehouse);
|
||||
if (warehouse == null || (warehouse.id.isEmpty() && warehouse.name.isEmpty())) {
|
||||
continue;
|
||||
}
|
||||
splitIdToWarehouse.putIfAbsent(splitId, warehouse);
|
||||
}
|
||||
}
|
||||
|
||||
for (MesXslRawMaterialEntry entry : entries) {
|
||||
List<String> splitIds = splitIdsByEntryId.get(entry.getId());
|
||||
if (splitIds == null || splitIds.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (String splitId : splitIds) {
|
||||
WarehouseRef warehouse = splitIdToWarehouse.get(splitId);
|
||||
if (warehouse != null && (!warehouse.id.isBlank() || !warehouse.name.isBlank())) {
|
||||
result.put(entry.getId(), warehouse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 降级:没有 splitDetailId 时,按 barcode+batchNo 找一张关联卡片取仓库。
|
||||
List<MesXslRawMaterialEntry> unresolved = entries.stream()
|
||||
.filter(e -> !result.containsKey(e.getId()))
|
||||
.filter(e -> !normalizeText(e.getBarcode()).isEmpty() || !normalizeText(e.getBatchNo()).isEmpty())
|
||||
.toList();
|
||||
if (unresolved.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
Set<String> barcodes = unresolved.stream()
|
||||
.map(MesXslRawMaterialEntry::getBarcode)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
Set<String> batchNos = unresolved.stream()
|
||||
.map(MesXslRawMaterialEntry::getBatchNo)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet());
|
||||
if (barcodes.isEmpty() && batchNos.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
LambdaQueryWrapper<MesXslRawMaterialCard> cardQw = new LambdaQueryWrapper<>();
|
||||
if (!barcodes.isEmpty() && !batchNos.isEmpty()) {
|
||||
cardQw.and(w -> w.in(MesXslRawMaterialCard::getBarcode, barcodes)
|
||||
.or()
|
||||
.in(MesXslRawMaterialCard::getBatchNo, batchNos));
|
||||
} else if (!barcodes.isEmpty()) {
|
||||
cardQw.in(MesXslRawMaterialCard::getBarcode, barcodes);
|
||||
} else {
|
||||
cardQw.in(MesXslRawMaterialCard::getBatchNo, batchNos);
|
||||
}
|
||||
cardQw.select(MesXslRawMaterialCard::getBarcode, MesXslRawMaterialCard::getBatchNo, MesXslRawMaterialCard::getWarehouseArea);
|
||||
List<MesXslRawMaterialCard> cards = mesXslRawMaterialCardService.list(cardQw);
|
||||
Map<String, WarehouseRef> areaToWarehouse = resolveAreaToWarehouseMap(cards.stream()
|
||||
.map(MesXslRawMaterialCard::getWarehouseArea)
|
||||
.filter(s -> s != null && !s.isBlank())
|
||||
.collect(Collectors.toSet()));
|
||||
for (MesXslRawMaterialEntry entry : unresolved) {
|
||||
String barcode = normalizeText(entry.getBarcode());
|
||||
String batchNo = normalizeText(entry.getBatchNo());
|
||||
for (MesXslRawMaterialCard card : cards) {
|
||||
boolean match = false;
|
||||
if (!barcode.isEmpty() && barcode.equals(normalizeText(card.getBarcode()))) {
|
||||
match = true;
|
||||
}
|
||||
if (!match && !batchNo.isEmpty() && batchNo.equals(normalizeText(card.getBatchNo()))) {
|
||||
match = true;
|
||||
}
|
||||
if (!match) {
|
||||
continue;
|
||||
}
|
||||
WarehouseRef warehouse = resolveWarehouse(card.getWarehouseArea(), areaToWarehouse);
|
||||
if (warehouse != null && (!warehouse.id.isBlank() || !warehouse.name.isBlank())) {
|
||||
result.put(entry.getId(), warehouse);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Map<String, WarehouseRef> resolveAreaToWarehouseMap(Set<String> areaRefs) {
|
||||
if (areaRefs == null || areaRefs.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<MesXslWarehouseArea> areas = mesXslWarehouseAreaService.lambdaQuery()
|
||||
.and(w -> w.in(MesXslWarehouseArea::getId, areaRefs)
|
||||
.or()
|
||||
.in(MesXslWarehouseArea::getAreaName, areaRefs))
|
||||
.select(MesXslWarehouseArea::getId, MesXslWarehouseArea::getAreaName, MesXslWarehouseArea::getWarehouseId, MesXslWarehouseArea::getWarehouseName)
|
||||
.list();
|
||||
Map<String, WarehouseRef> map = new HashMap<>();
|
||||
for (MesXslWarehouseArea area : areas) {
|
||||
WarehouseRef warehouse = new WarehouseRef();
|
||||
warehouse.id = normalizeText(area.getWarehouseId());
|
||||
warehouse.name = normalizeText(area.getWarehouseName());
|
||||
if (warehouse.id.isEmpty() && warehouse.name.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String id = normalizeText(area.getId());
|
||||
if (!id.isEmpty()) {
|
||||
map.putIfAbsent(id, warehouse);
|
||||
}
|
||||
String name = normalizeText(area.getAreaName());
|
||||
if (!name.isEmpty()) {
|
||||
map.putIfAbsent(name, warehouse);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private WarehouseRef resolveWarehouse(String areaRef, Map<String, WarehouseRef> areaToWarehouse) {
|
||||
String area = normalizeText(areaRef);
|
||||
if (area.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return areaToWarehouse.get(area);
|
||||
}
|
||||
|
||||
private static String parseMaterialCodeFromBarcode(String barcodeRaw) {
|
||||
String barcode = normalizeText(barcodeRaw);
|
||||
// 规则:QH + 物料编码 + yyMMdd + 3位流水;最短长度 QH+1+6+3=12
|
||||
if (!barcode.startsWith("QH") || barcode.length() < 12) {
|
||||
return "";
|
||||
}
|
||||
int endExclusive = barcode.length() - 9;
|
||||
if (endExclusive <= 2) {
|
||||
return "";
|
||||
}
|
||||
return barcode.substring(2, endExclusive).trim();
|
||||
}
|
||||
|
||||
private Map<String, MaterialBaseInfo> loadMaterialInfoByIds(Set<String> ids) {
|
||||
if (ids == null || ids.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<String> idList = ids.stream().filter(s -> s != null && !s.isBlank()).toList();
|
||||
if (idList.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String placeholders = String.join(",", Collections.nCopies(idList.size(), "?"));
|
||||
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE id IN (" + placeholders + ") "
|
||||
+ "AND (del_flag = 0 OR del_flag IS NULL)";
|
||||
List<MaterialBaseInfo> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
|
||||
MaterialBaseInfo info = new MaterialBaseInfo();
|
||||
info.id = normalizeText(rs.getString("id"));
|
||||
info.materialCode = normalizeText(rs.getString("material_code"));
|
||||
info.materialName = normalizeText(rs.getString("material_name"));
|
||||
return info;
|
||||
}, idList.toArray());
|
||||
Map<String, MaterialBaseInfo> map = new HashMap<>();
|
||||
for (MaterialBaseInfo row : rows) {
|
||||
if (row.id.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
map.putIfAbsent(row.id, row);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private Map<String, MaterialBaseInfo> loadMaterialInfoByCodes(Set<String> codes) {
|
||||
if (codes == null || codes.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
List<String> codeList = codes.stream().filter(s -> s != null && !s.isBlank()).toList();
|
||||
if (codeList.isEmpty()) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
String placeholders = String.join(",", Collections.nCopies(codeList.size(), "?"));
|
||||
String sql = "SELECT id, material_code, material_name FROM mes_mixer_material WHERE material_code IN (" + placeholders + ") "
|
||||
+ "AND (del_flag = 0 OR del_flag IS NULL)";
|
||||
List<MaterialBaseInfo> rows = jdbcTemplate.query(sql, (rs, rowNum) -> {
|
||||
MaterialBaseInfo info = new MaterialBaseInfo();
|
||||
info.id = normalizeText(rs.getString("id"));
|
||||
info.materialCode = normalizeText(rs.getString("material_code"));
|
||||
info.materialName = normalizeText(rs.getString("material_name"));
|
||||
return info;
|
||||
}, codeList.toArray());
|
||||
Map<String, MaterialBaseInfo> map = new HashMap<>();
|
||||
for (MaterialBaseInfo row : rows) {
|
||||
if (row.materialCode.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
map.putIfAbsent(row.materialCode, row);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static class MaterialBaseInfo {
|
||||
private String id;
|
||||
private String materialCode;
|
||||
private String materialName;
|
||||
}
|
||||
|
||||
private static class WarehouseRef {
|
||||
private String id = "";
|
||||
private String name = "";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.jeecg.modules.xslmes.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import org.jeecg.modules.xslmes.entity.MesXslRawMaterialInventory;
|
||||
import org.jeecg.modules.xslmes.mapper.MesXslRawMaterialInventoryMapper;
|
||||
import org.jeecg.modules.xslmes.service.IMesXslRawMaterialInventoryService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 原材料库存
|
||||
*/
|
||||
@Service
|
||||
public class MesXslRawMaterialInventoryServiceImpl extends ServiceImpl<MesXslRawMaterialInventoryMapper, MesXslRawMaterialInventory>
|
||||
implements IMesXslRawMaterialInventoryService {
|
||||
}
|
||||
@@ -46,7 +46,7 @@ public class MesMixerMaterial implements Serializable {
|
||||
@Excel(name = "物料别名", width = 15)
|
||||
private String aliasName;
|
||||
|
||||
@Excel(name = "投管状态", width = 12, replace = {"在投管_1", "未投管_0"})
|
||||
@Excel(name = "投罐状态", width = 12, replace = {"在投管_1", "未投管_0"})
|
||||
private Integer feedManageStatus;
|
||||
@Excel(name = "使用状态", width = 12, replace = {"使用中_1", "停用_0"})
|
||||
private Integer useStatus;
|
||||
|
||||
@@ -39,7 +39,7 @@ public class JeecgSystemApplication extends SpringBootServletInitializer {
|
||||
SpringApplication app = new SpringApplication(JeecgSystemApplication.class);
|
||||
Map<String, Object> defaultProperties = new HashMap<>();
|
||||
defaultProperties.put("management.health.elasticsearch.enabled", false);
|
||||
app.setDefaultProperties(defaultProperties);
|
||||
app.setDefaultProperties(defaultProperties);
|
||||
log.info("[JEECG] Elasticsearch Health Check Enabled: false" );
|
||||
|
||||
ConfigurableApplicationContext application = app.run(args);;
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
-- 原材料库存:建表 + 菜单权限(幂等)
|
||||
|
||||
-- ===================== 1. 建表 =====================
|
||||
CREATE TABLE IF NOT EXISTS `mes_xsl_raw_material_inventory` (
|
||||
`id` varchar(32) NOT NULL COMMENT '主键',
|
||||
`warehouse_name` varchar(200) DEFAULT NULL COMMENT '所在仓库',
|
||||
`material_name` varchar(200) DEFAULT NULL COMMENT '物料名称',
|
||||
`material_code` varchar(100) DEFAULT NULL COMMENT '物料编码',
|
||||
`test_result` varchar(10) DEFAULT NULL COMMENT '状态检验(字典 xslmes_test_result)',
|
||||
`total_packages` decimal(18,3) DEFAULT 0 COMMENT '总包数',
|
||||
`total_weight` decimal(18,3) DEFAULT 0 COMMENT '总重量',
|
||||
`create_by` varchar(50) DEFAULT NULL COMMENT '创建人',
|
||||
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
|
||||
`update_by` varchar(50) DEFAULT NULL COMMENT '更新人',
|
||||
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
|
||||
`tenant_id` int DEFAULT 1002 COMMENT '租户ID',
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_rmi_wh_mat_result` (`warehouse_name`, `material_code`, `test_result`),
|
||||
KEY `idx_rmi_material_code` (`material_code`),
|
||||
KEY `idx_rmi_test_result` (`test_result`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='原材料库存';
|
||||
|
||||
-- ===================== 2. 菜单权限 =====================
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`)
|
||||
SELECT '1900000000000000600', '1900000000000000300', '原材料库存', '/xslmes/mesXslRawMaterialInventory', 'xslmes/mesXslRawMaterialInventory/MesXslRawMaterialInventoryList', 1, NULL, NULL, 1, NULL, '0', 12.00, 0, 'ant-design:inbox-outlined', 0, 1, 0, 0, '原材料库存', 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000600');
|
||||
|
||||
INSERT INTO `sys_permission` (`id`, `parent_id`, `name`, `url`, `component`, `is_route`, `component_name`, `redirect`, `menu_type`, `perms`, `perms_type`, `sort_no`, `always_show`, `icon`, `is_leaf`, `keep_alive`, `hidden`, `hide_tab`, `description`, `create_by`, `create_time`, `update_by`, `update_time`, `del_flag`, `rule_flag`, `status`, `internal_or_external`)
|
||||
SELECT '1900000000000000601', '1900000000000000600', '导出', NULL, NULL, 0, NULL, NULL, 2, 'xslmes:mes_xsl_raw_material_inventory:exportXls', '1', 1.00, 0, NULL, 1, 0, 0, 0, NULL, 'admin', NOW(), 'admin', NOW(), 0, 0, '1', 0
|
||||
FROM DUAL WHERE NOT EXISTS (SELECT 1 FROM `sys_permission` WHERE `id` = '1900000000000000601');
|
||||
|
||||
-- 默认管理员角色授权
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', '1900000000000000600', NULL, NOW(), '127.0.0.1'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission`
|
||||
WHERE `role_id` = 'f6817f48af4fb3af11b9e8bf182f618b' AND `permission_id` = '1900000000000000600'
|
||||
);
|
||||
|
||||
INSERT INTO `sys_role_permission` (`id`, `role_id`, `permission_id`, `data_rule_ids`, `operate_date`, `operate_ip`)
|
||||
SELECT REPLACE(UUID(), '-', ''), 'f6817f48af4fb3af11b9e8bf182f618b', '1900000000000000601', NULL, NOW(), '127.0.0.1'
|
||||
WHERE NOT EXISTS (
|
||||
SELECT 1 FROM `sys_role_permission`
|
||||
WHERE `role_id` = 'f6817f48af4fb3af11b9e8bf182f618b' AND `permission_id` = '1900000000000000601'
|
||||
);
|
||||
@@ -0,0 +1,53 @@
|
||||
-- 原材料库存:改为保存物料ID(关联 mes_mixer_material.id),并调整唯一键口径
|
||||
|
||||
-- 1) 新增 material_id 字段(若不存在)
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND COLUMN_NAME = 'material_id'
|
||||
);
|
||||
SET @ddl := IF(@col_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD COLUMN `material_id` varchar(32) DEFAULT NULL COMMENT ''物料ID(关联 mes_mixer_material.id)'' AFTER `warehouse_name`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s1 FROM @ddl; EXECUTE s1; DEALLOCATE PREPARE s1;
|
||||
|
||||
-- 2) 删除旧唯一键(若存在)
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wh_mat_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists > 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` DROP INDEX `uk_rmi_wh_mat_result`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s2 FROM @ddl; EXECUTE s2; DEALLOCATE PREPARE s2;
|
||||
|
||||
-- 3) 新唯一键:所在仓库 + 物料ID + 状态检验
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wh_mid_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD UNIQUE KEY `uk_rmi_wh_mid_result` (`warehouse_name`, `material_id`, `test_result`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s3 FROM @ddl; EXECUTE s3; DEALLOCATE PREPARE s3;
|
||||
|
||||
-- 4) 查询索引:material_id
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'idx_rmi_material_id'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD KEY `idx_rmi_material_id` (`material_id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s4 FROM @ddl; EXECUTE s4; DEALLOCATE PREPARE s4;
|
||||
@@ -0,0 +1,72 @@
|
||||
-- 原材料库存:历史数据回填 material_id(按 material_code 关联 mes_mixer_material)
|
||||
-- 处理策略:
|
||||
-- 1) 对 material_id 为空且可按编码匹配的库存行,先聚合后写入(遇唯一键冲突自动累加)
|
||||
-- 2) 删除已迁移的旧空 material_id 行,避免重复数据
|
||||
-- 3) 对已有 material_id 但编码/名称为空的数据做展示字段补齐
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_rmi_backfill`;
|
||||
CREATE TEMPORARY TABLE `tmp_rmi_backfill` AS
|
||||
SELECT
|
||||
i.warehouse_name,
|
||||
m.id AS material_id,
|
||||
i.test_result,
|
||||
MAX(COALESCE(NULLIF(i.material_code, ''), m.material_code)) AS material_code,
|
||||
MAX(COALESCE(NULLIF(i.material_name, ''), m.material_name)) AS material_name,
|
||||
SUM(IFNULL(i.total_packages, 0)) AS total_packages,
|
||||
SUM(IFNULL(i.total_weight, 0)) AS total_weight,
|
||||
MAX(i.tenant_id) AS tenant_id
|
||||
FROM mes_xsl_raw_material_inventory i
|
||||
INNER JOIN mes_mixer_material m
|
||||
ON m.material_code = i.material_code
|
||||
AND (m.del_flag = 0 OR m.del_flag IS NULL)
|
||||
WHERE (i.material_id IS NULL OR i.material_id = '')
|
||||
AND i.material_code IS NOT NULL
|
||||
AND i.material_code <> ''
|
||||
GROUP BY i.warehouse_name, m.id, i.test_result;
|
||||
|
||||
INSERT INTO mes_xsl_raw_material_inventory
|
||||
(`id`, `warehouse_name`, `material_id`, `material_name`, `material_code`, `test_result`, `total_packages`, `total_weight`, `create_by`, `create_time`, `update_by`, `update_time`, `tenant_id`)
|
||||
SELECT
|
||||
REPLACE(UUID(), '-', ''),
|
||||
t.warehouse_name,
|
||||
t.material_id,
|
||||
t.material_name,
|
||||
t.material_code,
|
||||
t.test_result,
|
||||
t.total_packages,
|
||||
t.total_weight,
|
||||
'admin',
|
||||
NOW(),
|
||||
'admin',
|
||||
NOW(),
|
||||
IFNULL(t.tenant_id, 1002)
|
||||
FROM tmp_rmi_backfill t
|
||||
ON DUPLICATE KEY UPDATE
|
||||
total_packages = IFNULL(total_packages, 0) + VALUES(total_packages),
|
||||
total_weight = IFNULL(total_weight, 0) + VALUES(total_weight),
|
||||
material_code = CASE WHEN material_code IS NULL OR material_code = '' THEN VALUES(material_code) ELSE material_code END,
|
||||
material_name = CASE WHEN material_name IS NULL OR material_name = '' THEN VALUES(material_name) ELSE material_name END,
|
||||
update_by = 'admin',
|
||||
update_time = NOW();
|
||||
|
||||
DELETE i
|
||||
FROM mes_xsl_raw_material_inventory i
|
||||
INNER JOIN mes_mixer_material m
|
||||
ON m.material_code = i.material_code
|
||||
AND (m.del_flag = 0 OR m.del_flag IS NULL)
|
||||
WHERE (i.material_id IS NULL OR i.material_id = '')
|
||||
AND i.material_code IS NOT NULL
|
||||
AND i.material_code <> '';
|
||||
|
||||
UPDATE mes_xsl_raw_material_inventory i
|
||||
INNER JOIN mes_mixer_material m
|
||||
ON m.id = i.material_id
|
||||
AND (m.del_flag = 0 OR m.del_flag IS NULL)
|
||||
SET
|
||||
i.material_code = CASE WHEN i.material_code IS NULL OR i.material_code = '' THEN m.material_code ELSE i.material_code END,
|
||||
i.material_name = CASE WHEN i.material_name IS NULL OR i.material_name = '' THEN m.material_name ELSE i.material_name END,
|
||||
i.update_by = 'admin',
|
||||
i.update_time = NOW()
|
||||
WHERE (i.material_code IS NULL OR i.material_code = '' OR i.material_name IS NULL OR i.material_name = '');
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_rmi_backfill`;
|
||||
@@ -0,0 +1,63 @@
|
||||
-- 原材料库存:改为保存 warehouse_id(关联 mes_xsl_warehouse.id),并调整唯一键口径
|
||||
|
||||
-- 1) 新增 warehouse_id 字段(若不存在)
|
||||
SET @col_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND COLUMN_NAME = 'warehouse_id'
|
||||
);
|
||||
SET @ddl := IF(@col_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD COLUMN `warehouse_id` varchar(32) DEFAULT NULL COMMENT ''所在仓库ID(关联 mes_xsl_warehouse.id)'' AFTER `warehouse_name`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s1 FROM @ddl; EXECUTE s1; DEALLOCATE PREPARE s1;
|
||||
|
||||
-- 2) 回填 warehouse_id(按仓库名称关联)
|
||||
UPDATE mes_xsl_raw_material_inventory i
|
||||
INNER JOIN mes_xsl_warehouse w
|
||||
ON w.warehouse_name = i.warehouse_name
|
||||
AND (w.del_flag = 0 OR w.del_flag IS NULL)
|
||||
SET i.warehouse_id = w.id
|
||||
WHERE (i.warehouse_id IS NULL OR i.warehouse_id = '')
|
||||
AND i.warehouse_name IS NOT NULL
|
||||
AND i.warehouse_name <> '';
|
||||
|
||||
-- 3) 删除旧唯一键(若存在)
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wh_mid_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists > 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` DROP INDEX `uk_rmi_wh_mid_result`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s2 FROM @ddl; EXECUTE s2; DEALLOCATE PREPARE s2;
|
||||
|
||||
-- 4) 新唯一键:仓库ID + 物料ID + 状态检验
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wid_mid_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD UNIQUE KEY `uk_rmi_wid_mid_result` (`warehouse_id`, `material_id`, `test_result`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s3 FROM @ddl; EXECUTE s3; DEALLOCATE PREPARE s3;
|
||||
|
||||
-- 5) 查询索引:warehouse_id
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'idx_rmi_warehouse_id'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD KEY `idx_rmi_warehouse_id` (`warehouse_id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s4 FROM @ddl; EXECUTE s4; DEALLOCATE PREPARE s4;
|
||||
@@ -0,0 +1,86 @@
|
||||
-- 原材料库存:按「所属仓库 + 物料」唯一,数量/重量累计
|
||||
-- 处理内容:
|
||||
-- 1) 删除旧唯一键(含 test_result 维度)
|
||||
-- 2) 合并历史重复行(同仓库同物料累计 total_packages/total_weight)
|
||||
-- 3) 新建唯一键:warehouse_id + material_id
|
||||
|
||||
-- 1) 删除旧唯一键(若存在)
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wid_mid_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists > 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` DROP INDEX `uk_rmi_wid_mid_result`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s1 FROM @ddl; EXECUTE s1; DEALLOCATE PREPARE s1;
|
||||
|
||||
-- 兼容旧脚本里的唯一键名
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wh_mid_result'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists > 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` DROP INDEX `uk_rmi_wh_mid_result`',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s2 FROM @ddl; EXECUTE s2; DEALLOCATE PREPARE s2;
|
||||
|
||||
-- 2) 合并历史数据
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_rmi_merge`;
|
||||
CREATE TEMPORARY TABLE `tmp_rmi_merge` AS
|
||||
SELECT
|
||||
-- 用空串承载未回填ID,避免唯一键上的 NULL 语义导致重复
|
||||
COALESCE(NULLIF(MAX(NULLIF(warehouse_id, '')), ''), '') AS warehouse_id,
|
||||
COALESCE(NULLIF(MAX(NULLIF(material_id, '')), ''), '') AS material_id,
|
||||
MAX(COALESCE(NULLIF(warehouse_name, ''), '')) AS warehouse_name,
|
||||
MAX(COALESCE(NULLIF(material_code, ''), '')) AS material_code,
|
||||
MAX(COALESCE(NULLIF(material_name, ''), '')) AS material_name,
|
||||
MAX(COALESCE(NULLIF(test_result, ''), '')) AS test_result,
|
||||
SUM(IFNULL(total_packages, 0)) AS total_packages,
|
||||
SUM(IFNULL(total_weight, 0)) AS total_weight,
|
||||
MAX(tenant_id) AS tenant_id
|
||||
FROM mes_xsl_raw_material_inventory
|
||||
GROUP BY
|
||||
COALESCE(NULLIF(warehouse_id, ''), CONCAT('NAME:', IFNULL(warehouse_name, ''))),
|
||||
COALESCE(NULLIF(material_id, ''), CONCAT('CODE:', IFNULL(material_code, '')));
|
||||
|
||||
TRUNCATE TABLE mes_xsl_raw_material_inventory;
|
||||
|
||||
INSERT INTO mes_xsl_raw_material_inventory
|
||||
(`id`, `warehouse_name`, `warehouse_id`, `material_id`, `material_name`, `material_code`, `test_result`, `total_packages`, `total_weight`, `create_by`, `create_time`, `update_by`, `update_time`, `tenant_id`)
|
||||
SELECT
|
||||
REPLACE(UUID(), '-', ''),
|
||||
warehouse_name,
|
||||
warehouse_id,
|
||||
material_id,
|
||||
material_name,
|
||||
material_code,
|
||||
test_result,
|
||||
total_packages,
|
||||
total_weight,
|
||||
'admin',
|
||||
NOW(),
|
||||
'admin',
|
||||
NOW(),
|
||||
IFNULL(tenant_id, 1002)
|
||||
FROM tmp_rmi_merge;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS `tmp_rmi_merge`;
|
||||
|
||||
-- 3) 新唯一键:warehouse_id + material_id
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'mes_xsl_raw_material_inventory'
|
||||
AND INDEX_NAME = 'uk_rmi_wid_mid'
|
||||
);
|
||||
SET @ddl := IF(@idx_exists = 0,
|
||||
'ALTER TABLE `mes_xsl_raw_material_inventory` ADD UNIQUE KEY `uk_rmi_wid_mid` (`warehouse_id`, `material_id`)',
|
||||
'SELECT 1'
|
||||
);
|
||||
PREPARE s3 FROM @ddl; EXECUTE s3; DEALLOCATE PREPARE s3;
|
||||
Reference in New Issue
Block a user