push rasad front on new repo

This commit is contained in:
2026-01-18 14:32:49 +03:30
commit 4fe6e70525
2139 changed files with 303150 additions and 0 deletions

View File

@@ -0,0 +1,86 @@
import { Button, IconButton, Popover } from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import TuneIcon from "@mui/icons-material/Tune";
import { stewardDeleteFreeBarService } from "../../../guild/services/steward-submit-free-bar-service";
import { Grid } from "../../../../components/grid/Grid";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { StewardSubmitFreeBar } from "../steward-submit-free-bar/StewardSubmitFreeBar";
export const StewardSellOutOperations = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const handleDelete = () => {
handleClose();
dispatch(stewardDeleteFreeBarService(item.key)).then(() => {
updateTable();
});
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div style={{ padding: "20px" }}>
<Grid container direction="column" gap={1}>
<Button
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش خرید خارج استان",
content: (
<StewardSubmitFreeBar
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
ویرایش
</Button>
<Button color="error" onClick={handleDelete}>
حذف
</Button>
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,99 @@
import React, { useContext, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Button, TextField } from "@mui/material";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { guildEditOutOfProvinceService } from "../../../slaughter-house/services/slaughterEditOutOfProvinceService";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Done } from "@mui/icons-material";
export const StewardOutProvinceRegistrationCodeInput = ({
item,
fetchApiData,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [registrationCode, setRegistrationCode] = useState(
item?.loggedRegistrationCode || ""
);
const handleSubmit = () => {
dispatch(
guildEditOutOfProvinceService({
key: item?.key,
register_code: parseInt(registrationCode),
role: getRoleFromUrl(),
// Include all required fields from item
date: item?.date,
buyer_name: item?.buyerName,
buyer_mobile: item?.buyerMobile,
province: item?.province,
city: item?.city,
clearance_code: item?.clearanceCode,
number_of_carcasses: item?.numberOfCarcasses,
quarantine_weight_of_carcasses: item?.quarantineWeightOfCarcasses,
weight_of_carcasses: item?.weightOfCarcasses,
...(item?.buyer?.key && { buyer_key: item?.buyer?.key }),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "کد احراز با موفقیت ثبت شد.",
severity: "success",
});
fetchApiData();
}
});
};
return (
<Grid
container
direction="row"
alignItems="center"
justifyContent="flex-start"
gap={1}
sx={{ position: "relative" }}
>
<TextField
value={registrationCode}
size="small"
onChange={(e) => setRegistrationCode(e.target.value)}
style={{ minWidth: "150px" }}
disabled={item?.loggedRegistrationCode}
placeholder="کد احراز"
inputProps={{
inputMode: "numeric",
pattern: "[0-9]*",
}}
type="number"
/>
{!item?.loggedRegistrationCode && registrationCode && (
<Button
size="small"
variant="contained"
color="primary"
onClick={handleSubmit}
sx={{
position: "absolute",
right: "0",
minWidth: "40px",
width: "40px",
height: "38px",
}}
>
<Done />
</Button>
)}
</Grid>
);
};

View File

@@ -0,0 +1,177 @@
import React, { useContext, useState } from "react";
import { Button, IconButton, Popover, Tooltip } from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { stewardDeleteOutSellService } from "../../../guild/services/steward-sell-out-delete-service";
import { StewardSellOutOfProvinceEditSell } from "../steward-purchase-out-province-edit-sell/StewardSellOutOfProvinceEditSell";
import { fetchStewardBroadcastAndProducts } from "../../services/handle-fetch-steward-products";
import { stewardResendOutProvinceRegistrationCodeService } from "../../services/steward-resend-out-province-registration-code";
import TuneIcon from "@mui/icons-material/Tune";
import EditIcon from "@mui/icons-material/Edit";
import DeleteIcon from "@mui/icons-material/Delete";
import SendIcon from "@mui/icons-material/Send";
export const StewardOutProvinceSalesOperations = ({
item,
updateTable,
fetchApiData,
page,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const openPopover = (event) => {
setPopoverOpen(true);
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setPopoverOpen(false);
setAnchorEl(null);
};
const handleEdit = () => {
closePopover();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش فروش خارج از استان",
content: (
<StewardSellOutOfProvinceEditSell
fetchItems={updateTable}
isEdit={true}
item={item}
/>
),
})
);
};
const handleDelete = () => {
closePopover();
dispatch(stewardDeleteOutSellService(item?.key)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "error",
});
} else {
updateTable();
dispatch(fetchStewardBroadcastAndProducts());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "success",
});
}
});
};
const handleResend = () => {
closePopover();
dispatch(
stewardResendOutProvinceRegistrationCodeService({
key: item?.key,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "کد با موفقیت ارسال شد.",
severity: "success",
});
fetchApiData(page);
}
});
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={openPopover}
>
<TuneIcon />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={closePopover}
>
<div style={{ padding: "20px" }}>
<Grid container direction="column">
<Tooltip title="ویرایش" placement="left-start">
<Button
aria-label="edit"
color="primary"
variant="text"
onClick={handleEdit}
startIcon={<EditIcon />}
>
ویرایش
</Button>
</Tooltip>
<Tooltip title="حذف" placement="left-start">
<Button
aria-label="delete"
color="error"
variant="text"
onClick={handleDelete}
startIcon={<DeleteIcon />}
>
حذف
</Button>
</Tooltip>
{item?.systemRegistrationCode &&
item?.registrationCode &&
!item?.loggedRegistrationCode && (
<Tooltip title="ارسال مجدد کد" placement="left-start">
<Button
aria-label="resend"
color="success"
variant="text"
onClick={handleResend}
startIcon={<SendIcon />}
>
ارسال مجدد کد
</Button>
</Tooltip>
)}
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,420 @@
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
Autocomplete,
Button,
IconButton,
TextField,
Typography,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import { Yup } from "../../../../lib/yup/yup";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../../slaughter-house/services/slaughter-get-provinces";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import {
stewardSellOutGetBuyers,
stewatdSubmitBuyerDataService,
} from "../../../guild/services/steward-sell-out-get-buyers";
import { DRAWER, LOADING_END } from "../../../../lib/redux/slices/appSlice";
import { slaughterEditBuyerDataService } from "../../../slaughter-house/services/slaughter-house-submit-buyer";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardSellOutOfProvinceAddBuyer = ({
updateTable,
isEdit,
data,
}) => {
const [openNotif] = useContext(AppContext);
const [userData, setUserData] = useState(null);
const [notFound, setNotFound] = useState(false);
const dispatch = useDispatch();
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const formik = useFormik({
initialValues: {
mobile: "",
firstName: "",
lastName: "",
unitName: "",
province: "",
city: "",
},
validationSchema: Yup.object({
mobile: Yup.string()
.required("این فیلد اجباری است!")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
firstName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
lastName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
unitName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
province: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
city: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
}),
});
const formik2 = useFormik({
initialValues: {
userInfoCheck: "",
},
validationSchema: Yup.object({
userInfoCheck: Yup.string()
.required("این فیلد اجباری است!")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
}),
validateOnMount: true,
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
if (userData) {
formik.setValues({
mobile: userData.mobile || "",
firstName: userData.firstName || "",
lastName: userData.lastName || "",
unitName: userData.unitName || "",
province: userData.province || "",
city: userData.city || "",
});
setTimeout(() => {
formik.validateForm();
}, 1);
}
}, [userData]);
useEffect(() => {
if (isEdit) {
formik.setValues({
mobile: data.mobile || "",
firstName: data.firstName || "",
lastName: data.lastName || "",
unitName: data.unitName || "",
province: data.province || "",
city: data.city || "",
});
setTimeout(() => {
formik.validateForm();
}, 1);
}
}, [isEdit]);
useEffect(() => {
if (notFound) {
formik.setFieldValue("mobile", formik2.values.userInfoCheck);
}
}, [notFound]);
useEffect(() => {
dispatch(slaughterGetProvinceService()).then((r) => {
setProvinceData(r.payload.data);
});
}, []);
useEffect(() => {
if (formik.values.province) {
setCityData(
[],
dispatch(slaughterGetCitiesService(formik.values.province)).then(
(r) => {
setCityData(r.payload.data);
}
)
);
}
}, [formik.values.province]);
return (
<Grid
container
justifyContent="space-between"
alignItems="center"
xs={12}
direction="column"
gap={2}
>
{!userData && !isEdit ? (
<Grid container xs={12}>
<Typography>جستجو کاربر</Typography>
<Grid mt={SPACING.SMALL} display="flex" width={1}>
<TextField
fullWidth
id="userInfoCheck"
label="شماره موبایل"
variant="outlined"
value={formik2.values.userInfoCheck}
error={
formik2.touched.userInfoCheck &&
Boolean(formik2.errors.userInfoCheck)
}
onChange={(e) => {
formik2.handleChange(e);
// Reset notFound when user starts typing
if (notFound) {
setNotFound(false);
}
}}
onBlur={formik2.handleBlur}
helperText={
formik2.touched.userInfoCheck && formik2.errors.userInfoCheck
}
/>
<IconButton
disabled={!formik2.isValid}
aria-label="search"
color="primary"
onClick={() => {
dispatch(
stewardSellOutGetBuyers({
mobile: formik2.values.userInfoCheck,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
setNotFound(true);
setUserData(null);
openNotif({
vertical: "top",
horizontal: "center",
msg: "خریدار یافت نشد، یک خریدار جدید ثبت کنید!",
severity: "error",
});
} else {
const responseData = r.payload?.data;
// Check if response is an empty array
if (
Array.isArray(responseData) &&
responseData.length === 0
) {
setNotFound(true);
setUserData(null);
} else {
setNotFound(false);
setUserData(responseData);
}
}
});
}}
>
<SearchIcon />
</IconButton>
</Grid>
{notFound && (
<Grid container xs={12} mt={SPACING.SMALL}>
<Typography variant="body2" color="error" sx={{ width: "100%" }}>
خریداری یافت نشد
</Typography>
</Grid>
)}
</Grid>
) : (
<Grid
container
justifyContent="space-between"
alignItems="start"
xs={12}
direction="column"
gap={2}
>
<TextField
fullWidth
id="mobile"
label="شماره موبایل"
variant="outlined"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.mobile && formik.errors.mobile}
/>
<TextField
fullWidth
id="firstName"
label="نام"
variant="outlined"
value={formik.values.firstName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.firstName && formik.errors.firstName}
/>
<TextField
fullWidth
id="lastName"
label="نام خانوادگی"
variant="outlined"
value={formik.values.lastName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.lastName && formik.errors.lastName}
/>
<TextField
fullWidth
id="unitName"
label="نام واحد"
variant="outlined"
value={formik.values.unitName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.unitName && formik.errors.unitName}
/>
<Autocomplete
style={{ width: "100%" }}
disablePortal
id="province"
options={
provinceData
? provinceData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("province", value ? value.id : "");
formik.setFieldValue("city", "");
}}
renderInput={(params) => (
<TextField {...params} label="استان را انتخاب کنید" />
)}
/>
{!notFound && (
<Typography variant="caption" color="error">
استان: {formik.values.province}
</Typography>
)}
<Autocomplete
minWidth={210}
style={{ width: "100%" }}
disabled={!formik.values.province}
disablePortal
id="city"
options={
cityData
? cityData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("city", value ? value.id : "");
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
{!notFound && (
<Typography variant="caption" color="error">
شهر: {formik.values.city}
</Typography>
)}
</Grid>
)}
{(userData || isEdit) && (
<Grid container xs={12}>
<Button
fullWidth
variant="contained"
disabled={!formik.isValid}
onClick={() => {
if (isEdit) {
dispatch(
slaughterEditBuyerDataService({
buyer_key: data?.key,
mobile: formik.values.mobile,
first_name: formik.values.firstName,
last_name: formik.values.lastName,
unit_name: formik.values.unitName,
city: formik.values.city,
province: formik.values.province,
})
).then((r) => {
updateTable();
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
stewatdSubmitBuyerDataService({
role: getRoleFromUrl(),
mobile: formik.values.mobile,
first_name: formik.values.firstName,
last_name: formik.values.lastName,
unit_name: formik.values.unitName,
city: formik.values.city,
province: formik.values.province,
})
).then((r) => {
updateTable();
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
}}
>
{isEdit ? "ویرایش" : "ثبت"}
</Button>
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,184 @@
import { useDispatch } from "react-redux";
import { useContext } from "react";
import moment from "moment";
import { Field, Form, Formik } from "formik";
import { DatePicker } from "@mui/x-date-pickers";
import { Button, TextField } from "@mui/material";
import { Yup } from "../../../../lib/yup/yup";
import { AppContext } from "../../../../contexts/AppContext";
import { Grid } from "../../../../components/grid/Grid";
import { guildEditOutOfProvinceService } from "../../../slaughter-house/services/slaughterEditOutOfProvinceService";
import {
guildSubmitOutOfProvinceService,
slaughterSubmitOutOfProvinceService,
} from "../../../slaughter-house/services/slaughter-submit-out-province-service";
import { stewardEditBuyerDataService } from "../../../guild/services/steward-edit-sell-out-buyer";
import { guildGetInventoryStockService } from "../../../guild/services/guild-get-inventory-stock";
import { guildGetFreeSaleBarService } from "../../../guild/services/guild-get-free-sale-bar";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { fetchStewardBroadcastAndProducts } from "../../services/handle-fetch-steward-products";
const validationSchema = Yup.object().shape({
quarantineCode: Yup.string().required("کد قرنطینه الزامی است"),
carcassCount: Yup.number().required("حجم لاشه الزامی است"),
carcassWeight: Yup.number().required("وزن لاشه الزامی است"),
date: Yup.string().required("تاریخ الزامی است"),
});
export const StewardSellOutOfProvinceEditSell = ({
fetchItems,
isEdit,
item,
selectedDate,
stewardKey,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const initialValues = {
quarantineCode: item?.clearanceCode || "",
carcassCount: item?.numberOfCarcasses || "0",
carcassWeight: item?.weightOfCarcasses || "",
date: item?.date || moment().format("YYYY-MM-DD HH:mm:ss"),
};
return (
<Grid>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={(values) => {
const payload = {
...values,
date: values.date,
number_of_carcasses: values.carcassCount,
weight_of_carcasses: values.carcassWeight,
};
if (values.quarantineCode !== item?.quarantineCode) {
payload.quarantineCode = values.quarantineCode;
}
const service = stewardKey
? isEdit
? guildEditOutOfProvinceService({ key: item?.key, ...payload })
: guildSubmitOutOfProvinceService({
steward_key: stewardKey,
...payload,
})
: isEdit
? stewardEditBuyerDataService({
key: item?.key,
driver_mobile: values.driverPhone,
...payload,
})
: slaughterSubmitOutOfProvinceService({
driver_mobile: values.driverPhone,
...payload,
});
dispatch(service).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
stewardKey
? dispatch(guildGetInventoryStockService({ date: values.date }))
: fetchItems();
stewardKey &&
dispatch(
guildGetFreeSaleBarService({
date: values.date,
steward_key: stewardKey,
})
);
dispatch(fetchStewardBroadcastAndProducts());
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
{({ errors, touched, setFieldValue, values }) => (
<Form>
<Grid
container
spacing={1}
justifyContent="center"
alignItems="center"
>
<Grid item xs={12}>
<Field
as={TextField}
name="quarantineCode"
label="کد قرنطینه"
fullWidth
error={
touched.quarantineCode && Boolean(errors.quarantineCode)
}
helperText={touched.quarantineCode && errors.quarantineCode}
/>
</Grid>
<Grid item xs={12}>
<Field
as={TextField}
name="carcassCount"
label="حجم لاشه"
type="number"
fullWidth
error={touched.carcassCount && Boolean(errors.carcassCount)}
helperText={touched.carcassCount && errors.carcassCount}
/>
</Grid>
<Grid item xs={12}>
<DatePicker
label="تاریخ"
value={moment(values.date)}
onChange={(val) => {
const formatted = moment(val).format("YYYY-MM-DD HH:mm:ss");
setFieldValue("date", formatted);
}}
renderInput={(params) => (
<TextField
{...params}
fullWidth
error={touched.date && Boolean(errors.date)}
helperText={touched.date && errors.date}
/>
)}
/>
</Grid>
<Grid item xs={12}>
<Field
as={TextField}
name="carcassWeight"
label="وزن لاشه"
type="number"
fullWidth
error={touched.carcassWeight && Boolean(errors.carcassWeight)}
helperText={touched.carcassWeight && errors.carcassWeight}
/>
</Grid>
<Grid item xs={12}>
<Button variant="contained" fullWidth type="submit">
{isEdit ? "ویرایش" : "ثبت"}
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
</Grid>
);
};

View File

@@ -0,0 +1,546 @@
import { useContext, useEffect, useState, useCallback, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
TextField,
} from "@mui/material";
import { Field, Form, Formik } from "formik";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import { Yup } from "../../../../lib/yup/yup";
import { stewardSellOutGetBuyers } from "../../../guild/services/steward-sell-out-get-buyers";
import { AppContext } from "../../../../contexts/AppContext";
import { stewardGetOutSellService } from "../../../guild/services/steward-get-sell-out-service";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { stewardSellOuutSubmitSell } from "../../../guild/services/steward-sell-out-submit-sell";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { fetchStewardBroadcastAndProducts } from "../../services/handle-fetch-steward-products";
import MonthlyDataCalendar from "../../../../components/date-picker/MonthlyDataCalendar";
import PersianDate from "persian-date";
import axios from "axios";
import { LabelField } from "../../../../components/label-field/LabelField";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const validationSchema = (selectedDateAmount) =>
Yup.object({
weight_of_carcasses: Yup.number()
.required("وزن لاشه‌ها الزامی است")
.positive("وزن باید عددی مثبت باشد")
.test(
"max-production-date-amount",
`وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${
selectedDateAmount?.toLocaleString() || 0
} کیلوگرم) باشد!`,
function (value) {
if (!selectedDateAmount || selectedDateAmount === null) return true;
return value <= selectedDateAmount;
}
),
clearance_code: Yup.string()
.required("کد قرنطینه الزامی است")
.matches(
/^(?=.*[A-Z])(?=.*\d)[A-Z0-9]+$/,
"کد قرنطینه باید ترکیبی از حروف بزرگ انگلیسی و عدد باشد"
),
date: Yup.date().required("تاریخ الزامی است"),
production_date: Yup.string().required("تاریخ تولید الزامی است"),
});
export const StewardSellOutOfProvinceSubmitSell = ({
updateTable,
fetchItems,
isInventory,
}) => {
const [productData, setProductData] = useState([]);
const [productKey, setProductKey] = useState(null);
const [buterData, setBuyerData] = useState([]);
const [buyerSelected, setBuyerSelected] = useState(null);
const [selectedInventory] = useState("free");
const [approvedStatus, setApprovedStatus] = useState("governmental");
const [selectedCalendarDate, setSelectedCalendarDate] = useState(null);
const [calendarDayData, setCalendarDayData] = useState({});
const [productionDate, setProductionDate] = useState(null);
const [selectedDateAmount, setSelectedDateAmount] = useState(null);
const [calendarDateError, setCalendarDateError] = useState(null);
const formikRef = useRef(null);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [openNotif] = useContext(AppContext);
const handleDateSelect = (dateInfo) => {
if (dateInfo && dateInfo.formattedDate) {
setSelectedCalendarDate(dateInfo.formattedDate);
// Get the data for the selected date
const data = calendarDayData[dateInfo.formattedDate];
// Use the original Gregorian date from the API data for production_date
if (data && data.originalDay) {
// Prevent choosing a production date after the main form date
if (
formikRef.current?.values?.date &&
moment(data.originalDay).isAfter(
moment(formikRef.current.values.date),
"day"
)
) {
setCalendarDateError(
"تاریخ تولید نمی‌تواند بعد از تاریخ انتخابی باشد"
);
return;
}
setCalendarDateError(null);
setProductionDate(data.originalDay);
if (formikRef.current) {
formikRef.current.setFieldValue("production_date", data.originalDay);
}
}
// Get the amount for the selected date
if (data && data.value1 !== undefined) {
setSelectedDateAmount(data.value1);
} else {
setSelectedDateAmount(null);
}
}
};
const transformCalendarData = useCallback((dataArray) => {
if (!Array.isArray(dataArray)) return {};
const transformedData = {};
dataArray.forEach((item) => {
// Include all items to show amounts, but store active status
if (item.day && item.amount !== undefined) {
const persianDate = new PersianDate(new Date(item.day));
const persianDateStr = persianDate.format("YYYY/MM/DD");
transformedData[persianDateStr] = {
value1: item.amount,
originalDay: item.day,
active: item.active === true, // Store active status
};
}
});
return transformedData;
}, []);
const updateCalendarData = useCallback(
(dataArray) => {
const transformed = transformCalendarData(dataArray);
setCalendarDayData(transformed);
},
[transformCalendarData]
);
const [selectedFormDate, setSelectedFormDate] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const fetchCalendarData = useCallback(
async (dateParam = selectedFormDate) => {
try {
const response = await axios.get("/steward-remain-weight/", {
params: {
date: dateParam,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key
: "",
},
});
if (response.data) {
const dataToShow =
approvedStatus === "governmental"
? response.data.governmental
: response.data.free;
updateCalendarData(dataToShow || []);
}
} catch (error) {
console.error("Error fetching calendar data:", error);
}
},
[approvedStatus, updateCalendarData, selectedFormDate, selectedSubUser]
);
useEffect(() => {
dispatch(
stewardSellOutGetBuyers({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setBuyerData(r.payload.data);
});
}, [selectedSubUser?.key]);
useEffect(() => {
dispatch(
stewardGetOutSellService({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
const data = r.payload?.data;
if (Array.isArray(data)) {
setProductData(data);
} else if (data?.data && Array.isArray(data.data)) {
setProductData(data.data);
} else {
setProductData([]);
}
});
}, [dispatch, selectedSubUser?.key]);
useEffect(() => {
fetchCalendarData(selectedFormDate);
}, [selectedFormDate, fetchCalendarData]);
useEffect(() => {
let dateToUse = selectedFormDate;
if (approvedStatus === "governmental" && formikRef.current) {
const today = moment(new Date()).format("YYYY-MM-DD");
formikRef.current.setFieldValue("date", today);
setSelectedFormDate(today);
dateToUse = today;
}
fetchCalendarData(dateToUse);
setSelectedCalendarDate(null);
setProductionDate(null);
setSelectedDateAmount(null);
if (formikRef.current) {
formikRef.current.setFieldValue("production_date", "");
}
}, [
approvedStatus,
selectedFormDate,
fetchCalendarData,
selectedSubUser?.key,
]);
useEffect(() => {
if (formikRef.current) {
formikRef.current.validateForm();
}
}, [selectedDateAmount]);
// const handleSellType = (event) => {
// const newType = event.target.value;
// setSelectedInventory(newType);
// };
const handleApprovedPrice = (event) => {
const newType = event.target.value;
setApprovedStatus(newType);
if (newType === "governmental" && formikRef.current) {
const today = moment(new Date()).format("YYYY-MM-DD");
formikRef.current.setFieldValue("date", today);
setSelectedFormDate(today);
fetchCalendarData(today);
}
};
return (
<Grid container gap={SPACING.SMALL} direction="column" display={"flex"}>
<Grid container gap={SPACING.SMALL} direction={"column"}>
<Grid minWidth={210}>
<Autocomplete
disablePortal
id="combo-box-demo"
getOptionDisabled={(option) => option.disabled}
options={
buterData
? buterData.map((i) => ({
id: i?.key,
label: `${i?.fullname} (${i.mobile}) / استان ${i.province} / شهر ${i.city}`,
item: i,
}))
: []
}
onChange={(event, value) => {
setBuyerSelected(value?.id);
}}
renderInput={(params) => (
<TextField {...params} label="انتخاب خریدار" />
)}
/>
</Grid>
{buyerSelected && (
<>
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
Array.isArray(productData) && productData.length > 0
? productData.map((i) => {
return {
data: i,
label: `${i.name || ""}`,
};
})
: []
}
onChange={(event, value) => {
setProductKey(value?.data || null);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
<Formik
innerRef={formikRef}
initialValues={{
weight_of_carcasses: "",
clearance_code: "",
date: moment(new Date()).format("YYYY-MM-DD"),
production_date: "",
}}
validationSchema={validationSchema(selectedDateAmount)}
onSubmit={(values) => {
dispatch(
stewardSellOuutSubmitSell({
buyer_key: buyerSelected,
number_of_carcasses:
Math.round(
values?.weight_of_carcasses / productKey?.weightAverage
) || 0,
weight_of_carcasses: parseInt(values.weight_of_carcasses),
date: values.date,
clearance_code: values.clearance_code,
product_key: productKey?.key,
sale_type: selectedInventory,
quota: approvedStatus,
production_date: values.production_date,
distribution_type: "web",
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
if (isInventory) {
fetchItems();
} else {
updateTable();
}
dispatch(fetchStewardBroadcastAndProducts());
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
{({ values, errors, touched, setFieldValue }) => (
<Form>
<Grid container gap={SPACING.SMALL} direction={"column"}>
<Grid item>
<TextField
disabled
label="حجم تقریبی"
value={
Math.round(
values?.weight_of_carcasses /
productKey?.weightAverage
) || 0
}
InputProps={{
readOnly: true,
}}
/>
</Grid>
<LabelField label="نوع انبار">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={approvedStatus}
onChange={handleApprovedPrice}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
value="governmental"
control={<Radio />}
label="انبار دولتی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="انبار آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField>
<Grid xs={12}>
<DatePicker
label="تاریخ"
disabled={approvedStatus === "governmental"}
value={moment(values.date)}
onChange={(newValue) => {
const formatted =
moment(newValue).format("YYYY-MM-DD");
setFieldValue("date", formatted);
setSelectedFormDate(formatted);
fetchCalendarData(formatted);
}}
renderInput={(params) => (
<TextField
fullWidth
{...params}
size="small"
error={touched.date && Boolean(errors.date)}
helperText={touched.date && errors.date}
/>
)}
/>
</Grid>
<Grid item>
<Field
name="clearance_code"
as={TextField}
label="کد قرنطینه"
fullWidth
onChange={(e) =>
setFieldValue(
"clearance_code",
e.target.value.toUpperCase()
)
}
error={
touched.clearance_code &&
Boolean(errors.clearance_code)
}
helperText={
touched.clearance_code && errors.clearance_code
}
/>
</Grid>
{/* <LabelField label="نوع قیمت">
<FormControl fullWidth>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={selectedInventory}
onChange={handleSellType}
sx={{
justifyContent: "space-between",
}}
>
<FormControlLabel
disabled
value="governmental"
control={<Radio />}
label="قیمت دولتی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="قیمت آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField> */}
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={SPACING.TINY}
sx={{ width: "100%" }}
>
<MonthlyDataCalendar
onDateSelect={handleDateSelect}
dayData={calendarDayData}
selectedDate={selectedCalendarDate}
maxGregorianDate={values.date}
label={`تاریخ تولید گوشت ${
selectedDateAmount !== null
? `(موجودی: ${selectedDateAmount?.toLocaleString()} کیلوگرم)`
: ""
}`}
/>
{calendarDateError && (
<TextField
error
helperText={calendarDateError}
sx={{
visibility: "hidden",
height: 0,
mt: 0,
mb: 0,
}}
/>
)}
</Grid>
<Grid item>
<Field
name="weight_of_carcasses"
as={TextField}
label="وزن لاشه‌ها"
fullWidth
disabled={!selectedDateAmount && !productionDate}
error={
!selectedDateAmount && !productionDate
? true
: touched.weight_of_carcasses &&
Boolean(errors.weight_of_carcasses)
}
helperText={
!selectedDateAmount && !productionDate
? "لطفاً ابتدا تاریخ تولید را انتخاب کنید!"
: touched.weight_of_carcasses &&
errors.weight_of_carcasses
}
/>
</Grid>
<Grid item>
<Button
fullWidth
type="submit"
variant="contained"
disabled={!productKey || !productionDate}
>
ارسال
</Button>
</Grid>
</Grid>
</Form>
)}
</Formik>
</>
)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,293 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { Button, TextField, Tooltip } from "@mui/material";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import { stawardGetOutDashboardService } from "../../../guild/services/steward-get-out-dashboard";
import moment from "moment";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { formatJustDate } from "../../../../utils/formatTime";
import ShowImage from "../../../../components/show-image/ShowImage";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { StewardSellOutOperations } from "../steward-buy-out-operations/StewardSellOutOperations";
import { StewardSubmitFreeBar } from "../steward-submit-free-bar/StewardSubmitFreeBar";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardPurchaseOutProvince = ({ isBarManagemen }) => {
const [data, setData] = useState([]);
const [tableData, setTableData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [dashboardData, setDashboardData] = useState([]);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
// const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const getDashboardData = () => {
dispatch(
stawardGetOutDashboardService({
date1: selectedDate1,
date2: selectedDate2,
search: "filter",
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`steward_free_bar/?search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
getDashboardData();
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
// Initialize dates on mount
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
// Fetch data when dates or perPage change (only if dates are set)
useEffect(() => {
if (selectedDate1 && selectedDate2) {
fetchApiData(1);
setPage(1);
}
}, [selectedDate1, selectedDate2, perPage]);
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const updateTable = () => {
setPage(1);
getDashboardData();
fetchApiData(1);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
formatJustDate(item.createDate),
`${item?.killHouseName} (${item?.killHouseMobile})`,
item?.product?.name?.toLocaleString() || "-",
item?.killHouseName?.toLocaleString(),
`${item?.city} (${item?.province})`,
item?.numberOfCarcasses?.toLocaleString() || "-",
item?.weightOfCarcasses?.toLocaleString() || "-",
<ShowImage key={i} src={item?.barImage} />,
<StewardSellOutOperations
key={i}
item={item}
updateTable={updateTable}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`steward_free_bar/?search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
getDashboardData();
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
return (
<Grid container direction="column" flexWrap="nowrap" mt={SPACING.SMALL}>
<Grid
container
mt={SPACING.MEDIUM}
alignItems="center"
justifyContent="center"
gap={2}
>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
{getRoleFromUrl() === "Steward" && !isBarManagemen && (
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت اطلاعات خرید",
content: (
<StewardSubmitFreeBar
selectedDate={selectedDate1}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت اطلاعات خرید
</Button>
)}
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) =>
setSelectedDate1(moment(e).format("YYYY-MM-DD"))
}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) =>
setSelectedDate2(moment(e).format("YYYY-MM-DD"))
}
/>
</Grid>
<Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
const link = `${
axios.defaults.baseURL
}kill_house_free_bar_excel/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}&date1=${selectedDate1}&date2=${selectedDate2}&type=carcass&search=filter&value=${textValue}&date_type=buy`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
</Tooltip>
</Grid>
</form>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={["تعداد کل بارها", "تعداد کل", "وزن کل (کیلوگرم)"]}
data={[
[
dashboardData?.totalBars?.toLocaleString() || "0",
dashboardData?.totalQuantity?.toLocaleString() || "0",
dashboardData?.totalWeight?.toLocaleString() || "0",
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ خرید",
"خریدار",
"محصول",
"فروشنده",
"استان/شهر",
// "پلاک ماشین",
// "نام راننده",
// "تلفن راننده",
// "تاریخ ورود به انبار",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"بارنامه",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="خرید های خارج استان"
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,109 @@
import React, { useEffect, useRef, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Button } from "@mui/material";
import { useNavigate } from "react-router-dom";
import { getKillhouseApprovedPriceState } from "../../../province/services/get-approved-price-state";
import { Grid } from "../../../../components/grid/Grid";
import { StewardShowProducts } from "../steward-show-products/StewardShowProducts";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { StewardAllocationToGuild } from "../../../guild/components/StewardAllocationToGuild";
import { ROUTE_STEWARD_DAILY_LIST } from "../../../../routes/routes";
import { StewardShowAllocations } from "../steward-show-allocations/StewardShowAllocations";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardSellInProvince = () => {
const dispatch = useDispatch();
const allocationsRef = useRef(null);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const { stewardProducts } = useSelector((state) => state.stewardSlice);
const navigate = useNavigate();
const { priceInfo } = useSelector((state) => state.slaughterSlice);
const fetchData = useCallback(async () => {
dispatch(
getKillhouseApprovedPriceState({
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
);
if (allocationsRef.current?.updateTable) {
allocationsRef.current.updateTable();
}
}, [dispatch, selectedSubUser?.key]);
useEffect(() => {
fetchData();
}, [selectedSubUser?.key]);
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Grid container width="100%" isDashboard>
<StewardShowProducts />
</Grid>
<Grid container xs={12} my={2} gap={2}>
<Button
disabled={
!stewardProducts ||
!Array.isArray(stewardProducts) ||
stewardProducts.length === 0
}
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت توزیع/ فروش درون استان",
size: {
xs: "100%",
md: "360px",
},
content: (
<StewardAllocationToGuild
fetchData={fetchData}
sellerType={"Steward"}
sellType="exclusive"
priceInfo={priceInfo}
/>
),
})
);
}}
>
ثبت توزیع/ فروش
</Button>
<Button
disabled
variant="contained"
color="success"
onClick={() => {
navigate(ROUTE_STEWARD_DAILY_LIST);
}}
>
لیست روزانه
</Button>
</Grid>
<Grid container xs={12} mt={4}>
<StewardShowAllocations
ref={allocationsRef}
handleUpdate={fetchData}
priceInfo={priceInfo}
remainWeight={
stewardProducts?.[0]?.totalRemainWeight !== undefined
? stewardProducts[0].totalRemainWeight
: undefined
}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,387 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { useFormik } from "formik";
import {
Autocomplete,
Button,
IconButton,
TextField,
Typography,
} from "@mui/material";
import SearchIcon from "@mui/icons-material/Search";
import { AppContext } from "../../../../contexts/AppContext";
import { Yup } from "../../../../lib/yup/yup";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../../slaughter-house/services/slaughter-get-provinces";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import {
slaughterEditBuyerDataService,
slaughterGetBuyerDataService,
slaughterSubmitBuyerDataService,
} from "../../../slaughter-house/services/slaughter-house-submit-buyer";
import { DRAWER, LOADING_END } from "../../../../lib/redux/slices/appSlice";
import { fetchStewardBroadcastAndProducts } from "../../services/handle-fetch-steward-products";
export const StewardSellOutOfProvinceBuyersEditBuyer = ({
updateTable,
isEdit,
data,
}) => {
const [openNotif] = useContext(AppContext);
const [userData, setUserData] = useState(null);
const [notFound, setNotFound] = useState(false);
const dispatch = useDispatch();
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const formik = useFormik({
initialValues: {
mobile: "",
firstName: "",
lastName: "",
unitName: "",
province: "",
city: "",
},
validationSchema: Yup.object({
mobile: Yup.string()
.required("این فیلد اجباری است!")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
firstName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
lastName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
unitName: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
province: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
city: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
}),
});
const formik2 = useFormik({
initialValues: {
userInfoCheck: "",
},
validationSchema: Yup.object({
userInfoCheck: Yup.string()
.required("این فیلد اجباری است!")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
}),
validateOnMount: true,
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
if (userData) {
formik.setValues({
mobile: userData.mobile || "",
firstName: userData.firstName || "",
lastName: userData.lastName || "",
unitName: userData.unitName || "",
province: userData.province || "",
city: userData.city || "",
});
setTimeout(() => {
formik.validateForm();
}, 1);
}
}, [userData]);
useEffect(() => {
if (isEdit) {
formik.setValues({
mobile: data.mobile || "",
firstName: data.firstName || "",
lastName: data.lastName || "",
unitName: data.unitName || "",
province: data.province || "",
city: data.city || "",
});
setTimeout(() => {
formik.validateForm();
}, 1);
}
}, [isEdit]);
useEffect(() => {
if (notFound) {
formik.setFieldValue("mobile", formik2.values.userInfoCheck);
}
}, [notFound]);
useEffect(() => {
dispatch(slaughterGetProvinceService()).then((r) => {
setProvinceData(r.payload.data);
});
}, []);
useEffect(() => {
if (formik.values.province) {
setCityData(
[],
dispatch(slaughterGetCitiesService(formik.values.province)).then(
(r) => {
setCityData(r.payload.data);
}
)
);
}
}, [formik.values.province]);
return (
<Grid
container
justifyContent="space-between"
alignItems="center"
xs={12}
direction="column"
gap={2}
>
{!userData && !notFound && !isEdit ? (
<Grid container xs={12}>
<Typography>جستجو کاربر</Typography>
<Grid mt={SPACING.SMALL} display="flex" width={1}>
<TextField
fullWidth
id="userInfoCheck"
label="شماره موبایل"
variant="outlined"
value={formik2.values.userInfoCheck}
error={
formik2.touched.userInfoCheck &&
Boolean(formik2.errors.userInfoCheck)
}
onChange={formik2.handleChange}
onBlur={formik2.handleBlur}
helperText={
formik2.touched.userInfoCheck && formik2.errors.userInfoCheck
}
/>
<IconButton
disabled={!formik2.isValid}
aria-label="search"
color="primary"
onClick={() => {
dispatch(
slaughterGetBuyerDataService(formik2.values.userInfoCheck)
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
setNotFound(true);
openNotif({
vertical: "top",
horizontal: "center",
msg: "خریدار یافت نشد، یک خریدار جدید ثبت کنید!",
severity: "error",
});
} else {
setUserData(r.payload.data);
}
});
}}
>
<SearchIcon />
</IconButton>
</Grid>
</Grid>
) : (
<Grid
container
justifyContent="space-between"
alignItems="start"
xs={12}
direction="column"
gap={2}
>
<TextField
fullWidth
id="mobile"
label="شماره موبایل"
variant="outlined"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.mobile && formik.errors.mobile}
/>
<TextField
fullWidth
id="firstName"
label="نام"
variant="outlined"
value={formik.values.firstName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.firstName && formik.errors.firstName}
/>
<TextField
fullWidth
id="lastName"
label="نام خانوادگی"
variant="outlined"
value={formik.values.lastName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.lastName && formik.errors.lastName}
/>
<TextField
fullWidth
id="unitName"
label="نام واحد"
variant="outlined"
value={formik.values.unitName}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={formik.touched.unitName && formik.errors.unitName}
/>
<Autocomplete
style={{ width: "100%" }}
disablePortal
id="province"
options={
provinceData
? provinceData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("province", value ? value.id : "");
formik.setFieldValue("city", "");
}}
renderInput={(params) => (
<TextField {...params} label="استان را انتخاب کنید" />
)}
/>
{!notFound && (
<Typography variant="caption" color="error">
استان: {formik.values.province}
</Typography>
)}
<Autocomplete
minWidth={210}
style={{ width: "100%" }}
disabled={!formik.values.province}
disablePortal
id="city"
options={
cityData
? cityData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("city", value ? value.id : "");
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
{!notFound && (
<Typography variant="caption" color="error">
شهر: {formik.values.city}
</Typography>
)}
</Grid>
)}
{(userData || notFound || isEdit) && (
<Grid container xs={12}>
<Button
fullWidth
variant="contained"
disabled={!formik.isValid}
onClick={() => {
if (isEdit) {
dispatch(
slaughterEditBuyerDataService({
buyer_key: data?.key,
mobile: formik.values.mobile,
first_name: formik.values.firstName,
last_name: formik.values.lastName,
unit_name: formik.values.unitName,
city: formik.values.city,
province: formik.values.province,
})
).then((r) => {
updateTable();
dispatch(fetchStewardBroadcastAndProducts());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
slaughterSubmitBuyerDataService({
mobile: formik.values.mobile,
first_name: formik.values.firstName,
last_name: formik.values.lastName,
unit_name: formik.values.unitName,
city: formik.values.city,
province: formik.values.province,
})
).then((r) => {
updateTable();
dispatch(fetchStewardBroadcastAndProducts());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
DRAWER({ right: false, bottom: false, content: null })
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
}}
>
{isEdit ? "ویرایش" : "ثبت"}
</Button>
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,197 @@
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { Button, IconButton, TextField } from "@mui/material";
import { RiSearchLine } from "react-icons/ri";
// import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { StewardSellOutOfProvinceAddBuyer } from "../steward-purchase-out-province-add-buyer/StewardSellOutOfProvinceAddBuyer";
import { StewardSellOutOfProvinceBuyersEditBuyer } from "../steward-sell-out-of-province-buyers-edit-buyer/StewardSellOutOfProvinceBuyersEditBuyer";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardSellOutOfProvinceBuyers = () => {
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`out-province-carcasses-buyer/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=${page}&page_size=${perPage}&state=buyer-list`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
`${item?.fullname} (${item?.mobile})`,
item?.unitName,
item?.province,
item?.city,
item?.requestsInfo?.numberOfRequests?.toLocaleString(),
item?.requestsInfo?.totalQuantity?.toLocaleString(),
item?.requestsInfo?.totalWeight?.toLocaleString(),
<IconButton
color="primary"
key={i}
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<StewardSellOutOfProvinceBuyersEditBuyer
updateTable={updateTable}
isEdit
data={item}
/>
),
title: "ویرایش خریدار",
})
);
}}
>
<EditIcon />
</IconButton>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`out-province-carcasses-buyer/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward") ? selectedSubUser?.key || "" : ""
}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}&state=buyer-list`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<StewardSellOutOfProvinceAddBuyer updateTable={updateTable} />
),
title: "افزودن خریدار",
})
);
}}
>
افزودن خریدار
</Button>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"مشخصات خریدار",
"نام واحد",
"استان",
"شهر",
"تعداد درخواست ها",
"حجم تقریبی (قطعه)",
"وزن (کیلوگرم)",
"ویرایش",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="خریداران لاشه خارج استان"
/>
</Grid>
);
};

View File

@@ -0,0 +1,315 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import moment from "moment";
import axios from "axios";
import { Button, TextField } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
import { stewardSellOutGetDashboard } from "../../../guild/services/steward-sell-out-get-dashboard";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { StewardSellOutOfProvinceSubmitSell } from "../steward-purchase-out-province-submit-sell/StewardSellOutOfProvinceSubmitSell";
import { StewardOutProvinceRegistrationCodeInput } from "../steward-out-province-registration-code-input/StewardOutProvinceRegistrationCodeInput";
import { StewardOutProvinceSalesOperations } from "../steward-out-province-sales-operations/StewardOutProvinceSalesOperations";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardSellOutOfProvinceSells = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`steward_free_sale_bar/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
};
const getDashboardData = () => {
dispatch(
stewardSellOutGetDashboard({
selectedDate1,
selectedDate2,
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
useEffect(() => {
if (selectedDate1 && selectedDate2) {
fetchApiData(1);
setPage(1);
getDashboardData();
}
}, [selectedDate1, selectedDate2, perPage]);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
setPage(1);
fetchApiData(1);
getDashboardData();
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.date ? formatTime(item?.date) : "-",
item?.productionDate ? formatJustDate(item?.productionDate) : "-",
item?.distributionType === "web"
? "سایت"
: item?.distributionType === "app"
? "موبایل"
: item?.distributionType === "pos"
? "پوز"
: item?.distributionType || "-",
`${item?.buyerName} (${item?.buyerMobile})`,
item?.buyer ? `${item?.buyer?.unitName}` : `${item?.buyerName}`,
item?.province,
item?.city,
// item?.numberOfCarcasses?.toLocaleString(),
item?.clearanceCode && (
<CheckCleanceCode clearanceCode={item?.clearanceCode} />
),
item?.quarantineWeightOfCarcasses?.toLocaleString(),
item?.weightOfCarcasses?.toLocaleString(),
item?.systemRegistrationCode ? (
item?.loggedRegistrationCode ? (
"تایید شده"
) : item?.registrationCode ? (
<StewardOutProvinceRegistrationCodeInput
key={i}
item={item}
fetchApiData={() => fetchApiData(page)}
/>
) : (
"-"
)
) : (
"-"
),
<StewardOutProvinceSalesOperations
key={i}
item={item}
updateTable={updateTable}
fetchApiData={fetchApiData}
page={page}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`steward_free_sale_bar/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${1}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
getDashboardData();
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
dispatch(LOADING_END());
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
mt={2}
>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} size="small" />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} size="small" />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
{/* <Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}kill_house_free_sale_bar_information_for_excel_excel/?key=${userKey}&role=${getRoleFromUrl()}&date1=${selectedDate1}&date2=${selectedDate2}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip> */}
</Grid>
<Grid container xs={12} mt={2} mb={4} isDashboard>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد فروش",
"خریدار",
"حجم لاشه ها (تقریبی)",
"وزن کل لاشه ها",
]}
data={[
[
dashboardData?.numberOfBars?.toLocaleString(),
dashboardData?.numberOfBuyers?.toLocaleString(),
dashboardData?.barsQuantity?.toLocaleString(),
dashboardData?.barsWeight?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<Grid container xs={12}>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<StewardSellOutOfProvinceSubmitSell
updateTable={updateTable}
/>
),
title: "ثبت اطلاعات فروش",
})
);
}}
>
ثبت اطلاعات فروش
</Button>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ",
"تاریخ تولید گوشت",
"ثبت شده",
"مشخصات خریدار",
"نام واحد",
"استان",
"شهر",
// "حجم لاشه (تقریبی)",
"کد قرنطینه",
"وزن استعلامی",
"وزن لاشه",
"کد احراز",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="فروش لاشه به خارج استان"
/>
</Grid>
);
};

View File

@@ -0,0 +1,78 @@
import { Box, Tab, Tabs } from "@mui/material";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { fetchStewardBroadcastAndProducts } from "../../services/handle-fetch-steward-products";
import { SPACING } from "../../../../data/spacing";
import { StewardShowProducts } from "../steward-show-products/StewardShowProducts";
import { Grid } from "../../../../components/grid/Grid";
import { StewardSellOutOfProvinceSells } from "../steward-sell-out-of-province-sells/StewardSellOutOfProvinceSells";
import { StewardSellOutOfProvinceBuyers } from "../steward-sell-out-of-province-buyers/StewardSellOutOfProvinceBuyers";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardSellOutOfProvince = () => {
const dispatch = useDispatch();
const [valueOutProvince, setValueOutProvince] = useState(0);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleChangeTabsetOutProvince = (event, newValue) => {
setValueOutProvince(newValue);
};
useEffect(() => {
dispatch(
fetchStewardBroadcastAndProducts({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
);
}, [selectedSubUser?.key]);
return (
<Grid
container
direction="column"
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
width="100%"
>
<Grid container width="100%" isDashboard>
<StewardShowProducts />
</Grid>
<Grid
container
direction="column"
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
mb={SPACING.SMALL}
width="100%"
>
<Box
sx={{
borderBottom: 1,
borderColor: "divider",
}}
>
<Tabs
className="insidetabs"
size="small"
value={valueOutProvince}
onChange={handleChangeTabsetOutProvince}
aria-label="basic tabs example"
>
<Tab label="فروش" />
<Tab label="خریداران" />
</Tabs>
</Box>
{valueOutProvince === 0 && <StewardSellOutOfProvinceSells />}
{valueOutProvince === 1 && <StewardSellOutOfProvinceBuyers />}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,165 @@
import { Button, IconButton, Popover } from "@mui/material";
import { useState } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import { useDispatch, useSelector } from "react-redux";
import { SlaughterAllocateToGuild } from "../../../slaughter-house/components/slaughter-allocate-to-guild/SlaughterAllocateToGuild";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import { StewardNorEnterdBarChangeState } from "../../../guild/components/StewardNorEnterdBarChangeState";
import { slaughterDeleteAllocatedService } from "../../../slaughter-house/services/salughter-delete-allocated";
import { fetchSlaughterBroadcastAndProducts } from "../../../slaughter-house/services/handle-fetch-slaughter-products";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardShowAllocationsOperations = ({
item,
type,
handleUpdate,
priceInfo,
remainWeight,
updateTable,
}) => {
const dispatch = useDispatch();
const [popoverOpen, setPopoverOpen] = useState(false);
const [anchorEl, setAnchorEl] = useState(null);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const openPopover = (event) => {
setPopoverOpen(true);
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setPopoverOpen(false);
setAnchorEl(null);
};
const handleDistributeToGuild = () => {
closePopover();
const handleEditSuccess = () => {
if (updateTable) updateTable(1);
if (handleUpdate) handleUpdate();
};
dispatch(
OPEN_MODAL({
title: "ویرایش توزیع و فروش محصول",
content: (
<SlaughterAllocateToGuild
updateTable={handleEditSuccess}
fetchApiData={handleEditSuccess}
sellerType="KillHouse"
sellType="exclusive"
priceInfo={priceInfo}
remainWeight={remainWeight}
editData={item}
item={item}
/>
),
})
);
};
return (
<Grid>
<IconButton
variant="contained"
color="primary"
onClick={openPopover}
disabled={!type && item?.registrationCode}
>
<TuneIcon />
</IconButton>
<Popover
open={popoverOpen}
anchorEl={anchorEl}
onClose={closePopover}
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
>
<div style={{ padding: 10 }}>
<Grid
container
direction="column"
alignItems="center"
justifyContent="center"
gap={1}
>
{type && (
<Button
size="small"
color="primary"
variant="outlined"
onClick={() => {
closePopover();
dispatch(
OPEN_MODAL({
title: "ویرایش اطلاعات بار",
content: (
<StewardNorEnterdBarChangeState
updateTable={updateTable}
handleUpdate={handleUpdate}
item={item}
/>
),
})
);
}}
>
تایید / رد
</Button>
)}
{!type && (
<Button
size="small"
color="primary"
variant="outlined"
onClick={handleDistributeToGuild}
>
ویرایش
</Button>
)}
{!type && (
<Button
size="small"
disabled={item?.registrationCode}
variant="outlined"
color="error"
onClick={() => {
closePopover();
dispatch(
slaughterDeleteAllocatedService({
steward_allocation_key: item.key,
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key
: "",
})
).then(() => {
dispatch(
fetchSlaughterBroadcastAndProducts({
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key
: "",
})
);
if (updateTable) updateTable(1);
if (handleUpdate) handleUpdate();
});
}}
>
حذف
</Button>
)}
</Grid>
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,483 @@
import React, {
useContext,
useEffect,
useState,
useImperativeHandle,
forwardRef,
} from "react";
import axios from "axios";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import { Button, TextField, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { RiSearchLine } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import {
CLOSE_MODAL,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { slaughterInventoryFinalSubmitService } from "../../../slaughter-house/services/slaughter-inventory-final-submit";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { StewardShowAllocationsOperations } from "../steward-show-allocations-operations/StewardShowAllocationsOperations";
import { getAllocationType } from "../../../../utils/getAllocationType";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
// import { format } from "date-fns-jalali";
export const StewardShowAllocations = forwardRef(
({ type, handleUpdate, priceInfo, remainWeight }, ref) => {
const dispatch = useDispatch();
const [
,
,
selectedDate1,
setSelectedDate1,
selectedDate2,
setSelectedDate2,
] = useContext(AppContext);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
8;
};
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const [openNotif] = useContext(AppContext);
const fetchApiData = async (pageParam = 1) => {
try {
dispatch(LOADING_START());
const response = await axios.get(
`steward-allocation/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}${
type !== "not_entered"
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ""
}&page=${pageParam}&page_size=${perPage}${
type ? "&type=" + type : ""
}`
);
dispatch(LOADING_END());
setData(response.data.results || []);
setTotalRows(response.data.count || 0);
} catch (err) {
dispatch(LOADING_END());
console.error("Error fetching allocations:", err);
}
};
const updateTable = () => {
fetchApiData(1);
};
useImperativeHandle(ref, () => ({
updateTable,
}));
const handlePageChange = (newPage) => {
fetchApiData(newPage);
setPage(newPage);
};
const getAllocationData = (item) => {
let typeText = `${item?.toKillHouse?.name} - ${item?.toKillHouse?.killHouseOperator?.user?.fullname} (${item?.toKillHouse?.killHouseOperator?.user?.mobile})`;
switch (item?.allocationType) {
case "steward_killhouse":
typeText = `${item?.toKillHouse?.name} - ${item?.toKillHouse?.killHouseOperator?.user?.fullname} (${item?.toKillHouse?.killHouseOperator?.user?.mobile})`;
break;
case "steward_steward":
typeText = `${item?.toStewards?.name} - ${item?.toStewards?.user?.fullname} (${item?.toStewards?.user?.mobile})`;
break;
case "steward_guild":
typeText = `${item?.toGuilds?.guildsName} - ${item?.toGuilds?.user?.fullname} (${item?.toGuilds?.user?.mobile})`;
break;
case "ColdHouse":
typeText = `${item?.toColdHouse?.name}`;
break;
case "killhouse_steward":
typeText = `${item?.toStewards?.name || "-"} - ${
item?.toStewards?.user?.fullname || "-"
} (${item?.toStewards?.user?.mobile || "-"})`;
break;
case "killhouse_guild":
typeText = `${item?.toGuilds?.guildsName || "-"} - ${
item?.toGuilds?.user?.fullname || "-"
} (${item?.toGuilds?.user?.mobile || "-"})`;
break;
default:
typeText = `${item?.toKillHouse?.name} - ${item?.toKillHouse?.killHouseOperator?.user?.fullname} (${item?.toKillHouse?.killHouseOperator?.user?.mobile})`;
break;
}
return typeText;
};
const getSellerName = (item) => {
let type = "";
switch (item?.allocationType) {
case "steward_guild":
case "steward_steward":
type = `${!item?.stewards ? "-" : item?.stewards?.user?.fullname} (${
item?.stewards?.user?.mobile
})`;
break;
case "killhouse_steward":
type = `${!item?.killHouse ? "-" : item?.killHouse?.name} (${
item?.killHouse?.killHouseOperator?.user?.fullname
} - ${item?.killHouse?.killHouseOperator?.user?.mobile})`;
break;
case "killhouse_guild":
type = `${!item?.killHouse ? "-" : item?.killHouse?.name} (${
item?.killHouse?.killHouseOperator?.user?.fullname
} - ${item?.killHouse?.killHouseOperator?.user?.mobile})`;
break;
default:
type = `${!item?.steward ? "-" : item?.steward?.user?.fullname} (${
item?.steward?.user?.mobile
})`;
break;
}
return type;
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const getLastItem = () => {
if (!type || type === "not_entered") {
return ["عملیات"];
} else {
return [];
}
};
const getRegCodeItemData = (item) => {
if (type === "not_entered") {
return [];
} else {
return [
item?.loggedRegistrationCode ? item.loggedRegistrationCode : "-",
item?.registrationCode ? "ارسال شده" : "ارسال نشده",
];
}
};
const getRegCodeItemColumns = () => {
if (type === "not_entered") {
return [];
} else {
return ["کداحراز", "وضعیت کد احراز"];
}
};
const getAprovedItemData = (item) => {
if (!type) {
return [item?.receiverRealWeightOfCarcasses?.toLocaleString()];
} else if (type === "not_entered") {
return [];
} else {
return [
item?.receiverRealNumberOfCarcasses?.toLocaleString(),
item?.receiverRealWeightOfCarcasses?.toLocaleString(),
];
}
};
const getAprovedItemColumns = () => {
if (!type) {
return ["وزن تایید شده"];
} else if (type === "not_entered") {
return [];
} else {
return ["حجم تایید شده", "وزن تایید شده"];
}
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.date ? formatTime(item?.date) : "-",
item?.productionDate ? formatJustDate(item?.productionDate) : "-",
item?.distributionType === "web"
? "سایت"
: item?.distributionType === "app"
? "موبایل"
: item?.distributionType === "pos"
? "پوز"
: item?.distributionType || "-",
getAllocationType(item),
getAllocationData(item),
getSellerName(item),
item?.sellType === "exclusive" ? "اختصاصی" : "آزاد",
item?.amount?.toLocaleString() + " ریال",
item?.totalAmount?.toLocaleString() + " ریال",
item?.realWeightOfCarcasses?.toLocaleString(),
...getAprovedItemData(item),
...getRegCodeItemData(item),
item?.weightLossOfCarcasses?.toLocaleString(),
item?.quota === "governmental"
? "دولتی"
: item?.quota === "free"
? "آزاد"
: "-",
item?.approvedPriceStatus ? "دولتی" : "آزاد",
item?.receiverState === "accepted"
? "تایید شده"
: item?.receiverState === "rejected"
? "رد شده"
: "در انتظار تایید",
<StewardShowAllocationsOperations
key={i}
item={item}
updateTable={updateTable}
handleUpdate={handleUpdate}
type={type}
priceInfo={priceInfo}
remainWeight={remainWeight}
/>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, selectedDate1, selectedDate2, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`steward-allocation/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}${
type !== "not_entered"
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ""
}&page=${page}&page_size=${perPage}${type ? "&type=" + type : ""}`
);
setData(response.data.results);
setTotalRows(response.data.count);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
dispatch(LOADING_END());
}
};
return (
<Grid container justifyContent="start" alignItems="center" xs={12}>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
{type !== "not_entered" && (
<>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
style={{ width: "160px" }}
{...params}
size="small"
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
style={{ width: "160px" }}
{...params}
size="small"
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</>
)}
<Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
{!(type === "entered" || type === "not_entered") && (
<Button
variant="outlined"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت نهایی",
content: (
<Grid container gap={SPACING.SMALL}>
<Typography>
در صورت ثبت نهایی انجام هیچگونه عملیاتی مانند حذف و
ویرایش امکان پذیر نمی باشد.
</Typography>
<Grid
container
direction="column"
gap={SPACING.TINY}
width="100%"
>
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(
slaughterInventoryFinalSubmitService({
steward_allocation_list: data.map(
(item) => item.key
),
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
handleUpdate?.();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
تایید نهایی (یکجا)
</Button>
)}
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ ثبت",
"تاریخ تولید گوشت",
"ثبت شده",
"نوع تخصیص",
"مشخصات خریدار",
"مشخصات فروشنده",
"نوع فروش",
"قیمت هر کیلو",
"قیمت کل",
"وزن تخصیصی",
...getAprovedItemColumns(),
...getRegCodeItemColumns(),
"افت وزن(کیلوگرم)",
"سهمیه",
"نوع فروش",
"وضعیت",
...getLastItem(),
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={
type === "entered"
? "وارد شده به انبار"
: type === "not_entered"
? "در انتظار ورود"
: "تخصیصات صورت گرفته"
}
/>
</Grid>
);
}
);
StewardShowAllocations.displayName = "StewardShowAllocations";

View File

@@ -0,0 +1,243 @@
import React, { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
Button,
FormControlLabel,
IconButton,
Radio,
RadioGroup,
TextField,
Tooltip,
} from "@mui/material";
import SystemUpdateAltIcon from "@mui/icons-material/SystemUpdateAlt";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import axios from "axios";
import { useLocation } from "react-router-dom";
import { ROUTE_STEWARD_INVENTORY } from "../../../../routes/routes";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardShowProducts = () => {
const { distributionInfo, stewardProducts } = useSelector(
(state) => state.stewardSlice
);
const [productsTable, setProductsTable] = useState();
const { pathname } = useLocation();
const dispatch = useDispatch();
const getWeight = (item) => {
if (getRoleFromUrl() === "KillHouse") {
return [
item?.totalGovernmentalCarcassesWeight?.toLocaleString(),
item?.provinceFreeCarcassesWeight?.toLocaleString(),
];
} else {
return [
item?.receiveGovernmentalCarcassesWeight?.toLocaleString(),
item?.receiveFreeCarcassesWeight?.toLocaleString(),
];
}
};
useEffect(() => {
const d = stewardProducts?.map((item, i) => {
return [
i + 1,
item?.name,
...getWeight(item),
item?.freeBuyingCarcassesWeight?.toLocaleString(),
item?.totalCarcassesWeight?.toLocaleString(),
item?.realAllocatedWeight?.toLocaleString(),
item?.totalRemainWeight?.toLocaleString(),
distributionInfo?.totalGovernmentalRemainWeight?.toLocaleString(),
distributionInfo?.totalFreeRemainWeight?.toLocaleString(),
distributionInfo?.totalGovernmentalInputWeight?.toLocaleString(),
distributionInfo?.totalFreeInputWeight?.toLocaleString(),
];
});
setProductsTable(d);
}, [stewardProducts, distributionInfo]);
const getDistributionKeys = () => {
if (pathname === ROUTE_STEWARD_INVENTORY) {
return [];
} else {
return [
"مانده دولتی (کیلوگرم)",
" مانده آزاد (کیلوگرم)",
"وزن دولتی (کیلوگرم)",
"وزن آزاد (کیلوگرم)",
];
}
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<ResponsiveTable
noPagination={productsTable?.length === 1}
paginated={!productsTable?.length === 1}
operation={
<Grid container>
{getRoleFromUrl() === "KillHouse" && (
<Tooltip title="خروجی اکسل" placement="top">
<IconButton
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "دریافت خروجی اکسل",
content: <HandleDownloadInventoryData />,
})
);
}}
>
<SystemUpdateAltIcon />
</IconButton>
</Tooltip>
)}
</Grid>
}
title={"موجودی انبار"}
columns={[
"ردیف",
"نام محصول",
"وزن خریدهای دولتی داخل استان (کیلوگرم)",
"وزن خریدهای آزاد داخل استان (کیلوگرم)",
"وزن خریدهای خارج استان (کیلوگرم)",
"کل ورودی به انبار (کیلوگرم)",
"کل فروش (کیلوگرم)",
"مانده انبار (کیلوگرم)",
...getDistributionKeys(),
]}
data={productsTable}
customColors={[
{ name: "ردیف", color: "red" },
{ name: "نام محصول", color: "red" },
{ name: "کل ورودی به انبار (کیلوگرم)", color: "red" },
{ name: "وزن خریدهای دولتی داخل استان (کیلوگرم)", color: "red" },
{ name: "وزن خریدهای آزاد داخل استان (کیلوگرم)", color: "red" },
{ name: "وزن خریدهای خارج استان (کیلوگرم)", color: "red" },
{ name: "کل فروش (کیلوگرم)", color: "red" },
{ name: "مانده انبار (کیلوگرم)", color: "green" },
]}
/>
</Grid>
);
};
const HandleDownloadInventoryData = () => {
const [downloadType, setDownloadType] = useState("withdate");
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleChange = (event) => {
setDownloadType(event.target.value);
};
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const [selectedDate2, setSelectedDate2] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<RadioGroup
row
aria-labelledby="radio-group-label"
name="radio-buttons-group"
value={downloadType}
onChange={handleChange}
>
<FormControlLabel
value="withdate"
control={<Radio />}
label="دانلود بر اساس بازه"
/>
<FormControlLabel
value="nodate"
control={<Radio />}
label="دانلود کلی"
/>
</RadioGroup>
{downloadType === "withdate" && (
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
gap={2}
>
<Grid>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
)}
<Button
fullWidth
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}kill_house_inventory_data/?role=${getRoleFromUrl()}${
checkPathStartsWith("steward")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}${
downloadType === "withdate"
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ``
}`;
window.location.href = link;
}}
>
دانلود فایل اکسل
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,116 @@
import React, { useEffect, useState, useRef } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
Accordion,
AccordionSummary,
AccordionDetails,
Typography,
} from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import { stewardGetBarsInfo } from "../../services/steward-get-bars-info";
import { Grid } from "../../../../components/grid/Grid";
import { StewardShowProducts } from "../steward-show-products/StewardShowProducts";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { StewardShowAllocations } from "../steward-show-allocations/StewardShowAllocations";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const StewardStock = () => {
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [barsInfo, setBarsInfo] = useState([]);
const notEnteredRef = useRef();
const enteredRef = useRef();
const handleUpdate = () => {
dispatch(
stewardGetBarsInfo({
role_key: checkPathStartsWith("steward") ? selectedSubUser?.key : "",
})
).then((r) => {
setBarsInfo(r.payload.data);
});
if (notEnteredRef.current) {
notEnteredRef.current.updateTable();
}
if (enteredRef.current) {
enteredRef.current.updateTable();
}
};
useEffect(() => {
handleUpdate();
}, [dispatch]);
return (
<Grid container xs={12} justifyContent="end" alignItems="center">
<Grid container width="100%" isDashboard>
<StewardShowProducts />
</Grid>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
mt={2}
>
<Grid container justifyContent="start" alignItems="center" xs={12}>
<ResponsiveTable
title="اطلاعات کلی بارها"
noPagination
data={[
[
barsInfo?.totalBars?.toLocaleString(),
barsInfo?.totalBarsWeight?.toLocaleString(),
barsInfo?.totalEnteredBars?.toLocaleString(),
barsInfo?.totalEnteredBarsWeight?.toLocaleString(),
barsInfo?.totalNotEnteredBars?.toLocaleString(),
barsInfo?.totalNotEnteredKillHouseRequestsWeight?.toLocaleString(),
barsInfo?.totalRejectedBars?.toLocaleString(),
barsInfo?.totalRejectedBarsWeight?.toLocaleString(),
],
]}
columns={[
"تعداد کل بارها",
"وزن کل بارها (کیلوگرم)",
"تعداد کل بارهای وارد شده",
"وزن کل بار وارد شده (کیلوگرم)",
"تعداد کل بارهای وارد نشده",
"وزن کل بار وارد نشده (کیلوگرم)",
"تعداد کل بارهای رد شده",
"وزن کل بارهای رد شده",
]}
allColors={{ color: "#f3bda3", text: "#332a3d" }}
/>
</Grid>
<Grid container xs={12} mt={4}>
<StewardShowAllocations
ref={notEnteredRef}
type="not_entered"
handleUpdate={handleUpdate}
/>
</Grid>
<Accordion sx={{ width: "100%", mt: 4 }}>
<AccordionSummary expandIcon={<ExpandMoreIcon />}>
<Typography>بارهای وارد شده</Typography>
</AccordionSummary>
<AccordionDetails>
<Grid container xs={12}>
<StewardShowAllocations
ref={enteredRef}
type="entered"
handleUpdate={handleUpdate}
/>
</Grid>
</AccordionDetails>
</Accordion>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,369 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useFormik } from "formik";
import moment from "moment";
import { Autocomplete, Button, InputAdornment, TextField } from "@mui/material";
import { Yup } from "../../../../lib/yup/yup";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterGetProductsService } from "../../../slaughter-house/services/slaughter-inventory-gets";
import {
slaughterGetCitiesService,
slaughterGetProvinceService,
} from "../../../slaughter-house/services/slaughter-get-provinces";
import { fixBase64 } from "../../../../utils/toBase64";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import {
stewardEditFreeBarService,
stewardSubmitFreeBarService,
} from "../../../guild/services/steward-submit-free-bar-service";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const ValidationSchema = Yup.object().shape({
kill_house_name: Yup.string().required("نام فروشنده الزامی است"),
kill_house_mobile: Yup.string()
.required("شماره موبایل فروشنده الزامی است")
.min(11, "شماره موبایل باید دقیقاً 11 رقم باشد")
.max(11, "شماره موبایل باید دقیقاً 11 رقم باشد"),
province: Yup.string().required("استان الزامی است"),
city: Yup.string().required("شهر الزامی است"),
bar_image: Yup.string().when("$isEdit", {
is: true,
then: Yup.string(),
otherwise: Yup.string().required("عکس بار الزامی است"),
}),
number_of_carcasses: Yup.number()
.required("حجم لاشه الزامی است")
.min(1, "حجم لاشه باید بیشتر از 0 باشد"),
weight_of_carcasses: Yup.number()
.required("وزن لاشه الزامی است")
.min(0.01, "وزن باید بیشتر از 0 باشد"),
product_key: Yup.string().required("انتخاب محصول الزامی است"),
});
export const StewardSubmitFreeBar = ({ updateTable, item }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [productData, setProductData] = useState([]);
const [provinceData, setProvinceData] = useState([]);
const [cityData, setCityData] = useState([]);
const [profileImages, setProfileImages] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
dispatch(
slaughterGetProductsService({
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
// Handle both direct array response and nested data response
const data = r.payload?.data;
if (Array.isArray(data)) {
setProductData(data);
} else if (data?.data && Array.isArray(data.data)) {
setProductData(data.data);
} else {
setProductData([]);
}
});
dispatch(slaughterGetProvinceService()).then((r) => {
setProvinceData(r.payload.data);
});
}, [dispatch, selectedSubUser]);
const formik = useFormik({
initialValues: {
product_key: item?.productKey || "",
kill_house_name: item?.killHouseName || "",
kill_house_mobile: item?.killHouseMobile || "",
province: item?.province || "",
city: item?.city || "",
bar_image: item?.barImage || "",
number_of_carcasses: item?.numberOfCarcasses || "",
weight_of_carcasses: item?.weightOfCarcasses || "",
date: item?.date || moment(new Date()).format("YYYY-MM-DD HH:mm:ss"),
role_key: checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
...(item?.key && { key: item.key }),
},
validationSchema: ValidationSchema,
onSubmit: (values) => {
if (item?.key) {
if (profileImages.length && profileImages[0]?.data_url) {
values.bar_image = fixBase64(profileImages[0]?.data_url);
} else {
values.bar_image = item.barImage;
}
}
const thenCallback = (r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
updateTable();
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: item?.key
? "اطلاعات خرید با موفقیت ویرایش شد"
: "اطلاعات خرید جدید با موفقیت ثبت شد",
severity: "success",
});
}
};
if (item?.key) {
dispatch(stewardEditFreeBarService(values)).then(thenCallback);
} else {
dispatch(stewardSubmitFreeBarService(values)).then(thenCallback);
}
},
});
const factorPaymentHandler = (imageList) => {
if (imageList[0]) {
formik.setFieldValue("bar_image", fixBase64(imageList[0]?.data_url));
}
setProfileImages(imageList);
};
useEffect(() => {
if (formik.values.province) {
dispatch(slaughterGetCitiesService(formik.values.province)).then((r) => {
setCityData(r.payload.data);
});
}
}, [formik.values.province]);
useEffect(() => {
if (item?.barImage) {
setProfileImages([{ data_url: item?.barImage }]);
}
}, [item]);
return (
<Grid container direction="column" justifyContent="center" gap={2}>
<Grid container direction="column" justifyContent="center" gap={2}>
<Grid xs={12} container gap={2}>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="product_key"
options={
Array.isArray(productData) && productData.length > 0
? productData.map((i) => ({
id: i.key,
label: i.name || "",
}))
: []
}
value={
Array.isArray(productData) && productData.length > 0
? productData
.map((i) => ({ id: i.key, label: i.name || "" }))
.find((opt) => opt.id === formik.values.product_key) || null
: null
}
onChange={(event, value) => {
formik.setFieldValue("product_key", value ? value.id : "");
}}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب محصول"
error={
formik.touched.product_key &&
Boolean(formik.errors.product_key)
}
helperText={
formik.touched.product_key && formik.errors.product_key
}
/>
)}
/>
</Grid>
<form
onSubmit={formik.handleSubmit}
style={{
display: "flex",
flexDirection: "column",
gap: SPACING.LARGE,
}}
>
<TextField
id="kill_house_name"
name="kill_house_name"
label="نام فروشنده"
value={formik.values.kill_house_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.kill_house_name &&
Boolean(formik.errors.kill_house_name)
}
helperText={
formik.touched.kill_house_name && formik.errors.kill_house_name
}
/>
<TextField
id="kill_house_mobile"
name="kill_house_mobile"
label="تلفن فروشنده"
value={formik.values.kill_house_mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.kill_house_mobile &&
Boolean(formik.errors.kill_house_mobile)
}
helperText={
formik.touched.kill_house_mobile &&
formik.errors.kill_house_mobile
}
/>
<Autocomplete
style={{ width: "100%" }}
disablePortal
id="province"
options={provinceData.map((i) => ({
id: i.name,
label: i.name,
}))}
value={
formik.values.province
? {
id: formik.values.province,
label: formik.values.province,
}
: null
}
onChange={(e, value) => {
formik.setFieldValue("province", value ? value.id : "");
formik.setFieldValue("city", "");
}}
renderInput={(params) => (
<TextField
{...params}
label="استان را انتخاب کنید"
error={
formik.touched.province && Boolean(formik.errors.province)
}
helperText={formik.touched.province && formik.errors.province}
/>
)}
/>
<Autocomplete
minWidth={210}
style={{ width: "100%" }}
disabled={!formik.values.province}
disablePortal
id="city"
value={
formik.values.city
? {
id: formik.values.city,
label: formik.values.city,
}
: null
}
options={
cityData
? cityData.map((i) => ({ id: i.name, label: i.name }))
: []
}
onChange={(e, value) => {
formik.setFieldValue("city", value ? value.id : "");
formik.setFieldValue("city", value ? value.id : "");
}}
renderInput={(params) => (
<TextField {...params} label="شهر را انتخاب کنید" />
)}
/>
<TextField
id="number_of_carcasses"
name="number_of_carcasses"
label="حجم لاشه"
type="number"
InputProps={{
endAdornment: (
<InputAdornment position="end">قطعه</InputAdornment>
),
}}
value={formik.values.number_of_carcasses}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.number_of_carcasses &&
Boolean(formik.errors.number_of_carcasses)
}
helperText={
formik.touched.number_of_carcasses &&
formik.errors.number_of_carcasses
}
/>
<TextField
id="weight_of_carcasses"
name="weight_of_carcasses"
label="وزن لاشه"
type="number"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
value={formik.values.weight_of_carcasses}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.weight_of_carcasses &&
Boolean(formik.errors.weight_of_carcasses)
}
helperText={
formik.touched.weight_of_carcasses &&
formik.errors.weight_of_carcasses
}
/>
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"تصویر بار"}
/>
{!profileImages.length && item?.barImage && (
<Grid container justifyContent="center">
<img style={{ width: "60px" }} alt="bar" src={item?.barImage} />
</Grid>
)}
<Button
type="submit"
fullWidth
variant="contained"
color="primary"
disabled={!formik.isValid}
>
{item?.key ? "ویرایش خرید" : "ثبت خرید جدید"}
</Button>
</form>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,37 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const fetchStewardBroadcastAndProducts = createAsyncThunk(
"STEWARD_FETCH_BROADCAST_AND_PRODUCTS",
async (d, { dispatch }) => {
try {
dispatch(LOADING_START());
const [broadcastResponse, productsResponse] = await Promise.all([
axios.get("steward-sales-info-dashboard/", {
params: {
role: getRoleFromUrl(),
role_key: d.role_key || "",
},
}),
axios.get("roles-products", {
params: {
role: getRoleFromUrl(),
role_key: d.role_key || "",
},
}),
]);
dispatch(LOADING_END());
return {
broadcastData: broadcastResponse.data,
productsData: productsResponse.data,
};
} catch (error) {
dispatch(LOADING_END());
throw error;
}
}
);

View File

@@ -0,0 +1,19 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
export const stewardGetBarsInfo = createAsyncThunk(
"STEWARD_GET_BARS_INFO",
async (d, { dispatch }) => {
dispatch(LOADING_START());
const { data, status } = await axios.get("bars_for_kill_house_dashboard/", {
params: {
role: getRoleFromUrl(),
...d,
},
});
dispatch(LOADING_END());
return { data, status };
}
);

View File

@@ -0,0 +1,15 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import { getRoleFromUrl } from "../../../utils/getRoleFromUrl";
import axios from "axios";
export const stewardGetVBroadcastInfo = createAsyncThunk(
"STEWARD_GET_BROARDCAST_INFO",
async (d, { dispatch }) => {
const { data, status } = await axios.get("steward-sales-info-dashboard/", {
params: {
role: getRoleFromUrl(),
},
});
return { data, status };
}
);

View File

@@ -0,0 +1,21 @@
import { createAsyncThunk } from "@reduxjs/toolkit";
import axios from "axios";
import { LOADING_END, LOADING_START } from "../../../lib/redux/slices/appSlice";
export const stewardResendOutProvinceRegistrationCodeService = createAsyncThunk(
"STEWARD_RESEND_OUT_PROVINCE_REGISTRATION_CODE",
async (d, { dispatch }) => {
dispatch(LOADING_START());
try {
const { data, status } = await axios.post(
"send_again_sms_steward_free_sale_bar/",
d
);
dispatch(LOADING_END());
return { data, status };
} catch (e) {
dispatch(LOADING_END());
return { error: e.response?.data?.result || "خطا در ارسال مجدد کد" };
}
}
);