From c46d1d8c3b9875f051a6ec3c4a1d3fa7bd32e5db Mon Sep 17 00:00:00 2001 From: skyouc Date: 星期四, 15 五月 2025 17:15:21 +0800 Subject: [PATCH] 新增库存管理 新增库存明细 --- rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java | 27 rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx | 121 ++++ rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx | 165 +++++ rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java | 14 rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml | 51 + rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx | 207 +++++++ rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java | 7 rsf-admin/src/page/statistics/stockManage/index.jsx | 18 rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java | 13 rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java | 52 + rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx | 181 ++++++ rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx | 211 +++++++ rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx | 383 +++++++++++++ rsf-admin/src/i18n/zh.js | 29 + rsf-admin/src/i18n/en.js | 27 rsf-admin/src/page/ResourceContent.js | 3 rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx | 183 ++++++ rsf-server/src/main/resources/application-dev.yml | 2 18 files changed, 1,679 insertions(+), 15 deletions(-) diff --git a/rsf-admin/src/i18n/en.js b/rsf-admin/src/i18n/en.js index 7811a63..1891dfd 100644 --- a/rsf-admin/src/i18n/en.js +++ b/rsf-admin/src/i18n/en.js @@ -11,6 +11,8 @@ }, button: { edit: "Edit", + detail: "Details", + histories: "Histories", }, field: { id: 'ID', @@ -30,6 +32,9 @@ partners: 'Strategic Partners', }, list: { + titles: { + stockInfo: 'Stock Details', + }, empty: { tip: 'No data to display', } @@ -185,6 +190,7 @@ outStockItem: 'Out Stock Item', inStockPoces: 'In Stock Pocess', outStockPoces: 'Out Stock Pocess', + warehouseStock: 'Instant Inventory', deviceBind: 'Device Bind', tasks: 'Tasks', wave: 'Wave Manage', @@ -924,6 +930,27 @@ model: "model", fieldsIndex: "fieldsIndex", }, + warehouseStock: { + locId: "Loc ID", + locCode: "Code", + warehouse: 'Warehouse', + orderId: "Order Id", + type: "Type", + orderItemId: "Order Detail ID", + wkType: "Work Type", + matnrId: "Matnr Id", + maktx: "Mats Name", + unit: 'Unit', + anfme: 'Stock Qty', + matnrCode: "Matnr Code", + workQty: 'Work Qty', + qty: 'Non Stock Qty', + splrId: 'supplier', + batch: "Supplier Batch", + spec: "Spec", + model: "Model", + fieldsIndex: "Fields Index", + }, deviceBind: { currentRow: "currentRow", startRow: "startRow", diff --git a/rsf-admin/src/i18n/zh.js b/rsf-admin/src/i18n/zh.js index ff79c91..7e9dc69 100644 --- a/rsf-admin/src/i18n/zh.js +++ b/rsf-admin/src/i18n/zh.js @@ -11,6 +11,8 @@ }, button: { edit: "缂栬緫", + detail: "搴撳瓨鏄庣粏", + histories: "娴佹按璁板綍", }, field: { id: 'ID', @@ -30,6 +32,10 @@ partners: '鎴樼暐鍚堜綔', }, list: { + titles: { + stockInfo: '搴撳瓨鏄庣粏', + }, + empty: { tip: '娌℃湁鍙樉绀烘暟鎹�', } @@ -186,6 +192,7 @@ outStockItem: '鍑哄簱鍗曟槑缁�', inStockPoces: '鍏ュ簱绠$悊', outStockPoces: '鍑哄簱绠$悊', + warehouseStock: '鍗虫椂搴撳瓨', deviceBind: '璁惧缁戝畾', tasks: '浠诲姟绠$悊', wave: '娉㈡绠$悊', @@ -954,6 +961,28 @@ model: "鍨嬪彿", fieldsIndex: "鍔ㄦ�佺储寮�", }, + warehouseStock: { + locId: "搴撲綅ID", + locCode: "搴撲綅", + warehouse: '浠撳簱', + orderId: "璁㈠崟ID", + type: "鍗曟嵁绫诲瀷", + orderItemId: "鍗曟嵁鏄庣粏ID", + wkType: "涓氬姟绫诲瀷", + matnrId: "鐗╂枡ID", + maktx: "鐗╂枡鍚嶇О", + unit: '鍗曚綅', + anfme: '搴撳瓨鏁伴噺', + matnrCode: "鐗╂枡缂栫爜", + workQty: '鎵ц鏁�', + qty: '涓嶅彲鐢ㄥ簱瀛�', + splrId: '渚涘簲鍟�', + batch: "渚涘簲鍟嗘壒娆�", + splrBatch: "渚涘簲鍟嗘壒娆�", + spec: "瑙勬牸", + model: "鍨嬪彿", + fieldsIndex: "鍔ㄦ�佺储寮�", + }, deviceBind: { currentRow: "褰撳墠鎺掑彿", startRow: "璧峰鎺掑彿", diff --git a/rsf-admin/src/page/ResourceContent.js b/rsf-admin/src/page/ResourceContent.js index 9b0737a..1a87dd5 100644 --- a/rsf-admin/src/page/ResourceContent.js +++ b/rsf-admin/src/page/ResourceContent.js @@ -47,6 +47,7 @@ import wave from './orders/wave'; import locItem from './locItem' import basStation from './basicInfo/basStation'; +import warehouseStock from './statistics/stockManage'; const ResourceContent = (node) => { switch (node.component) { @@ -78,6 +79,8 @@ return warehouseAreas; case 'warehouseAreasItem': return warehouseAreasItem; + case 'warehouseStock': + return warehouseStock; case 'loc': return loc; case 'container': diff --git a/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx b/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx new file mode 100644 index 0000000..dd93b57 --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/MatnrListAside.jsx @@ -0,0 +1,121 @@ +import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; +import request from '@/utils/request'; +import { + SavedQueriesList, + FilterLiveSearch, + useNotify, + useListContext, + SearchInput +} from 'react-admin'; +import BookmarkIcon from '@mui/icons-material/BookmarkBorder'; +import { Box, Typography, Card, CardContent, useTheme, Input } from '@mui/material'; +import { RichTreeView } from "@mui/x-tree-view/RichTreeView"; +import { TreeItem2 } from "@mui/x-tree-view/TreeItem2"; + + +const MatListAside = () => { + const theme = useTheme(); + const notify = useNotify(); + const { setFilters } = useListContext(); // 鑾峰彇鍒楄〃涓婁笅鏂� + const [selectedOption, setSelectedOption] = useState(null); + const [treeData, setTreeData] = useState([]); + const [defaultIds, setDefaultIds] = useState(['65']); + const [condition, setCondition] = useState(''); + + const haveChildren = (item) => { + if (Array.isArray(item)) { + return item.map((k) => haveChildren(k)); + } + + if (item && typeof item === 'object') { + if (item.id !== undefined) { + item.id = item.id.toString(); + } + + if (item.children && Array.isArray(item.children)) { + item.children = haveChildren(item.children); + } + } + + return item; + }; + useEffect(() => { + http() + }, [condition]); + + const http = () => { + request.post('/matnrGroup/tree', { condition }) + .then(res => { + if (res?.data?.code === 200) { + let data = res.data.data; + let items = haveChildren(data) + setTreeData(items) + setDefaultIds([items.at(0).id]) + + } else { + notify(res.data.msg); + } + }) + .catch(error => { + notify('Error fetching tree data'); + }); + + } + const handleNodeSelect = (event, nodeId) => { + setFilters({ groupId: nodeId }); + }; + const handleSearch = (e) => { + setCondition(e.target.value) + }; + + + const CustomCheckbox = React.forwardRef(function CustomCheckbox(props, ref) { + return <input type="checkbox" ref={ref} {...props} />; + }); + + const CustomTreeItem = React.forwardRef(function CustomTreeItem(props, ref) { + return ( + <TreeItem2 + {...props} + ref={ref} + slots={{ + checkbox: CustomCheckbox, + }} + /> + ); + }); + + + return ( + <Card + sx={{ + order: -1, + mr: 2, + mt: 8, + alignSelf: 'flex-start', + border: theme.palette.mode === 'light' && '1px solid #e0e0e3', + width: 250, + minWidth: 150, + height: `100%`, + }} + > + <CardContent> + <Input + placeholder="鎼滅储鐗╂枡鍒嗙粍" + sx={{ '--Input-focused': 1, marginBottom: '10px' }} + onChange={handleSearch} + /> + <RichTreeView + defaultExpandedItems={defaultIds} + expansionTrigger="iconContainer" + items={treeData} + slots={CustomTreeItem} + onItemClick={handleNodeSelect} // 鐩戝惉鑺傜偣鐐瑰嚮浜嬩欢 + /> + + </CardContent> + </Card> + ) +} + +export default MatListAside; diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx new file mode 100644 index 0000000..236bf9a --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseHistories.jsx @@ -0,0 +1,211 @@ +import { Dialog, DialogActions, DialogContent, DialogTitle, Box } from "@mui/material"; +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + List, + DatagridConfigurable, + SearchInput, + TopToolbar, + Button, + SelectColumnsButton, + EditButton, + FilterButton, + CreateButton, + ExportButton, + BulkDeleteButton, + WrapperField, + Toolbar, + useRecordContext, + useTranslate, + useNotify, + useListContext, + FunctionField, + TextField, + NumberField, + DateField, + BooleanField, + ReferenceField, + TextInput, + DateTimeInput, + DateInput, + SelectInput, + NumberInput, + ReferenceInput, + ReferenceArrayInput, + AutocompleteInput, + DeleteButton, + Form, + SaveButton, + useRefresh, + useGetList, +} from 'react-admin'; +import DialogCloseButton from "../../components/DialogCloseButton"; +import { styled } from '@mui/material/styles'; +import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; +import { Grid, Stack, width } from "@mui/system"; +import request from '@/utils/request'; +import SaveIcon from '@mui/icons-material/Save'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; + +const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({ + '& .css-1vooibu-MuiSvgIcon-root': { + height: '.9em' + }, + '& .RaDatagrid-row': { + cursor: 'auto' + }, + '& .status': { + width: 90 + }, +})); + +const WarehouseHistories = (props) => { + const { open, setOpen, record } = props; + const translate = useTranslate(); + const [params, setParams] = useState({}); + const [item, setItem] = useState({}); + const [poItemDialog, setPoItemDialog] = useState(false); + const [drawerVal, setDrawerVal] = useState(false); + const refresh = useRefresh(); + + const handleClose = (event, reason) => { + if (reason !== "backdropClick") { + setOpen(false); + } + }; + // const CustomFilter = () => { + // const { filterValues, setFilters, refetch } = useListContext('deliveryItem'); + // const [formValues, setFormValues] = useState(filterValues); + // const handleChange = (event) => { + // if (event.target == undefined || event.target == null) { return } + // setFormValues(formValues => ({ + // ...formValues, + // [event.target.name]: event.target.value + // })); + // }; + + // const handleSubmit = (event) => { + // setParams(formValues) + // }; + + // return ( + // <Box sx={{ width: '100%', margin: 1, marginBottom: 8, "& .MuiDialogActions-root": { padding: 0 } }}> + // <Form> + // <Grid container rowSpacing={2} columnSpacing={2} sx={{ padding: 2 }}> + // <Stack> + // <TextInput + // source="condition" + // label="common.action.search" + // resettable + // defaultValue={params?.condition} + // onChange={handleChange} /> + // </Stack> + // </Grid> + // <DialogActions> + // <Toolbar sx={{ width: '100%', justifyContent: 'end' }} > + // <SaveButton onClick={handleSubmit} label={"toolbar.query"} /> + // </Toolbar> + // </DialogActions> + // </Form> + // </Box> + // ); + // }; + return ( + <Box> + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="form-dialog-title" + aria-hidden + fullWidth + disableRestoreFocus + maxWidth="lg" + > + <DialogTitle id="form-dialog-title" sx={{ + position: 'sticky', + top: 0, + backgroundColor: 'background.paper', + zIndex: 1000 + }}> + <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> + <DialogCloseButton onClose={handleClose} /> + </Box> + </DialogTitle> + <Grid container rowSpacing={2} columnSpacing={2}> + <DialogContent> + <Grid item sx={24}> + <List + storeKey="selectPurchase" + resource="purchase/filters" + sx={{ + flexGrow: 1, + marginTop: 8, + height: 500, + transition: (theme) => + theme.transitions.create(['all'], { + duration: theme.transitions.duration.enteringScreen, + }), + marginRight: 0, + }} + // filters={<CustomFilter />} + queryOptions={{ meta: { ...params } }} + empty={false} + sort={{ field: "create_time", order: "desc" }} + actions={false} + perPage={DEFAULT_PAGE_SIZE} + > + <StyledDatagrid + preferenceKey='selectPurchase' + bulkActionButtons={ + <> + <ConfirmSelectButton + setOpen={setOpen} + setPoItemDialog={setPoItemDialog} + setItem={setItem} + mutationMode="pessimistic" /> + </> + } + rowClick={false} + expand={false} + expandSingle={true} + omit={['id', 'createTime', 'createBy', 'channel', 'platCode', 'memo', 'channel', 'startTime', 'workQty', 'endTime']} + > + <NumberField source="id" /> + <TextField source="code" label="table.field.purchase.code" /> + <TextField source="type$" label="table.field.purchase.type" /> + <TextField source="wkType$" label="table.field.purchase.wkType" /> + <TextField source="source" label="table.field.purchase.source" /> + <NumberField source="anfme" label="table.field.purchase.anfme" /> + <NumberField source="qty" label="table.field.purchase.qty" /> + <TextField source="channel" label="table.field.purchase.channel" /> + <TextField source="platCode" label="table.field.purchase.platCode" /> + <DateField source="preArr" label="table.field.purchase.preArr" showTime /> + <DateField source="startTime" label="table.field.purchase.startTime" showTime /> + <DateField source="endTime" label="table.field.purchase.endTime" showTime /> + <TextField source="project" label="table.field.purchase.project" /> + <TextField source="memo" label="common.field.memo" sortable={false} /> + </StyledDatagrid> + </List> + </Grid> + </DialogContent> + </Grid> + </Dialog > + </Box> + ) +} + +export default WarehouseHistories; + + +const ConfirmSelectButton = ({ setOpen, setPoItemDialog, setItem }) => { + const { selectedIds, onUnselectItems } = useListContext(); + const confirmSelect = async (event) => { + setItem(selectedIds[0]) + onUnselectItems(); + setPoItemDialog(true) + setOpen(false); + } + + return ( + <Button label={"toolbar.confirm"} variant="contained" color="primary" size="medium" startIcon={<SaveIcon />} onClick={confirmSelect} /> + ) +} diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx new file mode 100644 index 0000000..b0b241d --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockCreate.jsx @@ -0,0 +1,207 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + CreateBase, + useTranslate, + TextInput, + NumberInput, + BooleanInput, + DateInput, + SaveButton, + SelectInput, + ReferenceInput, + ReferenceArrayInput, + AutocompleteInput, + Toolbar, + required, + useDataProvider, + useNotify, + Form, + useCreateController, +} from 'react-admin'; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Stack, + Grid, + Box, +} from '@mui/material'; +import DialogCloseButton from "../../components/DialogCloseButton"; +import StatusSelectInput from "../../components/StatusSelectInput"; +import MemoInput from "../../components/MemoInput"; + +const WarehouseStockCreate = (props) => { + const { open, setOpen } = props; + + const translate = useTranslate(); + const notify = useNotify(); + + const handleClose = (event, reason) => { + if (reason !== "backdropClick") { + setOpen(false); + } + }; + + const handleSuccess = async (data) => { + setOpen(false); + notify('common.response.success'); + }; + + const handleError = async (error) => { + notify(error.message || 'common.response.fail', { type: 'error', messageArgs: { _: error.message } }); + }; + + return ( + <> + <CreateBase + record={{}} + transform={(data) => { + return data; + }} + mutationOptions={{ onSuccess: handleSuccess, onError: handleError }} + > + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="form-dialog-title" + fullWidth + disableRestoreFocus + maxWidth="md" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' + > + <Form> + <DialogTitle id="form-dialog-title" sx={{ + position: 'sticky', + top: 0, + backgroundColor: 'background.paper', + zIndex: 1000 + }} + > + {translate('create.title')} + <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> + <DialogCloseButton onClose={handleClose} /> + </Box> + </DialogTitle> + <DialogContent sx={{ mt: 2 }}> + <Grid container rowSpacing={2} columnSpacing={2}> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.locId" + source="locId" + autoFocus + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.locCode" + source="locCode" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.type" + source="type" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.orderItemId" + source="orderItemId" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.wkType" + source="wkType" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.matnrId" + source="matnrId" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.maktx" + source="maktx" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.matnrCode" + source="matnrCode" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.unit" + source="unit" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.anfme" + source="anfme" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.qty" + source="qty" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <NumberInput + label="table.field.locItem.workQty" + source="workQty" + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.batch" + source="batch" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.spec" + source="spec" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.model" + source="model" + parse={v => v} + /> + </Grid> + <Grid item xs={6} display="flex" gap={1}> + <TextInput + label="table.field.locItem.fieldsIndex" + source="fieldsIndex" + parse={v => v} + /> + </Grid> + </Grid> + </DialogContent> + <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> + <Toolbar sx={{ width: '100%', justifyContent: 'space-between' }} > + <SaveButton /> + </Toolbar> + </DialogActions> + </Form> + </Dialog> + </CreateBase> + </> + ) +} + +export default WarehouseStockCreate; diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx new file mode 100644 index 0000000..7ab90ce --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockEdit.jsx @@ -0,0 +1,181 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + Edit, + SimpleForm, + FormDataConsumer, + useTranslate, + TextInput, + NumberInput, + BooleanInput, + DateInput, + SelectInput, + ReferenceInput, + ReferenceArrayInput, + AutocompleteInput, + SaveButton, + Toolbar, + Labeled, + NumberField, + required, + useRecordContext, + DeleteButton, +} from 'react-admin'; +import { useWatch, useFormContext } from "react-hook-form"; +import { Stack, Grid, Box, Typography } from '@mui/material'; +import * as Common from '@/utils/common'; +import { EDIT_MODE, REFERENCE_INPUT_PAGESIZE } from '@/config/setting'; +import EditBaseAside from "../../components/EditBaseAside"; +import CustomerTopToolBar from "../../components/EditTopToolBar"; +import MemoInput from "../../components/MemoInput"; +import StatusSelectInput from "../../components/StatusSelectInput"; + +const FormToolbar = () => { + const { getValues } = useFormContext(); + + return ( + <Toolbar sx={{ justifyContent: 'end' }}> + <SaveButton /> + {/* <DeleteButton mutationMode="optimistic" /> */} + </Toolbar> + ) +} + +const WarehouseStockEdit = () => { + const translate = useTranslate(); + + return ( + <Edit + redirect="list" + mutationMode={EDIT_MODE} + actions={<CustomerTopToolBar />} + aside={<EditBaseAside />} + > + <SimpleForm + shouldUnregister + warnWhenUnsavedChanges + toolbar={<FormToolbar />} + mode="onTouched" + defaultValues={{}} + // validate={(values) => { }} + > + <Grid container width={{ xs: '100%', xl: '100%' }} rowSpacing={3} columnSpacing={3}> + <Grid item xs={14} md={10}> + <Typography variant="h6" gutterBottom> + {translate('common.edit.title.main')} + </Typography> + <Stack direction='row' gap={2}> + <NumberInput + label="table.field.locItem.locId" + source="locId" + autoFocus + /> + <TextInput + label="table.field.locItem.locCode" + source="locCode" + parse={v => v} + /> + <NumberInput + label="table.field.locItem.orderId" + source="orderId" + /> + <TextInput + label="table.field.locItem.type" + source="type$" + parse={v => v} + /> + </Stack> + <Stack direction='row' gap={2}> + <NumberInput + label="table.field.locItem.orderItemId" + source="orderItemId" + /> + <NumberInput + label="table.field.locItem.wkType" + source="wkType" + /> + <NumberInput + label="table.field.locItem.matnrId" + source="matnrId" + /> + </Stack> + <Stack direction='row' gap={2}> + <TextInput + label="table.field.locItem.maktx" + source="maktx" + parse={v => v} + /> + + <TextInput + label="table.field.locItem.matnrCode" + source="matnrCode" + parse={v => v} + /> + <TextInput + label="table.field.locItem.trackCode" + source="trackCode" + parse={v => v} + /> + <TextInput + label="table.field.locItem.unit" + source="unit" + parse={v => v} + /> + </Stack> + <Stack direction='row' gap={2}> + <NumberInput + label="table.field.locItem.anfme" + source="anfme" + /> + <NumberInput + label="table.field.locItem.qty" + source="qty" + /> + <NumberInput + label="table.field.locItem.workQty" + source="workQty" + /> + <TextInput + label="table.field.locItem.batch" + source="batch" + parse={v => v} + /> + </Stack> + <Stack direction='row' gap={2}> + <TextInput + label="table.field.locItem.splrBatch" + source="splrBatch" + parse={v => v} + /> + <TextInput + label="table.field.locItem.spec" + source="spec" + parse={v => v} + /> + <TextInput + label="table.field.locItem.model" + source="model" + parse={v => v} + /> + <TextInput + label="table.field.locItem.fieldsIndex" + source="fieldsIndex" + parse={v => v} + /> + </Stack> + + </Grid> + <Grid item xs={10} md={2}> + <Typography variant="h6" gutterBottom> + {translate('common.edit.title.common')} + </Typography> + <StatusSelectInput /> + <Box mt="2em" /> + <MemoInput /> + </Grid> + </Grid> + </SimpleForm> + </Edit > + ) +} + +export default WarehouseStockEdit; diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx new file mode 100644 index 0000000..deaf6ff --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockInfo.jsx @@ -0,0 +1,383 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + CreateBase, + useTranslate, + TextInput, + NumberInput, + BooleanInput, + DateInput, + SaveButton, + SelectInput, + ReferenceInput, + ReferenceArrayInput, + AutocompleteInput, + Toolbar, + required, + useDataProvider, + useNotify, + Form, + useCreateController, + useListContext, + useRefresh, +} from 'react-admin'; +import { + Dialog, + DialogActions, + DialogContent, + DialogTitle, + Stack, + Grid, + TextField, + Box, + Button, + Paper, + TableContainer, + Table, + TableHead, + TableBody, + TableRow, + TableCell, + Tooltip, + IconButton, + styled, + Select, + MenuItem + +} from '@mui/material'; +import DialogCloseButton from "../../components/DialogCloseButton"; +import StatusSelectInput from "../../components/StatusSelectInput"; +import { useForm, Controller, useWatch, FormProvider, useFormContext } from "react-hook-form"; +import SaveIcon from '@mui/icons-material/Save'; +import request from '@/utils/request'; +import { Add, Edit, Delete } from '@mui/icons-material'; +import _, { set } from 'lodash'; +import { DataGrid, useGridApiRef, GRID_DATE_COL_DEF, GRID_DATETIME_COL_DEF, getGridDateOperators, useGridApiContext } from '@mui/x-data-grid'; +import { LocalizationProvider, DatePicker, DateTimePicker } from '@mui/x-date-pickers'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import DictionarySelect from "../../components/DictionarySelect"; +import DictSelect from "../../components/DictSelect"; +import { 'zhCN' as locale } from 'date-fns/locale'; +import { format, } from 'date-fns'; +import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; + +const WarehouseStockInfo = (props) => { + const { open, setOpen, billReload, record } = props; + const translate = useTranslate(); + const notify = useNotify(); + const refresh = useRefresh(); + const [disabled, setDisabled] = useState(false) + const [createDialog, setCreateDialog] = useState(false); + const tableRef = useRef(); + + useEffect(() => { + if (open && record !== 0) { + requestGetBody() + } + setDisabled(false) + }, [open]) + + const handleClose = (event, reason) => { + if (reason !== "backdropClick") { + setOpen(false); + refresh(); + setTableData([]) + } + }; + + + const [tabelData, setTableData] = useState([]); + + const resetData = () => { + setTableData([]) + } + + const setFinally = () => { + const rows = tableRef.current.state.editRows; + for (const key in rows) { + const find = tabelData.find(item => item.id === +key); + find.anfme = rows[key].anfme.value; + } + setTableData([...tabelData]); + } + + const handleSubmit = async () => { + setFinally() + setDisabled(true) + const parmas = { + "purchaseId": record, + "items": tabelData, + } + const res = await request.post(`/asnOrder/purchases/save`, parmas); + if (res?.data?.code === 200) { + notify(res.data.msg); + } else { + notify(res.data.msg); + } + setOpen(false); + refresh(); + resetData() + setDisabled(false) + }; + + const requestGetBody = async () => { + const res = await request.post(`warehouse/stock/page`, { matnrCode: record }); + if (res?.data?.code === 200) { + setTableData(res.data.data.records) + } else { + notify(res.data.msg); + } + } + + const [selectedRows, setSelectedRows] = useState([]); + + return ( + <> + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="form-dialog-title" + aria-hidden + fullWidth + disableRestoreFocus + maxWidth="xl" // 'xs' | 'sm' | 'md' | 'lg' | 'xl' + > + <DialogTitle id="form-dialog-title" sx={{ + position: 'sticky', + top: 0, + backgroundColor: 'background.paper', + zIndex: 1000 + }}> + {translate('common.list.titles.stockInfo')} + <Box sx={{ position: 'absolute', top: 8, right: 8, zIndex: 1001 }}> + <DialogCloseButton onClose={handleClose} /> + </Box> + </DialogTitle> + <DialogContent sx={{ mt: 2 }}> + <Box></Box> + <Box sx={{ mt: 2 }}> + <AsnOrderModalTable tabelData={tabelData} + setTableData={setTableData} + record={record} + selectedRows={selectedRows} + setSelectedRows={setSelectedRows} + tableRef={tableRef} /> + </Box> + </DialogContent> + <DialogActions sx={{ position: 'sticky', bottom: 0, backgroundColor: 'background.paper', zIndex: 1000 }}> + <Toolbar sx={{ width: '100%', justifyContent: 'end' }} > + <Button disabled={disabled} onClick={handleSubmit} variant="contained" startIcon={<SaveIcon />}> + {translate('toolbar.confirm')} + </Button> + </Toolbar> + </DialogActions> + </Dialog> + </> + ) +} + +export default WarehouseStockInfo; + +const SelectInputSplrNameEditCell = (params) => { + const [formData, setFormData] = useState([{}]) + useEffect(() => { + getOptions(); + }, []); + const getOptions = async () => { + const parmas = { + "type": "supplier" + } + const { + data: { code, data, msg }, + } = await request.post("companys/page", parmas); + if (code === 200) { + setFormData(data.records) + } else { + notify(msg); + } + } + + return ( + <Select + value={params.value} + onChange={(e) => { + params.api.setEditCellValue({ + id: params.id, + field: params.field, + value: e.target.value, + }) + // 鎵惧埌閫変腑鐨勪緵搴斿晢璁板綍 + const selectedSupplier = formData.find(supplier => supplier.name === e.target.value); + + // 濡傛灉鎵惧埌瀵瑰簲鐨勪緵搴斿晢璁板綍锛屽悓鏃舵洿鏂皊plrCode瀛楁 + if (selectedSupplier) { + params.api.setEditCellValue({ + id: params.id, + field: 'splrCode', + value: selectedSupplier.id, + }); + } + }} + fullWidth + > + {formData.map(e => { + return ( + <MenuItem value={e.name} children={e.name} key={e.id} /> + ); + + })} + + </Select> + ); +}; + +const AsnOrderModalTable = ({ tabelData, setTableData, record, selectedRows, setSelectedRows, tableRef }) => { + const translate = useTranslate(); + const notify = useNotify(); + + const [columns, setColumns] = useState([ + { + field: 'matnrCode', + headerName: translate('table.field.asnOrderItem.matnrCode'), + width: 130, + editable: false, + }, + { + field: 'matnrName', + headerName: translate('table.field.asnOrderItem.maktx'), + width: 250, + editable: false, + }, + { + field: 'splrName', + headerName: translate('table.field.asnOrderItem.splrName') + "*", + minWidth: 150, + flex: 1, + editable: false, + renderEditCell: (params) => ( + <SelectInputSplrNameEditCell {...params} /> + ), + }, + { + field: 'platItemId', + headerName: translate('table.field.asnOrderItem.platItemId') + "*", + minWidth: 100, + flex: 1, + editable: false, + }, + { + field: 'anfme', + headerName: translate('table.field.asnOrderItem.anfme') + "*", + type: 'number', + minWidth: 100, + flex: 1, + editable: false, + valueFormatter: (val) => val < 0 ? 0 : val, + }, + { + field: 'qty', + headerName: translate('table.field.asnOrderItem.qty') + "*", + type: 'number', + minWidth: 100, + flex: 1, + valueFormatter: (val) => val < 0 ? 0 : val, + }, + { + field: 'unit', + headerName: translate('table.field.asnOrderItem.stockUnit'), + minWidth: 100, + flex: 1, + editable: false, + }, + ]) + + let cdata = useRef([]); + + useEffect(() => { + getDynamicFields(); + }, []); + + useEffect(() => { + cdata.current = tabelData + }, [tabelData]); + + + const getDynamicFields = async () => { + const { + data: { code, data, msg }, + } = await request.get("/fields/enable/list"); + if (code === 200) { + const cols = data.map(el => ({ + field: el.fields, + valueGetter: (value, row) => { + if (value != null && value != undefined) { + return value; + } + if (row.extendFields == null || row.extendFields[el.fields] == null) { + return '' + } else { + return `${row.extendFields[el.fields] == null ? '' : row.extendFields[el.fields]}`; + } + }, + headerName: el.fieldsAlise, + minWidth: 100, + flex: 1, + editable: true + })) + setColumns([...columns, ...cols]) + } else { + notify(msg); + } + } + + const processRowUpdate = (newRow, oldRow) => { + const rows = tabelData.map((r) => + r.id === newRow.id ? { ...newRow } : r + ) + setTableData(rows) + + return newRow; + }; + + + const handleSelectionChange = (ids) => { + setSelectedRows(ids) + }; + + tableRef.current = useGridApiRef(); + + + return ( + <div style={{ height: 400, width: '100%' }}> + <DataGrid + apiRef={tableRef} + rows={tabelData} + columns={columns} + disableRowSelectionOnClick + getRowId={(row) => row.id} + disableColumnFilter + disableColumnSelector + disableColumnSorting + disableMultipleColumnsSorting + processRowUpdate={processRowUpdate} + initialState={{ + pagination: { + paginationModel: { + pageSize: 25, + }, + }, + }} + pageSizeOptions={[10, 25, 50, 100]} + editMode="row" + checkboxSelection + onRowSelectionModelChange={handleSelectionChange} + selectionModel={selectedRows} + sx={{ + '& .MuiDataGrid-cell input': { + border: '1px solid #ccc' + }, + }} + /> + </div> + ); +}; + diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx new file mode 100644 index 0000000..376e6ab --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockList.jsx @@ -0,0 +1,183 @@ +import React, { useState, useRef, useEffect, useMemo, useCallback } from "react"; +import { useNavigate } from 'react-router-dom'; +import { + List, + DatagridConfigurable, + SearchInput, + TopToolbar, + SelectColumnsButton, + EditButton, + FilterButton, + CreateButton, + ExportButton, + BulkDeleteButton, + WrapperField, + useRecordContext, + useTranslate, + useNotify, + useListContext, + FunctionField, + TextField, + NumberField, + DateField, + BooleanField, + ReferenceField, + TextInput, + DateTimeInput, + DateInput, + SelectInput, + NumberInput, + ReferenceInput, + ReferenceArrayInput, + useRefresh, + AutocompleteInput, + DeleteButton, + Button, +} from 'react-admin'; +import { Box, Typography, Card, Stack, LinearProgress } from '@mui/material'; +import { styled } from '@mui/material/styles'; +import EmptyData from "../../components/EmptyData"; +import MyCreateButton from "../../components/MyCreateButton"; +import MyExportButton from '../../components/MyExportButton'; +import PageDrawer from "../../components/PageDrawer"; +import MyField from "../../components/MyField"; +import { PAGE_DRAWER_WIDTH, OPERATE_MODE, DEFAULT_PAGE_SIZE } from '@/config/setting'; +import * as Common from '@/utils/common'; +import request from '@/utils/request'; +import WarehouseStockCreate from "./WarehouseStockCreate"; +import WarehouseStockInfo from "./WarehouseStockInfo"; +import MatnrListAside from "./MatnrListAside"; + +const StyledDatagrid = styled(DatagridConfigurable)(({ theme }) => ({ + '& .css-1vooibu-MuiSvgIcon-root': { + height: '.9em' + }, + '& .RaDatagrid-row': { + cursor: 'auto' + }, + '& .column-name': { + }, + '& .opt': { + width: 220 + }, +})); + +const filters = [ + <SearchInput source="condition" alwaysOn />, + <NumberInput source="locId" label="table.field.locItem.locId" />, + <TextInput source="locCode" label="table.field.locItem.locCode" />, + <TextInput source="type" label="table.field.locItem.type" />, + <NumberInput source="orderItemId" label="table.field.locItem.orderItemId" />, + <NumberInput source="wkType" label="table.field.locItem.wkType" />, + <NumberInput source="matnrId" label="table.field.locItem.matnrId" />, + <TextInput source="maktx" label="table.field.locItem.maktx" />, + <TextInput source="unit" label="table.field.locItem.unit" />, + <NumberInput source="anfme" label="table.field.locItem.anfme" />, + <NumberInput source="workQty" label="table.field.locItem.workQty" />, + <TextInput source="batch" label="table.field.locItem.batch" />, + <TextInput source="spec" label="table.field.locItem.spec" />, + <TextInput source="model" label="table.field.locItem.model" />, + <TextInput source="fieldsIndex" label="table.field.locItem.fieldsIndex" />, + <TextInput label="common.field.memo" source="memo" />, +] + +const WarehouseStockList = () => { + const translate = useTranslate(); + const [createDialog, setCreateDialog] = useState(false); + const [matnrCode, setMatnrCode] = useState(''); + const [drawerVal, setDrawerVal] = useState(false); + + return ( + <Box display="flex"> + <List + sx={{ + flexGrow: 1, + transition: (theme) => + theme.transitions.create(['all'], { + duration: theme.transitions.duration.enteringScreen, + }), + marginRight: 0, + }} + resource="warehouse/stock" + title={"common.button.detail"} + empty={false} + filters={filters} + sort={{ field: "create_time", order: "desc" }} + actions={( + <TopToolbar> + <FilterButton /> + <SelectColumnsButton preferenceKey='locItem' /> + </TopToolbar> + )} + perPage={DEFAULT_PAGE_SIZE} + > + <StyledDatagrid + preferenceKey='locItem' + bulkActionButtons={false} + rowClick={false} + expand={false} + expandSingle={true} + omit={['id', 'createTime', 'locId', 'spec', 'model', 'locCode', 'orderId', 'orderItemId', 'matnrId', 'splrBatch', 'createBy', 'memo', 'fieldsIndex']} + > + <NumberField source="id" /> + <NumberField source="locId" label="table.field.warehouseStock.locId" /> + <TextField source="locCode" label="table.field.warehouseStock.locCode" /> + <NumberField source="orderId" label="table.field.warehouseStock.orderId" /> + <NumberField source="orderItemId" label="table.field.warehouseStock.orderItemId" /> + <NumberField source="matnrId" label="table.field.warehouseStock.matnrId" /> + <TextField source="matnrCode" label="table.field.warehouseStock.matnrCode" /> + <TextField source="maktx" label="table.field.warehouseStock.maktx" /> + <NumberField source="anfme" label="table.field.warehouseStock.anfme" /> + <NumberField source="workQty" label="table.field.warehouseStock.qty" /> + <TextField source="spec" label="table.field.warehouseStock.spec" /> + <TextField source="model" label="table.field.warehouseStock.model" /> + {/* <ReferenceField source="updateBy" label="common.field.updateBy" reference="user" link={false} sortable={false}> + <TextField source="nickname" /> + </ReferenceField> */} + {/* <ReferenceField source="splrId" label="table.field.warehouseStock.splrId" reference="companys"> + <TextField source="name" filterToQuery={(val) => ({ name: val })} /> + </ReferenceField>, */} + <TextField source="batch" label="table.field.warehouseStock.batch" /> + <TextField source="unit" label="table.field.warehouseStock.unit" /> + <TextField source="fieldsIndex" label="table.field.warehouseStock.fieldsIndex" /> + <TextField source="updateBy$" label="common.field.updateBy" /> + <DateField source="updateTime" label="common.field.updateTime" showTime /> + <WrapperField cellClassName="opt" label="common.field.opt"> + <StockInfoButton /> + <HistoriesButton setCreateDialog={setCreateDialog} setMatnrCode={setMatnrCode} /> + </WrapperField> + </StyledDatagrid> + </List> + <WarehouseStockInfo + open={createDialog} + setOpen={setCreateDialog} + record={matnrCode} + /> + </Box> + ) +} + +export default WarehouseStockList; + +const HistoriesButton = ({ setCreateDialog , setMatnrCode}) => { + const record = useRecordContext(); + const historyClick = (event) => { + event.stopPropagation(); + setCreateDialog(true) + setMatnrCode(record?.matnrCode) + } + return ( + <Button label="common.button.histories" onClick={historyClick}></Button> + ) +} + +const StockInfoButton = () => { + const stockClick = (event) => { + event.stopPropagation(); + } + + return ( + <Button label="common.button.detail" onClick={stockClick}></Button> + ) +} + diff --git a/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx b/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx new file mode 100644 index 0000000..9aea26d --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/WarehouseStockPanel.jsx @@ -0,0 +1,165 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { Box, Card, CardContent, Grid, Typography, Tooltip } from '@mui/material'; +import { + useTranslate, + useRecordContext, +} from 'react-admin'; +import PanelTypography from "../../components/PanelTypography"; +import * as Common from '@/utils/common' + +const WarehouseStockPanel = () => { + const record = useRecordContext(); + if (!record) return null; + const translate = useTranslate(); + return ( + <> + <Card sx={{ width: { xs: 300, sm: 500, md: 600, lg: 800 }, margin: 'auto' }}> + <CardContent> + <Grid container spacing={2}> + <Grid item xs={12} sx={{ display: 'flex', justifyContent: 'space-between' }}> + <Typography variant="h6" gutterBottom align="left" sx={{ + maxWidth: { xs: '100px', sm: '180px', md: '260px', lg: '360px' }, + whiteSpace: 'nowrap', + overflow: 'hidden', + textOverflow: 'ellipsis', + }}> + {Common.camelToPascalWithSpaces(translate('table.field.locItem.id'))}: {record.id} + </Typography> + {/* inherit, primary, secondary, textPrimary, textSecondary, error */} + <Typography variant="h6" gutterBottom align="right" > + ID: {record.id} + </Typography> + </Grid> + </Grid> + <Grid container spacing={2}> + <Grid item xs={12} container alignContent="flex-end"> + <Typography variant="caption" color="textSecondary" sx={{ wordWrap: 'break-word', wordBreak: 'break-all' }}> + {Common.camelToPascalWithSpaces(translate('common.field.memo'))}:{record.memo} + </Typography> + </Grid> + </Grid> + <Box height={20}> </Box> + <Grid container spacing={2}> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.locId" + property={record.locId} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.locCode" + property={record.locCode} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.orderId" + property={record.orderId} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.type" + property={record.type} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.orderItemId" + property={record.orderItemId} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.wkType" + property={record.wkType} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.matnrId" + property={record.matnrId} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.maktx" + property={record.maktx} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.matnrCode" + property={record.matnrCode} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.trackCode" + property={record.trackCode} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.unit" + property={record.unit} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.anfme" + property={record.anfme} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.qty" + property={record.qty} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.workQty" + property={record.workQty} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.batch" + property={record.batch} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.splrBatch" + property={record.splrBatch} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.spec" + property={record.spec} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.model" + property={record.model} + /> + </Grid> + <Grid item xs={6}> + <PanelTypography + title="table.field.locItem.fieldsIndex" + property={record.fieldsIndex} + /> + </Grid> + + </Grid> + </CardContent> + </Card > + </> + ); +}; + +export default WarehouseStockPanel; diff --git a/rsf-admin/src/page/statistics/stockManage/index.jsx b/rsf-admin/src/page/statistics/stockManage/index.jsx new file mode 100644 index 0000000..31547c4 --- /dev/null +++ b/rsf-admin/src/page/statistics/stockManage/index.jsx @@ -0,0 +1,18 @@ +import React, { useState, useRef, useEffect, useMemo } from "react"; +import { + ListGuesser, + EditGuesser, + ShowGuesser, +} from "react-admin"; + +import WarehouseStockList from "./WarehouseStockList"; +import WarehouseStockEdit from "./WarehouseStockEdit"; + +export default { + list: WarehouseStockList, + edit: WarehouseStockEdit, + show: ShowGuesser, + recordRepresentation: (record) => { + return `${record.id}` + } +}; diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java new file mode 100644 index 0000000..72b4b86 --- /dev/null +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/controller/WarehouseStockController.java @@ -0,0 +1,52 @@ +package com.vincent.rsf.server.manager.controller; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.vincent.rsf.framework.common.R; +import com.vincent.rsf.server.common.domain.BaseParam; +import com.vincent.rsf.server.common.domain.PageParam; +import com.vincent.rsf.server.common.utils.FieldsUtils; +import com.vincent.rsf.server.manager.entity.LocItem; +import com.vincent.rsf.server.manager.entity.WaitPakinItem; +import com.vincent.rsf.server.manager.service.WarehouseStockService; +import com.vincent.rsf.server.system.controller.BaseController; +import io.swagger.annotations.Api; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.Map; +import java.util.Objects; + +@Api("搴撳瓨绠$悊") +@RestController +@RequestMapping("/warehouse/stock") +public class WarehouseStockController extends BaseController { + + @Autowired + private WarehouseStockService warehouseStockService; + + + @PreAuthorize("hasAuthority('manager:locItem:list')") + @PostMapping("/page") + public R page(@RequestBody Map<String, Object> param) { + BaseParam baseParam = buildParam(param, BaseParam.class); + PageParam<LocItem, BaseParam> pageParam = new PageParam<>(baseParam, LocItem.class); + QueryWrapper<LocItem> queryWrapper = pageParam.buildWrapper(true); + IPage<LocItem> pageResult = warehouseStockService.pageByStock(pageParam, queryWrapper); + List<LocItem> records = pageResult.getRecords(); + for (LocItem record : records) { + if (!Objects.isNull(record.getFieldsIndex())) { + Map<String, String> fields = FieldsUtils.getFields(record.getFieldsIndex()); + record.setExtendFields(fields); + } + } + pageResult.setRecords(records); + return R.ok(pageResult); + } + +} diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java index 28d567b..608d388 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/entity/Stock.java @@ -226,18 +226,4 @@ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.updateTime); } - - - public Boolean getStatusBool(){ - if (null == this.status){ return null; } - switch (this.status){ - case 1: - return true; - case 0: - return false; - default: - return null; - } - } - } diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java index b6a4e29..669805d 100644 --- a/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/mapper/LocItemMapper.java @@ -1,12 +1,19 @@ package com.vincent.rsf.server.manager.mapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.vincent.rsf.server.common.domain.BaseParam; +import com.vincent.rsf.server.common.domain.PageParam; import com.vincent.rsf.server.manager.entity.LocItem; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; @Mapper @Repository public interface LocItemMapper extends BaseMapper<LocItem> { + IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, @Param(Constants.WRAPPER) QueryWrapper<LocItem> queryWrapper); } diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java new file mode 100644 index 0000000..164a367 --- /dev/null +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/WarehouseStockService.java @@ -0,0 +1,13 @@ +package com.vincent.rsf.server.manager.service; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.vincent.rsf.server.common.domain.BaseParam; +import com.vincent.rsf.server.common.domain.PageParam; +import com.vincent.rsf.server.manager.entity.LocItem; + +public interface WarehouseStockService extends IService<LocItem> { + + IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, QueryWrapper<LocItem> queryWrapper); +} diff --git a/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java new file mode 100644 index 0000000..951809a --- /dev/null +++ b/rsf-server/src/main/java/com/vincent/rsf/server/manager/service/impl/WarehouseStockServiceImpl.java @@ -0,0 +1,27 @@ +package com.vincent.rsf.server.manager.service.impl; + +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import com.vincent.rsf.server.common.domain.BaseParam; +import com.vincent.rsf.server.common.domain.PageParam; +import com.vincent.rsf.server.manager.entity.LocItem; +import com.vincent.rsf.server.manager.mapper.LocItemMapper; +import com.vincent.rsf.server.manager.service.WarehouseStockService; +import org.springframework.stereotype.Service; + +@Service("warehouseStockService") +public class WarehouseStockServiceImpl extends ServiceImpl<LocItemMapper, LocItem> implements WarehouseStockService { + + /** + * @author Ryan + * @date 2025/5/15 + * @description: 鑾峰彇铏氭嫙涓哄瓨涓庣墿鏂欏簱瀛樺苟闆� + * @version 1.0 + */ + @Override + public IPage<LocItem> pageByStock(PageParam<LocItem, BaseParam> pageParam, QueryWrapper<LocItem> queryWrapper) { + IPage<LocItem> results = this.baseMapper.pageByStock(pageParam, queryWrapper); + return results; + } +} diff --git a/rsf-server/src/main/resources/application-dev.yml b/rsf-server/src/main/resources/application-dev.yml index 203032b..19cef04 100644 --- a/rsf-server/src/main/resources/application-dev.yml +++ b/rsf-server/src/main/resources/application-dev.yml @@ -14,7 +14,7 @@ driver-class-name: com.mysql.jdbc.Driver # url: jdbc:mysql://47.76.147.249:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai username: root - url: jdbc:mysql://127.0.0.1:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai + url: jdbc:mysql://192.168.4.50:3306/rsf?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai # username: rsf password: 34821015 type: com.alibaba.druid.pool.DruidDataSource diff --git a/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml b/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml index 90ad348..852c4b5 100644 --- a/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml +++ b/rsf-server/src/main/resources/mapper/manager/LocItemMapper.xml @@ -2,4 +2,55 @@ <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.vincent.rsf.server.manager.mapper.LocItemMapper"> + <select id="pageByStock" resultType="com.vincent.rsf.server.manager.entity.LocItem"> + SELECT * + FROM (SELECT id, + loc_id, + loc_code, + type, + order_item_id, + wk_type, + matnr_id, + maktx, + matnr_code, + unit, + SUM(anfme) anfme, + SUM(qty) qty, + SUM(work_qty) work_qty, + batch, + spec, + model, + fields_index, + update_by, + create_by, + update_time, + create_time + FROM man_loc_item + GROUP BY matnr_id + UNION ALL + SELECT id, + '' AS loc_id, + '' AS loc_code, + type, + asn_item_id AS order_item_id, + wk_type, + matnr_id, + maktx, + matnr_code, + unit, + SUM(anfme) anfme, + SUM(qty) qty, + SUM(work_qty) work_qty, + batch, + spec, + model, + fields_index, + update_by, + create_by, + update_time, + create_time + FROM man_warehouse_areas_item + GROUP BY matnr_id) t + ${ew.customSqlSegment} + </select> </mapper> -- Gitblit v1.9.1