| | |
| | | loadMore: 'Load More Data', |
| | | complete: 'Complete', |
| | | deprecate: 'Deprecate', |
| | | stockError: 'Empty', |
| | | inputPlaceholder: 'Use commas to separate', |
| | | resend: 'RESEND', |
| | | selected: 'selected', |
| | |
| | | anfme: "anfme", |
| | | workQty: "workQty", |
| | | qty: "Qty", |
| | | stockLocs: "Stock Locs" |
| | | stockLocs: "Stock Locs", |
| | | stockQty: "Stock Qty", |
| | | |
| | | }, |
| | | task: { |
| | | taskCode: "TaskCode", |
| | |
| | | loadMore: '加载更多', |
| | | complete: '完成', |
| | | deprecate: '废弃', |
| | | stockError: '没有库存', |
| | | resend: '重发', |
| | | selected: '项选中', |
| | | batch: '批量编辑' |
| | |
| | | anfme: "数量", |
| | | workQty: "执行数", |
| | | qty: "完成数量", |
| | | stockLocs: "库存位置" |
| | | stockLocs: "库存位置", |
| | | stockQty: "库存数量", |
| | | }, |
| | | task: { |
| | | taskCode: "任务号", |
| | |
| | | parse={v => v} |
| | | /> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <NumberInput |
| | | label="table.field.deviceBind.typeId" |
| | | source="typeId" |
| | | /> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label="table.field.deviceBind.typeId" /> |
| | | </ReferenceInput> |
| | | </Grid> |
| | | <Grid item xs={6} display="flex" gap={1}> |
| | | <TextInput |
| | |
| | | /> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <NumberInput |
| | | label="table.field.deviceBind.typeId" |
| | | source="typeId" |
| | | /> |
| | | <ReferenceInput source="typeId" label="table.field.deviceBind.typeId" reference="warehouseAreas" filter={{}}> |
| | | <AutocompleteInput optionValue="id" optionText="name" label="table.field.deviceBind.typeId" /> |
| | | </ReferenceInput> |
| | | </Stack> |
| | | <Stack direction='row' gap={2}> |
| | | <TextInput |
| | |
| | | ListContextProvider, |
| | | useList, |
| | | Toolbar, |
| | | SingleFieldList, |
| | | } from 'react-admin'; |
| | | |
| | | import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; |
| | | import { Box, Typography, Card, Stack, DialogContent, DialogTitle, DialogActions, Dialog } from '@mui/material'; |
| | | import { Box, Typography, Card, Stack, DialogContent, DialogTitle, DialogActions, Dialog, Chip, ListItem, Paper } from '@mui/material'; |
| | | import { styled } from '@mui/material/styles'; |
| | | import DialogCloseButton from "../../components/DialogCloseButton"; |
| | | import request from '@/utils/request'; |
| | |
| | | <NumberField source="waveId" label="table.field.waveItem.waveId" /> |
| | | <TextField source="waveCode" label="table.field.waveItem.waveCode" /> |
| | | <TextField source="orderCode" label="table.field.waveItem.orderCode" /> |
| | | {/* <TextField source="maktx" label="table.field.waveItem.matnrName" /> */} |
| | | <NumberField source="matnrId" label="table.field.waveItem.matnrId" /> |
| | | <TextField source="matnrCode" label="table.field.waveItem.matnrCode" /> |
| | | <TextField source="batch" label="table.field.waveItem.batch" /> |
| | |
| | | <NumberField source="anfme" label="table.field.waveItem.anfme" /> |
| | | <NumberField source="workQty" label="table.field.waveItem.workQty" /> |
| | | <NumberField source="qty" label="table.field.waveItem.qty" /> |
| | | <NumberField source="stockQty" label="table.field.waveItem.stockQty" /> |
| | | <WrapperField cellClassName="opt" label="table.field.waveItem.stockLocs"> |
| | | <ArrayField source="stockLocs" stockLocs="table.field.waveItem.stockLocs"> |
| | | <SingleFieldList linkType={false}> |
| | | <ChilpField source="" size="small"></ChilpField> |
| | | </SingleFieldList> |
| | | </ArrayField> |
| | | {/* <NumberField source="anfme" label="table.field.waveItem.stockLocs" /> */} |
| | | {/* <EditButton sx={{ padding: '1px', fontSize: '.75rem' }} /> |
| | | <DeleteButton sx={{ padding: '1px', fontSize: '.75rem' }} mutationMode={OPERATE_MODE} /> */} |
| | | <TagsField /> |
| | | </WrapperField> |
| | | </StyledDatagrid> |
| | | </ListContextProvider> |
| | | </DialogContent> |
| | | <DialogActions> |
| | | <Toolbar sx={{ width: '100%', justifyContent: 'end' }} > |
| | | <GenerateTaskButton record={[record?.id]} /> |
| | | <GenerateTaskButton record={[record?.id]} dataSource={data} /> |
| | | </Toolbar> |
| | | </DialogActions> |
| | | </Dialog> |
| | |
| | | |
| | | export default ItemToTaskModal; |
| | | |
| | | const GenerateTaskButton = (record) => { |
| | | const GenerateTaskButton = (record, dataSource) => { |
| | | const refresh = useRefresh(); |
| | | const notify = useNotify(); |
| | | const redirect = useRedirect(); |
| | | const generateTask = async () => { |
| | | const res = await request.post(`/wave/public/task`, { ids: record?.record }); |
| | | const res = await request.post(`/wave/public/task`, { wave: record, waveItem: dataSource }); |
| | | if (res?.data?.code === 200) { |
| | | notify(res.data.msg); |
| | | redirect("/task") |
| | |
| | | refresh(); |
| | | } |
| | | return (<Button variant="contained" label={"ra.action.save"} onClick={generateTask}></Button>) |
| | | } |
| | | |
| | | const TagsField = () => { |
| | | const record = useRecordContext(); |
| | | const translate = useTranslate(); |
| | | const locs = JSON.parse(record.stockLocs); |
| | | if (locs == undefined || locs.length < 1) { |
| | | return ( |
| | | <> |
| | | <ListItem> |
| | | <Chip color="error" label={translate("common.action.stockError")} variant="outlined" /> |
| | | </ListItem> |
| | | </> |
| | | ) |
| | | } else { |
| | | return ( |
| | | <> |
| | | {locs.map((data) => { |
| | | return ( |
| | | <ListItem key={data?.id}> |
| | | <Chip label={data?.locCode} /> |
| | | </ListItem> |
| | | ) |
| | | })} |
| | | </> |
| | | ) |
| | | } |
| | | |
| | | } |
| | |
| | | }, |
| | | })); |
| | | |
| | | const SelectSiteButton = (props) => { |
| | | const { source, setOpen, refresh, notify } = props; |
| | | const record = useRecordContext(); |
| | | |
| | | const handleClick = async () => { |
| | | setOpen(false); |
| | | const id = record.id; |
| | | const res = await request.post(`/waitPakin/merge`, {waitPakins: source, siteId: id}); |
| | | if (res?.data?.code === 200) { |
| | | refresh(); |
| | | notify(res.data.msg); |
| | | } else { |
| | | notify(res.data.msg); |
| | | } |
| | | }; |
| | | |
| | | return ( |
| | | <Button label="toolbar.selectSite" onClick={handleClick} /> |
| | | ); |
| | | }; |
| | | |
| | | const SelectSiteModel = (props) => { |
| | | const { open, setOpen, source } = props; |
| | | const translate = useTranslate(); |
| | |
| | | <DialogContent> |
| | | <List |
| | | resource='deviceSite' |
| | | filter={{type : 1}} |
| | | sx={{ |
| | | flexGrow: 1, |
| | | transition: (theme) => |
| | |
| | | <StyledDatagrid |
| | | preferenceKey='deviceSite' |
| | | bulkActionButtons={false} |
| | | rowClick='toggleSelection' |
| | | onToggleItem={async (id)=>{ |
| | | setOpen(false); |
| | | const res = await request.post(`/waitPakin/merge`, {waitPakins: source, siteId: id}); |
| | | if (res?.data?.code === 200) { |
| | | refresh(); |
| | | notify(res.data.msg); |
| | | } else { |
| | | notify(res.data.msg); |
| | | } |
| | | }} |
| | | rowClick={false} |
| | | omit={['id','name', 'createTime','label', 'createBy', 'memo', 'updateBy$', 'createBy$', 'createTime', 'updateTime']} |
| | | > |
| | | <NumberField source="id" /> |
| | |
| | | <BooleanField source="statusBool" label="common.field.status" sortable={false} /> |
| | | <TextField source="memo" label="common.field.memo" sortable={false} /> |
| | | <WrapperField cellClassName="opt" label="common.field.opt"> |
| | | <Button label="toolbar.selectSite" onClick={(event)=>{ |
| | | setOpen(false) |
| | | }}/> |
| | | <SelectSiteButton source={source} setOpen={setOpen} refresh={refresh} notify={notify} /> |
| | | </WrapperField> |
| | | </StyledDatagrid> |
| | | </List> |
New file |
| | |
| | | import java.util.ArrayList; |
| | | import java.util.Arrays; |
| | | import java.util.List; |
| | | |
| | | public class CombinationFinder { |
| | | |
| | | public List<Integer> findCombination(double[] nums, double target) { |
| | | // 处理等值情况 |
| | | List<Integer> equal = findEqual(nums, target); |
| | | if (equal != null && !equal.isEmpty()) { |
| | | return equal; |
| | | } |
| | | |
| | | // 处理大于的情况 |
| | | List<Integer> greater = findGreater(nums, target); |
| | | if (greater != null && !greater.isEmpty()) { |
| | | return greater; |
| | | } |
| | | |
| | | // 处理小于的情况 |
| | | List<Integer> less = findLess(nums, target); |
| | | return less != null ? less : new ArrayList<>(); // 确保不返回null |
| | | } |
| | | |
| | | private List<Integer> findEqual(double[] nums, double target) { |
| | | int n = nums.length; |
| | | double[] dp = new double[(int) (target * 100 + 1)]; // 放大100倍处理精度问题 |
| | | Arrays.fill(dp, Double.MAX_VALUE); |
| | | dp[0] = 0; |
| | | int[] prev = new int[(int) (target * 100 + 1)]; |
| | | Arrays.fill(prev, -1); |
| | | |
| | | for (int j = 0; j < n; j++) { |
| | | int num = (int) (nums[j] * 100); // 放大100倍处理精度问题 |
| | | for (int i = (int) (target * 100); i >= num; i--) { |
| | | if (dp[i - num] != Double.MAX_VALUE && dp[i - num] + 1 < dp[i]) { |
| | | dp[i] = dp[i - num] + 1; |
| | | prev[i] = j; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (dp[(int) (target * 100)] == Double.MAX_VALUE) { |
| | | return null; |
| | | } |
| | | |
| | | List<Integer> indices = new ArrayList<>(); |
| | | int current = (int) (target * 100); |
| | | while (current > 0) { |
| | | int j = prev[current]; |
| | | if (j == -1) return null; |
| | | indices.add(j); |
| | | current -= (int) (nums[j] * 100); |
| | | } |
| | | |
| | | return indices; |
| | | } |
| | | |
| | | private List<Integer> findGreater(double[] nums, double target) { |
| | | List<double[]> sorted = new ArrayList<>(); |
| | | for (int i = 0; i < nums.length; i++) { |
| | | sorted.add(new double[]{nums[i], i}); |
| | | } |
| | | sorted.sort((a, b) -> Double.compare(b[0], a[0])); |
| | | |
| | | double sum = 0; |
| | | List<Integer> indices = new ArrayList<>(); |
| | | for (double[] pair : sorted) { |
| | | sum += pair[0]; |
| | | indices.add((int) pair[1]); |
| | | if (sum > target) { |
| | | return indices; |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | private List<Integer> findLess(double[] nums, double target) { |
| | | int n = nums.length; |
| | | List<Integer> bestIndices = new ArrayList<>(); |
| | | double[] maxSum = {-1.0}; |
| | | for (int k = 1; k <= n; k++) { |
| | | List<Integer> currentIndices = new ArrayList<>(); |
| | | double[] currentMax = {-1.0}; |
| | | backtrack(nums, target, 0, k, 0.0, 0, currentIndices, currentMax, new ArrayList<>()); |
| | | if (currentMax[0] != -1.0) { |
| | | if (currentMax[0] > maxSum[0] || (currentMax[0] == maxSum[0] && currentIndices.size() < bestIndices.size())) { |
| | | maxSum[0] = currentMax[0]; |
| | | bestIndices = new ArrayList<>(currentIndices); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return bestIndices.isEmpty() ? null : bestIndices; |
| | | } |
| | | |
| | | private void backtrack(double[] nums, double target, int start, int k, double currentSum, int count, List<Integer> currentIndices, double[] currentMax, List<Integer> path) { |
| | | if (count == k) { |
| | | if (currentSum <= target && currentSum > currentMax[0]) { |
| | | currentMax[0] = currentSum; |
| | | currentIndices.clear(); |
| | | currentIndices.addAll(path); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | for (int i = start; i < nums.length; i++) { |
| | | if (currentSum + nums[i] > target) continue; |
| | | path.add(i); |
| | | backtrack(nums, target, i + 1, k, currentSum + nums[i], count + 1, currentIndices, currentMax, path); |
| | | path.remove(path.size() - 1); |
| | | } |
| | | } |
| | | |
| | | public static void main(String[] args) { |
| | | CombinationFinder finder = new CombinationFinder(); |
| | | double[] nums = {3.0, 1.0, 4.0, 2.0}; |
| | | double target = 0.1; |
| | | List<Integer> result = finder.findCombination(nums, target); |
| | | System.out.println("最优组合的索引: " + result); // 例如,等值组合可能为索引2(4.0)和3(1.0) |
| | | } |
| | | } |
| | |
| | | private Integer sourceStaNo; //作业站点 or 来源站点 |
| | | private String barcode; //容器条码 |
| | | private Integer locType1; //库位类型 |
| | | private Integer area; |
| | | // private Integer locType2; //库位类型 |
| | | // private Integer locType3; //库位类型 |
| | | } |
| | |
| | | public enum TaskStsType {
|
| | |
|
| | | //任务状态
|
| | | GENERATE_IN(1L, "创建入库任务"),
|
| | | WCS_EXECUTE_IN(2L, "RCS任务已下发"),
|
| | | WCS_CONTAINER_RECEIVE(3L, "RCS容器到达"),
|
| | | WCS_CONVEYOR_START(4L, "RCS容器流动任务已下发"),
|
| | | WCS_TOTE_LOAD(5L, "RCS取箱完成"),
|
| | | WCS_TOTE_UNLOAD(6L, "RCS放箱完成"),
|
| | | WCS_PUTAWAY_SUCESS(7L, "RCS任务完成"),
|
| | | GENERATE_IN("1", "创建入库任务"),
|
| | | WCS_EXECUTE_IN("2", "RCS任务已下发"),
|
| | | WCS_CONTAINER_RECEIVE("3", "RCS容器到达"),
|
| | | WCS_CONVEYOR_START("4", "RCS容器流动任务已下发"),
|
| | | WCS_TOTE_LOAD("5", "RCS取箱完成"),
|
| | | WCS_TOTE_UNLOAD("6", "RCS放箱完成"),
|
| | | WCS_PUTAWAY_SUCESS("7", "RCS任务完成"),
|
| | |
|
| | | // WCS_PUTAWAY_FAILED(11L, "任务失败"),
|
| | | //
|
| | |
| | | //
|
| | | // WCS_PUTAWAY_SUSPEND(13L, "入库任务挂起"),
|
| | |
|
| | | COMPLETE_IN(99L, "入库完成"),
|
| | | COMPLETE_IN("99", "入库完成"),
|
| | |
|
| | | UPDATED_IN(100L, "库存更新完成"),
|
| | | UPDATED_IN("100", "库存更新完成"),
|
| | |
|
| | | GENERATE_OUT(101L, "创建出库任务"),
|
| | | GENERATE_OUT("101", "创建出库任务"),
|
| | |
|
| | | WCS_EXECUTE_OUT(102L, "RCS出库任务已下发"),
|
| | | WCS_EXECUTE_OUT("102", "RCS出库任务已下发"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TOTE_LOAD(103L, "RCS取箱完成"),
|
| | | WCS_EXECUTE_OUT_TOTE_LOAD("103", "RCS取箱完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TOTE_UNLOAD(104L, "RCS放箱完成"),
|
| | | WCS_EXECUTE_OUT_TOTE_UNLOAD("104", "RCS放箱完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_TASK_DONE(105L, "RCS任务完成"),
|
| | | WCS_EXECUTE_OUT_TASK_DONE("105", "RCS任务完成"),
|
| | |
|
| | | WCS_EXECUTE_OUT_ARRIVED(106L, "RCS容器已到达"),
|
| | | WCS_EXECUTE_OUT_ARRIVED("106", "RCS容器已到达"),
|
| | |
|
| | | WCS_EXECUTE_OUT_CONVEYOR(107L, "RCS容器流动任务已下发"),
|
| | | WCS_EXECUTE_OUT_CONVEYOR("107", "RCS容器流动任务已下发"),
|
| | |
|
| | | GENERATE_WAVE_SEED(197L, "等待容器到达"),
|
| | | WAVE_SEED(198L, "播种中"),
|
| | | GENERATE_WAVE_SEED("197", "等待容器到达"),
|
| | | WAVE_SEED("198", "播种中"),
|
| | |
|
| | | COMPLETE_OUT(199L, "出库完成"),
|
| | | UPDATED_OUT(200L, "库存更新完成"),
|
| | | COMPLETE_OUT("199", "出库完成"),
|
| | | UPDATED_OUT("200", "库存更新完成"),
|
| | | ;
|
| | |
|
| | | public Long id;
|
| | | public Short id;
|
| | | public String desc;
|
| | |
|
| | | TaskStsType(Long id, String desc) {
|
| | | this.id = id;
|
| | | TaskStsType(String id, String desc) {
|
| | | this.id = Short.parseShort(id);
|
| | | this.desc = desc;
|
| | | }
|
| | |
|
| | |
| | | */ |
| | | public enum TaskType { |
| | | |
| | | TASK_TYPE_IN(1, "入库"), |
| | | TASK_TYPE_EMPITY_IN(10, "空板入库"), |
| | | TASK_TYPE_LOC_MOVE(11, "库格移载"), |
| | | TASK_TYPE_PICK_IN(53, "拣料再入库"), |
| | | TASK_TYPE_MERGE_IN(54, "并板再入库"), |
| | | TASK_TYPE_CHECK_IN(57, "盘点再入库"), |
| | | TASK_TYPE_OUT(101, "出库"), |
| | | TASK_TYPE_PICK_AGAIN_IN(103, "拣料入库"), |
| | | TASK_TYPE_MERGE_OUT(104, "并板出库"), |
| | | TASK_TYPE_CHECK_OUT(107, "盘点出库"), |
| | | TASK_TYPE_EMPITY_OUT(110, "空板出库"), |
| | | TASK_TYPE_IN("1", "入库"), |
| | | TASK_TYPE_EMPITY_IN("10", "空板入库"), |
| | | TASK_TYPE_LOC_MOVE("11", "库格移载"), |
| | | TASK_TYPE_PICK_IN("53", "拣料再入库"), |
| | | TASK_TYPE_MERGE_IN("54", "并板再入库"), |
| | | TASK_TYPE_CHECK_IN("57", "盘点再入库"), |
| | | TASK_TYPE_OUT("101", "出库"), |
| | | TASK_TYPE_PICK_AGAIN_IN("103", "拣料入库"), |
| | | TASK_TYPE_MERGE_OUT("104", "并板出库"), |
| | | TASK_TYPE_CHECK_OUT("107", "盘点出库"), |
| | | TASK_TYPE_EMPITY_OUT("110", "空板出库"), |
| | | ; |
| | | public Integer type; |
| | | public Short type; |
| | | public String desc; |
| | | |
| | | TaskType(Integer type, String desc) { |
| | | this.type = type; |
| | | TaskType(String type, String desc) { |
| | | this.type = Short.parseShort(type); |
| | | this.desc = desc; |
| | | } |
| | | } |
| | |
| | | if (Cools.isEmpty(deviceSites)) { |
| | | throw new CoolException("未找到站点路径信息"); |
| | | } |
| | | WarehouseAreas warehouseArea = warehouseAreasService.getById(param.getArea()); |
| | | DeviceBind deviceBind = deviceBindService.getById(LocUtils.getAreaType(param.getSourceStaNo())); |
| | | if (Cools.isEmpty(deviceBind)) { |
| | | throw new CoolException("数据异常,请联系管理员===>库位规则未知"); |
| | | } |
| | | WarehouseAreas warehouseArea = warehouseAreasService.getById(deviceBind.getTypeId()); |
| | | if (Cools.isEmpty(warehouseArea)) { |
| | | throw new CoolException("未找到所属库区信息"); |
| | | } |
| | |
| | | InTaskMsgDto dto = null; |
| | | switch (warehouseArea.getType()) { |
| | | case "CRN": //堆垛机 |
| | | dto = getLocNoCrn(param.getArea(), param.getSourceStaNo(), matnr,batch, locTypeDto, 0, param.getIoType()); |
| | | dto = getLocNoCrn(deviceBind,warehouseArea.getId(), param.getSourceStaNo(), matnr,batch, locTypeDto, 0, param.getIoType()); |
| | | break; |
| | | case "SXC": //四向库 |
| | | break; |
| | | case "CTU": //四向库 |
| | | case "CTU": //ctu |
| | | dto = getLocNoCtu(deviceBind,warehouseArea.getId(), param.getSourceStaNo(), matnr,batch, locTypeDto, 0, param.getIoType()); |
| | | break; |
| | | } |
| | | return dto; |
| | | } |
| | | |
| | | private InTaskMsgDto getLocNoCrn(Integer area,Integer sourceStaNo, String matnr, String batch,LocTypeDto locTypeDto, int times,Integer ioType){ |
| | | private InTaskMsgDto getLocNoCrn(DeviceBind deviceBind,Long area,Integer sourceStaNo, String matnr, String batch,LocTypeDto locTypeDto, int times,Integer ioType){ |
| | | if (Cools.isEmpty(matnr)) { //物料号 |
| | | matnr = ""; |
| | | } |
| | |
| | | Loc loc = null; // 目标库位 |
| | | |
| | | InTaskMsgDto inTaskMsgDto = new InTaskMsgDto(); |
| | | DeviceBind deviceBind = deviceBindService.getById(LocUtils.getAreaType(sourceStaNo)); |
| | | if (Cools.isEmpty(deviceBind)) { |
| | | throw new CoolException("数据异常,请联系管理员===>库位规则未知"); |
| | | } |
| | | |
| | | int sRow = deviceBind.getStartRow(); |
| | | int eRow = deviceBind.getEndRow(); |
| | | int deviceQty = deviceBind.getDeviceQty(); |
| | |
| | | String shallowLocNo = LocUtils.getShallowLoc(slaveProperties, loc1.getCode()); |
| | | // 检测目标库位是否为空库位 |
| | | Loc shallowLoc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getCode,shallowLocNo)); |
| | | if (shallowLoc != null && shallowLoc.getUseStatus().equals("O")) { |
| | | if (shallowLoc != null && shallowLoc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) { |
| | | if (LocUtils.locMoveCheckLocTypeComplete(shallowLoc, locTypeDto)) { |
| | | loc = shallowLoc; |
| | | deviceNo = shallowLoc.getDeviceNo(); |
| | |
| | | List<Loc> locMasts = null; |
| | | locMasts = locService.list(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getRow, nearRow) |
| | | .eq(Loc::getUseStatus, "O") |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getType, locTypeDto.getLocType1()) |
| | | .eq(Loc::getAreaId,area) |
| | | .orderByAsc(Loc::getLev) |
| | |
| | | //相似物料打开,判断深库位有没有货,没货就放深库位,有货就不操作 |
| | | Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getRow, shallowLoc) |
| | | .eq(Loc::getUseStatus, "O") |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getAreaId,area) |
| | | ); |
| | | if (!Cools.isEmpty(locMast2)) { |
| | |
| | | //相似物料关闭,判断深库位有没有货,有货就放浅库位,无货就不操作 |
| | | Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getCode, shallowLoc) |
| | | .in(Loc::getUseStatus, "D","F") |
| | | .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type,LocStsType.LOC_STS_TYPE_F.type) |
| | | .eq(Loc::getAreaId,area) |
| | | ); |
| | | if (!Cools.isEmpty(locMast2)) { |
| | |
| | | }else{ |
| | | locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getCode, shallowLoc) |
| | | .eq(Loc::getUseStatus, "O") |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getAreaId,area) |
| | | ); |
| | | if (!Cools.isEmpty(locMast2)) { |
| | |
| | | String shallowLoc = LocUtils.getDeepLoc(slaveProperties, locMast1.getCode()); |
| | | Loc locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getCode, shallowLoc) |
| | | .eq(Loc::getUseStatus, "O") |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getAreaId,area) |
| | | ); |
| | | if (!Cools.isEmpty(locMast2)) { |
| | |
| | | } else { |
| | | locMast2 = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getCode, shallowLoc) |
| | | .in(Loc::getUseStatus, "D","F") |
| | | .in(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_D.type,LocStsType.LOC_STS_TYPE_D.type) |
| | | .eq(Loc::getAreaId,area) |
| | | ); |
| | | if (!Cools.isEmpty(locMast2)) { |
| | |
| | | } |
| | | //查询当前库位类型空库位 小于5个则locmast = null |
| | | List<Loc> locTypeLocMasts = locService.list(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getUseStatus, "O") |
| | | .eq(Loc::getUseStatus,LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getDeviceNo, deviceNo) |
| | | .eq(Loc::getType, locTypeDto.getLocType1()) |
| | | .eq(Loc::getAreaId,area) |
| | |
| | | loc = null; |
| | | } |
| | | // 递归查询 |
| | | if (Cools.isEmpty(loc) || !loc.getUseStatus().equals("O")) { |
| | | if (Cools.isEmpty(loc) || !loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) { |
| | | // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归 |
| | | if (times < rowCount * 2) { |
| | | times = times + 1; |
| | | return getLocNoCrn(area,sourceStaNo,matnr,batch,locTypeDto,times, ioType); |
| | | return getLocNoCrn(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,times, ioType); |
| | | |
| | | } |
| | | // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位 |
| | | if (locTypeDto.getLocType1() < 3) { |
| | | int i = locTypeDto.getLocType1() + 1; |
| | | locTypeDto.setLocType1(i); |
| | | return getLocNoCrn(area,sourceStaNo,matnr,batch,locTypeDto,0, ioType); |
| | | return getLocNoCrn(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,0, ioType); |
| | | } |
| | | throw new CoolException("没有空库位"); |
| | | } |
| | |
| | | inTaskMsgDto.setLocNo(locNo); |
| | | return inTaskMsgDto; |
| | | } |
| | | |
| | | private InTaskMsgDto getLocNoCtu(DeviceBind deviceBind,Long area,Integer sourceStaNo, String matnr, String batch,LocTypeDto locTypeDto, int times,Integer ioType){ |
| | | if (Cools.isEmpty(matnr)) { //物料号 |
| | | matnr = ""; |
| | | } |
| | | if (Cools.isEmpty(batch)) { //批次 |
| | | batch = ""; |
| | | } |
| | | int deviceNo = 0; |
| | | Loc loc = new Loc(); |
| | | InTaskMsgDto inTaskMsgDto = new InTaskMsgDto(); |
| | | List<Loc> loc1 = locService.list(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getAreaId, area) |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type) |
| | | .eq(Loc::getType, locTypeDto.getLocType1()) |
| | | .orderByAsc(Loc::getRow) |
| | | .orderByAsc(Loc::getCol) |
| | | .orderByAsc(Loc::getLev) |
| | | ); |
| | | for (Loc loc2 :loc1){ |
| | | if (!LocUtils.locMoveCheckLocTypeComplete(loc2, locTypeDto)) { |
| | | continue; |
| | | } |
| | | loc = loc2; |
| | | break; |
| | | } |
| | | //查找路径 |
| | | DeviceSite deviceSite = deviceSiteService.getOne(new LambdaQueryWrapper<DeviceSite>() |
| | | .eq(DeviceSite::getType, ioType) |
| | | .eq(DeviceSite::getSite, sourceStaNo) |
| | | .eq(DeviceSite::getDeviceCode, loc.getDeviceNo()) |
| | | ); |
| | | if (Cools.isEmpty(deviceSite)){ |
| | | deviceNo = 0; |
| | | loc = null; |
| | | }else { |
| | | inTaskMsgDto.setStaNo(Integer.parseInt(deviceSite.getDeviceSite())); |
| | | } |
| | | // 递归查询 |
| | | if (Cools.isEmpty(loc) || !loc.getUseStatus().equals(LocStsType.LOC_STS_TYPE_O.type)) { |
| | | // 当前巷道无空库位时,递归调整至下一巷道,检索全部巷道无果后,跳出递归 |
| | | if (times < 5) { |
| | | times = times + 1; |
| | | return getLocNoCtu(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,times, ioType); |
| | | |
| | | } |
| | | // 2.库位当前所属尺寸无空库位时,调整尺寸参数,向上兼容检索库位 |
| | | if (locTypeDto.getLocType1() < 3) { |
| | | int i = locTypeDto.getLocType1() + 1; |
| | | locTypeDto.setLocType1(i); |
| | | return getLocNoCtu(deviceBind,area,sourceStaNo,matnr,batch,locTypeDto,0, ioType); |
| | | } |
| | | throw new CoolException("没有空库位"); |
| | | } |
| | | |
| | | String locNo = loc.getCode(); |
| | | |
| | | // 返回dto |
| | | inTaskMsgDto.setDeviceNo(deviceNo); |
| | | inTaskMsgDto.setSourceStaNo(sourceStaNo); |
| | | // inTaskMsgDto.setStaNo(); |
| | | inTaskMsgDto.setLocNo(locNo); |
| | | return inTaskMsgDto; |
| | | } |
| | | } |
| | |
| | | import com.alibaba.fastjson.JSONArray; |
| | | import com.alibaba.fastjson.JSONObject; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.framework.common.R; |
| | |
| | | public R page(@RequestBody Map<String, Object> map) { |
| | | BaseParam baseParam = buildParam(map, BaseParam.class); |
| | | PageParam<AsnOrder, BaseParam> pageParam = new PageParam<>(baseParam, AsnOrder.class); |
| | | return R.ok().add(asnOrderService.page(pageParam, pageParam.buildWrapper(true))); |
| | | QueryWrapper<AsnOrder> queryWrapper = pageParam.buildWrapper(true); |
| | | List<String> asList = Arrays.asList(OrderType.ORDER_OUT.type); |
| | | queryWrapper.notIn("type", asList); |
| | | return R.ok().add(asnOrderService.page(pageParam, queryWrapper)); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('manager:asnOrder:list')") |
| | |
| | | if (Objects.isNull(ids) || ids.length < 1) { |
| | | return R.error("参数不能为空!!"); |
| | | } |
| | | List<Long> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().in(Task::getId, ids).in(Task::getTaskStatus, longs)); |
| | | if (tasks.isEmpty()) { |
| | | throw new CoolException("任务已处执行状态不可取消!!"); |
| | |
| | | if (Objects.isNull(id)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | List<Long> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getId, id).in(Task::getTaskStatus, longs)); |
| | | // if (tasks.isEmpty()) { |
| | | // throw new CoolException("任务已处执行状态不可一键完成!!"); |
| | |
| | | if (Objects.isNull(id)) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | List<Long> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Short> longs = Arrays.asList(TaskStsType.GENERATE_IN.id, TaskStsType.GENERATE_OUT.id); |
| | | List<Task> tasks = taskService.list(new LambdaQueryWrapper<Task>().eq(Task::getId, id).in(Task::getTaskStatus, longs)); |
| | | if (tasks.isEmpty()) { |
| | | throw new CoolException("任务已处执行状态不可一键置顶!!"); |
| | |
| | | ExcelUtil.build(ExcelUtil.create(waveService.list(), Wave.class), response); |
| | | } |
| | | |
| | | |
| | | |
| | | @PreAuthorize("hasAuthority('manager:wave:update')") |
| | | @ApiOperation("波次下发任务") |
| | | @PostMapping("/wave/public/task") |
| | | public R publicTask(@RequestBody Map<String, Object> map) { |
| | | if (Cools.isEmpty(map)) { |
| | | if (Cools.isEmpty(map) || Cools.isEmpty(map.get("wave"))) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | return waveService.publicTask(map); |
| | | return waveService.publicTask(map, getLoginUserId()); |
| | | } |
| | | |
| | | @PreAuthorize("hasAuthority('manager:wave:list')") |
| | |
| | | import com.vincent.rsf.server.system.service.DictDataService; |
| | | import lombok.experimental.Accessors; |
| | | import org.springframework.format.annotation.DateTimeFormat; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import java.text.SimpleDateFormat; |
| | | |
| | | import java.util.Date; |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | |
| | | @ApiModelProperty(value= "送货数量") |
| | | private Double anfme; |
| | | |
| | | @ApiModelProperty("执行数量") |
| | | private Double workQty; |
| | | |
| | | /** |
| | | * 已收数量 |
| | | */ |
| | |
| | | @ApiModelProperty(value= "物流单号") |
| | | private String logisNo; |
| | | |
| | | @ApiModelProperty("波次ID") |
| | | private Long waveId; |
| | | |
| | | /** |
| | | * 预计到达时间 |
| | | */ |
| | |
| | | package com.vincent.rsf.server.manager.entity; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.*; |
| | | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | |
| | | import java.text.SimpleDateFormat; |
| | | import java.util.Date; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.IdType; |
| | | import com.baomidou.mybatisplus.annotation.TableId; |
| | | import com.baomidou.mybatisplus.annotation.TableLogic; |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import io.swagger.annotations.ApiModel; |
| | | import io.swagger.annotations.ApiModelProperty; |
| | | import lombok.Data; |
| | |
| | | @ApiModelProperty(value= "数量") |
| | | private Double anfme; |
| | | |
| | | |
| | | @ApiModelProperty("执行数量") |
| | | private Double workQty; |
| | | |
| | | |
| | | @ApiModelProperty("完成数量") |
| | | private Double qty; |
| | |
| | | @DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss") |
| | | private Date updateTime; |
| | | |
| | | @TableField(exist = false) |
| | | private Long waveId; |
| | | |
| | | @TableField(exist = false) |
| | | private Long waveItemId; |
| | | |
| | | @TableField(exist = false) |
| | | private String waveCode; |
| | | |
| | | |
| | | |
| | | /** |
| | | * 备注 |
| | | */ |
| | |
| | | */ |
| | | @ApiModelProperty("物料跟踪码") |
| | | private String trackCode; |
| | | |
| | | @ApiModelProperty("供应商波次") |
| | | private String splrBatch; |
| | | /** |
| | | * 物料编码 |
| | | */ |
| | |
| | | @ApiModelProperty(value= "已完成数量") |
| | | private Double qty; |
| | | |
| | | @ApiModelProperty("目标位置") |
| | | private String targSite; |
| | | |
| | | /** |
| | | * 单据数量 |
| | | */ |
| | |
| | | @ApiModelProperty(value= "修改人员") |
| | | private Long updateBy; |
| | | |
| | | /** |
| | | * 目标库位 |
| | | */ |
| | | @ApiModelProperty("目标库位") |
| | | @TableField(exist = false) |
| | | private String stockLocs; |
| | | |
| | | /** |
| | | * 库存数量 |
| | | */ |
| | | @ApiModelProperty("库存数量") |
| | | @TableField(exist = false) |
| | | private Double stockQty; |
| | | |
| | | |
| | | /*** |
| | | * 是否全拖出库 |
| | | */ |
| | | @TableField(exist = false) |
| | | private Short flagAll; |
| | | /** |
| | | * 备注 |
| | | */ |
| | | @ApiModelProperty(value= "备注") |
| | |
| | | public interface WaveService extends IService<Wave> { |
| | | |
| | | /** |
| | | * @param |
| | | * @param loginUserId |
| | | * @return |
| | | * @author Ryan |
| | | * @description 波次任务下发 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/25 16:24 |
| | | */ |
| | | R publicTask(Map<String, Object> map); |
| | | R publicTask(Map<String, Object> map, Long loginUserId); |
| | | |
| | | /** |
| | | * @author Ryan |
| | |
| | | throw new CoolException("主单修改失败!!"); |
| | | } |
| | | |
| | | for (int i = 0; i < orderItems.size(); i++) { |
| | | orderItems.get(i).setWorkQty(orderItems.get(i).getAnfme()); |
| | | } |
| | | |
| | | if (!asnOrderItemService.saveOrUpdateBatch(orderItems)) { |
| | | throw new CoolException("出库单执行数量修改失败!!"); |
| | | } |
| | | |
| | | double sum2 = orderItems.stream().mapToDouble(AsnOrderItem::getWorkQty).sum(); |
| | | if (!this.update(new LambdaUpdateWrapper<AsnOrder>() |
| | | .set(AsnOrder::getWaveId, wave.getId()) |
| | | .set(AsnOrder::getWorkQty, sum2) |
| | | .set(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WAVE.val) |
| | | .in(AsnOrder::getId, ids))) { |
| | | throw new CoolException("执行状态修改修改失败!!"); |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.vincent.rsf.framework.common.Cools; |
| | | import com.vincent.rsf.server.api.entity.enums.OrderType; |
| | | import com.vincent.rsf.server.api.entity.enums.TaskStsType; |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.api.entity.enums.TaskType; |
| | | import com.vincent.rsf.server.api.utils.LocUtils; |
| | | import com.vincent.rsf.server.manager.controller.params.GenerateTaskParams; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.PakinIOStatus; |
| | |
| | | @Autowired |
| | | private LocItemService locItemService; |
| | | |
| | | @Autowired |
| | | private DeviceBindService deviceBindService; |
| | | @Autowired |
| | | private WarehouseAreasService warehouseAreasService; |
| | | |
| | | |
| | | /** |
| | | * @param |
| | |
| | | if (Objects.isNull(deviceSite)) { |
| | | throw new CoolException("站点不存在!!"); |
| | | } |
| | | DeviceBind deviceBind = deviceBindService.getById(LocUtils.getAreaType(Integer.valueOf(deviceSite.getSite()))); |
| | | if (Cools.isEmpty(deviceBind)) { |
| | | throw new CoolException("库位规则未知"); |
| | | } |
| | | WarehouseAreas warehouseArea = warehouseAreasService.getById(deviceBind.getTypeId()); |
| | | if (Cools.isEmpty(warehouseArea)) { |
| | | throw new CoolException("未找到所属库区信息"); |
| | | } |
| | | |
| | | /**获取库位*/ |
| | | String targetLoc = LocManageUtil.getTargetLoc(warehouseArea.getId()); |
| | | if (Cools.isEmpty(targetLoc)) { |
| | | throw new CoolException("该站点对应库区未找到库位"); |
| | | } |
| | | /**获取组拖*/ |
| | | List<Long> ids = waitPakin.getWaitPakins().stream().map(WaitPakin::getId).collect(Collectors.toList()); |
| | | List<WaitPakin> waitPakins = waitPakinService.list(new LambdaQueryWrapper<WaitPakin>() |
| | |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskStatus(TaskStsType.GENERATE_IN.id.shortValue()) |
| | | .setTaskType(TaskType.TASK_TYPE_IN.type.shortValue()) |
| | | .setTargLoc(LocManageUtil.getTargetLoc()) |
| | | .setTargLoc(targetLoc) |
| | | .setBarcode(pakin.getBarcode()) |
| | | .setTargSite(deviceSite.getDeviceCode()) |
| | | .setOrgSite(deviceSite.getSite()) |
| | | .setTargSite(deviceSite.getDeviceSite()) |
| | | .setCreateBy(loginUserId) |
| | | .setUpdateBy(loginUserId); |
| | | |
| | | if (!Objects.isNull(waitPakin.getSiteId()) && waitPakin.getSiteId() > 0) { |
| | | DeviceSite site = deviceSiteService.getById(waitPakin.getSiteId()); |
| | | task.setTargSite(site.getSite() + ""); |
| | | } else { |
| | | task.setTargSite(LocManageUtil.getTargetSite()); |
| | | } |
| | | if (!Objects.isNull(waitPakin.getLocCode()) && StringUtils.isNotBlank(waitPakin.getLocCode())) { |
| | | List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, waitPakin.getLocCode())); |
| | | // if (!locs.isEmpty()) { |
| | | // throw new CoolException("库位错误:相同库位应只一条") |
| | | // } |
| | | Loc loc = locs.stream().findFirst().get(); |
| | | task.setTargSite(loc.getCode()); |
| | | } else { |
| | | task.setTargSite(LocManageUtil.getTargetSite()); |
| | | } |
| | | // if (!Objects.isNull(waitPakin.getSiteId()) && waitPakin.getSiteId() > 0) { |
| | | // DeviceSite site = deviceSiteService.getById(waitPakin.getSiteId()); |
| | | // task.setTargSite(site.getSite() + ""); |
| | | // } else { |
| | | // task.setTargSite(LocManageUtil.getTargetSite()); |
| | | // } |
| | | // if (!Objects.isNull(waitPakin.getLocCode()) && StringUtils.isNotBlank(waitPakin.getLocCode())) { |
| | | // List<Loc> locs = locService.list(new LambdaQueryWrapper<Loc>().eq(Loc::getCode, waitPakin.getLocCode())); |
| | | //// if (!locs.isEmpty()) { |
| | | //// throw new CoolException("库位错误:相同库位应只一条") |
| | | //// } |
| | | // Loc loc = locs.stream().findFirst().get(); |
| | | // task.setTargSite(loc.getCode()); |
| | | // } else { |
| | | // task.setTargSite(LocManageUtil.getTargetSite()); |
| | | // } |
| | | |
| | | if (!this.save(task)) { |
| | | throw new CoolException("任务保存失败!!"); |
| | |
| | | if (!locService.update(new LambdaUpdateWrapper<Loc>().set(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_F.type).in(Loc::getCode, locCodes))) { |
| | | throw new CoolException("库位状态修改失败!!"); |
| | | } |
| | | if (!this.update(new LambdaUpdateWrapper<Task>().set(Task::getTaskStatus, TaskStsType.UPDATED_IN.id))) { |
| | | if (!this.update(new LambdaUpdateWrapper<Task>().in(Task::getId, list).set(Task::getTaskStatus, TaskStsType.UPDATED_IN.id))) { |
| | | throw new CoolException("任务状态修改失败!!"); |
| | | } |
| | | } |
| | |
| | | * @time 2025/4/15 15:28 |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | void saveLocItem(List<TaskItem> items, Long taskId) throws Exception { |
| | | public void saveLocItem(List<TaskItem> items, Long taskId) throws Exception { |
| | | Task task = this.getById(taskId); |
| | | if (Objects.isNull(task)) { |
| | | throw new CoolException("任务不存在!!"); |
| | |
| | | * @return |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | void saveStockItems(List<TaskItem> items, WaitPakinItem order) throws Exception { |
| | | public void saveStockItems(List<TaskItem> items, WaitPakinItem order) throws Exception { |
| | | Stock stock = new Stock(); |
| | | // if (!Objects.isNull(order.getPoCode()) && StringUtils.isNotBlank(order.getPoCode())) { |
| | | // Purchase purchase = purchaseService.getOne(new LambdaQueryWrapper<Purchase>().eq(Purchase::getCode, order.getPoCode())); |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
| | | import com.vincent.rsf.framework.common.R; |
| | | import com.vincent.rsf.framework.exception.CoolException; |
| | | import com.vincent.rsf.server.manager.entity.AsnOrder; |
| | | import com.vincent.rsf.server.manager.entity.LocItem; |
| | | import com.vincent.rsf.server.manager.entity.WaveItem; |
| | | import com.vincent.rsf.server.api.entity.enums.TaskStsType; |
| | | import com.vincent.rsf.server.api.entity.enums.TaskType; |
| | | import com.vincent.rsf.server.manager.entity.*; |
| | | import com.vincent.rsf.server.manager.enums.AsnExceStatus; |
| | | import com.vincent.rsf.server.manager.enums.WaveExceStatus; |
| | | import com.vincent.rsf.server.manager.mapper.WaveMapper; |
| | | import com.vincent.rsf.server.manager.entity.Wave; |
| | | import com.vincent.rsf.server.manager.service.*; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.vincent.rsf.server.manager.utils.OptimalAlgorithmUtil; |
| | | import com.vincent.rsf.server.system.constant.SerialRuleCode; |
| | | 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; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Objects; |
| | | import java.util.*; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service("waveService") |
| | |
| | | private TaskItemService taskItemService; |
| | | @Autowired |
| | | private LocItemService locItemService; |
| | | @Autowired |
| | | private LocService locService; |
| | | |
| | | /** |
| | | * @param |
| | | * @param loginUserId |
| | | * @return |
| | | * @author Ryan |
| | | * @description 波次任务下发 |
| | |
| | | */ |
| | | @Override |
| | | @Transactional(rollbackFor = Exception.class) |
| | | public R publicTask(Map<String, Object> map) { |
| | | List<Long> ids = (List<Long>) map.get("ids"); |
| | | if (Objects.isNull(ids) || ids.isEmpty()) { |
| | | public R publicTask(Map<String, Object> map, Long loginUserId) { |
| | | List<WaveItem> itemParams = (List<WaveItem>) map.get("waveItem"); |
| | | if (Objects.isNull(itemParams) || itemParams.isEmpty()) { |
| | | throw new CoolException("参数不能为空!!"); |
| | | } |
| | | List<Wave> waves = this.list(new LambdaQueryWrapper<Wave>().in(Wave::getId, ids)); |
| | | if (Objects.isNull(waves) || waves.isEmpty()) { |
| | | Wave wave = (Wave) map.get("wave"); |
| | | Wave waves = this.getById(new LambdaQueryWrapper<Wave>().in(Wave::getId, wave.getId())); |
| | | if (Objects.isNull(waves)) { |
| | | throw new CoolException("波次数据不存在!!"); |
| | | } |
| | | List<Long> list = waves.stream().map(Wave::getId).collect(Collectors.toList()); |
| | | List<Long> list = itemParams.stream().map(WaveItem::getWaveId).collect(Collectors.toList()); |
| | | List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().in(WaveItem::getWaveId, list)); |
| | | if (waveItems.isEmpty()) { |
| | | throw new CoolException("波次明细不存在!!"); |
| | | } |
| | | List<Long> orderIds = waveItems.stream().map(WaveItem::getOrderId).collect(Collectors.toList()); |
| | | |
| | | /**查询每条明细匹配的库位*/ |
| | | /**生成出库任务*/ |
| | | try { |
| | | List<WaveItem> items = getLocs(waveItems); |
| | | generateOutTask(itemParams, loginUserId, wave); |
| | | } catch (Exception e) { |
| | | throw new CoolException("库位获取失败!!!"); |
| | | } |
| | |
| | | // 2. 根据物料SKU寻找符合物料库位 {1. 根据物料编码,批次,动态字段 查询符合的库位,再根据库位中物料的数量选择最适合的库位 2. 判断当前订单是全拖出库还是拣料入库} |
| | | // 3. 修改主单、波次执行数量 |
| | | // 4. 判断全仓出库或拣料出库 |
| | | List<Long> orderIds = waveItems.stream().map(WaveItem::getOrderId).collect(Collectors.toList()); |
| | | |
| | | List<AsnOrder> orders = asnOrderService.list(new LambdaQueryWrapper<AsnOrder>().in(AsnOrder::getId, orderIds)); |
| | | /**修改原出库单状态*/ |
| | | /**修改出库单状态*/ |
| | | if (!asnOrderService.update(new LambdaQueryWrapper<AsnOrder>() |
| | | .eq(AsnOrder::getExceStatus, AsnExceStatus.OUT_STOCK_STATUS_TASK_WORKING.val) |
| | | .in(AsnOrder::getId, orders))) { |
| | | throw new CoolException("出库单据状态修改失败!!"); |
| | | } |
| | | if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).in(Wave::getId, ids))) { |
| | | /**修改波次单据执行状态*/ |
| | | if (!this.update(new LambdaUpdateWrapper<Wave>().set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK).eq(Wave::getId, wave.getId()))) { |
| | | throw new CoolException("波次状态修改失败!!"); |
| | | } |
| | | return R.ok(); |
| | | } |
| | | |
| | | /** |
| | | * @param |
| | | * @param loginUserId |
| | | * @param wave |
| | | * @return |
| | | * @author Ryan |
| | | * @description 生成出库任务 |
| | | * @time 2025/4/28 14:01 |
| | | */ |
| | | @Transactional(rollbackFor = Exception.class) |
| | | private synchronized void generateOutTask(List<WaveItem> itemParams, Long loginUserId, Wave wave) throws Exception { |
| | | List<LocItem> locItemList = new ArrayList<>(); |
| | | for (WaveItem param : itemParams) { |
| | | String locs = param.getStockLocs(); |
| | | List<LocItem> locItems = JSONArray.parseArray(locs, LocItem.class); |
| | | if (locItems.isEmpty()) { |
| | | continue; |
| | | } |
| | | List<Long> list = locItems.stream().map(LocItem::getId).collect(Collectors.toList()); |
| | | /**根据供应商批次,物料码, 动态字段查询指定的物料库存信息*/ |
| | | List<LocItem> items = locItemService.list(new LambdaQueryWrapper<LocItem>() |
| | | .eq(LocItem::getSplrBatch, param.getSplrBatch()) |
| | | .in(LocItem::getLocId, list) |
| | | .eq(StringUtils.isNotBlank(param.getFieldsIndex()), LocItem::getFieldsIndex, param.getFieldsIndex()) |
| | | .eq(LocItem::getMatnrCode, param.getMatnrCode())); |
| | | if (items.isEmpty()) { |
| | | throw new CoolException("库存信息有变,请取消当前波次,生新生成新的波次!!"); |
| | | } |
| | | /***将有货有的明细信息存放到库位信息中*/ |
| | | for (int i = 0; i < items.size(); i++) { |
| | | items.get(i) |
| | | .setWaveId(param.getWaveId()) |
| | | .setWaveCode(param.getWaveCode()) |
| | | .setWaveItemId(param.getId()); |
| | | } |
| | | locItemList.addAll(items); |
| | | } |
| | | if (locItemList.isEmpty()) { |
| | | throw new CoolException("没有合适库位!!"); |
| | | } |
| | | |
| | | /**拆分波次明细库位集,合并相同库位,分解任务明细*/ |
| | | Map<Long, List<LocItem>> listMap = locItemList.stream().collect(Collectors.groupingBy(LocItem::getLocId)); |
| | | /**根据库位汇总信息,生成任务明细**/ |
| | | listMap.keySet().forEach(key -> { |
| | | List<LocItem> locItems = listMap.get(key); |
| | | LocItem item1 = locItems.stream().findFirst().get(); |
| | | WaveItem waveItem = waveItemService.getById(item1.getWaveItemId()); |
| | | if (null == waveItem || Objects.isNull(waveItem)) { |
| | | throw new CoolException("数据错误:波次明细不存在!!"); |
| | | } |
| | | //TODO 当前任务完成后,通过定时事件判断是全盘出库,还是拣料再入库 |
| | | Loc loc = locService.getById(key); |
| | | Task task = new Task(); |
| | | String ruleCode = SerialRuleUtils.generateRuleCode(SerialRuleCode.SYS_TASK_CODE, null); |
| | | if (StringUtils.isBlank(ruleCode)) { |
| | | throw new CoolException("编码规则错误:请检查「SYS_TASK_CODE」是否设置完成!!"); |
| | | } |
| | | if (Objects.isNull(loc)) { |
| | | throw new CoolException("库位不存在!!"); |
| | | } |
| | | task.setTaskCode(ruleCode) |
| | | .setTaskType(TaskType.TASK_TYPE_OUT.type) |
| | | .setTaskStatus(TaskStsType.GENERATE_OUT.id) |
| | | .setBarcode(loc.getBarcode()) |
| | | .setOrgLoc(loc.getCode()) |
| | | .setCreateBy(loginUserId) |
| | | .setUpdateBy(loginUserId) |
| | | .setTargSite(wave.getTargSite()); |
| | | |
| | | if (!taskService.save(task)) { |
| | | throw new CoolException("任务生成失败!!"); |
| | | } |
| | | List<TaskItem> taskItems = new ArrayList<>(); |
| | | /**生成任务明细信息*/ |
| | | for (LocItem item : locItems) { |
| | | TaskItem taskItem = new TaskItem(); |
| | | BeanUtils.copyProperties(item, taskItem); |
| | | taskItem.setTaskId(task.getId()) |
| | | .setId(null) |
| | | .setSource(item.getWaveItemId()); |
| | | taskItems.add(taskItem); |
| | | } |
| | | if (!taskItemService.saveBatch(taskItems)) { |
| | | throw new CoolException("任务明细保存失败!!"); |
| | | } |
| | | |
| | | /**修改波次执行数量*/ |
| | | taskItems.forEach(item -> { |
| | | boolean update = waveItemService.update(new LambdaUpdateWrapper<WaveItem>() |
| | | .eq(WaveItem::getWaveId, item.getSource()) |
| | | .set(WaveItem::getWorkQty, item.getAnfme())); |
| | | if (!update) { |
| | | throw new CoolException("波次执行数量修改失败!!"); |
| | | } |
| | | }); |
| | | |
| | | List<WaveItem> waveItems = waveItemService.list(new LambdaQueryWrapper<WaveItem>().eq(WaveItem::getWaveId, wave.getId())); |
| | | double sum = waveItems.stream().mapToDouble(WaveItem::getWorkQty).sum(); |
| | | /**波次主单信息修改*/ |
| | | if (!update(new LambdaUpdateWrapper<Wave>() |
| | | .eq(Wave::getId, wave.getId()) |
| | | .set(Wave::getWorkQty, sum) |
| | | .set(Wave::getExceStatus, WaveExceStatus.WAVE_EXCE_STATUS_TASK.val))) { |
| | | throw new CoolException("波次主单信息修改失败!!"); |
| | | } |
| | | |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | //TODO 根据物料编码,批次,动态字段 查询符合的库位,再根据库位中物料的数量选择最适合的库位 |
| | | waveItems.forEach(waveItem -> { |
| | | List<LocItem> locItems = locItemService.list(new QueryWrapper<LocItem>() |
| | | .select("id", "loc_id", "loc_code", "order_id", "SUM(anfme) anfme", "SUM(work_qty) work_qty", "splr_batch", "fields_index", "matnr_code") |
| | | .select("id", "loc_id", "loc_code", "order_id", "SUM(anfme) anfme", "SUM(qty) qty", "SUM(work_qty) work_qty", "splr_batch", "fields_index", "matnr_code") |
| | | .lambda() |
| | | .eq(LocItem::getMatnrCode, waveItem.getMatnrCode()) |
| | | .eq(LocItem::getSplrBatch, waveItem.getSplrBatch()) |
| | | .eq(StringUtils.isNotBlank(waveItem.getFieldsIndex()), LocItem::getFieldsIndex, waveItem.getFieldsIndex()) |
| | | .groupBy(LocItem::getMatnrCode, LocItem::getSplrBatch, LocItem::getFieldsIndex, LocItem::getId)); |
| | | List<Double> doubles1 = locItems.stream().map(LocItem::getAnfme).collect(Collectors.toList()); |
| | | double[] doubles = doubles1.stream().mapToDouble(Double::doubleValue).toArray(); |
| | | |
| | | Double[] doubles = locItems.stream().map(LocItem::getAnfme).toArray(Double[]::new); |
| | | List<Double> result = OptimalAlgorithmUtil.findBestCombination(doubles, waveItem.getAnfme()); |
| | | |
| | | /**使用回溯算法计算,获取符合出库量的最简组合*/ |
| | | List<Integer> result = OptimalAlgorithmUtil.findCombination(doubles, waveItem.getAnfme()); |
| | | |
| | | String locs = JSONArray.toJSONString(new ArrayList<>()); |
| | | String locs = "[]"; |
| | | if (Objects.isNull(result) || result.isEmpty()) { |
| | | waveItem.setStockLocs(locs).setStockQty(0.0); |
| | | } else { |
| | | /**过滤集合中最简短的组合*/ |
| | | List<LocItem> locsInfo = result.stream() |
| | | .filter(i -> i >= 0 && i < locItems.size()) |
| | | .map(locItems::get).collect(Collectors.toList()); |
| | | |
| | | if (!locItems.isEmpty()) { |
| | | List<String> codes = locItems.stream().map(LocItem::getLocCode).collect(Collectors.toList()); |
| | | locs = JSONArray.toJSONString(codes); |
| | | locs = JSONArray.toJSONString(locsInfo); |
| | | Double sumQty = locsInfo.stream().mapToDouble(LocItem::getAnfme).sum(); |
| | | Double surQty = locsInfo.stream().mapToDouble(LocItem::getWorkQty).sum(); |
| | | Double qty = locsInfo.stream().mapToDouble(LocItem::getQty).sum(); |
| | | Double v = sumQty - surQty - qty; |
| | | waveItem.setStockLocs(locs).setStockQty(v); |
| | | } |
| | | waveItem.setStockLocs(locs); |
| | | }); |
| | | return waveItems; |
| | | } |
| | |
| | | * @return |
| | | * @time 2025/3/31 08:50 |
| | | */ |
| | | public static String getTargetLoc() { |
| | | public static String getTargetLoc(Long areaId) { |
| | | //TODO 库位策略后续排期 |
| | | LocService locService = SpringUtils.getBean(LocService.class); |
| | | |
| | | Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>().eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type), false); |
| | | Loc loc = locService.getOne(new LambdaQueryWrapper<Loc>() |
| | | .eq(Loc::getAreaId, areaId) |
| | | .eq(Loc::getUseStatus, LocStsType.LOC_STS_TYPE_O.type), false |
| | | ); |
| | | |
| | | return !Objects.isNull(loc) ? loc.getCode() : null; |
| | | } |
| | |
| | | import java.util.stream.Collectors; |
| | | |
| | | /** |
| | | * @param |
| | | * @author Ryan |
| | | * @description 根据对象多个属性合并处理 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/25 10:55 |
| | | */ |
| | |
| | | } |
| | | |
| | | /** |
| | | * @param |
| | | * @author Ryan |
| | | * @description 用于存储多个分组键的内部类 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/25 10:56 |
| | | */ |
| | |
| | | } |
| | | |
| | | |
| | | public static List<Double> findBestCombination(Double[] candidates, double targetSum) { |
| | | bestSolution = null; |
| | | target = targetSum; |
| | | minDifference = Double.MAX_VALUE; |
| | | Arrays.sort(candidates); |
| | | backtrack(candidates, 0, new ArrayList<>(), 0.0); |
| | | |
| | | // 如果没有精确解,返回最接近的近似解 |
| | | return bestSolution != null ? bestSolution : new ArrayList<>(); |
| | | } |
| | | |
| | | private static void backtrack(Double[] candidates, int start, |
| | | List<Double> current, double currentSum) { |
| | | // 计算当前和与目标的差值 |
| | | double difference = Math.abs(currentSum - target); |
| | | |
| | | // 如果找到更优解(差值更小或差值相同但组合更短) |
| | | if (difference < minDifference - EPSILON || |
| | | (Math.abs(difference - minDifference) < EPSILON && |
| | | (bestSolution == null || current.size() < bestSolution.size()))) { |
| | | minDifference = difference; |
| | | bestSolution = new ArrayList<>(current); |
| | | /** |
| | | * @param |
| | | * @return |
| | | * @author Ryan |
| | | * @description 获取全拖或非全拖库位 |
| | | * @time 2025/4/28 09:41 |
| | | */ |
| | | public static List<Integer> findCombination(double[] nums, Double target) { |
| | | // 处理等值情况 |
| | | List<Integer> equal = findEqual(nums, target); |
| | | if (equal != null && !equal.isEmpty()) { |
| | | return equal; |
| | | } |
| | | |
| | | // 如果已经找到精确解,不需要继续搜索更长的组合 |
| | | if (minDifference < EPSILON) { |
| | | // 处理大于的情况 |
| | | List<Integer> greater = findGreater(nums, target); |
| | | if (greater != null && !greater.isEmpty()) { |
| | | return greater; |
| | | } |
| | | |
| | | // 处理小于的情况 |
| | | List<Integer> less = findLess(nums, target); |
| | | return less != null ? less : new ArrayList<>(); // 确保不返回null |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 使用动态规划寻找和等于目标值的最少元素组合。动态规划数组dp记录达到每个和所需的最少元素数目,prev数组记录路径以便回溯找到具体索引。 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:33 |
| | | */ |
| | | private static List<Integer> findEqual(double[] nums, double target) { |
| | | int n = nums.length; |
| | | double[] dp = new double[(int) (target * 100 + 1)]; // 放大100倍处理精度问题 |
| | | Arrays.fill(dp, Double.MAX_VALUE); |
| | | dp[0] = 0; |
| | | int[] prev = new int[(int) (target * 100 + 1)]; |
| | | Arrays.fill(prev, -1); |
| | | |
| | | for (int j = 0; j < n; j++) { |
| | | int num = (int) (nums[j] * 100); // 放大100倍处理精度问题 |
| | | for (int i = (int) (target * 100); i >= num; i--) { |
| | | if (dp[i - num] != Double.MAX_VALUE && dp[i - num] + 1 < dp[i]) { |
| | | dp[i] = dp[i - num] + 1; |
| | | prev[i] = j; |
| | | } |
| | | } |
| | | } |
| | | |
| | | if (dp[(int) (target * 100)] == Double.MAX_VALUE) { |
| | | return null; |
| | | } |
| | | |
| | | List<Integer> indices = new ArrayList<>(); |
| | | int current = (int) (target * 100); |
| | | while (current > 0) { |
| | | int j = prev[current]; |
| | | if (j == -1) return null; |
| | | indices.add(j); |
| | | current -= (int) (nums[j] * 100); |
| | | } |
| | | |
| | | return indices; |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 将数组降序排序后,贪心地累加元素直到总和超过目标值,返回这些元素的索引 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:34 |
| | | */ |
| | | private static List<Integer> findGreater(double[] nums, double target) { |
| | | List<double[]> sorted = new ArrayList<>(); |
| | | for (int i = 0; i < nums.length; i++) { |
| | | sorted.add(new double[]{nums[i], i}); |
| | | } |
| | | sorted.sort((a, b) -> Double.compare(b[0], a[0])); |
| | | |
| | | double sum = 0; |
| | | List<Integer> indices = new ArrayList<>(); |
| | | for (double[] pair : sorted) { |
| | | sum += pair[0]; |
| | | indices.add((int) pair[1]); |
| | | if (sum > target) { |
| | | return indices; |
| | | } |
| | | } |
| | | |
| | | return null; |
| | | } |
| | | |
| | | /** |
| | | * @author Ryan |
| | | * @description 使用回溯法生成所有可能的组合,寻找总和最大但不超过目标值的组合,并记录元素数目最少的组合。 |
| | | * @param |
| | | * @return |
| | | * @time 2025/4/28 12:34 |
| | | */ |
| | | private static List<Integer> findLess(double[] nums, double target) { |
| | | int n = nums.length; |
| | | List<Integer> bestIndices = new ArrayList<>(); |
| | | double[] maxSum = {-1.0}; |
| | | for (int k = 1; k <= n; k++) { |
| | | List<Integer> currentIndices = new ArrayList<>(); |
| | | double[] currentMax = {-1.0}; |
| | | backtrack(nums, target, 0, k, 0.0, 0, currentIndices, currentMax, new ArrayList<>()); |
| | | if (currentMax[0] != -1.0) { |
| | | if (currentMax[0] > maxSum[0] || (currentMax[0] == maxSum[0] && currentIndices.size() < bestIndices.size())) { |
| | | maxSum[0] = currentMax[0]; |
| | | bestIndices = new ArrayList<>(currentIndices); |
| | | } |
| | | } |
| | | } |
| | | |
| | | return bestIndices.isEmpty() ? null : bestIndices; |
| | | } |
| | | |
| | | private static void backtrack(double[] nums, double target, int start, int k, double currentSum, int count, List<Integer> currentIndices, double[] currentMax, List<Integer> path) { |
| | | if (count == k) { |
| | | if (currentSum <= target && currentSum > currentMax[0]) { |
| | | currentMax[0] = currentSum; |
| | | currentIndices.clear(); |
| | | currentIndices.addAll(path); |
| | | } |
| | | return; |
| | | } |
| | | |
| | | // 遍历候选数字 |
| | | for (int i = start; i < candidates.length; i++) { |
| | | double num = candidates[i]; |
| | | |
| | | // 剪枝:如果当前和已经远大于目标值,跳过 |
| | | if (currentSum + num > target + minDifference + EPSILON) { |
| | | continue; |
| | | } |
| | | |
| | | // 跳过重复数字 |
| | | if (i > start && Math.abs(candidates[i] - candidates[i - 1]) < EPSILON) { |
| | | continue; |
| | | } |
| | | |
| | | current.add(num); |
| | | backtrack(candidates, i + 1, current, currentSum + num); |
| | | current.remove(current.size() - 1); |
| | | for (int i = start; i < nums.length; i++) { |
| | | if (currentSum + nums[i] > target) continue; |
| | | path.add(i); |
| | | backtrack(nums, target, i + 1, k, currentSum + nums[i], count + 1, currentIndices, currentMax, path); |
| | | path.remove(path.size() - 1); |
| | | } |
| | | } |
| | | |