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,144 @@
import { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { fixBase64 } from "../../../../utils/toBase64";
import { Typography } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { killHouseAssignmentInformationAggregateLoadService } from "../../services/killhouse-assignment-information-aggregate-load";
import { useDispatch, useSelector } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { resizeImage } from "../../../../utils/resizeImage";
export const AggregateUploadDoc = ({ isSingular, item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [profileImages, setProfileImages] = useState([]);
const [profileImg, setProfileImg] = useState();
const { slaughterGetAggregateLoadInformation } = useSelector(
(state) => state.slaughterSlice
);
const factorPaymentHandler = (imageList, addUpdateIndex) => {
if (imageList[0]) {
const file = imageList[0]?.file;
resizeImage(file, (resizedDataUrl) => {
const optimizedBase64 = fixBase64(resizedDataUrl);
setProfileImg(optimizedBase64);
});
}
setProfileImages(imageList);
};
useEffect(() => {
if (profileImg) {
if (isSingular) {
dispatch(
killHouseAssignmentInformationAggregateLoadService({
image: profileImg,
bar_key: item.key,
role: getRoleFromUrl(),
})
).then((r) => {
setProfileImg(null);
setProfileImages([]);
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
if (
item?.assingmentInformation?.realQuantity &&
item?.assingmentInformation?.carWeightWithLoadImage
) {
dispatch(
OPEN_MODAL({
title: "بار به تب مدیریت بارها منتقل شد.",
})
);
}
updateTable(1);
}
});
} else {
dispatch(
killHouseAssignmentInformationAggregateLoadService({
image: profileImg,
bar_keys: slaughterGetAggregateLoadInformation?.map(
(item) => item.key
),
role: getRoleFromUrl(),
})
).then((r) => {
setProfileImg(null);
setProfileImages([]);
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
updateTable(1);
if (
item?.assingmentInformation?.realQuantity &&
item?.assingmentInformation?.carWeightWithLoadImage
) {
dispatch(
OPEN_MODAL({
title: "بار به تب مدیریت بارها منتقل شد.",
// content: "",
})
);
}
}
});
}
}
}, [profileImg, profileImages]);
return (
<Grid width="100%">
{!isSingular && (
<Grid container gap={SPACING.TINY} mb={SPACING.SMALL}>
<Typography color={"error"}>نکته:</Typography>
<Typography>
این سند برای تمامی بارهایی که فاقد سند هستند ثبت خواهد شد.
</Typography>
</Grid>
)}
<Grid mt={SPACING.SMALL}>
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
showImages={false}
title={
item?.assingmentInformation?.carWeightWithLoadImage
? "ویرایش"
: "ثبت"
}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,125 @@
import { useContext, useEffect, useState } from "react";
import { verificationDirectBuyingCode } from "../../services/edit-verification-direct-buying-code";
import EditIcon from "@mui/icons-material/Edit";
import { AppContext } from "../../../../contexts/AppContext";
import { useDispatch } from "react-redux";
import {
Grid,
IconButton,
InputAdornment,
TextField,
Typography,
} from "@mui/material";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
function EditVerificationDirectBuy({
inputDirectBuyingCode,
kill_request_key,
updateTable,
}) {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [isEditMode, setIsEditMode] = useState(false);
const formik = useFormik({
initialValues: {
input_direct_buying_code: inputDirectBuyingCode,
},
validationSchema: Yup.object({
input_direct_buying_code: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!")
.matches(/^[0-9]+$/, "فقط عدد وارد کنید"),
// .min(5, "کد باید حداقل ۵ کاراکتر باشد")
// .max(20, "کد نمی‌تواند بیشتر از ۲۰ کاراکتر باشد"),
}),
onSubmit: (values) => {
dispatch(
verificationDirectBuyingCode({
kill_request_key: kill_request_key,
input_direct_buying_code: values.input_direct_buying_code,
role: "KillHouse",
})
).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",
});
updateTable();
}
setIsEditMode(false);
});
},
});
useEffect(() => {
formik.setValues({ input_direct_buying_code: inputDirectBuyingCode });
setIsEditMode(false);
}, [inputDirectBuyingCode]);
return isEditMode ? (
<Grid container minWidth="130px">
<form onSubmit={formik.handleSubmit}>
<TextField
variant="standard"
name="input_direct_buying_code"
value={formik.values.input_direct_buying_code}
size="small"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.input_direct_buying_code &&
Boolean(formik.errors.input_direct_buying_code)
}
helperText={
formik.touched.input_direct_buying_code &&
formik.errors.input_direct_buying_code
}
InputProps={{
endAdornment: (
<InputAdornment position="end">
<Typography
style={{ cursor: "pointer" }}
color="primary"
fontWeight="bold"
variant="caption"
onClick={formik.handleSubmit}
>
ثبت
</Typography>
</InputAdornment>
),
}}
/>
</form>
</Grid>
) : (
<Grid container alignItems="center">
<Typography variant="caption">{inputDirectBuyingCode}</Typography>
<IconButton
aria-label="delete"
size="small"
color="primary"
onClick={() => {
setIsEditMode(!isEditMode);
}}
>
<EditIcon />
</IconButton>
</Grid>
);
}
export default EditVerificationDirectBuy;

View File

@@ -0,0 +1,732 @@
import {
Button,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import axios from "axios";
import { useContext, useEffect, useState, useMemo, useCallback } from "react";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { format } from "date-fns-jalali";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { AggregateUploadDoc } from "../aggregate-upload-doc/aggregate-upload-doc";
import ShowImage from "../../../../components/show-image/ShowImage";
import { useFormik } from "formik";
import { salughterAggregateQuantityService } from "../../services/salughter-aggregate-quantity";
import { useDispatch, useSelector } from "react-redux";
import {
CLOSE_MODAL,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { VetFarmEditTrafficCode } from "../../../vet-farm/components/vet-farm-edit-traffic-code/VetFarmEditTrafficCode";
import { RiFileExcel2Fill } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { slaughterEnterLoadInformationGetDashboard } from "../../services/slaughter-enter-load-information-get-dashboard";
import { RiSearchLine } from "react-icons/ri";
import EditIcon from "@mui/icons-material/Edit";
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
import { provincePolicyGetWeightRange } from "../../../province/services/province-policy-get-weight-range";
import { isValidIndexWeight } from "../../../../utils/isValidIndexWeight";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
// Constants
const ROLES = {
PROVINCE_OPERATOR: "ProvinceOperator",
SUPER_ADMIN: "SuperAdmin",
ADMIN_X: "AdminX",
SUPPORTER: "Supporter",
VET_SUPERVISOR: "VetSupervisor",
VET_FARM: "VetFarm",
CITY_VET: "CityVet",
};
const KILL_TYPES = {
FREEZING: "انجماد",
EXPORT: "صادرات",
NORMAL: "عادی",
};
const DEFAULT_PER_PAGE = 10;
const DEFAULT_PAGE = 1;
// Helper functions
const formatDate = (date) => {
if (!date) return "-";
return format(new Date(date), "yyyy/MM/dd");
};
const formatCurrency = (value) => {
return value ? `${value.toLocaleString()}` : "-";
};
const formatNumber = (value) => {
return value ? value.toLocaleString() : "-";
};
const formatUserInfo = (name, mobile) => {
return name && mobile ? `${name} (${mobile})` : "-";
};
const getKillType = (item) => {
if (item?.poultryRequest?.freezing) return KILL_TYPES.FREEZING;
if (item?.poultryRequest?.export) return KILL_TYPES.EXPORT;
return KILL_TYPES.NORMAL;
};
const buildApiUrl = (params) => {
const { textValue, role, date1, date2, page, perPage, roleKey } = params;
const baseUrl = "kill_house_request_aggregate_load/";
const queryParams = new URLSearchParams({
check: "",
search: "filter",
value: textValue || "",
role: role || "",
date1: date1 || "",
date2: date2 || "",
page: page || DEFAULT_PAGE,
page_size: perPage || DEFAULT_PER_PAGE,
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseUrl}?${queryParams.toString()}`;
};
const buildExcelUrl = (params) => {
const { baseURL, date1, date2, role, roleKey, userKey, textValue } = params;
const queryParams = new URLSearchParams({
start: date1 || "",
end: date2 || "",
role: role || "",
state: "bar_pending",
key: userKey || "",
search: "filter",
value: textValue || "",
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseURL}bar_excel/?${queryParams.toString()}`;
};
const isTrafficCodeEditable = (role, item) => {
const adminRoles = [
ROLES.PROVINCE_OPERATOR,
ROLES.SUPER_ADMIN,
ROLES.ADMIN_X,
ROLES.SUPPORTER,
ROLES.VET_SUPERVISOR,
];
if (adminRoles.includes(role)) {
return true;
}
const vetRoles = [ROLES.VET_FARM, ROLES.CITY_VET];
return (
item.trash !== true &&
item.assignmentStateArchive === "pending" &&
!item?.clearanceCode &&
vetRoles.includes(role)
);
};
export const EnterAggregateLoadInformation = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
// Redux
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const dispatch = useDispatch();
// State
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(DEFAULT_PER_PAGE);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(DEFAULT_PAGE);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
// Memoized values
const currentRole = useMemo(() => getRoleFromUrl(), []);
const roleKey = useMemo(
() => (checkPathStartsWith("slaughter") ? selectedSubUser?.key || "" : ""),
[selectedSubUser?.key]
);
// Initialize dates
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, [setSelectedDate1, setSelectedDate2]);
// Fetch API data
const fetchApiData = useCallback(
async (pageNumber = page) => {
dispatch(LOADING_START());
try {
const url = buildApiUrl({
textValue,
role: currentRole,
date1: selectedDate1,
date2: selectedDate2,
page: pageNumber,
perPage,
roleKey,
});
const response = await axios.get(url);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
},
[
textValue,
currentRole,
selectedDate1,
selectedDate2,
perPage,
roleKey,
page,
dispatch,
]
);
// Fetch dashboard data
const fetchDashboardData = useCallback(() => {
dispatch(
slaughterEnterLoadInformationGetDashboard({
selectedDate1,
selectedDate2,
text: textValue,
role_key: roleKey,
})
).then((r) => {
setDashboardData(r.payload.data);
});
}, [selectedDate1, selectedDate2, textValue, roleKey, dispatch]);
// Handlers
const handlePageChange = (newPage) => {
fetchApiData(newPage);
setPage(newPage);
};
const handlePerRowsChange = (newPerRows) => {
setPerPage(newPerRows);
setPage(DEFAULT_PAGE);
};
const handleSubmit = async (event) => {
event.preventDefault();
await fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleDateChange1 = (date) => {
if (date) {
setSelectedDate1(moment(date).format("YYYY-MM-DD"));
}
};
const handleDateChange2 = (date) => {
if (date) {
setSelectedDate2(moment(date).format("YYYY-MM-DD"));
}
};
const handleOpenModal = useCallback(
(item) => {
dispatch(
OPEN_MODAL({
title: "ثبت اطلاعات بار",
content: <Operation item={item} fetchApiData={fetchApiData} />,
})
);
},
[dispatch, fetchApiData]
);
// Initial data fetch
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Refetch when filters change
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate1, selectedDate2, perPage, roleKey]);
// Transform data to table format
useEffect(() => {
const transformedData = data?.map((item, i) => {
const rowNumber =
page === DEFAULT_PAGE ? i + 1 : i + perPage * (page - 1) + 1;
const hasAssignmentInfo = !!item?.assignmentInfo?.realQuantity;
return [
rowNumber,
hasAssignmentInfo ? (
<Grid container direction="column" key={item.key}>
<Grid>{formatNumber(item?.assignmentInfo?.realQuantity)} قطعه</Grid>
<Grid>{formatNumber(item?.assignmentInfo?.netWeight)} کیلوگرم</Grid>
<Tooltip title="ویرایش اطلاعات بار" placement="top">
<IconButton
color="primary"
onClick={() => handleOpenModal(item)}
size="small"
>
<EditIcon fontSize="small" />
</IconButton>
</Tooltip>
</Grid>
) : (
<Tooltip key={item.key} title="ثبت اطلاعات بار" placement="top">
<IconButton
color="primary"
onClick={() => handleOpenModal(item)}
size="small"
>
<AddCircleOutlineIcon fontSize="small" />
</IconButton>
</Tooltip>
),
item?.assingmentInformation?.carWeightWithLoadImage ? (
<Grid key={item.key}>
<ShowImage
src={item?.assingmentInformation?.carWeightWithLoadImage}
/>
<AggregateUploadDoc
isSingular
item={item}
updateTable={fetchApiData}
/>
</Grid>
) : (
<AggregateUploadDoc
key={item.key}
isSingular
item={item}
updateTable={fetchApiData}
/>
),
<ShowImage
key={`empty-${i}`}
src={item?.assignmentInfo?.imageWithoutBar}
/>,
<ShowImage
key={`full-${i}`}
src={item?.assignmentInfo?.imageWithBar}
/>,
<Typography
key={`barcode-${i}`}
style={{ fontSize: "13px", color: item?.trash ? "red" : "black" }}
>
{item.barCode}
</Typography>,
<VetFarmEditTrafficCode
key={`traffic-${i}`}
updateTable={fetchApiData}
killHouseRequestKey={item.key}
trafficCode={item?.trafficCode}
isEditable={isTrafficCodeEditable(currentRole, item)}
/>,
formatCurrency(item?.amount),
formatDate(item?.poultryRequest?.sendDate),
formatUserInfo(
item.killhouseUser?.name,
item.killhouseUser?.killHouseOperator?.user?.mobile
),
item?.killer
? formatUserInfo(
item.killer?.name,
item.killer?.killHouseOperator?.user?.mobile
)
: "-",
formatUserInfo(
item.poultryRequest?.poultry?.unitName,
item.poultryRequest?.poultry?.user?.mobile
),
item?.poultryRequest?.age || "-",
formatNumber(item.quantity),
formatNumber(item?.weightInfo?.weight),
formatCurrency(item?.poultryRequest?.amount),
formatCurrency(item?.weightInfo?.killHousePrice),
`${item.addCar?.driver?.typeCar || ""} ${
item.addCar?.driver?.pelak || ""
}`.trim() || "-",
formatUserInfo(
item.addCar?.driver?.driverName,
item.addCar?.driver?.driverMobile
),
formatNumber(item.vetAcceptedRealQuantity),
formatNumber(item.vetAcceptedRealWeight),
item?.poultryRequest?.orderCode || "-",
item?.finalBarState || "-",
getKillType(item),
];
});
setTableData(transformedData || []);
}, [data, page, perPage, currentRole, handleOpenModal, fetchApiData]);
// Table columns
const dashboardColumns = [
"تعداد بار",
"مجموع تعداد اولیه",
"مجموع وزن اولیه (کیلوگرم)",
"مجموع تعداد تحویلی دامپزشک",
"مجموع وزن تحویلی دامپزشک (کیلوگرم)",
];
const tableColumns = [
"ردیف",
"تعداد/وزن خالص",
"سند",
"بارنامه خالی",
"بارنامه پر",
"کدبار",
"کد بهداشتی حمل و نقل",
"قیمت مرغ زنده‌ی بار",
"تاریخ کشتار",
"خریدار",
"کشتارکن اختصاصی",
"مرغدار",
"سن مرغ",
"تعداد اولیه",
"وزن اولیه بار (کیلوگرم)",
"قیمت مرغدار",
"قیمت کشتارگاه",
"ماشین",
"راننده",
"تحویلی دامپزشک (قطعه)",
"وزن تحویلی دامپزشک (کیلوگرم)",
"کدسفارش کشتار",
"وضعیت بار",
"نوع کشتار",
];
const dashboardRow = [
formatNumber(dashboardData?.lenKillHouseRequest),
formatNumber(dashboardData?.firstQuantity),
formatNumber(dashboardData?.firstWeight),
formatNumber(dashboardData?.vetAcceptedRealQuantity),
formatNumber(dashboardData?.vetAcceptedRealWeight),
];
const excelUrl = buildExcelUrl({
baseURL: axios.defaults.baseURL,
date1: selectedDate1,
date2: selectedDate2,
role: currentRole,
roleKey,
userKey,
textValue,
});
return (
<Grid container justifyContent="center">
<Grid
container
alignItems="start"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<DatePicker
label="از تاریخ"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={handleDateChange1}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={handleDateChange2}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
value={textValue}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
{!!data?.length && (
<Grid>
<Tooltip title="خروجی اکسل">
<a href={excelUrl} rel="noreferrer">
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
)}
</Grid>
</Grid>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={dashboardColumns}
data={[dashboardRow]}
title="خلاصه اطلاعات"
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={tableColumns}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="وارد کردن اطلاعات بار"
/>
</Grid>
);
};
const Operation = ({ item, fetchApiData }) => {
const [openNotif] = useContext(AppContext);
const { weightRange } = useSelector((state) => state.provinceSlice);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const currentRole = useMemo(() => getRoleFromUrl(), []);
// Fetch weight range on mount
useEffect(() => {
dispatch(
provincePolicyGetWeightRange({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedSubUser?.key]);
// Validation helpers
const validateNumeric = (value) => /^\d+$/.test(value);
const checkVolumeLimits = (quantity) => {
const { maximumLoadVolumeReduction, maximumLoadVolumeIncrease } =
item?.killhouseUser || {};
const acceptedQuantity = item?.acceptedRealQuantity || 0;
if (
maximumLoadVolumeReduction !== 0 &&
quantity < acceptedQuantity * (1 - maximumLoadVolumeReduction / 100)
) {
return {
isValid: false,
message:
"حجم وارد شده با مجوز حداکثر افزایش/کاهش ورود اطلاعات بار مطابقت ندارد!",
};
}
if (
maximumLoadVolumeIncrease !== 0 &&
quantity > acceptedQuantity * (1 + maximumLoadVolumeIncrease / 100)
) {
return {
isValid: false,
message:
"حجم وارد شده با مجوز حداکثر افزایش/کاهش ورود اطلاعات بار مطابقت ندارد!",
};
}
return { isValid: true };
};
const checkWeightValidation = (weight, quantity) => {
const adminRoles = [ROLES.SUPER_ADMIN, ROLES.ADMIN_X];
if (adminRoles.includes(currentRole)) {
return { isValid: true };
}
const averageWeight = parseInt(weight) / parseInt(quantity);
if (
!isValidIndexWeight(weightRange, item?.poultryRequest?.age, averageWeight)
) {
return {
isValid: false,
message:
"میانگین وزنی با احراز سنی مطابقت ندارد. لطفا با اتحادیه تماس بگیرید.",
};
}
return { isValid: true };
};
const formik = useFormik({
initialValues: {
quantity: "",
weight: "",
},
onSubmit: async (values) => {
// Validate volume limits
const volumeCheck = checkVolumeLimits(parseInt(values.quantity));
if (!volumeCheck.isValid) {
openNotif({
vertical: "top",
horizontal: "center",
msg: volumeCheck.message,
severity: "error",
});
return;
}
// Validate weight
const weightCheck = checkWeightValidation(values.weight, values.quantity);
if (!weightCheck.isValid) {
openNotif({
vertical: "top",
horizontal: "center",
msg: weightCheck.message,
severity: "error",
});
return;
}
// Submit data
const result = await dispatch(
salughterAggregateQuantityService({
kill_house_request_key: item.key,
role: currentRole,
net_weight: values.weight,
exploited_carcass: 0,
real_quantity: values.quantity,
})
);
if (result.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: result.payload.error,
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
fetchApiData(DEFAULT_PAGE);
}
},
validate: (values) => {
const errors = {};
if (!validateNumeric(values.weight)) {
errors.weight = "لطفا عدد وارد کنید";
}
if (!validateNumeric(values.quantity)) {
errors.quantity = "لطفا عدد وارد کنید";
}
return errors;
},
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid
container
gap={SPACING.SMALL}
alignItems="center"
justifyContent="center"
>
<TextField
label="وزن خالص (کیلوگرم)"
variant="outlined"
fullWidth
type="text"
name="weight"
id="weight"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.weight}
error={formik.touched.weight && Boolean(formik.errors.weight)}
helperText={formik.touched.weight && formik.errors.weight}
/>
<TextField
label="تعداد واقعی (قطعه)"
variant="outlined"
fullWidth
type="text"
name="quantity"
id="quantity"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.quantity}
error={formik.touched.quantity && Boolean(formik.errors.quantity)}
helperText={formik.touched.quantity && formik.errors.quantity}
/>
<Button type="submit" fullWidth variant="contained">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,79 @@
import React, { useContext } from "react";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { Button, TextField } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterEditFreeBuyService } from "../../services/slaughter-edit-free-buy";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
export const EnterAuthCodeDirectBuy = ({ item, updateTable }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
textFieldValue: "", // Initial value for your text field
},
validationSchema: Yup.object({
textFieldValue: Yup.string().required("این فیلد الزامی است"), // Validation schema for the text field
}),
onSubmit: (values) => {
dispatch(
slaughterEditFreeBuyService({
role: getRoleFromUrl(),
kill_request_key: item.key,
input_direct_buying_code: values.textFieldValue,
})
).then((r) => {
if (r?.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r?.payload?.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
},
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.TINY}>
<TextField
fullWidth
id="textFieldValue"
name="textFieldValue"
label="کداحراز"
variant="outlined"
onChange={formik.handleChange}
onBlur={formik.handleBlur}
value={formik.values.textFieldValue}
error={
formik.touched.textFieldValue &&
Boolean(formik.errors.textFieldValue)
}
helperText={
formik.touched.textFieldValue && formik.errors.textFieldValue
}
/>
<Button fullWidth type="submit" variant="contained" color="primary">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,445 @@
import { useContext, useEffect, useState, useMemo, useCallback } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { slaughterGetPermissionToVetService } from "../../services/slaughter-get-premisson-to-vet";
import { Box, Switch, TextField, Typography, Button } from "@mui/material";
import moment from "moment";
import { slaughterPermissionToVetService } from "../../services/slaughter-premisson-to-vet";
import { SPACING } from "../../../../data/spacing";
import { DatePicker } from "@mui/x-date-pickers";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch, useSelector } from "react-redux";
import { format } from "date-fns-jalali";
import axios from "axios";
import { VetFarmEditTrafficCode } from "../../../vet-farm/components/vet-farm-edit-traffic-code/VetFarmEditTrafficCode";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { slaughterEnterLoadInformationGetDashboard } from "../../services/slaughter-enter-load-information-get-dashboard";
import { Grid } from "../../../../components/grid/Grid";
import { RiSearchLine } from "react-icons/ri";
import { SlaughterEnterInformationOperation } from "../slaughter-enter-information-operation/SlaughterEnterInformationOperation";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
// Constants
const ROLES = {
KILL_HOUSE: "KillHouse",
PROVINCE_OPERATOR: "ProvinceOperator",
SUPER_ADMIN: "SuperAdmin",
VET_SUPERVISOR: "VetSupervisor",
VET_FARM: "VetFarm",
CITY_VET: "CityVet",
};
const DEFAULT_PER_PAGE = 10;
const DEFAULT_PAGE = 1;
// Helper functions
const formatDate = (date) => {
if (!date) return "-";
return format(new Date(date), "yyyy/MM/dd");
};
const formatCurrency = (value) => {
return value ? `${value.toLocaleString()}` : "-";
};
const formatNumber = (value) => {
return value ? value.toLocaleString() : "-";
};
const formatUserInfo = (name, mobile) => {
return name && mobile ? `${name} (${mobile})` : "-";
};
const buildApiUrl = (params) => {
const { textValue, role, date1, date2, page, perPage, roleKey } = params;
const baseUrl = "kill_house_request_complete_information/";
const queryParams = new URLSearchParams({
search: "filter",
value: textValue || "",
role: role || "",
date1: date1 || "",
date2: date2 || "",
page: page || DEFAULT_PAGE,
page_size: perPage || DEFAULT_PER_PAGE,
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseUrl}?${queryParams.toString()}`;
};
const isTrafficCodeEditable = (role, item) => {
const adminRoles = [
ROLES.PROVINCE_OPERATOR,
ROLES.SUPER_ADMIN,
ROLES.VET_SUPERVISOR,
];
if (adminRoles.includes(role)) {
return true;
}
const vetRoles = [ROLES.VET_FARM, ROLES.CITY_VET];
return (
item.trash !== true &&
item.assignmentStateArchive === "pending" &&
!item?.clearanceCode &&
vetRoles.includes(role)
);
};
export const EnterLoadInformation = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
// State
const [checked, setChecked] = useState(false);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(DEFAULT_PER_PAGE);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(DEFAULT_PAGE);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
// Redux
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const dispatch = useDispatch();
// Memoized values
const currentRole = useMemo(() => getRoleFromUrl(), []);
const isKillHouse = useMemo(
() => currentRole === ROLES.KILL_HOUSE,
[currentRole]
);
const roleKey = useMemo(
() => (checkPathStartsWith("slaughter") ? selectedSubUser?.key || "" : ""),
[selectedSubUser?.key]
);
// Initialize dates
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, [setSelectedDate1, setSelectedDate2]);
// Fetch permission for KillHouse role
useEffect(() => {
if (isKillHouse) {
dispatch(
slaughterGetPermissionToVetService({
role_key: roleKey,
})
).then((r) => {
setChecked(r.payload.data?.[0]?.allow || false);
});
}
}, [isKillHouse, selectedSubUser?.key, dispatch]);
// Fetch API data
const fetchApiData = useCallback(
async (pageNumber = page) => {
dispatch(LOADING_START());
try {
const url = buildApiUrl({
textValue,
role: currentRole,
date1: selectedDate1,
date2: selectedDate2,
page: pageNumber,
perPage,
roleKey,
});
const response = await axios.get(url);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
},
[
textValue,
currentRole,
selectedDate1,
selectedDate2,
perPage,
roleKey,
page,
dispatch,
]
);
// Fetch dashboard data
const fetchDashboardData = useCallback(() => {
dispatch(
slaughterEnterLoadInformationGetDashboard({
selectedDate1,
selectedDate2,
text: textValue,
role_key: roleKey,
})
).then((r) => {
setDashboardData(r.payload.data);
});
}, [selectedDate1, selectedDate2, textValue, roleKey, dispatch]);
// Update table data
const updateTable = useCallback(() => {
fetchDashboardData();
fetchApiData(DEFAULT_PAGE);
setPage(DEFAULT_PAGE);
}, [fetchDashboardData, fetchApiData]);
// Transform data to table format
useEffect(() => {
const transformedData = data?.map((item, i) => [
<SlaughterEnterInformationOperation
key={i}
item={item}
updateTable={updateTable}
/>,
<Typography
key={i}
style={{ fontSize: "13px", color: item?.trash ? "red" : "black" }}
>
{item.barCode}
</Typography>,
<VetFarmEditTrafficCode
key={i}
updateTable={fetchApiData}
killHouseRequestKey={item.key}
trafficCode={item?.trafficCode}
isEditable={isTrafficCodeEditable(currentRole, item)}
/>,
formatDate(item?.poultryRequest?.sendDate),
formatUserInfo(
item.killhouseUser?.name,
item.killhouseUser?.killHouseOperator?.user?.mobile
),
item?.killer
? formatUserInfo(
item.killer?.name,
item.killer?.killHouseOperator?.user?.mobile
)
: "-",
formatUserInfo(
item.poultryRequest?.poultry?.unitName,
item.poultryRequest?.poultry?.user?.mobile
),
item?.poultryRequest?.age || "-",
formatNumber(item.quantity),
formatNumber(item?.weightInfo?.weight),
`${item.addCar?.driver?.typeCar || ""} ${
item.addCar?.driver?.pelak || ""
}`.trim() || "-",
formatUserInfo(
item.addCar?.driver?.driverName,
item.addCar?.driver?.driverMobile
),
formatCurrency(item?.poultryRequest?.amount),
formatCurrency(item?.weightInfo?.killHousePrice),
formatNumber(item.vetAcceptedRealQuantity),
formatNumber(item.vetAcceptedRealWeight),
item?.poultryRequest?.orderCode || "-",
item?.barDocumentStatus?.title || "-",
item?.finalBarState || "-",
item?.poultryRequest?.freezing ? "انجماد" : "عادی",
]);
setTableData(transformedData || []);
}, [data, currentRole, updateTable, fetchApiData]);
// Initial data fetch
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
}, []);
// Refetch when filters change
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
}, [selectedDate1, selectedDate2, perPage, selectedSubUser?.keys]);
// Handlers
const handlePageChange = (newPage) => {
fetchApiData(newPage);
setPage(newPage);
};
const handlePerRowsChange = (newPerRows) => {
setPerPage(newPerRows);
setPage(DEFAULT_PAGE);
};
const handleSubmit = async (event) => {
event.preventDefault();
await fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleDateChange1 = (date) => {
if (date) {
setSelectedDate1(moment(date).format("YYYY-MM-DD"));
}
};
const handleDateChange2 = (date) => {
if (date) {
setSelectedDate2(moment(date).format("YYYY-MM-DD"));
}
};
const handleSwitchChange = () => {
const newValue = !checked;
dispatch(
slaughterPermissionToVetService({
allow: newValue,
})
);
setChecked(newValue);
};
// Table columns
const dashboardColumns = [
"تعداد بار",
"مجموع تعداد اولیه",
"مجموع وزن اولیه (کیلوگرم)",
"مجموع تعداد تحویلی دامپزشک",
"مجموع وزن تحویلی دامپزشک (کیلوگرم)",
];
const tableColumns = [
"عملیات",
"کدبار",
"کدبهداشتی حمل و نقل",
"تاریخ کشتار",
"خریدار",
"کشتارکن اختصاصی",
"مرغدار",
"سن مرغ",
"تعداد اولیه",
"وزن اولیه بار(کیلوگرم)",
"ماشین",
"راننده",
"قیمت مرغدار",
"قیمت کشتارگاه",
"تحویلی دامپزشک (قطعه)",
"وزن تحویلی دامپزشک(کیلوگرم)",
"کد سفارش کشتار",
"وضعیت سند",
"وضعیت بار",
"نوع کشتار",
];
const dashboardRow = [
formatNumber(dashboardData?.lenKillHouseRequest),
formatNumber(dashboardData?.firstQuantity),
formatNumber(dashboardData?.firstWeight),
formatNumber(dashboardData?.vetAcceptedRealQuantity),
formatNumber(dashboardData?.vetAcceptedRealWeight),
];
return (
<Box width="100%">
{isKillHouse && (
<Grid container alignItems="end" justifyContent="center">
<Grid container alignItems="center" xs={12} justifyContent="end">
<Grid>
<Typography color="gray">
دسترسی دامپزشک برای وارد کردن اطلاعات بار
</Typography>
</Grid>
<Grid>
<Switch
checked={checked}
onChange={handleSwitchChange}
name="switch-state"
inputProps={{ "aria-label": "switch with state" }}
/>
</Grid>
</Grid>
</Grid>
)}
<Grid container alignItems="center" gap={SPACING.SMALL} xs={12}>
<Grid style={{ width: "150px" }}>
<DatePicker
label="از تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={handleDateChange1}
/>
</Grid>
<Grid style={{ width: "150px" }}>
<DatePicker
label="تا تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={handleDateChange2}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
autoComplete="off"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
value={textValue}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
</Grid>
<Grid
container
justifyContent="center"
gap={SPACING.SMALL}
xs={12}
mt={2}
>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
columns={dashboardColumns}
data={[dashboardRow]}
title="خلاصه اطلاعات"
isDashboard
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={tableColumns}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="درخواست های در انتظار وارد کردن اطلاعات بار"
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,63 @@
import { Button, TextField } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { provinceRejectSlaughterFreeBuyService } from "../../services/province-reject-slaughter-free-buy";
export const ProviceRejectFreeBuy = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [text, setText] = useState("");
const handleTextChange = (event) => {
setText(event.target.value);
};
return (
<Grid container direction="column" gap={SPACING.TINY} width="100%">
<TextField
label="دلیل رد درخواست"
variant="outlined"
fullWidth
value={text}
onChange={handleTextChange}
/>
<Button
variant="contained"
color="primary"
onClick={() => {
dispatch(
provinceRejectSlaughterFreeBuyService({
state: "rejected",
kill_request_key: item.key,
direct_buying_message: text,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(CLOSE_MODAL());
}
});
}}
>
ثبت رد درخواست
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,35 @@
import { Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
export const RespGuildExclusiveItem = ({ data }) => {
return (
<Grid>
{data?.map((item, i) => {
return (
<Grid
container
key={i}
alignItems="center"
justifyContent="start"
flexWrap="nowrap"
style={{
backgroundColor:
i % 2 === 0 ? "rgb(251 245 255)" : "rgb(224 192 245)",
padding: "10px",
}}
>
<Grid xs={10}>
<Typography textAlign={"justify"} variant="body1">
{`${i + 1}- ${item[6]} `}
</Typography>
<Typography textAlign={"justify"} variant="body2">
{`با ماهیت ${item[3]} با کدملی ${item[7]} در شهرستان ${item[12]}`}
</Typography>
</Grid>
<Grid xs={2}>{item[16]}</Grid>
</Grid>
);
})}
</Grid>
);
};

View File

@@ -0,0 +1,35 @@
import { Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
export const RespSlaughterGuildListItem = ({ data }) => {
return (
<Grid mt={2}>
{data?.map((item, i) => {
return (
<Grid
container
key={i}
alignItems="center"
justifyContent="start"
flexWrap="nowrap"
style={{
backgroundColor:
i % 2 === 0 ? "rgb(225 225 155)" : "rgb(227 227 128)",
padding: "10px",
}}
>
<Grid xs={10}>
<Typography textAlign={"justify"} variant="body1">
{`${i + 1}- ${item[1]} ${item[3]}`}
</Typography>
<Typography textAlign={"justify"} variant="body2">
{` تلفن ${item[5]} در شهرستان ${item[9]} با میانگین سهم ${item[10]}`}
</Typography>
</Grid>
<Grid xs={2}>{item[11]}</Grid>
</Grid>
);
})}
</Grid>
);
};

View File

@@ -0,0 +1,66 @@
import { IconButton, Tooltip, Typography } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterSubmitRealInventory } from "../slaughter-submit-real-inventory/SlaughterSubmitRealInventory";
import { useDispatch } from "react-redux";
import SettingsIcon from "@mui/icons-material/Settings";
export const RespSlaughterStockBarSystem = ({
data,
inventorySelectedKillHouse,
}) => {
const dispatch = useDispatch();
return (
<Grid>
{data?.map((item, i) => {
return (
<Grid
container
key={i}
alignItems="center"
justifyContent="start"
p={SPACING.SMALL}
flexWrap="nowrap"
>
<Grid>
<Typography textAlign={"justify"} variant="caption">
{`${i + 1}- مرغدار ${item[1]} کشتارگاه ${item[2]} با کدسفارش ${
item[3]
} در شهرستان ${item[4]} با تعداد تخصیصی ${item[12]}`}
</Typography>
{item[17]}
<Tooltip title="ورود بار به انبار" key={i}>
<IconButton
// disabled={
// item?.wareHouseAcceptedRealQuantity ||
// item?.wareHouseAcceptedRealWeight
// }
size={"small"}
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت موجودی واقعی",
content: (
<SlaughterSubmitRealInventory
item={item}
inventorySelectedKillHouse={
inventorySelectedKillHouse
}
/>
),
})
);
}}
>
<SettingsIcon fontSize="small" />
</IconButton>
</Tooltip>
</Grid>
</Grid>
);
})}
</Grid>
);
};

View File

@@ -0,0 +1,153 @@
import { Card, Grid, IconButton, TextField, Typography } from "@mui/material";
import { ROUTE_SLAUGHTER_FILE } from "../../../../routes/routes";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { SPACING } from "../../../../data/spacing";
import { useFormik } from "formik";
import moment from "moment";
import { Yup } from "../../../../lib/yup/yup";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetActiveRequests } from "../../services/slaughter-get-active-requests";
import { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { format } from "date-fns-jalali";
import { AppContext } from "../../../../contexts/AppContext";
import { DatePicker } from "@mui/x-date-pickers";
export const SlaughterActiveRequests = () => {
const navigate = useNavigate();
const [dataTable, setDataTable] = useState([]);
const { slaughterActiveRequests } = useSelector(
(state) => state.slaughterSlice
);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const dispatch = useDispatch();
useEffect(() => {
dispatch(slaughterGetActiveRequests({ selectedDate1, selectedDate2 }));
}, [selectedDate1, selectedDate2]);
useEffect(() => {
const filteredData = slaughterActiveRequests?.filter(
(item, i) => item.provinceKillState !== "rejected"
);
const key = "orderCode";
const arrayUniqueByKey = [
...new Map(filteredData?.map((item) => [item[key], item])).values(),
];
const d = arrayUniqueByKey.map((item, i) => {
return [
i + 1,
item.orderCode,
format(new Date(item?.sendDate), "yyyy/MM/dd"),
item.poultryName,
item.poultryMobile,
item.city,
item.province,
item.age,
item.mainQuantity + " قطعه",
<IconButton
key={i}
aria-label="delete"
color="primary"
onClick={() => navigate(ROUTE_SLAUGHTER_FILE + item.poultryReqId)}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [slaughterActiveRequests]);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"تاریخ درخواست",
"مرغدار",
"تلفن مرغدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"مشاهده",
]);
const formik = useFormik({
initialValues: {
capacity: "",
recieveTime: "",
recieveDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
},
validationSchema: Yup.object({
capacity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
recieveTime: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
>
<Card sx={{ width: "100%" }}>
<AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography>درخواست های فعال</Typography>
</Grid>
<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>
}
columns={tableDataCol}
data={dataTable}
/>
</Card>
</Grid>
);
};

View File

@@ -0,0 +1,120 @@
import { useContext, useEffect, useState } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { useDispatch, useSelector } from "react-redux";
import { Autocomplete, Button, Chip, TextField } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import {
slaughterAddDailyListService,
slaughterGetGuildsService,
} from "../../services/slaughter-add-daily-list";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterAddDailyList = ({ updateTable }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const [guildsData, setGuildsData] = useState([]);
const [selectedItems, setSelectedItems] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
const fetchGuilds = async () => {
dispatch(
slaughterGetGuildsService({
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setGuildsData(r.payload.data);
});
};
fetchGuilds();
}, [dispatch]);
const handleSubmit = () => {
dispatch(
slaughterAddDailyListService({
guild_key_list: selectedItems.map((item) => item.key),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "خطا در ثبت لیست",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لیست با موفقیت ثبت شد.",
severity: "success",
});
dispatch(DRAWER({ right: false, bottom: false, content: null }));
updateTable(1);
}
});
};
const handleDelete = (key) => {
setSelectedItems((prev) => prev.filter((item) => item.key !== key));
};
return (
<Grid container direction="column" gap={SPACING.SMALL}>
<Autocomplete
multiple
disablePortal
id="guilds-select"
options={guildsData}
renderTags={() => null}
getOptionLabel={(option) =>
`${option.steward ? "مباشر" : "صنف"} ${
option.name || option.guildsName
}
${option.user?.fullname || ""}
(${option.user?.mobile || ""})`
}
onChange={(event, values) => {
setSelectedItems(values);
}}
sx={{ width: "250px" }}
renderInput={(params) => (
<TextField
{...params}
fullWidth
label="انتخاب مباشر / صنف"
placeholder="انتخاب کنید"
/>
)}
/>
<Grid container direction="column" gap={1}>
{selectedItems.map((item) => (
<Chip
key={item.key}
label={`${item.steward ? "مباشر" : "صنف"} ${
item.name || item.guildsName
}`}
onDelete={() => handleDelete(item.key)}
sx={{ width: "fit-content" }}
/>
))}
</Grid>
<Button
variant="contained"
onClick={handleSubmit}
disabled={selectedItems.length === 0}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,330 @@
import { Autocomplete, Button, InputAdornment, TextField } from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { slaughterGetUpdatedInventoryStock } from "../../services/salughter-get-updated-inventory-stock";
import { slaughterManageInventoryAllocationsService } from "../../services/salughter-manage-inventory-allocations";
import { slaughterAllocateStewardService } from "../../services/slaughter-allocate-steward";
import { slaughterEditAllocateStewardService } from "../../services/slaughter-edit-allocate-steward";
import { slaughterGetGuildsService } from "../../services/slaughter-get-guilds";
import { slaughterGetKillhouseGuildsService } from "../../services/slaughter-get-killhouse-guilds";
import { slaughterGetKillhouseStewardsService } from "../../services/slaughter-get-killhouse-stewards";
import { slaughterGetStewardsService } from "../../services/slaughter-get-stewards";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
export const SlaughterAddSteward = ({
stewardKey,
guildKey,
sellType,
isGuild,
weight,
quantity,
item,
totalAverageWeightOfCarcasses,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const [price, setPrice] = useState();
const {
slaughterGetStewards,
inventorySelectedKillHouse,
slaughterGetGuilds,
} = useSelector((state) => state.slaughterSlice);
useEffect(() => {
dispatch(slaughterGetStewardsService());
dispatch(slaughterGetGuildsService());
}, []);
const getOptionLabel = (option) => option.label;
const validationSchema = Yup.object({
wholePrice: Yup.number().when("quantity", {
is: quantity,
then: Yup.number().required("لطفا این فیلد را پر کنید"),
otherwise: Yup.number(),
}),
slaughteSteward: Yup.string().required("لطفاً یک مورد را انتخاب کنید"),
numberOfPieces: Yup.number()
.required("حجم لاشه را وارد کنید")
.min(1, "حداقل یک قطعه"),
weight: Yup.number()
.required("وزن لاشه را وارد کنید")
.min(0.01, "حداقل 0.01 کیلوگرم"),
});
let defaultKey;
if (isGuild) {
defaultKey = guildKey;
} else {
defaultKey = stewardKey;
}
const formik = useFormik({
initialValues: {
slaughteSteward: defaultKey,
numberOfPieces: quantity ? quantity : "",
weight: weight ? weight : "",
},
validationSchema,
onSubmit: (values) => {
let req;
if (!isGuild) {
req = {
steward_key: values.slaughteSteward,
kill_house_key: inventorySelectedKillHouse,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
sell_type: sellType,
date: selectedDate1,
amount: Number(price),
total_amount: Number(values.wholePrice),
};
} else {
req = {
guild_key: values.slaughteSteward,
kill_house_key: inventorySelectedKillHouse,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
sell_type: sellType,
date: selectedDate1,
amount: Number(price),
total_amount: Number(values.wholePrice),
};
}
if (quantity) {
dispatch(
slaughterEditAllocateStewardService({
steward_allocation_key: item.key,
number_of_carcasses: Number(values.numberOfPieces),
weight_of_carcasses: Number(values.weight),
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
slaughterGetKillhouseStewardsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetUpdatedInventoryStock({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterManageInventoryAllocationsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetKillhouseGuildsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
slaughterGetKillhouseStewardsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetUpdatedInventoryStock({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterManageInventoryAllocationsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetKillhouseGuildsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
},
});
useEffect(() => {
const finalQuantity = formik.values.weight / totalAverageWeightOfCarcasses;
formik.setFieldValue("numberOfPieces", Number(finalQuantity).toFixed(0));
}, [formik.values.weight]);
useEffect(() => {
const calcPrice = formik.values.wholePrice / Number(formik.values.weight);
setPrice(Number(calcPrice.toFixed(0)));
}, [formik.values.wholePrice]);
return (
<form onSubmit={formik.handleSubmit}>
<Grid container width="100%" gap={SPACING.SMALL}>
{!stewardKey && !guildKey && !isGuild && !quantity && (
<Autocomplete
disablePortal
options={slaughterGetStewards}
getOptionLabel={getOptionLabel}
sx={{ width: "100%" }}
onChange={(event, value) =>
formik.setFieldValue("slaughteSteward", value.value)
}
onBlur={formik.handleBlur("slaughteSteward")}
// value={formik.values.slaughteSteward}
renderInput={(params) => <TextField {...params} label="مباشر" />}
/>
)}
{isGuild && !quantity && !stewardKey && !guildKey && (
<Autocomplete
disablePortal
options={slaughterGetGuilds ? slaughterGetGuilds : []}
getOptionLabel={getOptionLabel}
sx={{ width: "100%" }}
onChange={(event, value) =>
formik.setFieldValue("slaughteSteward", value.value)
}
onBlur={formik.handleBlur("slaughteSteward")}
// value={formik.values.slaughteSteward}
renderInput={(params) => <TextField {...params} label="اصناف" />}
/>
)}
<TextField
id="weight"
name="weight"
type="number"
label="وزن لاشه"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="end">کیلوگرم</InputAdornment>
),
}}
{...formik.getFieldProps("weight")}
error={formik.touched.weight && formik.errors.weight}
helperText={formik.touched.weight && formik.errors.weight}
/>
{!quantity && (
<>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="wholePrice"
label="هزینه کل"
variant="outlined"
InputProps={{
endAdornment: (
<InputAdornment position="start">ریال</InputAdornment>
),
}}
value={formik.values.wholePrice}
error={
formik.touched.wholePrice
? Boolean(formik.errors.wholePrice)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.wholePrice && Boolean(formik.errors.wholePrice)
? formik.errors.wholePrice
: null
}
/>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="price"
label="قیمت هر کیلو مرغ"
variant="outlined"
InputLabelProps={{
shrink: true,
}}
InputProps={{
endAdornment: (
<InputAdornment position="start">ریال</InputAdornment>
),
readOnly: true,
}}
value={price}
/>
</>
)}
<TextField
id="numberOfPieces"
name="numberOfPieces"
type="number"
label="حجم لاشه"
variant="outlined"
value={formik.values.numberOfPieces}
InputProps={{
endAdornment: <InputAdornment position="end">قطعه</InputAdornment>,
}}
{...formik.getFieldProps("numberOfPieces")}
error={formik.touched.numberOfPieces && formik.errors.numberOfPieces}
helperText={
formik.touched.numberOfPieces && formik.errors.numberOfPieces
}
/>
<Button variant="contained" fullWidth type="submit">
ثبت
</Button>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,381 @@
import {
Button,
IconButton,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
Typography,
} from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import { Grid } from "../../../../components/grid/Grid";
import { useContext, useState } from "react";
import { useNavigate } from "react-router-dom";
import { ROUTE_SLAUGHTER_FILE } from "../../../../routes/routes";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import SettingsBackupRestoreIcon from "@mui/icons-material/SettingsBackupRestore";
import AddCircleOutlineIcon from "@mui/icons-material/AddCircleOutline";
import { slaughterDeleteAllocatedBackRequestService } from "../../services/slaughter-delete-allocated-back-kill-request";
import { slaughterRemoveAllocateCarService } from "../../services/slaughter-remove-allocate-car";
import { slaughterGetActiveRequests } from "../../services/slaughter-get-active-requests";
import { slaughterGetAllocatedCarsService } from "../../services/slaughter-get-allocated-cars";
import { AppContext } from "../../../../contexts/AppContext";
import { useDispatch, useSelector } from "react-redux";
import {
OPEN_MODAL,
CLOSE_MODAL,
DRAWER,
} from "../../../../lib/redux/slices/appSlice";
import { SlaughterEditAllocatedCar } from "../slaughter-edit-allocated-car/SlaughterEditAllocatedCar";
import { SlaughterSubmitChickenPrice } from "../slaughter-submit-chicken-price/SlaughterSubmitChickenPrice";
import SlaughterAssignCar from "../../../file/components/slaughter-assign-car/SlaughterAssignCar";
import { SPACING } from "../../../../data/spacing";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterAllocatedCarToRequestOperation = ({
item,
updateTable,
isAllocatedCar = false,
selectedDate1,
selectedDate2,
submitStatus,
}) => {
const [anchorEl, setAnchorEl] = useState(null);
const [openNotif, , contextDate1, , contextDate2] = useContext(AppContext);
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
// Use prop dates if provided, otherwise use context dates
const date1 = selectedDate1 || contextDate1;
const date2 = selectedDate2 || contextDate2;
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const navigate = useNavigate();
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const confirmDeleteAllocatedCar = () => {
dispatch(slaughterRemoveAllocateCarService(item.killHouseRequestKey)).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",
});
dispatch(CLOSE_MODAL());
// Refresh both tables
dispatch(
slaughterGetActiveRequests({
isCar: true,
selectedDate1: date1,
selectedDate2: date2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
slaughterGetAllocatedCarsService({
selectedDate1: date1,
selectedDate2: date2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (updateTable) updateTable();
}
}
);
};
const handleDeleteAllocatedCar = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تایید حذف",
content: (
<Grid
container
direction="column"
gap={SPACING.SMALL}
sx={{ p: 2, minWidth: "300px" }}
>
<Typography variant="body1">
آیا از حذف تخصیص این خودرو اطمینان دارید؟
</Typography>
<Grid
container
gap={SPACING.SMALL}
justifyContent="space-between"
xs={12}
>
<Button
variant="outlined"
color="secondary"
onClick={() => dispatch(CLOSE_MODAL())}
>
انصراف
</Button>
<Button
variant="contained"
color="error"
onClick={confirmDeleteAllocatedCar}
>
حذف
</Button>
</Grid>
</Grid>
),
})
);
};
const handleEditAllocatedCar = () => {
dispatch(
OPEN_MODAL({
title: "ویرایش خودرو و کد حمل",
content: (
<SlaughterEditAllocatedCar
item={item}
updateTable={() => {
// Refresh both tables
dispatch(
slaughterGetActiveRequests({
isCar: true,
selectedDate1: date1,
selectedDate2: date2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
slaughterGetAllocatedCarsService({
selectedDate1: date1,
selectedDate2: date2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (updateTable) updateTable();
}}
poultryRequestKey={item?.poultryReqKey}
killHouseKey={item?.killHouseKey}
killRequestKey={item?.killRequestKey}
/>
),
})
);
handleClose();
};
const handleViewFile = () => {
navigate(ROUTE_SLAUGHTER_FILE + item.poultryReqId);
};
const handleReturnAllocation = () => {
dispatch(
slaughterDeleteAllocatedBackRequestService({
provinceKillRequestKey: item?.provinceKillRequestKey,
returnAllocationQuantity: true,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
if (updateTable) {
updateTable();
}
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات برگشت تخصیص با موفقیت انجام شد.",
severity: "success",
});
}
});
};
const handleRegisterCar = () => {
if (submitStatus === true && item?.killHousePrice === 0) {
dispatch(
OPEN_MODAL({
title: "ثبت قیمت مرغ زنده",
content: (
<SlaughterSubmitChickenPrice item={item} fetchData={updateTable} />
),
})
);
} else {
dispatch(
DRAWER({
title: "انجام عملیات تخصیص",
top: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterAssignCar
indexWeight={item.indexWeight}
killHouseName={item.killHouseName}
killHouseCheckKey={item.killHouseCheckKey}
killHouseKey={item.killHouseKey}
killRequestKey={item.killRequestKey}
poultryRequestKey={item.poultryReqKey}
provinceAllocationLimit={item.remainQuantity || 0}
item={item}
/>
),
})
);
}
};
const options = [];
if (!isAllocatedCar) {
const needsPrice =
submitStatus === true && (item?.killHousePrice || 0) === 0;
options.push({
key: "register",
label: "ثبت خودرو",
color: needsPrice ? "error.main" : "primary.main",
icon: AddCircleOutlineIcon,
action: handleRegisterCar,
});
options.push({
key: "viewFile",
label: "مشاهده پرونده",
color: "primary.main",
icon: PlagiarismIcon,
action: handleViewFile,
});
const returnDisabled = item?.allocatedQuantity > 0;
options.push({
key: "return",
label: "برگشت تخصیص",
color: returnDisabled ? "text.disabled" : "warning.main",
icon: SettingsBackupRestoreIcon,
action: handleReturnAllocation,
disabled: returnDisabled,
});
}
if (isAllocatedCar) {
options.push({
key: "editAllocated",
label: "ویرایش خودرو و کد حمل",
color: "info.main",
icon: EditIcon,
action: handleEditAllocatedCar,
});
options.push({
key: "deleteAllocated",
label: "حذف تخصیص خودرو",
color: "error.main",
icon: DeleteIcon,
action: handleDeleteAllocatedCar,
});
}
const handleOptionClick = (option) => {
if (option.disabled) {
return;
}
handleClose();
option.action();
};
return (
<Grid container>
<IconButton
size="small"
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}
>
<List sx={{ minWidth: 150, p: 0.5 }}>
{options.map((option) => {
const IconComponent = option.icon;
const itemColor = option.disabled ? "text.disabled" : option.color;
return (
<ListItemButton
key={option.key}
disabled={option.disabled}
onClick={() => handleOptionClick(option)}
sx={{
borderRadius: 1,
mb: 0.25,
py: 0.5,
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: itemColor, minWidth: 32 }}>
<IconComponent fontSize="small" />
</ListItemIcon>
<ListItemText
primary={option.label}
primaryTypographyProps={{
sx: {
color: itemColor,
fontSize: "0.82rem",
fontWeight: 600,
},
}}
/>
</ListItemButton>
);
})}
</List>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,346 @@
import { Grid, IconButton, TextField, Tooltip } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { useFormik } from "formik";
import moment from "moment";
import { Yup } from "../../../../lib/yup/yup";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetActiveRequests } from "../../services/slaughter-get-active-requests";
import { useContext, useEffect, useState } from "react";
import EditIcon from "@mui/icons-material/Edit";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterGetAllocatedCarsService } from "../../services/slaughter-get-allocated-cars";
import { AppContext } from "../../../../contexts/AppContext";
import { format } from "date-fns-jalali";
import { DatePicker } from "@mui/x-date-pickers";
import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
import { useReactToPrint } from "react-to-print";
import { useRef } from "react";
import SlaughterSendKillerInvoice from "../slaughter-send-killer-invoice/SlaughterSendKillerInvoice";
import { useSystemName } from "../../../../utils/getSystemName";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { provincePolicyGetSLaughterSubmitBuyingPriceStatus } from "../../../province/services/province-policy-get-slaughter-buying-price-status";
import { SlaughterSubmitChickenPrice } from "../slaughter-submit-chicken-price/SlaughterSubmitChickenPrice";
import { SlaughterAllocatedCarToRequestOperation } from "../slaughter-allocate-car-to-request-operation/SlaughterAllocatedCarToRequestOperation";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterAllocateCarToRequests = () => {
const componentRef = useRef();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [data, setData] = useState();
const [dataTable, setDataTable] = useState([]);
const [allocatedDataTable, setAllocatedDataTable] = useState([]);
const { slaughterActiveRequests, slaughterGetAllocatedCars } = useSelector(
(state) => state.slaughterSlice
);
const systemName = useSystemName();
const dispatch = useDispatch();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [submitStatus, SetSubmitStatus] = useState();
const fetchData = () => {
dispatch(
slaughterGetActiveRequests({
isCar: true,
selectedDate1,
selectedDate2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
slaughterGetAllocatedCarsService({
selectedDate1,
selectedDate2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
provincePolicyGetSLaughterSubmitBuyingPriceStatus({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
SetSubmitStatus(r.payload.data.allow);
});
};
useEffect(() => {
fetchData();
}, [selectedDate1, selectedDate2, selectedSubUser?.key]);
const printPDF = useReactToPrint({
content: () => componentRef.current,
documentTitle: "حواله خرید",
});
const updateTable = () => {
fetchData();
};
const setPdfOptions = (item) => {
setData(item);
};
useEffect(() => {
const d = slaughterActiveRequests?.map((item, i) => {
return [
i + 1,
item.orderCode,
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.freeSaleInProvince ? "آزاد" : "دولتی",
item?.directBuying ? "خرید مستقیم" : "اتحادیه",
format(new Date(item?.sendDate), "yyyy/MM/dd"),
`${item.poultryName} (${item.poultryMobile})`,
`${item.killHouseName} (${item.killHouseMobile})`,
`${item.province}/${item.city}`,
item.age,
item.indexWeight,
item?.totalWeight.toLocaleString(),
item.mainQuantity.toLocaleString() + " قطعه",
item.amount.toLocaleString() + " ﷼",
item.allocatedQuantity.toLocaleString() + " قطعه",
item.remainQuantity.toLocaleString() + " قطعه",
item?.killHousePrice > 0 ? (
<Grid container alignItems="center" gap={1} key={i}>
{item?.killHousePrice?.toLocaleString() + " ریال"}
{formatJustDate(item?.sendDate) === formatJustDate(new Date()) ? (
<IconButton
size="small"
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش قیمت مرغ زنده",
content: (
<SlaughterSubmitChickenPrice
item={item}
fetchData={fetchData}
/>
),
})
);
}}
>
<EditIcon fontSize="small" />
</IconButton>
) : null}
</Grid>
) : (
<span key={i}>0</span>
),
<SlaughterAllocatedCarToRequestOperation
key={i}
updateTable={updateTable}
item={item}
submitStatus={submitStatus}
/>,
// <IconButton
// key={i}
// aria-label="delete"
// color="primary"
// onClick={() => navigate(ROUTE_SLAUGHTER_FILE + item.poultryReqId)}
// >
// <PlagiarismIcon />
// </IconButton>,
];
});
setDataTable(d);
}, [slaughterActiveRequests, submitStatus]);
useEffect(() => {
const d = slaughterGetAllocatedCars
?.filter((item) => item.vetState === "pending")
.map((item, i) => {
return [
i + 1,
item?.orderCode,
item?.barcod,
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.freeSaleInProvince ? "آزاد" : "دولتی",
formatJustDate(item?.sendDate),
formatTime(item?.killHouseCreateDate),
`${item?.poultryName} (${item.poultryMobile})`,
`${item?.killHouseName} (${item.killHouseMobile})`,
item?.killer
? `${item?.killer?.killerName} (${item?.killer?.killerMobile})`
: "-",
item?.driverName,
item?.typeCar,
item?.pelak,
// item?.realCar
// ? `${item?.realCar?.realTypeCar} (${
// item?.realCar?.realPelak ? item?.realCar?.realPelak : "نامشخص"
// }) - راننده: ${item?.realCar?.realDriverName}`
// : "-",
item?.acceptedRealQuantity.toLocaleString() + " قطعه",
item?.acceptedRealWeight.toLocaleString(),
item?.amount?.toLocaleString() + " ﷼",
item?.trafficCode,
item?.barAmount?.toLocaleString() + " ﷼",
<Grid key={i} container gap={1}>
<SlaughterAllocatedCarToRequestOperation
key={i}
item={item}
updateTable={updateTable}
isAllocatedCar={true}
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
/>
{systemName === "استان اردبیل" ? (
<Tooltip title="خروجی PDF">
<IconButton
onClick={() => {
printPDF();
setPdfOptions(item);
}}
size={"large"}
aria-label="pdf"
color="success"
>
<PictureAsPdfIcon />
</IconButton>
</Tooltip>
) : null}
</Grid>,
];
});
setAllocatedDataTable(d);
}, [slaughterGetAllocatedCars]);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"کشتار",
"فروش",
"درخواست",
"تاریخ کشتار",
"مرغدار",
"خریدار",
"استان/شهر",
"سن",
"میانگین وزن",
"وزن کل (کیلوگرم)",
"تعداد",
"قیمت مرغدار",
"تخصیص به خودرو",
"مانده قابل تخصیص",
"قیمت کشتارگاه",
"عملیات",
]);
const formik = useFormik({
initialValues: {
capacity: "",
recieveTime: "",
recieveDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
},
validationSchema: Yup.object({
capacity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
recieveTime: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
>
{data && (
<div style={{ visibility: "hidden", position: "absolute" }}>
<SlaughterSendKillerInvoice
ref={componentRef}
date={selectedDate1}
amount={data?.quantity}
breeder={data?.poultryName}
buyer={data?.killHouseName}
/>
</div>
)}
<Grid container alignItems="center" gap={SPACING.SMALL}>
<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>
<ResponsiveTable
paginated
title="در انتظار تخصیص خودرو"
columns={tableDataCol}
data={dataTable}
/>
<ResponsiveTable
title="خودروهای تخصیص داده شده"
paginated
columns={[
"ردیف",
"کد سفارش",
"کد بار",
"کشتار",
"فروش",
"تاریخ کشتار",
"تاریخ ثبت خودرو",
"مرغدار",
"خریدار",
"کشتارکن اختصاصی",
"راننده",
"ماشین",
"پلاک",
"تعداد",
"وزن (کیلوگرم)",
"قیمت مرغ زنده",
"کد حمل و نقل",
"قیمت مرغ زنده‌ی بار",
"عملیات",
]}
data={allocatedDataTable}
/>
</Grid>
);
};

View File

@@ -0,0 +1,595 @@
import React, { useContext, useEffect, useState, useCallback } from "react";
import { Grid } from "../../../../components/grid/Grid";
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetProductsService } from "../../services/slaughter-inventory-gets";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import {
slaughterAllocateStewardService,
slaughterEditAllocateStewardService,
} from "../../services/slaughter-allocate-steward";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { liveStockGetInventoryData } from "../../../live-stock-support/services/live-stock-get-inventory-data";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
import MonthlyDataCalendar from "../../../../components/date-picker/MonthlyDataCalendar";
import PersianDate from "persian-date";
import axios from "axios";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
import { LabelField } from "../../../../components/label-field/LabelField";
export const SlaughterAllocateForFreezing = ({
sellerType,
sellType,
updateTable,
fetchApiData,
editData,
remainWeight,
priceInfo,
}) => {
const dispatch = useDispatch();
const [productData, setProductData] = useState([]);
const [productKey, setProductKey] = useState(null);
const [coldHouseData, setColdHouseData] = useState([]);
const [coldHouseKey, setColdHouseKey] = useState(null);
const [selectedInventory, setSelectedInventory] = useState("governmental");
const [approvedStatus, setApprovedStatus] = useState(
priceInfo?.active === false ? false : true
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [openNotif] = useContext(AppContext);
// Calendar states
const [selectedCalendarDate, setSelectedCalendarDate] = useState(null);
const [calendarDayData, setCalendarDayData] = useState({});
const [productionDate, setProductionDate] = useState(null);
const [selectedDateAmount, setSelectedDateAmount] = useState(null);
const [calendarRawData, setCalendarRawData] = useState({
governmental: [],
free: [],
});
const transformCalendarData = useCallback((dataArray) => {
if (!Array.isArray(dataArray)) return {};
const transformedData = {};
dataArray.forEach((item) => {
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,
};
}
});
return transformedData;
}, []);
const updateCalendarData = useCallback(
(dataArray) => {
const transformed = transformCalendarData(dataArray);
setCalendarDayData(transformed);
},
[transformCalendarData]
);
// Fetch calendar data from API
const fetchCalendarData = useCallback(
async (dateParam) => {
try {
const response = await axios.get("/kill-house-remain-weight/", {
params: {
date: dateParam,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
},
});
if (response.data) {
setCalendarRawData({
governmental: response.data.governmental || [],
free: response.data.free || [],
});
const dataToShow =
selectedInventory === "governmental"
? response.data.governmental
: response.data.free;
updateCalendarData(dataToShow);
}
} catch (error) {
console.error("Error fetching calendar data:", error);
}
},
[selectedInventory, updateCalendarData, selectedSubUser]
);
const handleDateSelect = (dateInfo) => {
if (dateInfo && dateInfo.formattedDate) {
setSelectedCalendarDate(dateInfo.formattedDate);
const data = calendarDayData[dateInfo.formattedDate];
if (data && data.originalDay) {
const selectedOriginalDay = data.originalDay;
if (
selectedDate1 &&
moment(selectedOriginalDay).isAfter(moment(selectedDate1), "day")
) {
setCalendarDateError(
"تاریخ تولید نمی‌تواند بعد از تاریخ انتخابی باشد"
);
return;
}
setCalendarDateError(null);
setProductionDate(selectedOriginalDay);
}
if (data && data.value1 !== undefined) {
setSelectedDateAmount(data.value1);
} else {
setSelectedDateAmount(null);
}
}
};
const getValidationSchema = useCallback(
() =>
Yup.object({
weight: Yup.number()
.required("این فیلد اجباری است!")
.integer("عدد باید صحیح باشد!")
.min(1, "یک مقدار مثبت وارد کنید!")
.max(
remainWeight + (editData?.realWeightOfCarcasses || 0),
"وزن وارد شده بیش از موجودی انبار است!"
)
.test(
"max-production-date-amount",
`وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${
selectedDateAmount?.toLocaleString() || 0
} کیلوگرم) باشد!`,
function (value) {
if (!selectedDateAmount || selectedDateAmount === null)
return true;
return value <= selectedDateAmount;
}
),
}),
[remainWeight, editData, selectedDateAmount]
);
const validationSchema = getValidationSchema();
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const [dateRangeError, setDateRangeError] = useState(null);
const [calendarDateError, setCalendarDateError] = useState(null);
const formik = useFormik({
initialValues: {
weight: editData?.realWeightOfCarcasses || "",
},
validationSchema,
});
const successSubmit = () => {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
fetchSlaughterBroadcastAndProducts({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
fetchApiData();
updateTable();
};
const handleSellType = (event) => {
const newType = event.target.value;
setSelectedInventory(newType);
};
const handleApprovedPrice = (event) => {
const newType = event.target.value;
setApprovedStatus(newType);
};
useEffect(() => {
if (!editData) {
dispatch(
slaughterGetProductsService({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setProductData(r.payload.data);
});
dispatch(
liveStockGetInventoryData({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setColdHouseData(r.payload.data);
});
}
fetchCalendarData(selectedDate1);
}, [
dispatch,
editData,
fetchCalendarData,
selectedDate1,
selectedSubUser?.key,
]);
useEffect(() => {
fetchCalendarData(selectedDate1);
}, [selectedDate1, fetchCalendarData, selectedSubUser?.key]);
useEffect(() => {
if (
calendarRawData.governmental.length > 0 ||
calendarRawData.free.length > 0
) {
const dataToShow =
selectedInventory === "governmental"
? calendarRawData.governmental
: calendarRawData.free;
updateCalendarData(dataToShow);
setSelectedCalendarDate(null);
setProductionDate(null);
setSelectedDateAmount(null);
}
}, [selectedInventory, calendarRawData, updateCalendarData]);
useEffect(() => {
formik.validateForm();
}, [selectedDateAmount]);
return (
<Grid
container
xs={12}
direction="column"
justifyContent="center"
alignItems="center"
gap={1}
>
{!editData && (
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => (
<TextField
fullWidth
{...params}
error={Boolean(dateRangeError) || params.error}
helperText={dateRangeError || params.helperText}
/>
)}
shouldDisableDate={(date) => {
const d = moment(date);
const today = moment();
const yesterday = moment().subtract(1, "day");
return !(d.isSame(today, "day") || d.isSame(yesterday, "day"));
}}
value={selectedDate1}
onChange={(e) => {
if (!e) {
setDateRangeError(null);
return;
}
const d = moment(e);
const today = moment();
const yesterday = moment().subtract(1, "day");
const isAllowed =
d.isSame(today, "day") || d.isSame(yesterday, "day");
if (!isAllowed) {
setDateRangeError(
"تنها امکان انتخاب «امروز» یا «دیروز» وجود دارد."
);
return;
}
setDateRangeError(null);
const formatted = moment(e).format("YYYY-MM-DD");
setSelectedDate1(formatted);
fetchCalendarData(formatted);
}}
/>
)}
{!editData && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
productData
? productData.map((i) => {
return {
data: i,
label: `${i.name}`,
};
})
: []
}
onChange={(event, value) => {
setProductKey(value.data);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
)}
{!editData && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
coldHouseData
? coldHouseData.map((i) => {
return {
data: i,
label: `${i.name}`,
};
})
: []
}
onChange={(event, value) => {
setColdHouseKey(value.data?.key);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب سردخانه" />
)}
/>
</Grid>
)}
{!editData && priceInfo?.active && (
<FormControl>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={approvedStatus}
onChange={handleApprovedPrice}
>
<FormControlLabel
value={true}
control={<Radio />}
label="قیمت دولتی"
/>
<FormControlLabel
value={false}
control={<Radio />}
label="قیمت آزاد"
/>
</RadioGroup>
</FormControl>
)}
{!editData && (
<Grid my={1} xs={12}>
<LabelField label="نوع انبار">
<FormControl>
<RadioGroup
row
aria-labelledby="segment-type-radio-group"
name="segmentType"
value={selectedInventory}
onChange={handleSellType}
>
<FormControlLabel
value="governmental"
control={<Radio />}
label="دولتی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="آزاد"
/>
</RadioGroup>
</FormControl>
</LabelField>
</Grid>
)}
{!editData && (
<Grid
style={{ width: "100%" }}
container
xs={12}
lg={3}
justifyContent="center"
alignItems="center"
mb={3}
mt={2}
gap={1}
>
<MonthlyDataCalendar
onDateSelect={handleDateSelect}
dayData={calendarDayData}
selectedDate={selectedCalendarDate}
maxGregorianDate={selectedDate1}
label={`تاریخ تولید گوشت ${
selectedDateAmount !== null
? `(موجودی: ${selectedDateAmount?.toLocaleString()} کیلوگرم)`
: ""
}`}
/>
{calendarDateError && (
<Typography
sx={{
color: "#d32f2f",
fontSize: "0.75rem",
marginTop: "4px",
marginRight: "14px",
textAlign: "right",
}}
>
{calendarDateError}
</Typography>
)}
</Grid>
)}
<NumberInput
allowLeadingZeros
thousandSeparator=","
decimalScale={0}
allowNegative={false}
fullWidth
id="weight"
disabled={remainWeight < 1}
label="وزن لاشه"
variant="outlined"
value={formik.values.weight}
error={
remainWeight < 1
? true
: formik.touched.weight
? Boolean(formik.errors.weight)
: selectedDateAmount && formik.values.weight > selectedDateAmount
}
onChange={(e) => {
const value = e.target.value;
if (value === "" || value === null || value === undefined) {
formik.setFieldValue("weight", "");
return;
}
const intValue = Math.floor(Number(value));
if (intValue > 0) {
formik.setFieldValue("weight", intValue);
} else if (intValue === 0) {
formik.setFieldValue("weight", "");
}
}}
onBlur={formik.handleBlur}
helperText={
remainWeight < 1
? "موجودی انبار خالی است!"
: selectedDateAmount && formik.values.weight > selectedDateAmount
? `وزن نمی‌تواند بیشتر از موجودی تاریخ تولید (${selectedDateAmount?.toLocaleString()} کیلوگرم) باشد!`
: formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
/>
<Button
variant="contained"
fullWidth
disabled={
editData
? !formik.isValid
: !formik.isValid ||
!productKey ||
!coldHouseKey ||
!productionDate ||
(selectedDateAmount &&
formik.values.weight > selectedDateAmount) ||
(productionDate &&
selectedDate1 &&
moment(productionDate).isAfter(moment(selectedDate1), "day"))
}
onClick={() => {
let req = {};
if (!editData) {
req = {
seller_type: sellerType,
product_key: productKey.key,
type: "manual",
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
approved_price_status: approvedStatus === "true" ? true : false,
quota: selectedInventory,
sell_type: sellType,
buyer_type: "ColdHouse",
cold_house_key: coldHouseKey,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
allocation_type: "ColdHouse",
date: selectedDate1,
production_date: productionDate,
distribution_type: "web",
};
} else {
req = {
weight_of_carcasses: formik.values.weight,
allocation_key: editData?.key,
distribution_type: "web",
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
};
}
if (!editData) {
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
} else {
dispatch(slaughterEditAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,39 @@
import { Button } from "@mui/material";
import { useDispatch } from "react-redux";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterAddSteward } from "../slaughter-add-steward/SlaughterAddSteward";
export const SlaughterAllocateSelfSteward = ({
stewardKey,
guildKey,
isGuild,
finalSubmitDisabled,
slaughterUpdatedInventoryStock,
}) => {
const dispatch = useDispatch();
return (
<Button
disabled={finalSubmitDisabled}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تخصیص به مباشر",
content: (
<SlaughterAddSteward
sellType={"exclusive"}
stewardKey={stewardKey}
guildKey={guildKey}
isGuild={isGuild}
totalAverageWeightOfCarcasses={
slaughterUpdatedInventoryStock?.totalAverageWeightOfCarcasses
}
/>
),
})
);
}}
>
تخصیص
</Button>
);
};

View File

@@ -0,0 +1,602 @@
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import {
Autocomplete,
Button,
FormControl,
FormControlLabel,
InputAdornment,
Radio,
RadioGroup,
TextField,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetProductsService } from "../../services/slaughter-inventory-gets";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import {
slaughterAllocateStewardService,
slaughterEditAllocateStewardService,
} from "../../services/slaughter-allocate-steward";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import {
slaughterGetColdHousesForAllocateService,
slaughterGetGuildsForAllocateService,
} from "../../services/slaughter-get-guilds-for-allocate";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { fixBase64 } from "../../../../utils/toBase64";
import { provincePolicyGetUploadImageService } from "../../../province/services/province-policy-upload-image";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterAllocateToColdHouse = ({
item,
key,
sellerType,
fetchData,
buyerType,
allocationType,
sellType,
updateTable,
fetchApiData,
editData,
priceInfo,
coldHouseKey,
coldHouseItemKey,
remainWeight,
killHouseAllocation,
}) => {
const dispatch = useDispatch();
const [productData, setProductData] = useState([]);
const [guildsData, setGuildsData] = useState([]);
const [coldHouseData, setColdHouseData] = useState([]);
const [productKey, setProductKey] = useState(null);
const [openNotif] = useContext(AppContext);
const [sellGovernmental, setSellGovernmental] = useState(
priceInfo?.active ? "true" : "false"
);
const [profileImages, setProfileImages] = useState(
editData?.image ? [{ data_url: editData.image }] : []
);
const [value, setValue] = useState("own");
const [imageUploadLimit, setImageUploadLimit] = useState(1);
const [imageChanged, setImageChanged] = useState(false);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleChange = (event) => {
setValue(event.target.value);
setBuyerData({
key: "",
item: "",
buyerType: "",
allocationType: "",
});
};
const handleChangeSellGovernmental = (event) => {
setSellGovernmental(event.target.value);
if (event.target.value === "false") {
formik.setFieldValue("price", "");
}
};
const [buyerData, setBuyerData] = useState({
key,
item,
buyerType,
allocationType,
});
useEffect(() => {
if (getRoleFromUrl() === "Steward") {
setValue("free");
}
}, []);
useEffect(() => {
dispatch(provincePolicyGetUploadImageService()).then((r) => {
if (r.payload?.data) {
setImageUploadLimit(r.payload.data.killHouseAllocation);
}
});
if (!editData) {
dispatch(
slaughterGetProductsService({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setProductData(r.payload.data);
});
if (!item) {
if (value === "cold") {
dispatch(
slaughterGetColdHousesForAllocateService({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setColdHouseData(r.payload.data);
});
}
dispatch(
slaughterGetGuildsForAllocateService({
free: value === "free" ? true : false,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setGuildsData(r.payload.data);
});
}
}
}, [dispatch, value, selectedSubUser?.key]);
const validationSchema = Yup.object({
weight: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!")
.max(
editData
? remainWeight + editData?.realWeightOfCarcasses
: remainWeight,
"وزن وارد شده بیش از موجودی انبار است!"
),
price: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!"),
wholePrice: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!"),
...(killHouseAllocation && {
image: Yup.string().when([], {
is: () => (!editData || imageChanged) && imageUploadLimit > 0,
then: Yup.string().required("عکس الزامی است"),
otherwise: Yup.string().notRequired(),
}),
}),
});
const factorPaymentHandler = (imageList) => {
if (imageList[0]) {
formik.setFieldValue("image", fixBase64(imageList[0]?.data_url));
setImageChanged(true);
} else {
formik.setFieldValue("image", "");
setImageChanged(true);
}
setProfileImages(imageList);
};
const formik = useFormik({
initialValues: {
weight: editData?.realWeightOfCarcasses || "",
wholePrice: editData?.totalAmount || "",
price: editData?.amount || "",
image: editData?.image || "",
},
validationSchema,
});
useEffect(() => {
formik.validateForm();
}, []);
useEffect(() => {
if (sellGovernmental === "false") {
if (formik.values.weight && formik.values.price) {
formik.setFieldValue(
"wholePrice",
formik.values.price * formik.values.weight
);
}
} else {
if (priceInfo?.active && formik.values.weight) {
formik.setFieldValue(
"wholePrice",
priceInfo?.killHousePrice * formik.values.weight
);
}
}
}, [formik.values.price, formik.values.weight, sellGovernmental]);
useEffect(() => {
if (priceInfo?.active && sellGovernmental === "true") {
formik.setFieldValue("price", priceInfo?.killHousePrice);
}
}, [sellGovernmental]);
const successSubmit = () => {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
fetchSlaughterBroadcastAndProducts({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
fetchApiData && fetchApiData(1);
updateTable && updateTable();
fetchData && fetchData(1);
};
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
return (
<Grid
container
xs={12}
direction="column"
justifyContent="center"
alignItems="center"
gap={1}
>
{!editData && (
<DatePicker
label="تاریخ"
id="date"
renderInput={(params) => <TextField fullWidth {...params} />}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
)}
{!editData && !coldHouseKey && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
productData
? productData.map((i) => {
return {
data: i,
label: `${i.name}`,
};
})
: []
}
onChange={(event, value) => {
setProductKey(value.data);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
)}
{!editData && (
<FormControl>
<RadioGroup
row
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={value}
onChange={handleChange}
>
<FormControlLabel
value="own"
control={<Radio />}
label="صنوف اختصاصی"
/>
<FormControlLabel
value="free"
control={<Radio />}
label="صنوف آزاد"
/>
<FormControlLabel
value="cold"
control={<Radio />}
label="انتقال به سردخانه"
/>
</RadioGroup>
</FormControl>
)}
{!item && !editData && value !== "cold" && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
guildsData
? guildsData.map((i) => {
return {
data: i,
label: `${i?.steward ? "مباشر" : "صنف"} ${
i?.guildsName
} ${i?.user?.fullname} (${i?.user?.mobile})`,
};
})
: []
}
onChange={(event, value) => {
setBuyerData({
item: value?.data,
key: value?.data?.key,
allocationType: value?.data?.steward
? "killhouse_steward"
: "killhouse_guild",
buyerType: value?.data?.steward ? "Steward" : "Guild",
});
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب مباشر / صنف" />
)}
/>
</Grid>
)}
{!item && !editData && value === "cold" && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
coldHouseData
? coldHouseData.map((i) => {
return {
data: i,
label: `سردخانه ${i?.name} (${
i?.steward?.user?.mobile ||
i?.killHouse?.killHouseOperator?.user?.mobile
})`,
};
})
: []
}
onChange={(event, value) => {
setBuyerData({
item: value?.data,
key: value?.data?.key,
allocationType: "ColdHouse",
buyerType: "ColdHouse",
});
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب سردخانه" />
)}
/>
</Grid>
)}
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="weight"
label="وزن لاشه"
variant="outlined"
value={formik.values.weight}
error={
(formik.touched.weight && Boolean(formik.errors.weight)) ||
remainWeight <= 0
}
helperText={
remainWeight <= 0
? "موجودی انبار کافی نیست"
: formik.touched.weight && formik.errors.weight
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
disabled={remainWeight < 1}
sx={{
"& .MuiFormHelperText-root": {
color:
remainWeight && formik.values.weight > remainWeight
? "error.main"
: undefined,
},
}}
/>
{priceInfo?.active && (
<FormControl>
<RadioGroup
row
aria-labelledby="demo-controlled-radio-buttons-group"
name="controlled-radio-buttons-group"
value={sellGovernmental}
onChange={handleChangeSellGovernmental}
>
<FormControlLabel
value="true"
control={<Radio />}
label="قیمت مصوب"
/>
<FormControlLabel
value="false"
control={<Radio />}
label="قیمت آزاد"
/>
</RadioGroup>
</FormControl>
)}
<NumberInput
disabled={priceInfo?.active && sellGovernmental === "true"}
allowLeadingZeros
thousandSeparator=","
fullWidth
id="price"
label="قیمت هر کیلوگرم"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.price}
error={formik.touched.price ? Boolean(formik.errors.price) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.price && Boolean(formik.errors.price)
? formik.errors.price
: null
}
/>
<NumberInput
disabled
allowLeadingZeros
thousandSeparator=","
fullWidth
id="wholePrice"
label="هزینه کل"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.wholePrice}
error={
formik.touched.wholePrice ? Boolean(formik.errors.wholePrice) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.wholePrice && Boolean(formik.errors.wholePrice)
? formik.errors.wholePrice
: null
}
/>
{(killHouseAllocation || (editData && editData.image)) && (
<>
<ImageUpload
onChange={factorPaymentHandler}
images={profileImages}
maxNumber={1}
title={"بارگزاری سند"}
/>
{formik.touched.image && Boolean(formik.errors.image) && (
<Typography color="error">ثبت تصویر الزامی است</Typography>
)}
</>
)}
<Button
variant="contained"
fullWidth
disabled={
editData
? !formik.isValid
: !formik.isValid ||
(coldHouseKey ? false : !productKey) ||
!buyerData?.item?.key
}
onClick={() => {
let req = {};
if (coldHouseItemKey) {
req = {
allocation_key: coldHouseItemKey,
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
amount: formik.values.price,
total_amount: formik.values.wholePrice,
...(imageChanged && { image: formik.values.image }),
};
} else if (!editData) {
req = {
seller_type: sellerType,
buyer_type: buyerData?.buyerType,
other_cold_house_key:
buyerData?.buyerType === "ColdHouse"
? buyerData?.item?.key
: null,
guild_key:
buyerData?.buyerType === "Guild" ? buyerData?.item?.key : null,
steward_key:
buyerData?.buyerType === "Steward"
? buyerData?.item?.key
: null,
kill_house_key:
buyerData?.buyerType === "KillHouse"
? buyerData?.item?.key
: null,
cold_house_key: coldHouseKey || null,
product_key: coldHouseKey ? null : productKey.key,
type: "manual",
allocation_type: buyerData?.allocationType,
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
sell_type: value === "cold" ? "free" : sellType,
amount: formik.values.price,
total_amount: formik.values.wholePrice,
approved_price_status: sellGovernmental === "true" ? true : false,
date: selectedDate1,
...(profileImages.length > 0 && { image: formik.values.image }),
};
req = Object.fromEntries(
Object.entries(req).filter(([_, value]) => value !== null)
);
} else {
req = {
allocation_key: editData?.key,
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
amount: formik.values.price,
total_amount: formik.values.wholePrice,
...(imageChanged && { image: formik.values.image }),
};
}
if (!editData) {
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
} else {
dispatch(slaughterEditAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,223 @@
import {
Autocomplete,
Button,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
} from "@mui/material";
import React, { useContext, useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { useDispatch } from "react-redux";
import { slaughterGetColdHouses } from "../../services/slaughter-get-cold-houses";
import { slaughterSubmitAllocateToFreezing } from "../../services/slaughter-submit-allocate-to-freezing";
import { AppContext } from "../../../../contexts/AppContext";
import {
CLOSE_MODAL,
LOADING_END,
} from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { useFormik } from "formik";
import { slaughterUpdateAllocationForFreezing } from "../../services/slaughter-update-allocation-for-freezing";
import { slaughterGetProductsService } from "../../services/slaughter-inventory-gets";
export const SlaughterAllocateToFreezing = ({
fetchItems,
isEdit,
item,
updateTable,
handleUpdateFreezing,
}) => {
const [coldHouse, setColdHouse] = useState("");
const [coldHouses, setColdHouses] = useState([]);
const [openNotif] = useContext(AppContext);
const [productKey, setProductKey] = useState(null);
const [productData, setProductData] = useState([]);
const dispatch = useDispatch();
useEffect(() => {
if (!isEdit) {
dispatch(slaughterGetProductsService()).then((r) => {
setProductData(r.payload.data);
});
dispatch(slaughterGetColdHouses()).then((r) => {
setColdHouses(r.payload.data);
});
}
}, []);
const handleChange = (event) => {
setColdHouse(event.target.value);
};
const formik = useFormik({
initialValues: {
quantity: item?.quantity ? item?.quantity : "",
weight: item?.weight ? item?.weight : "",
},
validationSchema: Yup.object({
quantity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
weight: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container gap={2} xs={12} justifyContent="center">
{!isEdit && (
<Grid xs={12} container>
<Autocomplete
fullWidth
style={{ minWidth: 210 }}
disablePortal
id="hatching"
options={
productData
? productData.map((i) => {
return {
data: i,
label: `${i.name}`,
};
})
: []
}
onChange={(event, value) => {
setProductKey(value.data);
}}
renderInput={(params) => (
<TextField fullWidth {...params} label="انتخاب محصول" />
)}
/>
</Grid>
)}
{!isEdit && (
<>
<FormControl>
<InputLabel id="demo-simple-select-disabled-label">
انتخاب سردخانه
</InputLabel>
<Select
labelId="demo-simple-select-disabled-label"
id="demo-simple-select-disabled"
value={coldHouse}
label="سردخانه"
onChange={handleChange}
>
{coldHouses?.map((item, i) => {
return (
<MenuItem
key={i}
value={item?.key}
>{`سردخانه ${item.name}`}</MenuItem>
);
})}
</Select>
</FormControl>
</>
)}
<TextField
label="تعداد"
name="quantity"
value={formik.values.quantity}
onChange={formik.handleChange}
error={formik.touched.quantity && Boolean(formik.errors.quantity)}
helperText={formik.touched.quantity && formik.errors.quantity}
required
fullWidth
/>
<TextField
label="وزن"
name="weight"
value={formik.values.weight}
onChange={formik.handleChange}
error={formik.touched.weight && Boolean(formik.errors.weight)}
helperText={formik.touched.weight && formik.errors.weight}
required
fullWidth
/>
<Button
disabled={
isEdit
? !formik.isValid
: !coldHouse || !formik.isValid || !productKey
}
fullWidth
variant="contained"
onClick={() => {
if (isEdit) {
dispatch(
slaughterUpdateAllocationForFreezing({
allocation_key: item?.key,
quantity: parseInt(formik.values.quantity),
weight: parseInt(formik.values.weight),
})
).then((r) => {
if (r.payload.error) {
dispatch(LOADING_END());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
fetchItems();
updateTable();
handleUpdateFreezing();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
slaughterSubmitAllocateToFreezing({
cold_house_key: coldHouse,
quantity: parseInt(formik.values.quantity),
weight: parseInt(formik.values.weight),
product_key: productKey?.key,
})
).then((r) => {
if (r.payload.error) {
dispatch(LOADING_END());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
fetchItems();
updateTable();
handleUpdateFreezing();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,181 @@
import { Grid, IconButton, TextField } from "@mui/material";
import { ROUTE_SLAUGHTER_FILE } from "../../../../routes/routes";
import { SPACING } from "../../../../data/spacing";
import { useFormik } from "formik";
import moment from "moment";
import { Yup } from "../../../../lib/yup/yup";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetActiveRequests } from "../../services/slaughter-get-active-requests";
import { useContext, useEffect, useState } from "react";
import { useNavigate } from "react-router-dom";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import EditIcon from "@mui/icons-material/Edit";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import SlaughterCheckRequest from "../../../file/components/slaughter-check-request/SlaughterCheckRequest";
import { format } from "date-fns-jalali";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterAllocatedCheckRequests = () => {
const navigate = useNavigate();
const [dataTable, setDataTable] = useState([]);
const { slaughterActiveRequests } = useSelector(
(state) => state.slaughterSlice
);
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);
}, []);
useEffect(() => {
dispatch(
slaughterGetActiveRequests({
selectedDate1,
selectedDate2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedDate1, selectedDate2, selectedSubUser?.key]);
useEffect(() => {
const filteredData = slaughterActiveRequests?.filter(
(item) => item.provinceKillState === "pending"
);
// const key = "orderCode";
// const arrayUniqueByKey = [
// ...new Map(filteredData?.map((item) => [item[key], item])).values(),
// ];
const d = filteredData?.map((item, i) => {
return [
i + 1,
item.orderCode,
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.freeSaleInProvince === false ? "دولتی" : "آزاد",
format(new Date(item?.sendDate), "yyyy/MM/dd"),
`${item.poultryName} (${item.poultryMobile})`,
`${item.killHouseName} (${item.killHouseMobile})`,
item.city,
item.province,
item.age,
item.mainQuantity.toLocaleString() + " قطعه",
item.amount.toLocaleString() + " ﷼",
item.chickenBreed,
item.indexWeight + " کیلوگرم",
<IconButton
key={i}
color="primary"
onClick={() => {
dispatch(
DRAWER({
title: "انجام عملیات تخصیص",
// right: !(window.innerWidth <= 600),
top: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <SlaughterCheckRequest item={item} i={i} />,
})
);
}}
>
<EditIcon />
</IconButton>,
<IconButton
key={i}
aria-label="delete"
color="primary"
onClick={() => navigate(ROUTE_SLAUGHTER_FILE + item.poultryReqId)}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [slaughterActiveRequests]);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"کشتار",
"فروش",
"تاریخ درخواست",
"مرغدار",
"خریدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"قیمت مرغ زنده",
"نژاد",
"میانگین وزن",
"عملیات",
"مشاهده",
]);
const formik = useFormik({
initialValues: {
capacity: "",
recieveTime: "",
recieveDate: moment(Date()).format("YYYY-MM-DD hh:mm:ss"),
},
validationSchema: Yup.object({
capacity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
recieveTime: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
return (
<Grid container direction="column" gap={SPACING.SMALL} mt={SPACING.MEDIUM}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<ResponsiveTable
title={"درخواست های در انتظار تایید"}
columns={tableDataCol}
data={dataTable}
paginated
/>
</Grid>
);
};

View File

@@ -0,0 +1,540 @@
import {
Button,
Checkbox,
FormControlLabel,
Pagination,
TextField,
Tooltip,
tooltipClasses,
Typography,
} from "@mui/material";
import axios from "axios";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { PageTable } from "../../../../components/page-table/PageTable";
import { SPACING } from "../../../../data/spacing";
import {
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { formatJustDate } from "../../../../utils/formatTime";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import KeyboardDoubleArrowUpIcon from "@mui/icons-material/KeyboardDoubleArrowUp";
import KeyboardDoubleArrowDownIcon from "@mui/icons-material/KeyboardDoubleArrowDown";
import styled from "styled-components";
import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { SlaughterUnpaidFeesDetails } from "../slaughter-unpaid-fees-details/SlaughterUnpaidFeesDetails";
import { ProvinceArchiveFeeDetails } from "../../../province/components/province-archive-fee-details/ProvinceArchiveFeeDetails";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import BoxList from "../../../../components/box-list/BoxList";
import moment from "moment";
import { AppContext } from "../../../../contexts/AppContext";
import { DatePicker } from "@mui/x-date-pickers";
export const SlaughterArchivedFees = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [withDate, setWithDate] = useState(false);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const dispatch = useDispatch();
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const userInfo = useSelector((state) => state.userSlice);
const [dataTableM, setDataTableM] = useState([]);
const [textValue, setTextValue] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const fetchApiData = async (page, textValue) => {
setLoading(true);
const response = await axios.get(
`province_wage/?search=filter&value=${textValue}&page=${page}&page_size=${perPage}&type=archive&role=${getRoleFromUrl()}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
dispatch(LOADING_END());
};
const [page, setPage] = useState(0);
const handleChangePageM = (event, newPage) => {
dispatch(LOADING_START());
setPage(newPage);
fetchApiData(newPage + 1, textValue);
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
const handlePerRowsChange = async (newPerPage, page) => {
setLoading(true);
const response = await axios.get(
`province_wage/?search=filter&value=${textValue}&page=${page}&page_size=${newPerPage}&type=archive&role=${getRoleFromUrl()}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
};
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, withDate]);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`province_wage/?search=filter&value=${textValue}&type=archive&role=${getRoleFromUrl()}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
const updateTable = () => {
fetchApiData(1);
};
const columns = [
{
name: "کدسفارش",
selector: (item) => item?.provinceRequest?.orderCode,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "مرغدار (تلفن)",
selector: (item) =>
`${item?.provinceRequest?.poultryFullname} (${item?.provinceRequest?.poultryMobile})`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "شهر",
selector: (item) => `${item?.provinceRequest?.poultryCity}`,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "تاریخ کشتار",
selector: (item) => formatJustDate(item?.provinceRequest?.sendDate),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "محل کشتار",
selector: (item) => item?.provinceRequest?.killPlace,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
},
{
name: "نژاد",
selector: (item) => item?.provinceRequest?.breed,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "تعداد (قطعه)",
selector: (item) =>
item?.provinceRequest?.provinceKillRequestQuantity?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "وزن (کیلوگرم)",
selector: (item) =>
item?.provinceRequest?.provinceKillRequestTotalWeight?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "میانگین وزنی (کیلوگرم)",
selector: (item) => item?.provinceRequest?.indexWeight,
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "تعرفه اتحادیه (ریال)",
selector: (item) => item?.provinceRequest?.wage?.toLocaleString(),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "مبلغ کل تعرفه (ریال)",
selector: (item) => (
<ProvinceUnpaidFeeView item={item} updateTable={updateTable} />
),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "جزییات سفارش",
selector: (item) => (
<Button
onClick={() => {
dispatch(
DRAWER({
top: true,
title: "جزییات سفارش",
content: <SlaughterUnpaidFeesDetails item={item} />,
})
);
}}
>
مشاهده
</Button>
),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
{
name: "اطلاعات بایگانی",
selector: (item) => (
<Button
onClick={() => {
dispatch(
OPEN_MODAL({
title: "اطلاعات بایگانی",
content: <ProvinceArchiveFeeDetails item={item} />,
})
);
}}
>
مشاهده
</Button>
),
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
// width: "90px",
},
];
const tableTitle = (
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>بایگانی</Typography>
<Grid
container
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "10px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
>
<Grid>
<FormControlLabel
control={
<Checkbox
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary" // Change the color to your preference
/>
}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
id="outlined-basic"
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}kill_house_wage_archive_excel/?key=${userInfo?.userProfile?.key}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
);
const isMobile = window.innerWidth <= 600;
useEffect(() => {
const d = data?.map((item, i) => {
return [
item?.provinceRequest?.orderCode,
`${item?.provinceRequest?.poultryFullname} (${item?.provinceRequest?.poultryMobile})`,
`${item?.provinceRequest?.poultryCity}`,
<ProvinceUnpaidFeeView key={i} item={item} updateTable={updateTable} />,
<Button
key={i}
onClick={() => {
dispatch(
DRAWER({
top: true,
title: "جزییات سفارش",
content: <SlaughterUnpaidFeesDetails item={item} />,
})
);
}}
>
مشاهده
</Button>,
<Button
key={i}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "اطلاعات بایگانی",
content: <ProvinceArchiveFeeDetails item={item} />,
})
);
}}
>
مشاهده
</Button>,
];
});
setDataTableM(d);
}, [data]);
return (
<Grid>
{isMobile ? (
<Grid container justifyContent="center" gap={SPACING.SMALL}>
{tableTitle}
<BoxList
columns={[
"عملیات",
"ردیف",
"نام فارم",
"مرغدار",
"شهر/تعاونی",
"تاریخ جوجه ریزی",
"نژاد",
"سن",
"تعداد جوجه ریزی",
"مانده در سالن",
]}
data={dataTableM}
/>
<Pagination
count={Math.ceil(totalRows / 10)}
page={page + 1}
variant="outlined"
onChange={(event, newPage) => {
handleChangePageM(event, newPage - 1);
}}
/>
</Grid>
) : (
<PageTable
title={tableTitle}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
)}
</Grid>
);
};
const ProvinceUnpaidFeeView = ({ item, updateTable }) => {
const isBigger =
item?.provinceRequest.prevTotalAmount < item?.provinceRequest.totalAmount;
const isSmaller =
item?.provinceRequest.prevTotalAmount > item?.provinceRequest.totalAmount;
const prevTotalAmount = (
<Grid container direction="column">
<Grid>{isBigger && "افزایش یافته"}</Grid>
<Grid>{isSmaller && "کاهش یافته"}</Grid>
<Grid>
مبلغ اولیه: {item?.provinceRequest?.prevTotalAmount?.toLocaleString()}
</Grid>
<Grid>ویرایش کننده: {item?.totalAmountEditor?.fullname}</Grid>
<Grid>تاریخ ویرایش: {formatJustDate(item?.totalAmountEditor?.date)}</Grid>
<Grid>سمت: {getFaUserRole(item?.totalAmountEditor?.role)}</Grid>
<Grid>تلفن: {item?.totalAmountEditor?.mobile}</Grid>
</Grid>
);
return (
<Grid container alignItems="center" direction="column">
{Boolean(item?.provinceRequest?.prevTotalAmount) && isBigger && (
<Grid container alignItems="center">
<KeyboardDoubleArrowUpIcon style={{ color: "red" }} />
<HtmlTooltip title={prevTotalAmount}>
<Typography style={{ color: "red" }} variant="caption">
{item?.provinceRequest?.totalAmount?.toLocaleString()}
</Typography>
</HtmlTooltip>
</Grid>
)}
{Boolean(item?.provinceRequest?.prevTotalAmount) && isSmaller && (
<Grid container alignItems="center">
<KeyboardDoubleArrowDownIcon style={{ color: "green" }} />
<Tooltip title={prevTotalAmount}>
<Typography style={{ color: "green" }} variant="caption">
{item?.provinceRequest?.totalAmount?.toLocaleString()}
</Typography>
</Tooltip>
</Grid>
)}
{!item?.provinceRequest?.prevTotalAmount && (
<Typography color={"success"} variant="caption">
{item?.provinceRequest?.totalAmount?.toLocaleString()}
</Typography>
)}
</Grid>
);
};
const HtmlTooltip = styled(({ className, ...props }) => (
<Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
[`& .${tooltipClasses.tooltip}`]: {
backgroundColor: "#f5f5f9",
color: "rgba(0, 0, 0, 0.87)",
maxWidth: 220,
border: "1px solid #dadde9",
},
}));

View File

@@ -0,0 +1,133 @@
import { Card, IconButton, TextField, Typography } from "@mui/material";
import { useContext, useEffect, useState } from "react";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import PlagiarismIcon from "@mui/icons-material/Plagiarism";
import { useNavigate } from "react-router-dom";
import { ROUTE_SLAUGHTER_FILE } from "../../../../routes/routes";
import { formatJustDate } from "../../../../utils/formatTime";
import { DatePicker } from "@mui/x-date-pickers";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import moment from "moment/moment";
import { AppContext } from "../../../../contexts/AppContext";
import { useDispatch, useSelector } from "react-redux";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { avicultureGetRequests } from "../../../aviculture/services/aviculture-requests";
export const SlaughterArchivedRequests = () => {
const navigate = useNavigate();
const dispatch = useDispatch();
const [dataTable, setDataTable] = useState([]);
const { avicultureRequests } = useSelector((state) => state.avicultureSlice);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
useEffect(() => {
dispatch(LOADING_START());
dispatch(avicultureGetRequests({ selectedDate1, selectedDate2 })).then(
() => {
dispatch(LOADING_END());
}
);
}, [selectedDate1, selectedDate2]);
useEffect(() => {
const filteredData = avicultureRequests?.filter(
(item, i) => item.inspector != null
);
const d = filteredData?.map((item, i) => {
return [
i + 1,
item.orderCode,
formatJustDate(item?.createDate),
formatJustDate(item?.sendDate),
item?.process?.poultry?.poultryName,
item?.process?.poultry?.poultryMobile,
item?.process?.poultry?.poultryCity,
item?.process?.poultry?.poultryProvince,
item?.process?.poultry?.age,
item?.process?.poultry?.poultryQuantity,
<IconButton
key={i}
aria-label="delete"
color="primary"
onClick={() =>
navigate(
ROUTE_SLAUGHTER_FILE + item?.process?.poultry?.poultryRequestId
)
}
>
<PlagiarismIcon />
</IconButton>,
];
});
setDataTable(d);
}, [avicultureRequests]);
const [tableDataCol] = useState([
"ردیف",
"کد سفارش",
"تاریخ ثبت درخواست",
"تاریخ درخواست",
"مرغدار",
"تلفن مرغدار",
"شهر",
"استان",
"سن مرغ",
"تعداد",
"مشاهده",
]);
return (
<Card>
<AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography>درخواست های بایگانی شده</Typography>
</Grid>
<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>
}
expandable
columns={tableDataCol}
data={dataTable}
/>
</Card>
);
};

View File

@@ -0,0 +1,109 @@
import { Box, Typography } from "@mui/material";
import { useSelector } from "react-redux";
import { useState, useEffect } from "react";
import { slaughterGetKillHouseLockInfoService } from "../../services/slaughter-get-kill-house-lock-info";
import lockDollarIcon from "../../../../assets/images/lock-dollar.svg";
import lockAnbarIcon from "../../../../assets/images/lock-anbar.svg";
export const SlaughterBalanceStatusButton = () => {
const userRoles = useSelector((state) => state.userSlice.role);
const [lockInfo, setLockInfo] = useState(null);
const hasOnlyKillHouseRole = userRoles && userRoles.includes("KillHouse");
useEffect(() => {
if (hasOnlyKillHouseRole) {
const fetchLockInfo = async () => {
try {
const data = await slaughterGetKillHouseLockInfoService();
setLockInfo(data);
} catch (error) {
console.error("Error fetching lock info:", error);
setLockInfo(null);
}
};
fetchLockInfo();
}
}, [hasOnlyKillHouseRole, userRoles]);
return (
<Box
sx={{
width: { xs: "fit-content" },
display: "flex",
alignItems: "center",
gap: 0.25,
px: { xs: 1, md: 2 },
height: { xs: "36px", md: "46px" },
borderRadius: { xs: "8px", md: "12px" },
cursor: "pointer",
transition: "all 0.3s ease",
border: "0.25px solid rgba(151, 151, 151, 0.3)",
"&:hover": {
boxShadow: "0 4px 12px rgba(0,0,0,0.15)",
},
}}
style={{
background:
"linear-gradient(90deg, #FFFFFF 0%, #FFF4F4 78.85%, #FFE2E2 100%)",
}}
>
{lockInfo?.wageLock && (
<Box
component="img"
src={lockDollarIcon}
alt="dollar"
sx={{
width: { xs: "22px", md: "30px" },
height: { xs: "22px", md: "30px" },
marginLeft: { xs: "8px", md: "8px" },
}}
title="به علت بدهی"
/>
)}
{lockInfo?.wareHouseLock && (
<Box
component="img"
src={lockAnbarIcon}
alt="anbar"
sx={{
width: { xs: "22px", md: "30px" },
height: { xs: "22px", md: "30px" },
marginLeft: { xs: "12px", md: "12px" },
marginRight: { xs: "12px", md: "16px" },
}}
title="به علت پر بودن انبار"
/>
)}
<Typography
variant="h6"
sx={{
color:
lockInfo?.wageLock === true
? "rgba(235, 87, 87, 1)"
: "rgba(0, 0, 0, 1)",
fontWeight: "medium",
fontSize: { xs: "14px", md: "18px" },
marginLeft: { xs: "auto", sm: 0 },
}}
>
{(lockInfo?.wage || 0).toLocaleString()}
</Typography>
<Typography
variant="h6"
sx={{
color:
lockInfo?.wageLock === true
? "rgba(235, 87, 87, 1)"
: "rgba(0, 0, 0, 1)",
fontWeight: "medium",
fontSize: { xs: "10px", md: "12px" },
marginLeft: "2px",
marginTop: { xs: "1px", md: "2px" },
}}
>
ریال
</Typography>
</Box>
);
};

View File

@@ -0,0 +1,878 @@
import {
Button,
IconButton,
Pagination,
Tab,
Tabs,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { format } from "date-fns-jalali";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
// import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { Grid } from "../../../../components/grid/Grid";
// import { vetFarmGetAllocatedService } from "../../services/vet-farm-get-allocated";
import { DatePicker } from "@mui/x-date-pickers";
import CancelIcon from "@mui/icons-material/Delete";
import moment from "moment/moment";
import { SPACING } from "../../../../data/spacing";
import { AppContext } from "../../../../contexts/AppContext";
import axios from "axios";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
// import { getFaUserRole } from "../../../../utils/getFaUserRole";
import { PageTable } from "../../../../components/page-table/PageTable";
import { SimpleTable } from "../../../../components/simple-table/SimpleTable";
import { VetFarmCancelBar } from "../../../vet-farm/components/vet-farm-cancel-bar/VetFarmCancelBar";
import { VetFarmEditTrafficCode } from "../../../vet-farm/components/vet-farm-edit-traffic-code/VetFarmEditTrafficCode";
import { VetFarmDeletedBars } from "../../../vet-farm/components/vet-farm-deleted-bars/VetFarmDeletedBars";
import { ProvinceBarDifference } from "../../../province/components/province-bar-difference/ProvinceBarDifference";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SlaughterBarDashboardService } from "../../services/slaughter-bar-dashbored";
import { SlaughterEnterNoneReciept } from "../slaughter-enter-none-receipt/SlaughterEnterNoneReciept";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterBars = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [selectedTab, setSelectedTab] = useState(0);
const handleTabChange = (event, newValue) => {
setSelectedTab(newValue);
};
const dispatch = useDispatch();
const [dataTableM, setDataTableM] = useState([]);
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [dashboardData, setDashboardData] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const fetchApiData = async (page) => {
setLoading(true);
const response = await axios.get(
`kill_house_request_bar_management/?check&search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setLoading(false);
dispatch(LOADING_END());
};
const [page, setPage] = useState(0);
const handleChangePageM = (event, newPage) => {
dispatch(LOADING_START());
setPage(newPage);
fetchApiData(newPage + 1, textValue);
};
const handlePageChange = (page) => {
fetchApiData(page);
};
const handlePerRowsChange = async (newPerPage, page) => {
setLoading(true);
const response = await axios.get(
`kill_house_request_bar_management/?check&search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${newPerPage}`
);
setData(response.data.results);
setTotalRows(response.data.count);
setPerPage(newPerPage);
setLoading(false);
};
useEffect(() => {
fetchApiData(1);
}, []);
// const updateTable = () => {
// fetchApiData(1);
// };
const btnDisabled = !(
getRoleFromUrl() === "ProvinceOperator" || getRoleFromUrl() === "CityVet"
);
const columns = [
{
name: "کدبار",
selector: (item) => {
return item.barCode;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "خریدار",
selector: (item) => {
return `${item.killhouseUser?.name} (${item.killhouseUser?.killHouseOperator?.user?.mobile})`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کشتارکن اختصاصی",
selector: (item) => {
return item?.killer
? `${item?.killer?.name} (${item?.killer?.killHouseOperator?.user?.mobile})`
: "-";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "ماشین",
selector: (item) => {
return `${item.addCar.driver.typeCar} ${item.addCar.driver.pelak}`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "راننده",
selector: (item) => {
return `${item.addCar.driver.driverName} (${item.addCar.driver.driverMobile})`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کد بهداشتی حمل و نقل",
selector: (item) => {
return item?.trafficCode;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "قیمت مرغ زنده‌ی بار",
selector: (item) => {
return item?.amount?.toLocaleString() + " ﷼";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
// width: "80px",
},
{
name: "نژاد",
selector: (item) => {
return item.poultryRequest.chickenBreed;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "50px",
},
{
name: "تعداد اولیه",
selector: (item) => {
return item.quantity?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "50px",
},
{
name: "وزن اولیه بار (کیلوگرم)",
selector: (item) => {
return item?.weightInfo?.weight?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
{
name: "میانگین وزن اولیه (کیلوگرم)",
selector: (item) => {
return item?.weightInfo?.indexWeight?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
{
name: "قیمت مرغدار",
selector: (item) => {
return item?.poultryRequest?.amount?.toLocaleString() + " ﷼";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "قیمت کشتارگاه",
selector: (item) => {
return item?.weightInfo?.killHousePrice?.toLocaleString() + " ﷼";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "مرغدار",
selector: (item) => {
return `${item.poultryRequest?.poultry?.unitName} (${item.poultryRequest.poultry?.user?.mobile})`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "دامپزشک فارم",
selector: (item) => {
return item?.vetFarm?.vet?.user?.fullname
? item?.vetFarm?.vet?.user?.fullname +
`(${item?.vetFarm?.vet?.user?.mobile})`
: "فاقد دامپزشک";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "100px",
},
{
name: "کدرهگیری سامانه قرنطینه",
selector: (item) => {
return (
<>
{getRoleFromUrl() === "VetFarm" ||
getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "CityVet" ||
getRoleFromUrl() === "VetSupervisor" ? (
<>
{item?.registerar?.date
? `${format(
new Date(item?.registerar?.date),
"yyyy/MM/dd"
)} ${item?.registerar?.name}`
: ""}
</>
) : item?.clearanceCode ? (
item?.clearanceCode && (
<CheckCleanceCode clearanceCode={item?.clearanceCode} />
)
) : (
"-"
)}
</>
);
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "200px",
},
{
name: "محل کشتار",
selector: (item) => {
return `${item.killPlace}`;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "شهر",
selector: (item) => {
return item.poultryRequest.poultry.address.city.name;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "تاریخ کشتار",
selector: (item) => {
return item?.poultryRequest.sendDate
? format(new Date(item?.poultryRequest.sendDate), "yyyy/MM/dd")
: "-";
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "80px",
},
{
name: "کدسفارش کشتار",
selector: (item) => {
return item?.poultryRequest.orderCode;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
{
name: "تعداد نهایی",
selector: (item) => {
return item.acceptedRealQuantity?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "50px",
},
{
name: "وزن نهایی بار (کیلوگرم)",
selector: (item) => {
return item?.acceptedRealWeight?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
{
name: "میانگین وزن نهایی (کیلوگرم)",
selector: (item) => {
return item?.weightInfo?.finalIndexWeight?.toLocaleString();
},
sortable: true,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
{
name: "وضعیت",
selector: (item) => {
let state = "";
if (item.vetState === "accepted") {
state = "تایید تخلیه";
} else if (item.vetState === "pending") {
state = "در انتظار تخلیه";
}
return state;
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "90px",
},
{
name: "عملیات",
selector: (item) => {
return (
<Grid key={item.barCode}>
<Tooltip title="لغو بار">
<IconButton
disabled={btnDisabled}
color="error"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "لغو بار",
content: (
<VetFarmCancelBar
updateTable={updateTable}
killHouseRequestKey={item.key}
/>
),
})
);
}}
>
<CancelIcon />
</IconButton>
</Tooltip>
</Grid>
);
},
sortable: false,
wrap: true,
allowOverflow: true,
center: true,
width: "70px",
},
];
const handleDateChange1 = (date) => {
setSelectedDate1(date);
};
const handleDateChange2 = (date) => {
setSelectedDate2(date);
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage, selectedSubUser?.key]);
const [textValue, setTextValue] = useState("");
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
fetchApiData(1);
dispatch(
SlaughterBarDashboardService({
selectedDate1,
selectedDate2,
textValue,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
}, [dispatch, selectedDate1, selectedDate2, selectedSubUser?.key]);
const handleSubmit = async (event) => {
event.preventDefault();
setLoading(true);
try {
const response = await axios.get(
`kill_house_request_bar_management/?check&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&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);
dispatch(
SlaughterBarDashboardService({
selectedDate1,
selectedDate2,
textValue,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
} catch (error) {
console.error("Error fetching data:", error);
} finally {
setLoading(false);
}
};
const updateTable = () => {
fetchApiData(1);
};
const tableTitle = (
<Grid
container
alignItems="start"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Typography>مدیریت بارها</Typography>
<Grid style={{ width: "150px" }}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={(e) => {
handleDateChange1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid style={{ width: "150px" }}>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={(e) => {
handleDateChange2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
autoComplete="off"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}bar_excel/?start=${selectedDate1}&end=${selectedDate2}&key=${userKey}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}`}
rel="noreferrer"
>
<Button disabled={!data[0]?.killhouseUser?.key} color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
);
const getItemState = (item) => {
let state = "";
if (item.vetState === "accepted") {
state = "تایید تخلیه";
} else if (item.vetState === "pending") {
state = "در انتظار تخلیه";
}
return state;
};
const columnNames = columns.map((column) => column.name);
const isMobile = window.innerWidth <= 600;
useEffect(() => {
const d = data?.map((item, i) => {
return [
item.barCode,
`${item.killhouseUser?.name} (${item.killhouseUser?.killHouseOperator?.user?.mobile})`,
`${item.addCar.driver.typeCar} ${item.addCar.driver.pelak}`,
`${item.addCar.driver.driverName} (${item.addCar.driver.driverMobile})`,
<VetFarmEditTrafficCode
key={i}
updateTable={updateTable}
killHouseRequestKey={item.key}
trafficCode={item?.trafficCode}
isEditable={
getRoleFromUrl() === "VetFarm" ||
getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "KillHouseVet" ||
getRoleFromUrl() === "KillHouse" ||
getRoleFromUrl() === "CityVet" ||
getRoleFromUrl() === "VetSupervisor"
}
/>,
item.poultryRequest.chickenBreed,
item.quantity?.toLocaleString(),
item?.weightInfo?.weight?.toLocaleString(),
item?.weightInfo?.indexWeight?.toLocaleString(),
`${item.poultryRequest?.poultry?.unitName} (${item.poultryRequest.poultry?.user?.mobile})`,
item?.vetFarm?.vet?.user?.fullname
? item?.vetFarm?.vet?.user?.fullname +
`(${item?.vetFarm?.vet?.user?.mobile})`
: "فاقد دامپزشک",
<>
{getRoleFromUrl() === "VetFarm" ||
getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "CityVet" ||
getRoleFromUrl() === "VetSupervisor" ? (
<>
{item?.registerar?.date
? `${format(new Date(item?.registerar?.date), "yyyy/MM/dd")} ${
item?.registerar?.name
}`
: ""}
</>
) : item?.clearanceCode ? (
item?.clearanceCode
) : (
"-"
)}
</>,
`${item.killPlace}`,
item.poultryRequest.poultry.address.city.name,
item?.poultryRequest.sendDate
? format(new Date(item?.poultryRequest.sendDate), "yyyy/MM/dd")
: "-",
item?.poultryRequest.orderCode,
getItemState(item),
<Grid key={item.barCode}>
<Tooltip title="لغو بار">
<IconButton
disabled={btnDisabled}
color="error"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "لغو بار",
content: (
<VetFarmCancelBar
updateTable={updateTable}
killHouseRequestKey={item.key}
/>
),
})
);
}}
>
<CancelIcon />
</IconButton>
</Tooltip>
</Grid>,
];
});
setDataTableM(d);
}, [data]);
const tabs = (
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={selectedTab}
onChange={handleTabChange}
>
<Tab label="اطلاعات بارها" value={0} />
<Tab label="بارهای حذف شده" value={1} />
<Tab label="اختلاف کشتار" value={2} />
<Tab label="عدم وصول" value={3} />
</Tabs>
);
return (
<Grid container direction="column" flexWrap="nowrap" mt={SPACING.SMALL}>
<Grid container justifyContent="center" mb={SPACING.MEDIUM}>
{tabs}
</Grid>
{selectedTab === 0 && (
<Grid
container
mt={SPACING.MEDIUM}
alignItems="center"
justifyContent="center"
>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد بار",
"تعداداولیه بار",
"وزن اولیه بار",
"میانگین وزن اولیه",
"تعداد بار های دارای کد قرنطینه",
"تعداد نهایی بار",
"وزن نهایی بار",
"میانگین وزن نهایی ",
]}
data={[
[
dashboardData?.count?.toLocaleString(),
dashboardData?.firstQuantity?.toLocaleString(),
dashboardData?.firstWeight?.toLocaleString(),
dashboardData?.firstIndexWeight?.toLocaleString(),
dashboardData?.clearanceCodeCount?.toLocaleString(),
dashboardData?.acceptedRealQuantity?.toLocaleString(),
dashboardData?.acceptedRealWeight?.toLocaleString(),
dashboardData?.finalIndexWeight?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
{isMobile ? (
<Grid container justifyContent="center" gap={SPACING.SMALL}>
{tableTitle}
<SimpleTable columns={columnNames} data={dataTableM} />
<Pagination
count={Math.ceil(totalRows / 10)}
page={page + 1}
variant="outlined"
onChange={(event, newPage) => {
handleChangePageM(event, newPage - 1);
}}
/>
</Grid>
) : (
<PageTable
title={tableTitle}
columns={columns}
data={data}
progressPending={loading}
pagination
paginationServer
paginationTotalRows={totalRows}
onChangeRowsPerPage={handlePerRowsChange}
onChangePage={handlePageChange}
/>
)}
{/* <AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography>مدیریت بارها</Typography>
</Grid>
<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>
{getRoleFromUrl() === "ProvinceOperator" && (
<Tooltip title="خروجی اکسل">
<a
href={`${axios.defaults.baseURL}bar_excel/?start=${selectedDate1}&end=${selectedDate2}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
)}
</Grid>
}
columns={[
"ردیف",
"کدبار",
"دامپزشک فارم",
"ماشین",
"راننده",
// "کد بهداشتی",
"خریدار",
"محل کشتار",
"مرغدار",
"شهر",
"تاریخ کشتار",
"کدسفارش کشتار",
"نژاد",
"تعداد",
"کد بهداشتی حمل و نقل",
"کدرهگیری سامانه قرنطینه",
"وضعیت",
"عملیات",
]}
data={dataTable}
/> */}
</Grid>
)}
{selectedTab === 1 && (
<Grid mt={SPACING.MEDIUM}>
<VetFarmDeletedBars />
</Grid>
)}
{selectedTab === 2 && (
<Grid mt={SPACING.MEDIUM}>
<ProvinceBarDifference />
</Grid>
)}
{selectedTab === 3 && (
<Grid mt={SPACING.MEDIUM}>
<SlaughterEnterNoneReciept />
</Grid>
)}
</Grid>
);
};
// const DisabledWrap = ({ children, checked }) => {
// return <Grid style={{ opacity: !checked ? 0.5 : 1 }}>{children}</Grid>;
// };

View File

@@ -0,0 +1,136 @@
import React, { useContext, useState } from "react";
import { Button, IconButton, Popover } from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { Grid } from "../../../../components/grid/Grid";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteAllocatedService } from "../../services/salughter-delete-allocated";
import { SlaughterAllocateToGuild } from "../slaughter-allocate-to-guild/SlaughterAllocateToGuild";
export const SlaughterColdHouseBarsOperations = ({
fetchApiData,
item,
fetchData,
updateTable,
priceInfo,
remainWeight,
}) => {
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 [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
return (
<Grid container>
<IconButton
disabled={item?.registrationCode}
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",
display: "flex",
flexDirection: "column",
}}
>
<Grid container direction="column" gap={1}>
<Button
variant="outlined"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص",
content: (
<SlaughterAllocateToGuild
updateTable={fetchData}
fetchApiData={fetchApiData}
sellerType={"ColdHouse"}
sellType="exclusive"
coldHouseItemKey={item?.key}
priceInfo={priceInfo}
editData={item}
remainWeight={remainWeight}
/>
),
})
);
}}
>
ویرایش
</Button>
<Button
size="small"
disabled={item?.registrationCode}
variant="outlined"
color="error"
onClick={() => {
handleClose();
dispatch(
slaughterDeleteAllocatedService({
steward_allocation_key: item.key,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
fetchApiData(1);
fetchData();
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
حذف
</Button>
</Grid>
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,414 @@
import React, { useContext, useEffect, useState } from "react";
import {
Button,
IconButton,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import {
CLOSE_MODAL,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { formatJustDate } from "../../../../utils/formatTime";
import { RiFileExcel2Fill } from "react-icons/ri";
import { AppContext } from "../../../../contexts/AppContext";
import { RiSearchLine } from "react-icons/ri";
import { SlaughterColdHouseBarsOperations } from "../slaughter-cold-house-bars-operations/SlaughterColdHouseBarsOperations";
import { getKillhouseApprovedPriceState } from "../../../province/services/get-approved-price-state";
import { slaughterInventoryFinalSubmitService } from "../../services/slaughter-inventory-final-submit";
import { SPACING } from "../../../../data/spacing";
import { SlaughterAllocateToColdHouse } from "../slaughter-allocate-to-coldhouse/SlaughterAllocateToColdHouse";
import TuneIcon from "@mui/icons-material/Tune";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterColdHouseBars = ({
selectedDate1,
selectedDate2,
title,
type,
withDate,
coldHouseKey,
getDashboardsData,
remainWeight,
}) => {
const dispatch = useDispatch();
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [openNotif] = useContext(AppContext);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const { priceInfo } = useSelector((state) => state.slaughterSlice);
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 fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`steward-allocation/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${page}&page_size=${perPage}&cold_house=true&type=${type}&cold_house_key=${coldHouseKey}`
);
dispatch(
getKillhouseApprovedPriceState({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
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);
};
const getLastItem = (item) => {
if (item?.systemRegistrationCode) {
return [
<IconButton
key={item}
disabled={true}
aria-label="disabled-button"
color="primary"
>
<TuneIcon />
</IconButton>,
];
} else {
return [
<SlaughterColdHouseBarsOperations
key={item}
fetchData={updateTable}
fetchApiData={getDashboardsData}
item={item}
priceInfo={priceInfo}
remainWeight={remainWeight}
/>,
];
}
};
const getLastItemTitle = () => {
if (type === "output") {
return ["عملیات"];
} else {
return [];
}
};
const isOut = title === "بارهای خارج شده";
useEffect(() => {
const d = data?.map((item, i) => {
let registrationCodeStatus;
switch (item?.receiverState) {
case "pending":
registrationCodeStatus = "در انتظار تایید";
break;
case "accepted":
registrationCodeStatus = "تایید شده";
break;
case "rejected":
registrationCodeStatus = "رد شده";
break;
default:
registrationCodeStatus = "-";
}
console.log(item);
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
formatJustDate(item?.date),
isOut
? item?.toGuilds
? "سردخانه به صنف"
: item?.otherColdHouse
? "سردخانه به سردخانه"
: item?.toStewards
? "سردخانه به مباشر"
: "-"
: `${item?.killHouse ? "کشتارگاه" : "سردخانه"} به سردخانه`,
isOut
? item?.toGuilds
? `${item?.toGuilds?.guildsName} ( ${item?.toGuilds?.user?.fullname} ${item?.toGuilds?.user?.mobile})`
: item?.otherColdHouse
? item?.otherColdHouse?.steward
? `${item?.otherColdHouse?.steward?.user?.fullname} (${item?.otherColdHouse?.steward?.user?.mobile})`
: `${item?.otherColdHouse?.killHouse?.name} (${item?.otherColdHouse?.killHouse?.killHouseOperator?.user?.fullname} ${item?.otherColdHouse?.killHouse?.killHouseOperator?.user?.mobile})`
: `${item?.toStewards?.name} (${item?.toStewards?.user?.fullname}${item?.toStewards?.user?.mobile})`
: item?.killHouse
? `${item.toColdHouse?.name} (${item.toColdHouse?.killHouse?.killHouseOperator?.user?.mobile})`
: `${item?.otherColdHouse?.name} ${item?.otherColdHouse?.killHouse?.killHouseOperator?.user?.mobile}`,
item?.sellType === "exclusive" ? "اختصاصی" : "آزاد",
item?.amount?.toLocaleString() + " ریال",
item?.totalAmount?.toLocaleString() + " ریال",
// item?.realNumberOfCarcasses?.toLocaleString(),
item?.realWeightOfCarcasses?.toLocaleString(),
item?.loggedRegistrationCode?.toLocaleString(),
item?.systemRegistrationCode === true
? "کد احراز ارسال شده"
: "در انتظار ارسال کد احراز",
registrationCodeStatus,
// item?.receiverRealNumberOfCarcasses?.toLocaleString(),
// item?.receiverRealWeightOfCarcasses?.toLocaleString(),
...getLastItem(item),
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [
dispatch,
selectedDate1,
selectedDate2,
perPage,
withDate,
selectedSubUser?.key,
]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`steward-allocation/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}&cold_house=true&type=${type}&cold_house_key=${coldHouseKey}`
);
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(
OPEN_MODAL({
title: "ثبت توزیع/ فروش سرد خانه",
content: (
<SlaughterAllocateToColdHouse
updateTable={getDashboardsData}
sellerType={"ColdHouse"}
sellType="exclusive"
fetchApiData={fetchApiData}
coldHouseKey={coldHouseKey}
remainWeight={remainWeight}
/>
),
})
);
}}
>
ثبت توزیع/ فروش
</Button>
{type === "output" && (
<Button
disabled={!tableData.length}
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();
fetchApiData(1);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
تایید نهایی (یکجا)
</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>
<Tooltip title="خروجی اکسل">
{/* <a
href={`${axios.defaults.baseURL}0/hatching_excel/`}
rel="noreferrer"
> */}
<Button
color="success"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}cold_house_excel/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}&type=${type}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&cold_house_key=${coldHouseKey}&cold_house=true`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ ثبت",
"نوع تخصیص",
"مشخصات خریدار",
"نوع فروش",
"قیمت هر کیلو",
"قیمت کل",
// "حجم تخصیصی",
"وزن تخصیصی",
"کد احراز",
"وضعیت کد احراز",
"وضعیت",
// "حجم تایید شده",
// "وزن تایید شده",
...getLastItemTitle(),
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={title}
/>
</Grid>
);
};

View File

@@ -0,0 +1,118 @@
// import { Button, IconButton, Popover, Tooltip } from "@mui/material";
// import { useContext, useState } from "react";
// import TuneIcon from "@mui/icons-material/Tune";
// import { Grid } from "../../../../components/grid/Grid";
// import DeleteIcon from "@mui/icons-material/Delete";
// import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
// import { slaughterDeleteDailyListService } from "../../services/slaughter-add-daily-list";
// import { useDispatch } from "react-redux";
// import { AppContext } from "../../../../contexts/AppContext";
// export const SlaughterDailyListOperation = ({ 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 [openNotif] = useContext(AppContext);
// return (
// <div>
// <IconButton
// aria-describedby={id}
// variant="contained"
// color="primary"
// onClick={handleClick}
// >
// <TuneIcon />
// </IconButton>
// <Popover
// anchorOrigin={{
// vertical: "top",
// horizontal: "center",
// }}
// transformOrigin={{
// vertical: "top",
// horizontal: "left",
// }}
// id={id}
// open={open}
// anchorEl={anchorEl}
// onClose={handleClose}
// >
// <div style={{ padding: "20px" }}>
// <Grid container direction="column">
// <Tooltip title={"حذف"} placement="left-start">
// <IconButton
// aria-label="delete"
// color="error"
// onClick={() => {
// handleClose();
// dispatch(
// OPEN_MODAL({
// title: "آیا مطمئن هستید؟",
// content: (
// <Grid container spacing={2}>
// <Grid item>
// <Button
// variant="contained"
// color="error"
// onClick={() => {
// dispatch(
// slaughterDeleteDailyListService(item?.key)
// ).then((r) => {
// if (r.payload.error) {
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: r.payload.error,
// severity: "error",
// });
// } else {
// updateTable();
// dispatch(CLOSE_MODAL());
// openNotif({
// vertical: "top",
// horizontal: "center",
// msg: r.payload.data.result,
// severity: "success",
// });
// }
// });
// }}
// >
// تایید
// </Button>
// </Grid>
// <Grid item>
// <Button
// onClick={() => {
// dispatch(CLOSE_MODAL());
// }}
// >
// لغو
// </Button>
// </Grid>
// </Grid>
// ),
// })
// );
// }}
// >
// <DeleteIcon />
// </IconButton>
// </Tooltip>
// </Grid>
// </div>
// </Popover>
// </div>
// );
// };

View File

@@ -0,0 +1,713 @@
import React, { useContext, useEffect, useRef, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetProductsService } from "../../services/slaughter-inventory-gets";
import InfoIcon from "@mui/icons-material/Info";
import TextField from "@mui/material/TextField";
import InputAdornment from "@mui/material/InputAdornment";
import SearchIcon from "@mui/icons-material/Search";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import {
Box,
Button,
Card,
CardContent,
IconButton,
Typography,
} from "@mui/material";
import { SlaughterAddDailyList } from "../slaughter-add-daily-list/SlaughterAddDailyList";
import {
slaughterDeleteDailyListService,
slaughterGetPriceService,
submitBatchAllocationsService,
} from "../../services/slaughter-add-daily-list";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import moment from "moment";
import { AppContext } from "../../../../contexts/AppContext";
import AddIcon from "@mui/icons-material/Add";
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
import DeleteIcon from "@mui/icons-material/Delete";
import { fixBase64 } from "../../../../utils/toBase64";
import { provincePolicyGetUploadImageService } from "../../../province/services/province-policy-upload-image";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterDailyList = () => {
const [productsTable, setProductsTable] = useState();
const [slaughterProducts, setSlaughterProducts] = useState();
const [prices, setPrices] = useState([]);
const [rendered, setrendered] = useState(false);
const [weights, setWeights] = useState([]);
const [data, setData] = useState([]);
const [tableData, setTableData] = useState([]);
const [searchTerm, setSearchTerm] = useState("");
const [filteredData, setFilteredData] = useState([]);
const [rowImages, setRowImages] = useState([]);
const [uploadPolicy, setUploadPolicy] = useState({
killHouseAllocation: true,
});
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [openNotif] = useContext(AppContext);
const priceRefs = useRef([]);
const weightRefs = useRef([]);
const imageRefs = useRef([]);
const dispatch = useDispatch();
const [priceState, setPriceState] = useState({
active: false,
killHousePrice: 0,
stewardPrice: 0,
guildPrice: 0,
});
const getPriceByRole = () => {
const role = getRoleFromUrl();
if (role === "KillHouse") return priceState.killHousePrice;
if (role === "Steward") return priceState.stewardPrice;
if (role === "Guilds") return priceState.guildPrice;
return 0;
};
useEffect(() => {
priceRefs.current = priceRefs.current.slice(0, data?.length || 0);
weightRefs.current = weightRefs.current.slice(0, data?.length || 0);
imageRefs.current = imageRefs.current.slice(0, data?.length || 0);
}, [data]);
useEffect(() => {
if (searchTerm) {
const filtered = tableData.filter((item) =>
item.some((cell) =>
String(cell).toLowerCase().includes(searchTerm.toLowerCase())
)
);
setFilteredData(filtered);
} else {
setFilteredData(tableData);
}
}, [searchTerm, tableData]);
const handleKeyDown = (e, index, fieldType) => {
if (e.key === "Enter") {
e.preventDefault();
if (fieldType === "price") {
const updatedPrices = [...prices];
updatedPrices[index] = Number(e.target.value.replace(/,/g, ""));
setPrices(updatedPrices);
if (weightRefs.current[index]) {
weightRefs.current[index].focus();
}
} else if (fieldType === "weight") {
const updatedWeights = [...weights];
updatedWeights[index] = Number(e.target.value.replace(/,/g, ""));
setWeights(updatedWeights);
if (uploadPolicy?.killHouseAllocation || rowImages[index]) {
if (imageRefs.current[index]) {
imageRefs.current[index].focus();
}
} else {
moveToNextField(index);
}
}
}
};
const moveToNextField = (currentIndex) => {
if (priceState?.active) {
let nextIndex = currentIndex + 1;
while (nextIndex < data.length) {
if (weightRefs.current[nextIndex]) {
weightRefs.current[nextIndex].focus();
break;
}
nextIndex++;
}
if (nextIndex >= data.length && weightRefs.current[0]) {
weightRefs.current[0]?.focus();
}
} else {
let nextIndex = currentIndex + 1;
while (nextIndex < data.length) {
if (priceRefs.current[nextIndex]) {
priceRefs.current[nextIndex].focus();
break;
}
nextIndex++;
}
if (nextIndex >= data.length && priceRefs.current[0]) {
priceRefs.current[0]?.focus();
}
}
};
const fetchUploadPolicy = () => {
dispatch(
provincePolicyGetUploadImageService({
role_key: checkPathStartsWith("slaughter") ? selectedSubUser?.key : "",
})
).then((r) => {
if (r.payload?.data) {
setUploadPolicy(r.payload.data);
}
});
};
const fetchPriceStatus = async () => {
dispatch(
slaughterGetPriceService({
role: getRoleFromUrl(),
role_key: selectedSubUser?.key,
})
).then((r) => {
setPriceState(r.payload.data);
});
};
const fetchApiData = async () => {
dispatch(LOADING_START());
try {
const response = await axios.get(
`commonly-used/?search=filter&value=&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=1&page_size=10000`
);
setrendered(true);
setData(response.data.results || []);
} catch (e) {
console.error(e);
} finally {
dispatch(LOADING_END());
}
};
const handleRowImageChange = (images, index) => {
const newRowImages = [...rowImages];
newRowImages[index] = images[0]
? {
...images[0],
base64: fixBase64(images[0]?.data_url),
}
: null;
setRowImages(newRowImages);
if (
(uploadPolicy?.killHouseAllocation && images[0]) ||
!uploadPolicy?.killHouseAllocation
) {
moveToNextField(index);
}
};
useEffect(() => {
const d = data?.map((item, i) => {
const getPrice =
parseInt(priceState?.active ? getPriceByRole() : prices[i]) *
parseInt(weights[i]);
return [
i + 1,
item?.guild?.steward ? "مباشر" : "صنف",
`${item?.guild?.guildsName}/${item?.guild?.user?.fullname}/${item?.guild?.user?.city}/${item?.guild?.user?.mobile}`,
item?.exclusive ? "اختصاصی" : "آزاد",
<NumberInput
allowLeadingZeros
thousandSeparator=","
key={`price-${i}`}
size="small"
label="قیمت"
disabled={priceState?.active}
value={priceState?.active ? getPriceByRole() : prices[i] || ""}
onKeyDown={(e) => handleKeyDown(e, i, "price")}
inputRef={(el) => (priceRefs.current[i] = el)}
variant="outlined"
style={{ width: 100 }}
/>,
<NumberInput
allowLeadingZeros
thousandSeparator=","
key={`weight-${i}`}
size="small"
label="وزن"
value={weights[i] || ""}
onKeyDown={(e) => {
handleKeyDown(e, i, "weight");
}}
inputRef={(el) => (weightRefs.current[i] = el)}
variant="outlined"
style={{ width: 100 }}
/>,
isNaN(getPrice) ? "وارد نشده! " : getPrice?.toLocaleString() + " ریال",
<div style={{ width: 150 }} key={i}>
<ImageUpload
onChange={(images) => handleRowImageChange(images, i)}
images={rowImages[i] ? [rowImages[i]] : []}
maxNumber={1}
title={"بارگزاری سند"}
required={uploadPolicy?.killHouseAllocation}
inputRef={(el) => (imageRefs.current[i] = el)}
/>
{uploadPolicy?.killHouseAllocation && !rowImages[i] && (
<Typography variant="caption" color="error">
تصویر الزامی است
</Typography>
)}
</div>,
<IconButton key={i} color="error">
<DeleteIcon onClick={() => handleDeleteItem(item.key)} />
</IconButton>,
];
});
setTableData(d);
setFilteredData(d);
}, [data, prices, weights, priceState, rowImages, uploadPolicy]);
useEffect(() => {
fetchApiData();
fetchPriceStatus();
fetchUploadPolicy();
dispatch(slaughterGetProductsService()).then((r) => {
setSlaughterProducts(r.payload.data);
});
}, [selectedSubUser?.key]);
useEffect(() => {
const d = slaughterProducts?.map((item) => {
return [item?.name, item?.totalRemainWeight?.toLocaleString()];
});
setProductsTable(d);
}, [slaughterProducts]);
const handleDeleteItem = (key) => {
dispatch(slaughterDeleteDailyListService(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(1);
}
});
};
const handleSendAllocate = () => {
const filteredData = data
?.map((item, i) => {
const price = priceState?.active ? getPriceByRole() : prices[i];
const weight = weights[i];
if (!price || !weight) return null;
if (uploadPolicy?.killHouseAllocation && !rowImages[i]) {
return null;
}
let d = {
seller_type: "KillHouse",
buyer_type: item?.guild?.steward ? "Steward" : "Guild",
guild_key: !item?.guild?.steward ? item?.guild?.key : null,
steward_key: item?.guild?.steward ? item?.guild?.key : null,
product_key: slaughterProducts[0]?.key,
type: "manual",
allocation_type: item?.guild?.steward
? "killhouse_steward"
: "killhouse_guild",
number_of_carcasses: 0,
weight_of_carcasses: weight,
sell_type: "free",
amount: price,
total_amount: price * weight,
approved_price_status: priceState?.active,
date: moment(new Date()).format("YYYY-MM-DD"),
};
if (rowImages[i]) {
d.image = rowImages[i]?.base64;
}
return Object.fromEntries(
Object.entries(d).filter(([_, value]) => value !== null)
);
})
.filter(Boolean);
if (filteredData.length === 0) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا حداقل یک رکورد معتبر با عکس وارد کنید",
severity: "error",
});
return;
}
dispatch(submitBatchAllocationsService(filteredData)).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",
});
setPrices([]);
setWeights([]);
setRowImages([]);
fetchApiData(1);
fetchPriceStatus();
fetchUploadPolicy();
dispatch(slaughterGetProductsService()).then((r) => {
setSlaughterProducts(r.payload.data);
});
}
});
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Grid container xs={12} justifyContent="center" alignItems="center">
<ResponsiveTable
noPagination
title={"موجودی انبار"}
columns={["محصول", "مانده انبار (کیلوگرم)"]}
data={productsTable}
customColors={[{ name: "محصول", color: "red" }]}
/>
</Grid>
<Grid
xs={12}
container
spacing={2}
mt={2}
alignItems="center"
justifyContent="space-between"
>
<Grid item xs={12} md="auto">
<Button
variant="contained"
startIcon={<AddIcon />}
sx={{ borderRadius: 3, px: 3 }}
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <SlaughterAddDailyList updateTable={fetchApiData} />,
title: "افزودن",
})
);
}}
>
افزودن مباشر/صنف
</Button>
</Grid>
<Grid item xs={12} md>
<Card variant="outlined" sx={{ borderRadius: 3, boxShadow: 1 }}>
<CardContent>
<Grid
container
spacing={2}
alignItems="center"
justifyContent="space-between"
>
<Grid item>
<Typography variant="subtitle2">
مجموع وزن وارد شده
</Typography>
<Typography variant="body1" color="text.secondary">
{weights?.length
? weights.reduce((a, b) => a + b, 0).toLocaleString()
: "۰"}
</Typography>
</Grid>
<Grid item>
<Typography variant="subtitle2">وزن باقیمانده</Typography>
<Typography
variant="body1"
color={
weights?.length && slaughterProducts
? weights?.reduce((a, b) => a + b, 0) >
slaughterProducts[0]?.totalRemainWeight
? "error"
: "text.secondary"
: "text.secondary"
}
>
{slaughterProducts?.[0]?.totalRemainWeight !== undefined
? weights?.length
? (
slaughterProducts[0]?.totalRemainWeight -
weights.reduce((a, b) => a + b, 0)
).toLocaleString()
: slaughterProducts[0]?.totalRemainWeight.toLocaleString()
: "۰"}
</Typography>
</Grid>
<Grid item>
<Button
variant="contained"
color="success"
startIcon={<CheckCircleIcon />}
sx={{ borderRadius: 3, px: 3 }}
onClick={handleSendAllocate}
disabled={
weights.length
? weights.reduce((a, b) => a + b, 0) >
slaughterProducts[0]?.totalRemainWeight ||
(uploadPolicy?.killHouseAllocation &&
data.some(
(_, i) =>
prices[i] &&
weights[i] &&
(!rowImages[i] || !rowImages[i].base64)
))
: true
}
>
ثبت
</Button>
</Grid>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
<Grid container xs={12} mt={2} gap={1} sx={{ userSelect: "none" }}>
<InfoIcon color="error" />
<Typography variant="body1" color="error">
پس از وارد کردن هر مقدار، کلید Enter را فشار دهید!
</Typography>
</Grid>
<Grid container xs={12} mt={2} gap={1} sx={{ userSelect: "none" }}>
<InfoIcon color="primary" />
<Typography variant="body1" color="primary">
صرفا تخصیصاتی که هر دو مقدار قیمت و وزن آنها را وارد کنید ثبت خواهند
شد.
</Typography>
</Grid>
<Grid container xs={12} mt={2}>
<TextField
fullWidth
variant="outlined"
placeholder="جستجو..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
sx={{ mb: 2 }}
/>
</Grid>
{filteredData?.length ? (
<Grid container xs={12} gap={1} mt={2} mb={2}>
{filteredData?.map((item, i) => (
<Grid
container
xs={12}
key={i}
sx={{
p: 2,
pl: 5,
borderRadius: 2,
backgroundColor: i % 2 === 0 ? "#fef6f0" : "#ffffff",
boxShadow: "0 2px 6px rgba(0,0,0,0.05)",
position: "relative",
flexDirection: "row",
gap: 2,
alignItems: "center",
}}
>
<Box
sx={{
position: "absolute",
top: 12,
left: 12,
backgroundColor: "#fb8c00",
width: 28,
height: 28,
borderRadius: "50%",
color: "#fff",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: "0.8rem",
fontWeight: 700,
boxShadow: "0 1px 4px rgba(0,0,0,0.2)",
}}
>
{item[0]}
</Box>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
ماهیت:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[1]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
خریدار:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[2]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
نوع فروش:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[3]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography variant="caption" color="text.secondary">
قیمت هرکیلو:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[4]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography variant="caption" color="text.secondary">
وزن لاشه:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[5]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
<Typography
variant="caption"
color="text.secondary"
sx={{ minWidth: 64 }}
>
قیمت کل:
</Typography>
<Typography variant="body2" fontSize="0.8rem">
{item[6]}
</Typography>
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
{item[7]}
</Grid>
<Grid
item
sx={{ display: "flex", alignItems: "center", gap: 0.5 }}
>
{item[8]}
</Grid>
<Grid item>
{!priceState?.active &&
(!prices[i] || !weights[i]) &&
(prices[i] || weights[i]) && (
<Typography variant="caption" color="error">
لطفا همه موارد را وارد کنید و کلید Enter را بزنید
</Typography>
)}
</Grid>
</Grid>
))}
</Grid>
) : (
<Typography mt={4}>
{!rendered
? searchTerm
? "نتیجه‌ای یافت نشد"
: "در حال بارگزاری..."
: "موردی یافت نشد!"}
</Typography>
)}
</Grid>
);
};

View File

@@ -0,0 +1,239 @@
import { useContext, useEffect } from "react";
import { useDispatch } from "react-redux";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import {
Autocomplete,
Button,
TextField,
FormControl,
FormHelperText,
} from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterEditAllocatedCarService } from "../../services/slaughter-edit-allocated-car";
import { useSlaughterHouseCars } from "../../../file/hooks/useSlaughterHouseCars";
import { PropTypes } from "prop-types";
export const SlaughterEditAllocatedCar = ({
item,
updateTable,
poultryRequestKey,
killHouseKey,
killRequestKey,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
// Get available cars
const slaughterHouseCars = useSlaughterHouseCars(
poultryRequestKey,
killHouseKey,
killRequestKey
);
const formik = useFormik({
initialValues: {
car: null,
trafficCode: item?.trafficCode || "",
amount: item?.barAmount || "",
},
validationSchema: Yup.object({
car: Yup.object().nullable(),
trafficCode: Yup.string(),
amount: Yup.number().positive("قیمت باید عدد مثبت باشد").nullable(),
}),
});
useEffect(() => {
formik.validateForm();
}, []);
// Set initial car if available
useEffect(() => {
if (slaughterHouseCars && item) {
const currentCar = slaughterHouseCars.find(
(car) => car.key === item.carKey
);
if (currentCar) {
formik.setFieldValue("car", currentCar);
}
}
}, [slaughterHouseCars, item]);
const handleSubmit = () => {
// Check if at least one field has a value
if (
!formik.values.car &&
!formik.values.trafficCode &&
!formik.values.amount
) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا حداقل یکی از فیلدها را پر کنید",
severity: "error",
});
return;
}
// Build request object
const requestData = {
key: item?.killHouseRequestKey,
};
// If user is updating car or traffic code, send all 3 parameters together
if (formik.values.car || formik.values.trafficCode) {
if (formik.values.car?.key && formik.values.car.key !== item?.carKey) {
requestData.car_key = formik.values.car.key;
}
if (
formik.values.trafficCode &&
formik.values.trafficCode !== item?.trafficCode
) {
requestData.traffic_code = formik.values.trafficCode;
}
if (formik.values.amount && formik.values.amount !== item?.barAmount) {
requestData.amount = formik.values.amount;
}
}
// If user is updating only amount, send only amount
else if (formik.values.amount) {
requestData.amount = formik.values.amount;
}
dispatch(slaughterEditAllocatedCarService(requestData)).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",
});
dispatch(CLOSE_MODAL());
if (updateTable) updateTable();
}
});
};
return (
<Grid
container
direction="column"
gap={SPACING.SMALL}
sx={{
width: {
xs: "300px",
md: "400px",
},
p: 2,
}}
>
<FormControl fullWidth>
<Autocomplete
options={slaughterHouseCars || []}
getOptionLabel={(car) => {
if (car) {
const carType = car.type === "exclusive" ? "اختصاصی" : "اجاره ای";
return `${car.driverName} (${car.driverMobile}) ${car.typeCar} پلاک ${car.pelak} (${carType})`;
}
return "";
}}
value={formik.values.car}
onChange={(e, car) => {
formik.setFieldValue("car", car);
if (car?.healthCode) {
formik.setFieldValue("trafficCode", car.healthCode);
}
}}
onBlur={formik.handleBlur}
renderInput={(params) => (
<TextField
{...params}
label="انتخاب خودرو و راننده (اختیاری)"
error={formik.touched.car ? Boolean(formik.errors.car) : null}
/>
)}
/>
<FormHelperText>
{formik.touched.car && Boolean(formik.errors.car)
? formik.errors.car
: null}
</FormHelperText>
</FormControl>
<FormControl fullWidth>
<TextField
id="trafficCode"
label="کد حمل و نقل (اختیاری)"
value={formik.values.trafficCode}
error={
formik.touched.trafficCode
? Boolean(formik.errors.trafficCode)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.trafficCode && Boolean(formik.errors.trafficCode)
? formik.errors.trafficCode
: null
}
variant="outlined"
/>
</FormControl>
<FormControl fullWidth>
<TextField
id="amount"
name="amount"
label="قیمت (اختیاری)"
type="number"
value={formik.values.amount}
error={formik.touched.amount ? Boolean(formik.errors.amount) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.amount && Boolean(formik.errors.amount)
? formik.errors.amount
: null
}
variant="outlined"
/>
</FormControl>
<Button
variant="contained"
color="primary"
onClick={handleSubmit}
disabled={
!formik.values.car &&
!formik.values.trafficCode &&
!formik.values.amount
}
fullWidth
>
ثبت تغییرات
</Button>
</Grid>
);
};
SlaughterEditAllocatedCar.propTypes = {
item: PropTypes.object.isRequired,
updateTable: PropTypes.func,
poultryRequestKey: PropTypes.string,
killHouseKey: PropTypes.string,
killRequestKey: PropTypes.string,
};

View File

@@ -0,0 +1,130 @@
import { Button, TextField } from "@mui/material";
import { useFormik } from "formik";
import { useContext, useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { Yup } from "../../../../lib/yup/yup";
import { slaughterGetInventoryStock } from "../../services/salughter-get-inventory-stock";
import { slaughterEditStockService } from "../../services/slaughter-edit-stock";
const NumberValidationSchema = Yup.object().shape({
firstNumber: Yup.number()
.required("حجم لاشه ها الزامی می باشد")
.min(0, "Number must be greater than or equal to 0"),
secondNumber: Yup.number()
.required("موجودی وزن لاشه ها الزامی می باشد")
.min(0, "Number must be greater than or equal to 0"),
});
export const SlaughterEditStock = ({ inventoryKey, aveWeightOfCarcasses }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const { inventorySelectedKillHouse } = useSelector(
(state) => state.slaughterSlice
);
const formik = useFormik({
initialValues: {
firstNumber: "",
secondNumber: "",
},
validationSchema: NumberValidationSchema,
onSubmit: (values) => {
dispatch(
slaughterEditStockService({
key: inventoryKey,
updated_number_of_carcasses: Number(values.firstNumber),
updated_weight_of_carcasses: Number(values.secondNumber),
})
).then((r) => {
dispatch(
slaughterGetInventoryStock({
date: selectedDate1,
kill_house_key: inventorySelectedKillHouse,
})
);
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
useEffect(() => {
let quantity;
if (aveWeightOfCarcasses) {
quantity = formik.values.secondNumber / aveWeightOfCarcasses;
} else {
quantity = formik.values.secondNumber / 2;
}
formik.setFieldValue("firstNumber", Number(quantity).toFixed());
}, [formik.values.secondNumber]);
return (
<Grid container direction="column">
<form
onSubmit={formik.handleSubmit}
style={{
display: "flex",
flexDirection: "column",
gap: SPACING.LARGE,
}}
>
<Grid container direction="column" gap={SPACING.SMALL}>
<TextField
id="secondNumber"
name="secondNumber"
label="موجودی وزن لاشه ها (افت 25٪)"
type="number"
value={formik.values.secondNumber}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.secondNumber && Boolean(formik.errors.secondNumber)
}
helperText={
formik.touched.secondNumber && formik.errors.secondNumber
}
/>
<TextField
id="firstNumber"
name="firstNumber"
label="حجم لاشه ها"
type="number"
value={formik.values.firstNumber}
disabled={true}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.firstNumber && Boolean(formik.errors.firstNumber)
}
helperText={formik.touched.firstNumber && formik.errors.firstNumber}
/>
<Button type="submit" fullWidth variant="contained" color="primary">
ثبت
</Button>
</Grid>
</form>
</Grid>
);
};

View File

@@ -0,0 +1,44 @@
import { Box, Grid, Tab, Tabs } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { useState } from "react";
import { SlaughterManageBars } from "../slaughter-manage-bars/SlaughterManageBars";
import { EnterAggregateLoadInformation } from "../enter-aggregate-load-information/EnterAggregateLoadInformation";
import { EnterLoadInformation } from "../enter-load-information/EnterLoadInformation";
import { SlaughterEnterNoneReciept } from "../slaughter-enter-none-receipt/SlaughterEnterNoneReciept";
export const SalughterEnterBarInfo = () => {
const [value, setValue] = useState(0);
const handleChangeTab = (event, newValue) => {
setValue(newValue);
};
return (
<Grid
container
direction="column"
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
mt={SPACING.MEDIUM}
width="100%"
>
<Box sx={{ borderBottom: 1, borderColor: "divider" }}>
<Tabs
value={value}
onChange={handleChangeTab}
aria-label="basic tabs example"
>
<Tab label="وارد کردن اطلاعات بار" />
<Tab label="وارد کردن تک سندی بار" />
<Tab label="بارهای تکمیل شده" />
<Tab label="عدم وصول" />
</Tabs>
</Box>
{value === 0 && <EnterLoadInformation />}
{value === 1 && <EnterAggregateLoadInformation />}
{value === 2 && <SlaughterManageBars />}
{value === 3 && <SlaughterEnterNoneReciept />}
</Grid>
);
};

View File

@@ -0,0 +1,122 @@
import {
Box,
Button,
Typography,
Stack,
Divider,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { format } from "date-fns-jalali";
export const SlaughterEnterInformationModal = ({ handleSubmit, item }) => {
const validationSchema = Yup.object({
message: Yup.string().required("پیام الزامی است"),
});
const formik = useFormik({
initialValues: {
message: "",
},
validationSchema,
onSubmit: (values) => {
handleSubmit(values);
},
});
return (
<Grid container gap={SPACING.SMALL} direction="column">
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ p: 2, minWidth: 300 }}
>
<Grid container spacing={2} sx={{ mb: 3 }}>
<Grid item xs={6}>
<Stack spacing={1}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">کدبار:</Typography>
<Typography variant="body2">{item?.barCode || "-"}</Typography>
</Stack>
<Stack direction="row" spacing={1}>
<Typography variant="body2">تاریخ کشتار:</Typography>
<Typography variant="body2">
{item?.poultryRequest.sendDate
? format(
new Date(item?.poultryRequest.sendDate),
"yyyy/MM/dd"
)
: "-"}
</Typography>
</Stack>
<Stack direction="row" spacing={1}>
<Typography variant="body2">خریدار:</Typography>
<Typography variant="body2">{`${item.killhouseUser?.name}(${item.killhouseUser?.killHouseOperator?.user?.mobile})`}</Typography>
</Stack>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1}>
<Stack direction="row" spacing={1}>
<Typography variant="body2">مرغدار:</Typography>
<Typography variant="body2">{`${item.poultryRequest?.poultry?.unitName}`}</Typography>
</Stack>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2"> کد سفارش:</Typography>
<Typography variant="body2">
{item?.poultryRequest.orderCode}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">تعداد اولیه:</Typography>
<Typography variant="body2">
{item.quantity?.toLocaleString()} (قطعه)
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">وزن :</Typography>
<Typography variant="body2">
{item?.weightInfo?.weight?.toLocaleString()} (کیلوگرم)
</Typography>
</Stack>
</Grid>
</Grid>
<Divider sx={{ mt: 1, mb: 2 }} />
<TextField
name="message"
label="پیام (اجباری)"
multiline
rows={4}
value={formik.values.message}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.message && Boolean(formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
variant="outlined"
fullWidth
sx={{ mb: 2 }}
/>
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
ثبت
</Button>
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,145 @@
import { IconButton, Popover, Tooltip } from "@mui/material";
import { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import TuneIcon from "@mui/icons-material/Tune";
import EditIcon from "@mui/icons-material/Edit";
import { SlaughterEnterBarWeight } from "../../../file/components/slaughter-enter-bar-weight/SlaughterEnterBarWeight";
import { Grid } from "../../../../components/grid/Grid";
import ReceiptLongIcon from "@mui/icons-material/ReceiptLong";
import { SlaughterEnterInformationModal } from "../slaughter-enter-information-modal/SlaughterEnterInformationModal";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterUpdateNoneRecieptService } from "../../services/slaughter-update-none-recipt";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
export const SlaughterEnterInformationOperation = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
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 handleSubmit = (values) => {
dispatch(
slaughterUpdateNoneRecieptService({
non_receipt: true,
main_non_receipt: true,
non_receipt_message: values.message,
key: item.key,
role: getRoleFromUrl(),
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
handleClose();
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات عدم وصول با موفقیت انجام شد.",
severity: "success",
});
}
});
};
return (
<Grid>
<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}
>
<Grid style={{ padding: "20px" }} container direction="column">
<Tooltip title="ورود اطلاعات بار" placement="right">
<IconButton
color="primary"
disabled={
item?.documentStatus === "بدون مشکل" ||
item?.documentStatus === "بدون مشکل فاقد کیفیت"
}
onClick={() => {
handleClose();
dispatch(
DRAWER({
title: "انجام عملیات",
top: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterEnterBarWeight
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title={"عدم وصول"} placement="right">
<IconButton
disabled={item?.allocatedQuantity > 0}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "عدم وصول",
content: (
<SlaughterEnterInformationModal
handleSubmit={handleSubmit}
item={item}
/>
),
})
);
}}
aria-label="delete"
color="primary"
>
<ReceiptLongIcon />
</IconButton>
</Tooltip>
</Grid>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,473 @@
import { useContext, useEffect, useState, useMemo, useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import axios from "axios";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Button, TextField, Tooltip } from "@mui/material";
import { VetFarmEditTrafficCode } from "../../../vet-farm/components/vet-farm-edit-traffic-code/VetFarmEditTrafficCode";
import { format } from "date-fns-jalali";
import { SlaughterNoneRecieptOperation } from "../slaughter-none-reciept-operation/SlaughterNoneRecieptOperation";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import moment from "moment";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { AppContext } from "../../../../contexts/AppContext";
import { DatePicker } from "@mui/x-date-pickers";
import ShowImage from "../../../../components/show-image/ShowImage";
import { formatJustDate } from "../../../../utils/formatTime";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
// Constants
const ROLES = {
PROVINCE_OPERATOR: "ProvinceOperator",
SUPER_ADMIN: "SuperAdmin",
ADMIN_X: "AdminX",
SUPPORTER: "Supporter",
VET_SUPERVISOR: "VetSupervisor",
VET_FARM: "VetFarm",
CITY_VET: "CityVet",
};
const KILL_TYPES = {
FREEZING: "انجماد",
EXPORT: "صادرات",
NORMAL: "عادی",
};
const SALE_TYPES = {
FREE: "آزاد",
GOVERNMENT: "دولتی",
};
const NON_RECEIPT_STATES = {
ACCEPTED: "تایید شده",
REJECTED: "رد شده",
PENDING: "درانتظار تایید",
};
const DEFAULT_PER_PAGE = 10;
const DEFAULT_PAGE = 1;
// Helper functions
const formatDate = (date) => {
if (!date) return "-";
return format(new Date(date), "yyyy/MM/dd");
};
const formatCurrency = (value) => {
return value ? `${value.toLocaleString()}` : "-";
};
const formatNumber = (value) => {
return value ? value.toLocaleString() : "-";
};
const formatUserInfo = (name, mobile) => {
return name && mobile ? `${name} (${mobile})` : "-";
};
const getKillType = (item) => {
if (item?.poultryRequest?.freezing) return KILL_TYPES.FREEZING;
if (item?.poultryRequest?.export) return KILL_TYPES.EXPORT;
return KILL_TYPES.NORMAL;
};
const getSaleType = (item) => {
return item?.poultryRequest?.freeSaleInProvince
? SALE_TYPES.FREE
: SALE_TYPES.GOVERNMENT;
};
const getNonReceiptState = (item) => {
if (item?.nonReceiptState === "accepted") return NON_RECEIPT_STATES.ACCEPTED;
if (item?.nonReceiptState === "rejected") return NON_RECEIPT_STATES.REJECTED;
return NON_RECEIPT_STATES.PENDING;
};
const formatCheckerInfo = (checker, mobile) => {
return checker && mobile ? `${checker}(${mobile})` : "-";
};
const buildApiUrl = (params) => {
const { textValue, role, date1, date2, page, perPage, roleKey } = params;
const baseUrl = "non-receipt-request/";
const queryParams = new URLSearchParams({
search: "filter",
value: textValue || "",
role: role || "",
date1: date1 || "",
date2: date2 || "",
page: page || DEFAULT_PAGE,
page_size: perPage || DEFAULT_PER_PAGE,
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseUrl}?${queryParams.toString()}`;
};
const buildExcelUrl = (params) => {
const { baseURL, role, roleKey, userKey, textValue, date1, date2 } = params;
const queryParams = new URLSearchParams({
role: role || "",
key: userKey || "",
search: "filter",
value: textValue || "",
date1: date1 || "",
date2: date2 || "",
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseURL}non_receipt_request_excel/?${queryParams.toString()}`;
};
const isTrafficCodeEditable = (role, item) => {
const adminRoles = [
ROLES.PROVINCE_OPERATOR,
ROLES.SUPER_ADMIN,
ROLES.ADMIN_X,
ROLES.SUPPORTER,
ROLES.VET_SUPERVISOR,
];
if (adminRoles.includes(role)) {
return true;
}
const vetRoles = [ROLES.VET_FARM, ROLES.CITY_VET];
return (
item.trash !== true &&
item.assignmentStateArchive === "pending" &&
!item?.clearanceCode &&
vetRoles.includes(role)
);
};
export const SlaughterEnterNoneReciept = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [openNotif] = useContext(AppContext);
// Redux
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const dispatch = useDispatch();
// State
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(DEFAULT_PER_PAGE);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(DEFAULT_PAGE);
const [tableData, setTableData] = useState([]);
// Memoized values
const currentRole = useMemo(() => getRoleFromUrl(), []);
const roleKey = useMemo(
() => (checkPathStartsWith("slaughter") ? selectedSubUser?.key || "" : ""),
[selectedSubUser?.key]
);
// Fetch API data
const fetchApiData = useCallback(
async (pageNumber = page) => {
dispatch(LOADING_START());
try {
const url = buildApiUrl({
textValue,
role: currentRole,
date1: selectedDate1,
date2: selectedDate2,
page: pageNumber || DEFAULT_PAGE,
perPage,
roleKey,
});
const response = await axios.get(url);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
},
[
textValue,
currentRole,
selectedDate1,
selectedDate2,
perPage,
roleKey,
page,
dispatch,
]
);
// Update table callback
const updateTable = useCallback(() => {
fetchApiData(DEFAULT_PAGE);
setPage(DEFAULT_PAGE);
}, [fetchApiData]);
// Handlers
const handlePageChange = (newPage) => {
fetchApiData(newPage);
setPage(newPage);
};
const handlePerRowsChange = (newPerRows) => {
setPerPage(newPerRows);
setPage(DEFAULT_PAGE);
};
const handleSubmit = async (event) => {
event.preventDefault();
await fetchApiData(DEFAULT_PAGE);
setPage(DEFAULT_PAGE);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleDateChange1 = (date) => {
if (date) {
setSelectedDate1(moment(date).format("YYYY-MM-DD"));
}
};
const handleDateChange2 = (date) => {
if (date) {
setSelectedDate2(moment(date).format("YYYY-MM-DD"));
}
};
const handleExcelDownload = useCallback(() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const excelUrl = buildExcelUrl({
baseURL: axios.defaults.baseURL,
role: currentRole,
roleKey,
userKey,
textValue,
date1: selectedDate1,
date2: selectedDate2,
});
window.location.href = excelUrl;
}, [
openNotif,
currentRole,
roleKey,
userKey,
textValue,
selectedDate1,
selectedDate2,
]);
// Initial data fetch and refetch when filters change
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
setPage(DEFAULT_PAGE);
}, [selectedDate1, selectedDate2, perPage, roleKey, selectedSubUser?.key]);
// Transform data to table format
useEffect(() => {
const transformedData = data?.map((item, i) => {
const rowNumber =
page === DEFAULT_PAGE ? i + 1 : i + perPage * (page - 1) + 1;
return [
rowNumber,
<ShowImage
key={`empty-${i}`}
src={item?.assignmentInfo?.imageWithoutBar}
/>,
<ShowImage
key={`full-${i}`}
src={item?.assignmentInfo?.imageWithBar}
/>,
item?.barCode || "-",
<VetFarmEditTrafficCode
key={`traffic-${i}`}
updateTable={fetchApiData}
killHouseRequestKey={item.key}
trafficCode={item?.trafficCode}
isEditable={isTrafficCodeEditable(currentRole, item)}
/>,
formatCurrency(item?.amount),
formatDate(item?.poultryRequest?.sendDate),
formatUserInfo(
item.killhouseUser?.name,
item.killhouseUser?.killHouseOperator?.user?.mobile
),
item?.killer
? formatUserInfo(
item.killer?.name,
item.killer?.killHouseOperator?.user?.mobile
)
: "-",
formatUserInfo(
item.poultryRequest?.poultry?.unitName,
item.poultryRequest?.poultry?.user?.mobile
),
item?.poultryRequest?.age || "-",
formatNumber(item.quantity),
formatNumber(item?.weightInfo?.weight),
formatCurrency(item?.poultryRequest?.amount),
formatCurrency(item?.weightInfo?.killHousePrice),
`${item.addCar?.driver?.typeCar || ""} ${
item.addCar?.driver?.pelak || ""
}`.trim() || "-",
formatUserInfo(
item.addCar?.driver?.driverName,
item.addCar?.driver?.driverMobile
),
formatNumber(item.vetAcceptedRealQuantity),
formatNumber(item.vetAcceptedRealWeight),
item?.poultryRequest?.orderCode || "-",
getKillType(item),
getSaleType(item),
getNonReceiptState(item),
item?.nonReceiptMessage || "-",
formatJustDate(item?.nonReceiptCheckDate) || "-",
formatCheckerInfo(
item?.nonReceiptChecker,
item?.nonReceiptCheckerMobile
),
item?.message || "-",
<SlaughterNoneRecieptOperation
key={`operation-${i}`}
item={item}
updateTable={updateTable}
/>,
];
});
setTableData(transformedData || []);
}, [data, page, perPage, currentRole, fetchApiData, updateTable]);
// Table columns
const tableColumns = [
"ردیف",
"بارنامه خالی",
"بارنامه پر",
"کدبار",
"کد بهداشتی حمل و نقل",
"قیمت مرغ زنده‌ی بار",
"تاریخ کشتار",
"خریدار",
"کشتارکن اختصاصی",
"مرغدار",
"سن مرغ",
"تعداد اولیه",
"وزن اولیه بار (کیلوگرم)",
"قیمت مرغدار",
"قیمت کشتارگاه",
"ماشین",
"راننده",
"تحویلی دامپزشک (قطعه)",
"وزن تحویلی دامپزشک (کیلوگرم)",
"کدسفارش کشتار",
"نوع کشتار",
"نوع فروش",
"وضعیت عدم وصول",
"توضیحات کشتارگاه",
"تاریخ (تایید / رد)",
"بررسی کننده",
"توضیحات بررسی کننده",
"عملیات",
];
return (
<Grid container justifyContent="center">
<Grid
container
alignItems="start"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<DatePicker
label="از تاریخ"
renderInput={(params) => (
<TextField
{...params}
style={{ width: "160px" }}
size="small"
/>
)}
value={selectedDate1}
onChange={handleDateChange1}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
renderInput={(params) => (
<TextField
{...params}
style={{ width: "160px" }}
size="small"
/>
)}
value={selectedDate2}
onChange={handleDateChange2}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
value={textValue}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
<Tooltip title="خروجی اکسل">
<Button color="success" onClick={handleExcelDownload}>
<RiFileExcel2Fill size={32} />
</Button>
</Tooltip>
</Grid>
</Grid>
<ResponsiveTable
data={tableData}
columns={tableColumns}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="عدم وصول"
/>
</Grid>
);
};

View File

@@ -0,0 +1,132 @@
import React, { useContext, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { IconButton, Popover, Tooltip } from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { SlaughterSubmitExport } from "../slaughter-submit-export/SlaughterSubmitExport";
import EditIcon from "@mui/icons-material/Edit";
import { useDispatch } from "react-redux";
import { slaughterDeleteFreeBuyService } from "../../services/slaughter-delete-free-buy";
import { AppContext } from "../../../../contexts/AppContext";
import DeleteIcon from "@mui/icons-material/Delete";
export const SlaughterExportOptions = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [anchorEl, setAnchorEl] = useState(null);
const [openNotif] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
return (
<Grid container>
<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",
display: "flex",
flexDirection: "column",
}}
>
<Grid container direction="column">
<Tooltip title="ویرایش" placement="right">
<IconButton
color="primary"
onClick={() => {
handleClose();
dispatch(
DRAWER({
title: "ویرایش درخواست صادرات",
content: (
<SlaughterSubmitExport
updateTable={updateTable}
edit
item={item}
/>
),
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
})
);
}}
>
<EditIcon />
</IconButton>
</Tooltip>
<Tooltip title="حذف">
<IconButton
color="error"
onClick={() => {
dispatch(slaughterDeleteFreeBuyService(item.key)).then(
(r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
}
);
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
{/* <Tooltip title="خروجی اکسل" placement="left">
<a
href={`${axios.defaults.baseURL}bar_for_each_persion_excel/?code=${item.barCode}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={20} />
</Button>
</a>
</Tooltip> */}
</Grid>
</div>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,383 @@
import React, { useContext, useEffect, useState } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import moment from "moment";
import axios from "axios";
import {
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { useDispatch, useSelector } from "react-redux";
import { Button, Tab, Tabs, TextField, Tooltip } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { slaughterGetExportAllowState } from "../../services/slaughter-get-export-allow-state";
import { SlaughterSubmitExport } from "../slaughter-submit-export/SlaughterSubmitExport";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import { SlaughterExportOptions } from "../slaughter-export-options/SlaughterExportOptions";
import { EnterAuthCodeDirectBuy } from "../enter-auth-code-direct-buy/EnterAuthCodeDirectBuy";
import { RiFileExcel2Fill } from "react-icons/ri";
import { RiSearchLine } from "react-icons/ri";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterExport = () => {
const dispatch = useDispatch();
const [exportData, setExportData] = useState();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
dispatch(
slaughterGetExportAllowState({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setExportData(r.payload.data);
});
}, [selectedSubUser?.key]);
const [activeTab, setActiveTab] = useState(0);
const handleTabChange = (event, newValue) => {
setActiveTab(newValue);
};
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
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 [tableDataArchive, setTableDataArchive] = useState([]);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`kill_request/?export=true&type=${
activeTab === 0 ? "pending" : "archive"
}&search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&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 handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
fetchApiData(1);
}, [selectedSubUser?.key, selectedDate1, selectedDate2, perPage, activeTab]);
const getItemState = (item) => {
let state = "";
if (item.exportState === "pending") {
state = "در انتظار تایید";
} else if (item.exportState === "rejected") {
state = "رد شده";
} else if (item.exportState === "accepted") {
state = "تایید شده";
} else if (item.exportState === "deleted") {
state = "حذف شده";
}
return state;
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
formatJustDate(item?.createDate),
formatJustDate(item.reciveDate),
`${item.killHouse.name} (${item.killHouse.killHouseOperator.user.mobile})`,
item?.slaughterHouse
? item?.slaughterHouse?.name
: item?.killHouse?.name,
item?.exportCountry,
`${item?.poultry?.userprofile?.fullName} (${item?.poultry?.userprofile?.mobile})`,
item.killCapacity,
item.IndexWeight,
(item.IndexWeight * item.killCapacity)?.toLocaleString(),
<>
{item?.inputDirectBuyingCode ? (
item?.inputDirectBuyingCode
) : (
<Button
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت کد احراز",
content: (
<EnterAuthCodeDirectBuy
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت کداحراز
</Button>
)}
</>,
getItemState(item),
activeTab === 0 ? (
<SlaughterExportOptions
key={i}
updateTable={updateTable}
item={item}
/>
) : (
"-"
),
];
});
setTableData(d);
const a = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
formatJustDate(item?.createDate),
formatJustDate(item.reciveDate),
`${item.killHouse.name} (${item.killHouse.killHouseOperator.user.mobile})`,
item?.slaughterHouse
? item?.slaughterHouse?.name
: item?.killHouse?.name,
item?.exportCountry,
`${item?.poultry?.userprofile?.fullName} (${item?.poultry?.userprofile?.mobile})`,
item.killCapacity,
item.IndexWeight,
(item.IndexWeight * item.killCapacity)?.toLocaleString(),
getItemState(item),
item?.acceptRejectDate ? formatTime(item?.acceptRejectDate) : "-",
item?.directBuyingMessage,
];
});
setTableDataArchive(a);
}, [data]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`kill_request/?export=true&type=${
activeTab === 0 ? "pending" : "archive"
}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&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);
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const userKey = useSelector((state) => state.userSlice.userProfile.key);
return (
<Grid container direction="column" flexWrap="nowrap" mt={SPACING.SMALL}>
<Grid
container
justifyContent="center"
alignItems="center"
my={SPACING.SMALL}
>
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={activeTab}
onChange={handleTabChange}
>
<Tab label="درخواست های جدید" />
<Tab label="بایگانی" />
</Tabs>
</Grid>
<Grid
container
mt={SPACING.MEDIUM}
alignItems="center"
justifyContent="center"
gap={2}
>
<Button
variant="contained"
disabled={!exportData?.exportStatus}
onClick={() => {
dispatch(
DRAWER({
title: "ثبت درخواست صادرات",
content: <SlaughterSubmitExport updateTable={updateTable} />,
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
})
);
}}
>
ثبت درخواست صادرات
</Button>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
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>
{activeTab === 1 && (
<Grid>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}export_kill_house_excel/?date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
)}
</Grid>
</form>
{activeTab === 0 ? (
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"خریدار",
"محل کشتار",
"کشور مقصد",
"مرغدار (تلفن)",
"تعداد درخواست (قطعه)",
"میانگین وزنی (کیلوگرم)",
"وزن کل (کیلوگرم)",
"کد احراز",
"وضعیت",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="صادرات"
/>
) : (
<ResponsiveTable
data={tableDataArchive}
columns={[
"ردیف",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"خریدار",
"محل کشتار",
"کشور مقصد",
"مرغدار (تلفن)",
"تعداد درخواست (قطعه)",
"میانگین وزنی (کیلوگرم)",
"وزن کل (کیلوگرم)",
"وضعیت",
"تاریخ تایید/رد",
"دلیل رد",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بایگانی صادرات"
/>
)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,285 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { slaughterFactorsService } from "../../services/slaughter-factors";
import PaymentIcon from "@mui/icons-material/Payment";
import { Button, IconButton, TextField, Typography } from "@mui/material";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { SlaughterPayFactor } from "../slaughter-pay-factor/SlaughterPayFactor";
import { Grid } from "../../../../components/grid/Grid";
import { slaughterDeleteFactorService } from "../../services/slaughter-delete-factor";
import { AppContext } from "../../../../contexts/AppContext";
import DeleteIcon from "@mui/icons-material/Delete";
import { SPACING } from "../../../../data/spacing";
import { formatJustDate } from "../../../../utils/formatTime";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment/moment";
export const SlaughterFactorsComponent = () => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const { slaughterFactors } = useSelector((state) => state.slaughterSlice);
const [dataTable, setDataTable] = useState([]);
const [archiveDataTable, setArchiveDataTable] = useState([]);
useEffect(() => {
const d = slaughterFactors
?.filter((item) => item.state === "pending")
.map((item) => {
let state;
switch (item.state) {
case "pending":
state = "در انتظار پرداخت";
break;
case "paid":
state = "پرداخت شده";
break;
case "unpaid":
state = "پرداخت نشده";
break;
default:
state = "در انتظار پرداخت";
}
return [
item?.killRequest?.slaughterHouse?.name
? item?.killRequest?.slaughterHouse?.name
: item?.killRequest?.killHouse?.name,
formatJustDate(item?.createDate),
item?.killRequest?.IndexWeight,
item.killRequest.oldChickenBreed
? item.killRequest.oldChickenBreed
: item?.killRequest?.chickenBreed,
item.killRequest.chickenBreed,
item.killRequest.provinceQuantity
? item.killRequest.provinceQuantity.toLocaleString()
: item.killRequest.killCapacity?.toLocaleString(),
item.killRequest.provinceQuantity
? item.killRequest.killCapacity?.toLocaleString()
: 0,
item?.amount.toLocaleString() + " ﷼",
item?.minimumAmount.toLocaleString() + " ﷼",
state,
<IconButton
disabled={item.state === "paid"}
key="ee"
color="primary"
onClick={() => {
dispatch(
DRAWER({
title: "پرداخت فاکتور",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: <SlaughterPayFactor factorKey={item.key} />,
})
);
}}
>
<PaymentIcon />
</IconButton>,
<IconButton
disabled={item.paidState === "paid"}
key="ee"
color="error"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "لغو سفارش",
content: (
<Grid
container
direction="column"
alignItems="center"
justifyContent="center"
width="100%"
gap={SPACING.SMALL}
>
<Grid>
<Typography>
در صورت لغو فاکتور درخواست اولیه شما هم لغو می گردد.
</Typography>
</Grid>
<Grid>
<Button
variant="contained"
color="error"
onClick={() => {
dispatch(
slaughterDeleteFactorService({
state: "cancel",
factor_key: item.key,
})
).then((r) => {
if (r.payload.error) {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
slaughterFactorsService({
selectedDate1,
selectedDate2,
})
);
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "سفارش با موفقیت لغو شد.",
severity: "success",
});
}
});
}}
>
لغو سفارش
</Button>
</Grid>
</Grid>
),
})
);
}}
>
<DeleteIcon />
</IconButton>,
];
});
setDataTable(d);
const c = slaughterFactors
?.filter((item) => item.state === "paid" || item.state === "unpaid")
.map((item) => {
let state;
switch (item.state) {
case "pending":
state = "در انتظار پرداخت";
break;
case "paid":
state = "پرداخت شده";
break;
case "unpaid":
state = "پرداخت نشده";
break;
default:
state = "در انتظار پرداخت";
}
return [
item?.killRequest?.slaughterHouse?.name
? item?.killRequest?.slaughterHouse?.name
: item?.killRequest?.killHouse?.name,
formatJustDate(item?.createDate),
item?.killRequest?.IndexWeight,
item.killRequest.oldChickenBreed
? item.killRequest.oldChickenBreed
: item?.killRequest?.chickenBreed,
item.killRequest.chickenBreed,
item.killRequest.provinceQuantity
? item.killRequest.provinceQuantity.toLocaleString()
: item.killRequest.killCapacity?.toLocaleString(),
item.killRequest.provinceQuantity
? item.killRequest.killCapacity?.toLocaleString()
: 0,
item?.amount.toLocaleString() + " ﷼",
item?.minimumAmount.toLocaleString() + " ﷼",
state,
];
});
setArchiveDataTable(c);
}, [slaughterFactors]);
useEffect(() => {
dispatch(slaughterFactorsService({ selectedDate1, selectedDate2 }));
}, [selectedDate1, selectedDate2]);
return (
<Grid container flex={1}>
<Grid width="100%">
<AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography>فاکتور همراه با ثبت درخواست کشتار</Typography>
</Grid>
<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>
}
columns={[
"کاربر",
"تاریخ درخواست",
"وزن",
"نژاد درخواستی",
"نژاد تایید شده",
"تعداد درخواستی",
"تعداد تایید شده",
"مبلغ پیش فاکتور",
"حداقل پرداختی (70%)",
"وضعیت",
"پرداخت",
"لغو سفارش",
]}
data={dataTable}
/>
</Grid>
<Grid width="100%">
<AdvancedTable
name="بایگانی"
columns={[
"کاربر",
"تاریخ درخواست",
"وزن",
"نژاد درخواستی",
"نژاد تایید شده",
"تعداد درخواستی",
"تعداد تایید شده",
"مبلغ پیش فاکتور",
"حداقل پرداختی (70%)",
"وضعیت",
]}
data={archiveDataTable}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,130 @@
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import PaymentIcon from "@mui/icons-material/Payment";
import { IconButton, TextField, Typography } from "@mui/material";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { SlaughterPayFactor } from "../slaughter-pay-factor/SlaughterPayFactor";
import { slaughterFinalFactorsService } from "../../services/slaughter-final-factors";
import { Grid } from "../../../../components/grid/Grid";
import { DatePicker } from "@mui/x-date-pickers";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import moment from "moment/moment";
export const SlaughterFinalFactorsComponent = () => {
const dispatch = useDispatch();
const { slaughterFinalFactors } = useSelector(
(state) => state.slaughterSlice
);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const [dataFinalFactorsTable, setDataFinalFactorsTable] = useState([]);
useEffect(() => {
const c = slaughterFinalFactors?.map((item) => {
let state;
switch (item.paidState) {
case "pending":
state = "در انتظار پرداخت";
break;
case "paid":
state = "پرداخت شده";
break;
default:
state = "در انتظار پرداخت";
}
return [
item?.factorBarCode,
state,
item?.factorFee.toLocaleString() + " ﷼",
item?.totalPrice.toLocaleString() + " ﷼",
item?.totalWeight,
<IconButton
disabled={item.paidState === "paid"}
key="ee"
color="primary"
onClick={() => {
dispatch(
DRAWER({
title: "پرداخت فاکتور",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterPayFactor
factorKey={item.key}
isFinalFactor={true}
/>
),
})
);
}}
>
<PaymentIcon />
</IconButton>,
];
});
setDataFinalFactorsTable(c);
}, [slaughterFinalFactors]);
useEffect(() => {
dispatch(slaughterFinalFactorsService({ selectedDate1, selectedDate2 }));
}, [selectedDate1, selectedDate2]);
return (
<Grid container flex={1}>
<Grid width="100%">
<AdvancedTable
name={
<Grid container alignItems="center" gap={SPACING.SMALL}>
<Grid>
<Typography>فاکتور نهایی</Typography>
</Grid>
<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>
}
columns={[
"کدبار",
"وضعیت",
"قیمت هرکیلو",
"قابل پرداخت",
"وزن",
"پرداخت",
]}
data={dataFinalFactorsTable}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,144 @@
import { Button, IconButton, Popover } from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { DRAWER, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteInventoryFreeBarService } from "../../services/slaughter-delete-inventory-free-bar";
import { SlaughterSubmitFreeBar } from "../slaughter-submit-free-bar/SlaughterSubmitFreeBar";
import { SlaughterSubmitRealInventoryFreeBar } from "../slaughter-submit-real-inventory-free-bar/SLaughterSubmitRealInverntoryFreeBar";
import TuneIcon from "@mui/icons-material/Tune";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
export const SlaughterFreeBarsAlivesOperations = ({
item,
updateTable,
barState,
type,
}) => {
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 ableToSeeDelete = () => {
if (type === "carcass" || item?.buyType !== "live") {
return true;
} else {
if (!barState && !item.weightOfCarcasses) {
return true;
} else {
return false;
}
}
};
const isAbleToSee =
item?.registerType === "automatic"
? false
: item.weightOfCarcasses && !barState && item?.buyType === "live";
return (
<div>
<IconButton
disabled={isAbleToSee}
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">
{barState === "entered" && item?.registerType === "automatic" && (
<Button
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش بار آزاد",
content: (
<SlaughterSubmitFreeBar
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
ویرایش
</Button>
)}
{(barState === "entered"
? item?.registerType !== "automatic"
: true) && (
<Button
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ثبت اطلاعات لاشه ورودی به انبار",
content: (
<SlaughterSubmitRealInventoryFreeBar
item={item}
updateTable={updateTable}
IsEdit={barState === "entered"}
/>
),
})
);
}}
>
{barState === "entered" ? "ویرایش" : "ورود به انبار"}
</Button>
)}
{ableToSeeDelete() && item?.registerType !== "automatic" && (
<Button
color="error"
onClick={() => {
handleClose();
dispatch(
slaughterDeleteInventoryFreeBarService(item.key)
).then(() => {
updateTable();
dispatch(fetchSlaughterBroadcastAndProducts());
});
}}
>
حذف
</Button>
)}
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,178 @@
import {
IconButton,
Popover,
List,
ListItem,
ListItemButton,
Typography,
ListItemIcon,
} from "@mui/material";
import { useState } from "react";
import { useDispatch } from "react-redux";
import { DRAWER, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteInventoryFreeBarService } from "../../services/slaughter-delete-inventory-free-bar";
import { SlaughterSubmitFreeBar } from "../slaughter-submit-free-bar/SlaughterSubmitFreeBar";
import { SlaughterSubmitRealInventoryFreeBar } from "../slaughter-submit-real-inventory-free-bar/SLaughterSubmitRealInverntoryFreeBar";
import TuneIcon from "@mui/icons-material/Tune";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
import Inventory2OutlinedIcon from "@mui/icons-material/Inventory2Outlined";
import DeleteOutlineOutlinedIcon from "@mui/icons-material/DeleteOutlineOutlined";
export const SlaughterFreeBarsOperations = ({
item,
updateTable,
barState,
type,
}) => {
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 ableToSeeUpdate = () => {
if (item?.buyType === "live") {
return !item.weightOfCarcasses && !barState;
} else {
return false;
}
};
const ableToSeeDelete = () => {
if (type === "carcass" || item?.buyType !== "live") {
return true;
} else {
if (!barState && !item.weightOfCarcasses) {
return true;
} else {
return false;
}
}
};
const isAbleToSee =
item?.registerType === "automatic"
? false
: item.weightOfCarcasses && !barState && item?.buyType === "live";
return (
<div>
<IconButton
disabled={isAbleToSee}
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}
>
<List sx={{ p: 1 }}>
{(ableToSeeUpdate() || item?.registerType === "automatic") && (
<ListItem disablePadding>
<ListItemButton
sx={{ color: "primary.main" }}
onClick={() => {
handleClose();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش بار آزاد",
content: (
<SlaughterSubmitFreeBar
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
<EditOutlinedIcon fontSize="small" />
</ListItemIcon>
<Typography variant="body2">ویرایش</Typography>
</ListItemButton>
</ListItem>
)}
{barState && item?.registerType === "manual" && (
<ListItem disablePadding>
<ListItemButton
sx={{ color: "success.main" }}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ثبت اطلاعات لاشه ورودی به انبار",
content: (
<SlaughterSubmitRealInventoryFreeBar
item={item}
updateTable={updateTable}
IsEdit={barState === "entered"}
/>
),
})
);
}}
>
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
<Inventory2OutlinedIcon fontSize="small" />
</ListItemIcon>
<Typography variant="body2">
{barState === "entered" ? "ویرایش" : "ورود به انبار"}
</Typography>
</ListItemButton>
</ListItem>
)}
{ableToSeeDelete() && item?.registerType !== "automatic" && (
<ListItem disablePadding>
<ListItemButton
sx={{ color: "error.main" }}
onClick={() => {
handleClose();
dispatch(
slaughterDeleteInventoryFreeBarService(item.key)
).then(() => {
updateTable();
dispatch(fetchSlaughterBroadcastAndProducts());
});
}}
>
<ListItemIcon sx={{ minWidth: 36, color: "inherit" }}>
<DeleteOutlineOutlinedIcon fontSize="small" />
</ListItemIcon>
<Typography variant="body2" color="inherit">
حذف
</Typography>
</ListItemButton>
</ListItem>
)}
</List>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,215 @@
import { Tooltip } from "@mui/material";
import { Button, TextField } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import axios from "axios";
import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { RiFileExcel2Fill } from "react-icons/ri";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { formatJustDate } from "../../../../utils/formatTime";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { RiSearchLine } from "react-icons/ri";
import { slaughterGetFreeBuyRequests } from "../../services/slaughter-get-free-buy-requests";
export const SlaughterFreeBuyArchive = () => {
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const dispatch = useDispatch();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
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 handleTextChange = (event) => {
setTextValue(event.target.value);
};
const fetchApiData = async (page) => {
try {
const response = await dispatch(
slaughterGetFreeBuyRequests({
date1: selectedDate1,
date2: selectedDate2,
value: textValue || "",
type: "archive",
page: page,
page_size: perPage,
})
);
if (response.payload?.data) {
setData(response.payload.data.results);
setTotalRows(response.payload.data.count);
}
} catch (error) {
console.error("Error fetching data:", error);
}
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await dispatch(
slaughterGetFreeBuyRequests({
date1: selectedDate1,
date2: selectedDate2,
value: textValue || "",
type: "archive",
})
);
if (response.payload?.data) {
setData(response.payload.data.results);
setTotalRows(response.payload.data.count);
}
} catch (error) {
console.error("Error fetching data:", error);
}
};
const getItemState = (item) => {
let state = "";
if (item.directBuyingState === "pending") {
state = "در انتظار تایید";
} else if (item.directBuyingState === "rejected") {
state = "رد شده";
} else if (item.directBuyingState === "accepted") {
state = "تایید شده";
} else if (item.directBuyingState === "deleted") {
state = "حذف شده";
}
return state;
};
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage, textValue, dispatch]);
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
formatJustDate(item?.createDate),
formatJustDate(item.reciveDate),
`${item.killHouse.name} (${item.killHouse.killHouseOperator.user.mobile})`,
item?.slaughterHouse
? item?.slaughterHouse?.name
: item?.killHouse?.name,
`${item?.poultry?.userprofile?.fullName} (${item?.poultry?.userprofile?.mobile})`,
`${item.chickenBreed ? item.chickenBreed : "-"}`,
item.killCapacity,
item.IndexWeight,
(item.IndexWeight * item.killCapacity)?.toLocaleString(),
item?.inputDirectBuyingCode,
getItemState(item),
];
});
setTableData(d);
}, [data]);
return (
<Grid container direction="column" gap={SPACING.SMALL} alignItems="start">
<Grid container alignItems="center" gap={SPACING.SMALL} p="5px">
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<Grid container gap={2}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
<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="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}direct_purchase_archive_excel/?date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}&key=${userKey}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</form>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"خریدار",
"محل کشتار",
"مرغدار (تلفن)",
"نژاد",
"تعداد درخواست (قطعه)",
"میانگین وزنی (کیلوگرم)",
"وزن کل (کیلوگرم)",
"کداحراز",
"وضعیت",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بایگانی خرید مستقیم"
/>
</Grid>
);
};

View File

@@ -0,0 +1,776 @@
import React, { useContext, useEffect, useState } from "react";
import moment from "moment";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import {
Button,
IconButton,
Tab,
Tabs,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
CLOSE_MODAL,
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { SlaughterFreeBarsOperations } from "../slaughter-free-bars-operations/SlaughterFreeBarsOperations";
import { formatJustDate } from "../../../../utils/formatTime";
import ShowImage from "../../../../components/show-image/ShowImage";
import { SlaughterSubmitFreeBar } from "../slaughter-submit-free-bar/SlaughterSubmitFreeBar";
import { SlaughterHouseVetBarsOperation } from "../../../slaughter-house-vet/components/slaughter-house-vet-bars-operation/SlaughterHouseVetBarsOperation";
import { RiFileExcel2Fill } from "react-icons/ri";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
import { vetFarmGetOutProvinceDashboard } from "../../../vet-farm/services/vet-farm-get-out-province-dashboard";
import { RiSearchLine } from "react-icons/ri";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
import { slaughterDeleteInventoryFreeBarService } from "../../services/slaughter-delete-inventory-free-bar";
import { slaughterReturnEnteredFreeBarService } from "../../services/slaughter-return-entered-free-bar";
import DeleteIcon from "@mui/icons-material/Delete";
import UndoIcon from "@mui/icons-material/Undo";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterFreeBuyBars = ({ isBarManagemen }) => {
const [activeTab, setActiveTab] = useState(0);
const [tableDataArchive, setTableDataArchive] = useState([]);
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 dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [dashboardData, setDashboardData] = useState([]);
const handleTabChange = (event, newValue) => {
setActiveTab(newValue);
};
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const getDashboardData = () => {
dispatch(
vetFarmGetOutProvinceDashboard({
date1: selectedDate1,
date2: selectedDate2,
search: "filter",
role: getRoleFromUrl(),
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
value: textValue,
type: activeTab === 0 ? "live" : "carcass",
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const { inventorySelectedKillHouse } = useSelector(
(state) => state.slaughterSlice
);
useEffect(() => {
dispatch(
slaughterGetProfile({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedSubUser?.key]);
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`kill_house_free_bar/?type=${
activeTab === 0 ? "live" : "carcass"
}&search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}&date_type=buy`
);
getDashboardData();
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 = () => {
setPage(1);
fetchApiData(1);
getDashboardData();
};
useEffect(() => {
fetchApiData(1);
}, [selectedSubUser?.key]);
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.registerType === "automatic" ? "سیستمی" : "دستی",
item?.barCode || "-",
formatJustDate(item.createDate),
`${item?.killHouse?.name} (${item?.killHouse?.killHouseOperator?.user?.mobile})`,
item?.exclusiveKiller
? `${item?.exclusiveKiller?.name} (${item?.exclusiveKiller?.killHouseOperator?.user?.mobile})`
: "-",
item.buyType === "live" ? "مرغ زنده" : "لاشه",
item.poultryName,
`${item.province}/${item.city}`,
<CheckCleanceCode
key={item?.key}
clearanceCode={item?.barClearanceCode}
/>,
item.car || "-",
item?.driverName || "-",
item?.driverMobile || "-",
formatJustDate(item.date),
item.numberOfCarcasses.toLocaleString(),
item.weightOfCarcasses.toLocaleString(),
<ShowImage key={i} src={item.barImage} />,
<>
{getRoleFromUrl() === "KillHouse" ? (
<SlaughterFreeBarsOperations
key={item.key}
item={item}
inventoryKey={item?.key}
updateTable={updateTable}
/>
) : (
<Button
variant="outlined"
disabled={item?.killHouseVetState !== "pending"}
size="small"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تایید / رد",
content: (
<SlaughterHouseVetBarsOperation
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
تایید / رد
</Button>
)}
</>,
];
});
setTableData(d);
const getInformation = (item) => {
if (getRoleFromUrl() === "AdminX" || getRoleFromUrl() === "SuperAdmin") {
return [
<Tooltip
key={item}
title="بازگشت بار"
placement="top"
disableHoverListener={
!(
item?.weightOfCarcasses &&
item?.enteredMessage === "ورود به انبار مجازی"
)
}
>
<span>
<IconButton
color="primary"
size="small"
disabled={
!(
item?.weightOfCarcasses &&
item?.enteredMessage === "ورود به انبار مجازی"
)
}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "بازگشت بار",
content: (
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
>
<Typography variant="body2" color="red">
آیا از بازگشت بار از انبار اطمینان دارید؟
</Typography>
<Grid
container
xs={12}
mt={2}
justifyContent="center"
alignItems="center"
spacing={2}
>
<Grid item xs={6}>
<Button
onClick={() => {
dispatch(
slaughterReturnEnteredFreeBarService({
key: item.key,
return_entered_bar: true,
})
).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",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
variant="contained"
color="error"
fullWidth
>
تایید
</Button>
</Grid>
<Grid item xs={6}>
<Button
variant="contained"
color="primary"
fullWidth
onClick={() => dispatch(CLOSE_MODAL())}
>
لغو
</Button>
</Grid>
</Grid>
</Grid>
),
})
);
}}
>
<UndoIcon fontSize="small" />
</IconButton>
</span>
</Tooltip>,
];
} else {
return [];
}
};
const a = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.registerType === "automatic" ? "سیستمی" : "دستی",
item?.barCode || "-",
formatJustDate(item.createDate),
formatJustDate(item.registerDate) || "-",
`${item?.killHouse?.name} (${item?.killHouse?.killHouseOperator?.user?.mobile})`,
item?.exclusiveKiller
? `${item?.exclusiveKiller?.name} (${item?.exclusiveKiller?.killHouseOperator?.user?.mobile})`
: "-",
item.buyType === "live" ? "مرغ زنده" : "لاشه",
item.poultryName,
`${item.province}/${item.city}`,
<CheckCleanceCode
key={i}
bar_key={item?.key}
register_type={item?.registerType}
clearanceCode={item?.barClearanceCode}
/>,
item.car || "-",
item?.driverName || "-",
item?.driverMobile || "-",
item.quantity.toLocaleString(),
item.liveWeight.toLocaleString(),
formatJustDate(item.date),
item.numberOfCarcasses.toLocaleString(),
item.weightOfCarcasses.toLocaleString(),
item?.poultry?.age,
item.weightOfCarcasses ? "ورود به انبار" : "در انتظار ورود به انبار",
<ShowImage key={i} src={item.barImage} />,
...getInformation(item),
<>
{getRoleFromUrl() === "KillHouse" ? (
<SlaughterFreeBarsOperations
key={item.key}
item={item}
inventoryKey={item?.key}
updateTable={updateTable}
/>
) : (
isBarManagemen &&
getRoleFromUrl() === "AdminX" && (
<Tooltip title="حذف بار" placement="top">
<IconButton
color="error"
size="small"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "حذف بار",
content: (
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
>
<Typography variant="body2" color="red">
آیا از حذف بار اطمینان دارید؟
</Typography>
<Grid
container
xs={12}
mt={2}
justifyContent="center"
alignItems="center"
spacing={2}
>
<Grid item xs={6}>
<Button
onClick={() => {
dispatch(
slaughterDeleteInventoryFreeBarService(
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",
});
updateTable();
dispatch(CLOSE_MODAL());
}
});
}}
variant="contained"
color="error"
fullWidth
>
تایید
</Button>
</Grid>
<Grid item xs={6}>
<Button
variant="contained"
color="primary"
fullWidth
onClick={() => dispatch(CLOSE_MODAL())}
>
لغو
</Button>
</Grid>
</Grid>
</Grid>
),
})
);
}}
>
<DeleteIcon fontSize="small" />
</IconButton>
</Tooltip>
)
)}
</>,
];
});
setTableDataArchive(a);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage, activeTab, selectedSubUser?.key]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`kill_house_free_bar/?type=${
activeTab === 0 ? "live" : "carcass"
}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${1}&page_size=${perPage}&date_type=buy`
);
setData(response.data.results);
setTotalRows(response.data.count);
getDashboardData();
dispatch(LOADING_END());
} catch (error) {
console.error("Error fetching data:", error);
}
};
const getLastItem = () => {
let item = [];
if (isBarManagemen && getRoleFromUrl() === "AdminX") {
item = ["حذف"];
} else if (isBarManagemen) {
item = [];
} else {
item = ["عملیات"];
}
return item;
};
const information = () => {
if (getRoleFromUrl() === "AdminX" || getRoleFromUrl() === "SuperAdmin") {
return ["بازگشت از انبار"];
} else {
return [];
}
};
const LiveBars = (
<ResponsiveTable
data={tableDataArchive}
columns={[
"ردیف",
"نوع بار",
"کد بار",
"تاریخ خرید",
"تاریخ ثبت در سامانه",
"خریدار",
"کشتارکن",
"محصول",
"فروشنده",
"استان/شهر",
"کدقرنطینه",
"پلاک ماشین",
"نام راننده",
"تلفن راننده",
"حجم زنده",
"وزن زنده (کیلوگرم)",
"تاریخ ورود به انبار",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"سن مرغ",
"وضعیت",
"بارنامه",
...information(),
...getLastItem(),
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بار زنده خرید خارج استان"
/>
);
return (
<Grid
container
direction="column"
flexWrap="nowrap"
mt={SPACING.SMALL}
xs={12}
>
<Grid
container
justifyContent="center"
alignItems="center"
my={SPACING.SMALL}
>
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={activeTab}
onChange={handleTabChange}
>
<Tab label="زنده" />
<Tab label="لاشه" />
</Tabs>
</Grid>
<Grid
container
mt={SPACING.MEDIUM}
alignItems="center"
justifyContent="center"
gap={2}
>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
{getRoleFromUrl() === "KillHouse" &&
!isBarManagemen &&
inventorySelectedKillHouse && (
<Button
// disabled={!slaughterGetInventoryStockData}
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت اطلاعات خرید",
content: (
<SlaughterSubmitFreeBar
selectedDate={selectedDate1}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت اطلاعات خرید
</Button>
)}
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
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("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}&date1=${selectedDate1}&date2=${selectedDate2}&type=${
activeTab === 0 ? "live" : "carcass"
}&search=filter&value=${textValue}&date_type=buy`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
{/* </a> */}
</Tooltip>
</Grid>
</form>
<Grid container mt={2} mb={4} isDashboard xs={12}>
{activeTab === 0 ? (
<ResponsiveTable
noPagination
columns={[
"تعداد کل بارهای زنده",
"حجم کل بارهای زنده",
"وزن کل بارهای زنده",
"تعداد کل بارها وارد شده به انبار",
"حجم کل بارهای زنده وارد شده به انبار",
"وزن کل بارهای زنده وارد شده به انبار",
"وزن لاشه بارهای وارد شده به انبار",
"تعداد کل بارها وارد نشده به انبار",
"حجم کل بارهای زنده وارد نشده به انبار",
"وزن کل بارهای زنده وارد نشده به انبار",
]}
data={[
[
dashboardData?.totalBars?.toLocaleString(),
dashboardData?.totalBarsQuantity?.toLocaleString(),
dashboardData?.totalBarsLiveWeight?.toLocaleString(),
dashboardData?.enteredTotalBars?.toLocaleString(),
dashboardData?.enteredTotalBarsQuantity?.toLocaleString(),
dashboardData?.enteredTotalBarsLiveWeight?.toLocaleString(),
dashboardData?.enteredTotalBarsWeightOfCarcasses?.toLocaleString(),
dashboardData?.notEnteredTotalBars?.toLocaleString(),
dashboardData?.notEnteredTotalBarsQuantity?.toLocaleString(),
dashboardData?.notEnteredTotalBarsLiveWeight?.toLocaleString(),
],
]}
customColors={[
{
name: "وزن لاشه بارهای وارد شده به انبار",
color: "green",
},
]}
title={"خلاصه اطلاعات"}
/>
) : (
<Grid container xs={12}>
<ResponsiveTable
noPagination
isDashboard
customWidth={"85vw"}
columns={["تعداد کل بارها", "حجم لاشه", "وزن لاشه"]}
data={[
[
dashboardData?.totalBars?.toLocaleString(),
dashboardData?.totalBarsNumberOfCarcasses?.toLocaleString(),
dashboardData?.totalBarsWeightOfCarcasses?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
)}
</Grid>
{activeTab === 0 ? (
<>{LiveBars}</>
) : (
<ResponsiveTable
data={tableData}
columns={
isBarManagemen
? [
"ردیف",
"نوع بار",
"کد بار",
"تاریخ خرید",
"خریدار",
"کشتارکن",
"محصول",
"فروشنده",
"استان/شهر",
"کدقرنطینه",
"پلاک ماشین",
"نام راننده",
"تلفن راننده",
"تاریخ ورود به انبار",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"بارنامه",
]
: [
"ردیف",
"نوع بار",
"کد بار",
"تاریخ خرید",
"خریدار",
"کشتارکن",
"محصول",
"فروشنده",
"استان/شهر",
"کدقرنطینه",
"پلاک ماشین",
"نام راننده",
"تلفن راننده",
"تاریخ ورود به انبار",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"بارنامه",
"عملیات",
]
}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بار لاشه خرید خارج استان"
/>
)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,324 @@
import {
Button,
IconButton,
Popover,
List,
ListItemButton,
ListItemIcon,
ListItemText,
} from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import DoneOutlineIcon from "@mui/icons-material/DoneOutline";
import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb";
import { useDispatch } from "react-redux";
import {
CLOSE_MODAL,
DRAWER,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { SlaughterSubmitFreeBuy } from "../slaughter-submit-free-buy/SlaughterSubmitFreeBuy";
import { slaughterDeleteFreeBuyService } from "../../services/slaughter-delete-free-buy";
import { useContext, useState } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { ProviceRejectFreeBuy } from "../province-reject-free-buy/ProvinceRejectFreeBuy";
import { ProvinceAcceptDirectBuy } from "../../../province/components/province-accept-direct-buy/ProvinceAcceptDirectBuy";
import TuneIcon from "@mui/icons-material/Tune";
import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
import SmsIcon from "@mui/icons-material/Sms";
import { slaughterEditFreeBuyService } from "../../services/slaughter-edit-free-buy";
import { slaughterResendDirectBuyingSmsService } from "../../services/slaughter-resend-direct-buying-sms";
export const SlaughterFreeBuyOperations = ({
item,
updateTable,
poultryCodeMandatory,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
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 showOnlyDelete = poultryCodeMandatory && !item?.inputDirectBuyingCode;
const handleApproveRequest = () => {
dispatch(
OPEN_MODAL({
title: "تایید فروش مستقیم",
content: (
<ProvinceAcceptDirectBuy item={item} updateTable={updateTable} />
),
})
);
};
const handleRejectRequest = () => {
dispatch(
OPEN_MODAL({
title: "رد درخواست فروش آزاد",
content: <ProviceRejectFreeBuy item={item} updateTable={updateTable} />,
})
);
};
const handleFinalAccept = () => {
dispatch(
OPEN_MODAL({
title: "آیا مطمئن هستید؟",
content: (
<Grid container>
<Button
variant="contained"
onClick={() => {
dispatch(
slaughterEditFreeBuyService({
role: getRoleFromUrl(),
kill_request_key: item?.key,
final_accept: true,
})
).then((r) => {
if (r?.payload?.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r?.payload?.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
),
})
);
};
const handleEdit = () => {
dispatch(
DRAWER({
title: "ویرایش خرید مستقیم",
content: (
<SlaughterSubmitFreeBuy edit={item} updateTable={updateTable} />
),
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
})
);
};
const handleResendSms = () => {
dispatch(
slaughterResendDirectBuyingSmsService({
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",
});
}
});
};
const handleDelete = () => {
dispatch(slaughterDeleteFreeBuyService(item.key)).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.error,
severity: "error",
});
} else {
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
DRAWER({
right: false,
bottom: false,
left: false,
content: null,
})
);
}
});
};
const options = [];
if (
!showOnlyDelete &&
(getRoleFromUrl() === "ProvinceOperator" ||
getRoleFromUrl() === "SuperAdmin" ||
getRoleFromUrl() === "AdminX")
) {
options.push({
key: "approve",
label: "تایید درخواست",
color: "success.main",
icon: <DoneOutlineIcon fontSize="small" />,
action: handleApproveRequest,
});
options.push({
key: "reject",
label: "رد درخواست",
color: "warning.main",
icon: <DoNotDisturbIcon fontSize="small" />,
action: handleRejectRequest,
});
}
if (getRoleFromUrl() === "KillHouse" && !item?.finalAccept) {
options.push({
key: "finalAccept",
label: "تایید نهایی",
color: "primary.main",
icon: <ThumbUpAltIcon fontSize="small" />,
action: handleFinalAccept,
});
options.push({
key: "edit",
label: "ویرایش",
color: "info.main",
icon: <EditIcon fontSize="small" />,
action: handleEdit,
});
}
if (showOnlyDelete && item?.finalAccept) {
options.push({
key: "resendSms",
label: "ارسال مجدد پیامک",
color: "info.main",
icon: <SmsIcon fontSize="small" />,
action: handleResendSms,
});
}
options.push({
key: "delete",
label: "حذف",
color: "error.main",
icon: <DeleteIcon fontSize="small" />,
action: handleDelete,
});
return (
<Grid container>
<IconButton
size="small"
disabled={
item?.directBuyingState === "accepted" ||
(!showOnlyDelete &&
(getRoleFromUrl() === "KillHouse" && item?.finalAccept
? true
: item?.directBuyingState !== "pending"))
}
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}
>
<List sx={{ width: 130, p: 0.5 }}>
{options.map((option) => (
<ListItemButton
key={option.key}
onClick={() => {
handleClose();
option.action();
}}
sx={{
borderRadius: 1,
mb: 0.25,
py: 0.5,
color: option.color,
"&:last-of-type": {
mb: 0,
},
}}
>
<ListItemIcon sx={{ color: option.color, minWidth: 32 }}>
{option.icon}
</ListItemIcon>
<ListItemText
primary={option.label}
primaryTypographyProps={{
sx: {
color: option.color,
fontSize: "0.82rem",
fontWeight: 600,
},
}}
/>
</ListItemButton>
))}
</List>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,410 @@
import { useCallback } from "react";
import { Button, TextField, Tooltip, Typography } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import axios from "axios";
import moment from "moment/moment";
import { useContext, useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { formatJustDate, formatTime } from "../../../../utils/formatTime";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
// import { EnterAuthCodeDirectBuy } from "../enter-auth-code-direct-buy/EnterAuthCodeDirectBuy";
import { SlaughterFreeBuyOperations } from "../slaughter-free-buy-operations/SlaughterFreeBuyOperations";
import { SlaughterSubmitFreeBuy } from "../slaughter-submit-free-buy/SlaughterSubmitFreeBuy";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { paymentGetDeadLines } from "../../services/payment-get-deadlines";
import EditVerificationDirectBuy from "../edit-verification-direct-buy/EditVerificationDirectBuy";
import SlaughterBuyPdfKillRequest from "../../../province/components/province-settlement-pdf-kill-request/ProvinceSettlementPdfKillRequest";
import { slaughterGetFreeBuyDashboardService } from "../../services/slaughter-free-buy-dashboard";
import { slaughterGetFreeBuyRequests } from "../../services/slaughter-get-free-buy-requests";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterFreeBuy = () => {
const dispatch = useDispatch();
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const [poultryCodeMandatory, setPoultryCodeMandatory] = useState(false);
const [dashboardData, setDashboardData] = useState([]);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
useEffect(() => {
dispatch(
paymentGetDeadLines({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((response) => {
setPoultryCodeMandatory(response.payload.data?.poultryCodeMandatory);
});
}, [selectedSubUser?.key]);
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 handleTextChange = (event) => {
setTextValue(event.target.value);
};
const getDashboardData = () => {
dispatch(
slaughterGetFreeBuyDashboardService({
direct_buying: true,
date1: selectedDate1,
date2: selectedDate2,
role: getRoleFromUrl(),
search: "filter",
value: textValue,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
const fetchApiData = useCallback(
async (pageNumber = page) => {
try {
const response = await dispatch(
slaughterGetFreeBuyRequests({
date1: selectedDate1,
date2: selectedDate2,
value: textValue || "",
page: pageNumber,
page_size: perPage,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload?.data) {
getDashboardData();
setData(response.payload.data.results);
setTotalRows(response.payload.data.count);
}
} catch (error) {
console.error("Error fetching data:", error);
}
},
[
selectedDate1,
selectedDate2,
perPage,
page,
textValue,
dispatch,
selectedSubUser?.key,
]
);
const updateTable = useCallback(() => {
fetchApiData(1);
}, [fetchApiData]);
useEffect(() => {
fetchApiData(1);
}, [fetchApiData]);
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const handlePageChange = (page) => {
fetchApiData(page, textValue);
};
const handleSubmit = async (event) => {
event.preventDefault();
try {
const response = await dispatch(
slaughterGetFreeBuyRequests({
date1: selectedDate1,
date2: selectedDate2,
value: textValue || "",
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload?.data) {
setData(response.payload.data.results);
setTotalRows(response.payload.data.count);
}
} catch (error) {
console.error("Error fetching data:", error);
}
};
const getItemState = (item) => {
let state = "";
if (item.finalAccept === false) {
state = "در انتظار ارسال به استان";
} else if (poultryCodeMandatory && !item?.inputDirectBuyingCode) {
state = "در انتظار ورود کد احراز";
} else if (item?.directBuyingState === "rejected") {
state = "رد شده";
} else if (item?.directBuyingState === "accepted") {
state = "تایید شده";
} else if (item?.directBuyingState === "deleted") {
state = "حذف شده";
} else {
state = "در انتظار تایید استان";
}
return state;
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.createDate ? formatTime(item?.createDate) : "-",
item?.reciveDate ? formatJustDate(item?.reciveDate) : "-",
`${item.killHouse.name} (${item.killHouse.killHouseOperator.user.mobile})`,
item?.freeDirectBuying ? "آزاد" : "دولتی",
item?.slaughterHouse
? item?.slaughterHouse?.name
: item?.killHouse?.name,
`${item?.poultry?.userprofile?.fullName} (${item?.poultry?.userprofile?.mobile})`,
item.poultryHatching?.chickenAge || "-",
`${item.chickenBreed ? item.chickenBreed : "-"}`,
item.killCapacity?.toLocaleString(),
item.IndexWeight,
(item.IndexWeight * item.killCapacity)?.toLocaleString(),
item?.amount?.toLocaleString(),
<Grid key={i}>
{item?.directBuyingState === "pending" &&
item?.finalAccept === true &&
(getRoleFromUrl() === "KillHouse" ||
getRoleFromUrl() === "AdminX" ||
getRoleFromUrl() === "SuperAdmin" ||
getRoleFromUrl() === "ProvinceOperator") &&
poultryCodeMandatory ? (
<EditVerificationDirectBuy
updateTable={fetchApiData}
kill_request_key={item?.key}
inputDirectBuyingCode={item?.inputDirectBuyingCode}
/>
) : (
<Typography variant="body2">
{item?.inputDirectBuyingCode || "-"}
</Typography>
)}
</Grid>,
item?.paymentDeadlineDate
? formatJustDate(item?.paymentDeadlineDate)
: "-",
getItemState(item),
item?.directBuyingState === "pending"
? "-"
: item?.automaticAccept
? "سیستم"
: "اپراتور",
item?.inputDirectBuyingCode ||
(!item?.freeDirectBuying && item?.directBuyingState === "accepted") ? (
<SlaughterBuyPdfKillRequest pdf_key={item?.key} />
) : (
"-"
),
<SlaughterFreeBuyOperations
key={`ops-${i}`}
item={item}
updateTable={updateTable}
poultryCodeMandatory={poultryCodeMandatory}
/>,
];
});
setTableData(d);
}, [data]);
return (
<Grid container gap={SPACING.SMALL} alignItems="center">
<Grid container xs={12} px={1} gap={2}>
<Grid container alignSelf="start">
{/* {getRoleFromUrl() === "KillHouse" && ( */}
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
title: "ثبت خرید مستقیم",
content: (
<SlaughterSubmitFreeBuy
updateTable={updateTable}
fetchApiData={fetchApiData}
/>
),
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
})
);
}}
>
ثبت خرید مستقیم
</Button>
{/* )} */}
</Grid>
<Grid container xs={12} justifyContent="start" alignItems="center">
<Grid
container
alignItems="center"
mb={SPACING.SMALL}
gap={SPACING.SMALL}
>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
/>
<Button
// disabled={!textValue}
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
<Grid container gap={2}>
<DatePicker
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<Tooltip title="خروجی اکسل">
<a
href={`${
axios.defaults.baseURL
}direct_purchase_excel/?date1=${selectedDate1}&date2=${selectedDate2}&role=${getRoleFromUrl()}&key=${userKey}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key || ""}`
: ""
}`}
rel="noreferrer"
>
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
</Grid>
</form>
</Grid>
<Grid container mt={2} mb={4} isDashboard sx={{ width: "100%" }}>
<ResponsiveTable
noPagination
isDashboard
columns={[
"تعداد بارها",
"تعداد درخواست دولتی",
"تعداد درخواست آزاد",
"تعداد بارهای دولتی",
"تعداد بارهای آزاد",
"تعداد درخواست (قطعه)",
"میانگین وزنی",
"وزن کل (کیلوگرم)",
"میانگین قیمت فروش مرغدار",
]}
data={[
[
dashboardData?.lenKillRequest?.toLocaleString(),
dashboardData?.freeDirectBuyingFalseQuantity?.toLocaleString(),
dashboardData?.freeDirectBuyingTrueQuantity?.toLocaleString(),
dashboardData?.lenKillRequestHasntFreeDirectBuying?.toLocaleString(),
dashboardData?.lenKillRequestHasFreeDirectBuying?.toLocaleString(),
dashboardData?.quantity?.toLocaleString(),
dashboardData?.indexWight?.toFixed(1),
Math.round(
dashboardData?.quantity * dashboardData?.indexWight
)?.toLocaleString(),
Math.round(dashboardData?.amount)?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"تاریخ ثبت درخواست",
"تاریخ کشتار",
"خریدار",
"نوع خرید",
"محل کشتار",
"مرغدار (تلفن)",
"سن",
"نژاد",
"تعداد درخواست (قطعه)",
"میانگین وزنی (کیلوگرم)",
"وزن کل (کیلوگرم)",
"قیمت فروش مرغدار (ریال)",
"کداحراز",
"حداکثر مهلت تسویه",
"وضعیت",
"تایید کننده",
"توافق نامه",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="خرید مستقیم"
/>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,123 @@
import { IconButton, Popover, Tooltip } from "@mui/material";
import React, { useContext, useState } from "react";
import { useDispatch } from "react-redux";
import TuneIcon from "@mui/icons-material/Tune";
import DeleteIcon from "@mui/icons-material/Delete";
import EditIcon from "@mui/icons-material/Edit";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterDeleteAllocationForFreezing } from "../../services/slaughter-delete-allocation-for-freezing";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterAllocateToFreezing } from "../slaughter-allocate-to-freezing/SlaughterAllocateToFreezing";
export const SlaughterFreezingAllocationsOperations = ({
item,
fetchApiData,
updateInventory,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [anchorEl, setAnchorEl] = useState(null);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
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">
<Tooltip placement="right" title="حذف انجماد">
<IconButton
aria-label="delete"
color="error"
onClick={() => {
handleClose();
dispatch(
slaughterDeleteAllocationForFreezing(item?.key)
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است",
severity: "error",
});
} else {
fetchApiData();
updateInventory();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
<DeleteIcon />
</IconButton>
</Tooltip>
<Tooltip placement="right" title="ویرایش انجماد">
<IconButton
aria-label="delete"
color="primary"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص به انجماد",
content: (
<SlaughterAllocateToFreezing
fetchItems={fetchApiData}
updateTable={updateInventory}
isEdit
item={item}
/>
),
})
);
}}
>
<EditIcon />
</IconButton>
</Tooltip>
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,191 @@
import React, { useEffect, useState } from "react";
import { Button, TextField } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch } from "react-redux";
import axios from "axios";
import {
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 { formatJustDate } from "../../../../utils/formatTime";
import { SlaughterFreezingAllocationsOperations } from "../slaughter-freezing-allocations-operations/SlaughterFreezingAllocationsOperations";
import { RiSearchLine } from "react-icons/ri";
export const SlaughterFreezingAllocations = ({
updateFreezing,
updateInventory,
}) => {
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 handleTextChange = (event) => {
setTextValue(event.target.value);
};
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 fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`cold-house-allocations/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}`
);
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?.state === "pending"
? "در انتظار تایید"
: item?.state === "accepted"
? "تایید شده"
: "رد شده",
formatJustDate(item?.date),
item?.coldHouse?.name,
item?.quantity?.toLocaleString(),
item?.weight?.toLocaleString(),
item?.realQuantity?.toLocaleString(),
item?.realWeight?.toLocaleString(),
<SlaughterFreezingAllocationsOperations
key={i}
item={item}
fetchApiData={updateTable}
updateInventory={updateInventory}
/>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [dispatch, selectedDate1, selectedDate2, perPage, updateFreezing]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`cold-house-allocations/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${1}&page_size=${perPage}`
);
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}
>
<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>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"وضعیت",
"تاریخ ثبت",
"سردخانه",
"حجم",
"وزن",
"حجم واقعی",
"وزن واقعی",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="تخصیص به انجماد"
/>
</Grid>
);
};

View File

@@ -0,0 +1,188 @@
import React, { useContext, useEffect } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Button, InputAdornment } from "@mui/material";
import { useDispatch } from "react-redux";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import {
slaughterAllocateStewardService,
slaughterEditAllocateStewardService,
} from "../../services/slaughter-allocate-steward";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
export const SlaughterHouseColdHouseBarsEdit = ({
sellerType,
fetchData,
sellType,
updateTable,
fetchApiData,
editData,
isColdHouse,
priceInfo,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const validationSchema = Yup.object({
weight: Yup.number()
.required("این فیلد اجباری است!")
.min(1, "یک مقدار مثبت وارد کنید!"),
});
const formik = useFormik({
initialValues: {
weight: editData?.realWeightOfCarcasses || "",
},
validationSchema,
});
useEffect(() => {
formik.validateForm();
}, []);
const successSubmit = () => {
dispatch(CLOSE_MODAL());
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(fetchSlaughterBroadcastAndProducts());
fetchApiData();
updateTable();
fetchData();
};
return (
<Grid
container
xs={12}
direction="column"
justifyContent="center"
alignItems="center"
gap={1}
>
<NumberInput
allowLeadingZeros
thousandSeparator=","
fullWidth
id="weight"
label="وزن لاشه"
variant="outlined"
value={formik.values.weight}
error={formik.touched.weight ? Boolean(formik.errors.weight) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.weight && Boolean(formik.errors.weight)
? formik.errors.weight
: null
}
/>
<NumberInput
disabled={priceInfo?.active}
allowLeadingZeros
thousandSeparator=","
fullWidth
id="price"
label="قیمت هر کیلوگرم"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.price}
error={formik.touched.price ? Boolean(formik.errors.price) : null}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.price && Boolean(formik.errors.price)
? formik.errors.price
: null
}
/>
<NumberInput
disabled
allowLeadingZeros
thousandSeparator=","
fullWidth
id="wholePrice"
label="هزینه کل"
variant="outlined"
InputProps={{
endAdornment: <InputAdornment position="start">ریال</InputAdornment>,
}}
value={formik.values.wholePrice}
error={
formik.touched.wholePrice ? Boolean(formik.errors.wholePrice) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.wholePrice && Boolean(formik.errors.wholePrice)
? formik.errors.wholePrice
: null
}
/>
<Button
variant="contained"
fullWidth
disabled={!formik.isValid}
onClick={() => {
let req = {};
if (!editData) {
req = {
seller_type: sellerType,
type: "manual",
number_of_carcasses: 0,
weight_of_carcasses: formik.values.weight,
sell_type: sellType,
buyer_type: "ColdHouse",
allocation_type: "ColdHouse",
};
} else {
req = {
weight_of_carcasses: formik.values.weight,
allocation_key: editData?.key,
};
}
if (!editData) {
dispatch(slaughterAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
} else {
dispatch(slaughterEditAllocateStewardService(req)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
successSubmit();
}
});
}
}}
>
ثبت
</Button>
</Grid>
);
};

View File

@@ -0,0 +1,131 @@
import React, { useContext, useState } from "react";
import {
Button,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterEditDelegatesService } from "../../services/slaughter-get-delegates-service";
export const DelegatesLimitationForm = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasLimitation, setHasLimitation] = useState(item?.limitation || false);
const [governmentalValue, setGovernmentalValue] = useState(
item?.governmentalLimitationWeight || 0
);
const [freeValue, setFreeValue] = useState(item?.freeLimitationWeight || 0);
const handleSubmit = (e) => {
e.preventDefault();
const submitData = {
key: item?.key,
limitation: hasLimitation,
governmental_limitation_weight: hasLimitation
? Number(governmentalValue)
: 0,
free_limitation_weight: hasLimitation ? Number(freeValue) : 0,
};
dispatch(slaughterEditDelegatesService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
};
return (
<form onSubmit={handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid container item xs={12} alignItems="center" gap={1}>
<Typography variant="body2" color="text.secondary">
اطلاعات نماینده:
</Typography>
<Typography variant="h6" mb={0.75}>
{item?.firstName || item?.first_name}{" "}
{item?.lastName || item?.last_name}
</Typography>
</Grid>
<Grid item xs={12} mb={1}>
<FormControlLabel
control={
<Checkbox
checked={hasLimitation}
onChange={(e) => setHasLimitation(e.target.checked)}
color="primary"
/>
}
label="محدودیت فروش روزانه"
/>
</Grid>
{hasLimitation && (
<>
<Grid item xs={12}>
<TextField
label="حداکثر فروش دولتی (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={governmentalValue}
onChange={(e) => setGovernmentalValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="حداکثر فروش آزاد (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={freeValue}
onChange={(e) => setFreeValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
</>
)}
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={
hasLimitation && governmentalValue === 0 && freeValue === 0
}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,139 @@
import { IconButton, Popover, Typography, Button } from "@mui/material";
import React, { useState, useContext } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import ToggleOffIcon from "@mui/icons-material/ToggleOff";
import BlockIcon from "@mui/icons-material/Block";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterEditDelegatesService } from "../../services/slaughter-get-delegates-service";
import { DelegatesLimitationForm } from "./DelegatesLimitationForm";
export const DelegatesOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const handleToggleActive = () => {
handleClose();
dispatch(
slaughterEditDelegatesService({
key: item?.key,
active: !isActive,
})
).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",
});
if (updateTable) {
updateTable();
}
}
});
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div
style={{
padding: "10px",
display: "flex",
flexDirection: "column",
gap: "8px",
}}
>
<Button
color={isActive ? "error" : "success"}
size="small"
onClick={handleToggleActive}
startIcon={
isActive ? (
<ToggleOffIcon fontSize="small" />
) : (
<ToggleOnIcon fontSize="small" />
)
}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
{isActive ? "غیرفعال کردن" : "فعال کردن"}
</Typography>
</Button>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تنظیم محدودیت فروش",
content: (
<DelegatesLimitationForm
item={item}
updateTable={updateTable}
/>
),
size: 400,
})
);
}}
startIcon={<BlockIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
تنظیم محدودیت
</Typography>
</Button>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDelegatesService } from "../../services/slaughter-get-delegates-service";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DelegatesOperations } from "./DelegatesOperations";
export const KillHouseDelegatesTab = () => {
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 isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDelegatesService({
type: "KillHouse",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const killhouseDisplay =
item?.killHouse?.name && item?.killHouse?.mobile
? `${item.killHouse.name} (${item.killHouse.mobile})`
: item?.killHouse?.name
? item.killHouse.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || item?.first_name || "-",
item?.lastName || item?.last_name || "-",
item?.mobile || "-",
item?.city || "-",
killhouseDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DelegatesOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDelegatesService({
type: "KillHouse",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="نمایندگان کشتارگاه‌ها"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"کشتارگاه",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,27 @@
import React, { useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs, Box } from "@mui/material";
import { StewardDelegatesTab } from "./StewardDelegatesTab";
import { KillHouseDelegatesTab } from "./KillHouseDelegatesTab";
export const SlaughterHouseDelegates = () => {
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Tabs value={value} onChange={handleChange}>
<Tab label="مباشرین" value={0} />
<Tab label="کشتارگاه‌ها" value={1} />
</Tabs>
<Box sx={{ width: "100%", mt: 2 }}>
{value === 0 && <StewardDelegatesTab />}
{value === 1 && <KillHouseDelegatesTab />}
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDelegatesService } from "../../services/slaughter-get-delegates-service";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DelegatesOperations } from "./DelegatesOperations";
export const StewardDelegatesTab = () => {
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 isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDelegatesService({
type: "Steward",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active !== undefined ? item.active : !item?.trash;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const stewardDisplay =
item?.steward?.name && item?.steward?.user?.mobile
? `${item.steward.name} (${item.steward.user.mobile})`
: item?.steward?.name
? item.steward.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || item?.first_name || "-",
item?.lastName || item?.last_name || "-",
item?.mobile || "-",
item?.city || "-",
stewardDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DelegatesOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDelegatesService({
type: "Steward",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="نمایندگان مباشرین"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"مباشر",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,307 @@
import React, { useContext, useEffect, useState } from "react";
import { Button, TextField } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch } from "react-redux";
import axios from "axios";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { useParams } from "react-router-dom";
import { formatJustDate } from "../../../../utils/formatTime";
import { slaughterGetDispenserDashboardService } from "../../services/slaughter-house-get-dispenser-dashboard-service";
import { RiSearchLine } from "react-icons/ri";
export const SlaughterHouseDispenserDetails = () => {
const { key } = useParams();
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
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) => {
let response;
dispatch(LOADING_START());
response = await axios.get(
`dispenser-allocations/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&date1=${selectedDate1}&date2=${selectedDate2}&page=${page}&page_size=${perPage}&dispenser_key=${key}`
);
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(() => {
fetchApiData(1);
}, []);
useEffect(() => {
dispatch(
slaughterGetDispenserDashboardService({
date1: selectedDate1,
date2: selectedDate2,
key: key,
})
).then((r) => {
setDashboardData(r.payload.data);
});
}, [selectedDate1, selectedDate2]);
const getDispenserType = (item) => {
let type = "";
switch (item?.dispenser?.dispenserType) {
case "inductor":
type = "واسطه";
break;
case "salesman":
type = "فروشنده";
break;
case "driver":
type = `راننده - ${item?.dispenser?.car} (${item?.dispenser?.pelak})`;
break;
default:
break;
}
return type;
};
useEffect(() => {
const d = data?.map((item, i) => {
let sellType, sellerType;
if (item.sellerType === "guilds") {
sellerType = "صنف";
} else if (item.sellerType === "steward") {
sellerType = "مباشر";
}
if (item.sellType === "free") {
sellType = "آزاد";
} else {
if (item.type === "manual") {
sellType = "اختصاصی (دستی)";
} else {
sellType = "اختصاصی (اتوماتیک)";
}
}
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
`${item?.dispenser?.user?.fullname} (${item?.dispenser?.user?.mobile})`,
getDispenserType(item),
item?.guilds ? item?.guilds?.guildsId : item?.steward?.guilds?.guildsId,
formatJustDate(item.date),
sellerType,
sellType,
item?.guilds
? item?.guilds?.guildsName
: item?.steward?.guilds?.guildsName,
item?.guilds
? item?.guilds?.user.fullname
: item?.steward?.guilds?.user.fullname,
item?.guilds
? item?.guilds?.user.nationalId
: item?.steward?.guilds?.user.nationalId,
item?.guilds
? item?.guilds?.user.mobile
: item?.steward?.guilds?.user.mobile,
item?.guilds
? item?.guilds?.typeActivity
: item?.steward?.guilds?.typeActivity,
item?.guilds
? item?.guilds?.areaActivity
: item?.steward?.guilds?.areaActivity,
item?.guilds
? item?.guilds?.licenseNumber
: item?.steward?.guilds?.licenseNumber,
item?.guilds
? item?.guilds?.user?.city?.name
: item?.steward?.guilds?.user?.city?.name,
item?.numberOfCarcasses,
item?.weightOfCarcasses,
item?.loggedRegistrationCode ? item.loggedRegistrationCode : "-",
item?.receiverState === "accepted"
? "تایید شده"
: item?.receiverState === "rejected"
? "رد شده"
: "در انتظار تایید",
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [selectedDate1, selectedDate2, perPage]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`dispenser-allocations/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&date1=${selectedDate1}&date2=${selectedDate2}&page=${1}&page_size=${perPage}&dispenser_key=${key}`
);
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}
>
<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>
</Grid>
<Grid container xs={12} justifyContent="start" alignItems="center">
<ResponsiveTable
noPagination
data={[
[
dashboardData?.numberOfAllocations?.toLocaleString(),
dashboardData?.totalQuantity?.toLocaleString(),
dashboardData?.totalWeight?.toLocaleString(),
dashboardData?.numberOfStewardsAllocations?.toLocaleString(),
dashboardData?.stewardTotalQuantity?.toLocaleString(),
dashboardData?.stewardTotalWeight?.toLocaleString(),
dashboardData?.numberOfGuildsAllocations?.toLocaleString(),
dashboardData?.guildTotalQuantity?.toLocaleString(),
dashboardData?.guildTotalWeight?.toLocaleString(),
],
]}
columns={[
"تعداد کل تخصیصات",
"حجم کل تخصیصات",
"وزن کل تخصیصات",
"تعداد مباشرین تختصیص داده شده",
"حجم تخصیصی به مباشرین",
"وزن تخصیصی به مباشرین",
"تعداد اصناف تختصیص داده شده",
"حجم تخصیصی به اصناف",
"وزن تخصیصی به اصناف",
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"توزیع کننده",
"نوع توزیع کننده",
"شناسه صنف",
"تاریخ ثبت",
"ماهیت",
"نوع تخصیص",
"نام واحد صنفی",
"نام شخص/شرکت",
"کدملی",
"موبایل",
"نوع فعالیت",
"حوزه فعالیت",
"شماره مجوز",
"شهرستان",
"حجم لاشه",
"وزن لاشه",
"کداحراز",
"وضعیت",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="اطلاعات توزیع کننده"
/>
</Grid>
);
};

View File

@@ -0,0 +1,111 @@
import React, { useContext } from "react";
import { Button, Checkbox, FormControlLabel, FormGroup } from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { useDispatch } from "react-redux";
import { slaughterHouseEditDispenserService } from "../../services/slaughter-house-submit-dispenser-service";
const validationSchema = Yup.object({
free_sale: Yup.boolean(),
free_guilds: Yup.boolean(),
free_stewards: Yup.boolean(),
});
export const SlaughterHouseDispensersAccessLevel = ({ updateTable, item }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const formik = useFormik({
initialValues: {
free_sale: item?.freeSale ? item?.freeSale : false,
free_guilds: item?.freeGuilds ? item?.freeGuilds : false,
free_stewards: item?.freeStewards ? item?.freeStewards : false,
},
validationSchema: validationSchema,
onSubmit: (values) => {
dispatch(
slaughterHouseEditDispenserService({
dispenser_key: item?.key,
type: "update-acceess-level",
free_sale: values.free_sale,
free_guilds: values.free_guilds,
free_stewards: values.free_stewards,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(CLOSE_MODAL());
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
},
});
return (
<Grid container xs={12} direction="column">
<form onSubmit={formik.handleSubmit}>
<FormGroup>
<FormControlLabel
control={
<Checkbox
id="free_sale"
name="free_sale"
checked={formik.values.free_sale}
onChange={formik.handleChange}
/>
}
label="اجازه فروش آزاد"
/>
<FormControlLabel
control={
<Checkbox
id="free_guilds"
name="free_guilds"
checked={formik.values.free_guilds}
onChange={formik.handleChange}
/>
}
label="فروش به اصناف آزاد"
/>
<FormControlLabel
control={
<Checkbox
id="free_stewards"
name="free_stewards"
checked={formik.values.free_stewards}
onChange={formik.handleChange}
/>
}
label="فروش به مباشرین آزاد"
/>
</FormGroup>
{formik.touched.free_stewards && formik.errors.free_stewards ? (
<div style={{ color: "red" }}>{formik.errors.free_stewards}</div>
) : null}
<Button
fullWidth
color="primary"
variant="contained"
type="submit"
sx={{ marginTop: 2 }}
>
ویرایش
</Button>
</form>
</Grid>
);
};

View File

@@ -0,0 +1,124 @@
import {
Button,
IconButton,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import { useState } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import { useDispatch } from "react-redux";
import { DRAWER, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { Grid } from "../../../../components/grid/Grid";
import { SlaughterHouseSubmitDispenser } from "../slaughter-house-submit-dispenser/SlaughterHouseSubmitDispenser";
import EditIcon from "@mui/icons-material/Edit";
import SubtitlesIcon from "@mui/icons-material/Subtitles";
import { SlaughterHouseDispensersAccessLevel } from "../slaughter-house-dispensers-access-level/SlaughterHouseDispensersAccessLevel";
export const SlaughterHouseDispensersOperation = ({ 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;
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: "10px" }}>
<Grid
container
direction="column"
gap={1}
alignItems="flex-start"
style={{ width: 180 }}
>
<Tooltip placement="left" title="ویرایش توزیع کننده">
<Button
size="small"
color="primary"
startIcon={<EditIcon fontSize="small" />}
onClick={() => {
handleClose();
dispatch(
DRAWER({
title: "ویرایش توزیع کننده",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterHouseSubmitDispenser
updateTable={updateTable}
isEdit
item={item}
/>
),
})
);
}}
>
<Typography variant="body2" fontWeight={600}>
ویرایش توزیع کننده
</Typography>
</Button>
</Tooltip>
<Tooltip placement="left" title="تعیین سطح دسترسی توزیع کننده">
<Button
size="small"
color="secondary"
startIcon={<SubtitlesIcon fontSize="small" />}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تعیین سطح دسترسی توزیع کننده",
content: (
<SlaughterHouseDispensersAccessLevel
updateTable={updateTable}
item={item}
/>
),
})
);
}}
>
<Typography variant="body2" fontWeight={600}>
تعیین سطح دسترسی
</Typography>
</Button>
</Tooltip>
</Grid>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,252 @@
import React from "react";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { NavLink } from "../../../../components/nav-link/NavLink";
import LinkItem from "../../../../components/link-item/LinkItem";
import { VscBroadcast, VscPerson } from "react-icons/vsc";
import {
ROUTE_ADMINX_DELEGATES_MANAGEMENT,
ROUTE_ADMINX_DISPENSERS_INVENTORY,
ROUTE_ADMINX_DISPENSERS_KILLHOUSES,
ROUTE_ADMINX_DISPENSERS_MANAGEMENT,
ROUTE_ADMINX_DISPENSERS_MANAGEMENT_V2,
ROUTE_ADMINX_DISPENSERS_STEWARDS,
ROUTE_ADMINX_DISPENSERS_STOCK,
ROUTE_ADMINX_SALE_DESTRIBUTION_DETAILS,
ROUTE_ADMINX_TRANSACTIONS,
ROUTE_CITY_DISPENSERS_INVENTORY,
ROUTE_CITY_REQUEST_DISTRIBUTION,
ROUTE_CITY_REQUEST_TRANSACTIONS,
ROUTE_PROVINCE_DISPENSERS_INVENTORY,
ROUTE_PROVINCE_DISPENSERS_KILLHOUSES,
ROUTE_PROVINCE_DISPENSERS_MANAGEMENT,
ROUTE_PROVINCE_DISPENSERS_STEWARDS,
ROUTE_PROVINCE_FINANCIAL_TRANSACTIONS,
ROUTE_PROVINCE_SALE_DESTRIBUTION_DETAILS,
ROUTE_PROVINCE_SUPERVISOR_DISPENSERS_INVENTORY,
ROUTE_PROVINCE_SUPERVISOR_DISPENSERS_STOCK,
ROUTE_PROVINCE_SUPERVISOR_REQUEST_DISTRIBUTION,
ROUTE_PROVINCE_SUPERVISOR_REQUEST_TRANSACTIONS,
ROUTE_PROVINCE_TRANSACTIONS,
ROUTE_SLAUGHTER_DISPENSERS_KILLHOUSES,
ROUTE_SLAUGHTER_DISPENSERS_MANAGEMENT,
ROUTE_SLAUGHTER_DISPENSERS_STEWARDS,
ROUTE_SUPER_ADMIN_DESTRIBUTION_DETAILS,
ROUTE_SUPER_ADMIN_DISPENSERS_INVENTORY,
ROUTE_SUPER_ADMIN_DISPENSERS_KILLHOUSES,
ROUTE_SUPER_ADMIN_DISPENSERS_MANAGEMENT,
ROUTE_SUPER_ADMIN_DISPENSERS_STEWARDS,
ROUTE_SUPER_ADMIN_DISPENSERS_STOCK,
ROUTE_SUPER_ADMIN_TRANSACTIONS,
} from "../../../../routes/routes";
import { useLocation } from "react-router-dom";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { FaMoneyBill, FaStore, FaWarehouse } from "react-icons/fa";
import BadgeIcon from "@mui/icons-material/Badge";
const ROUTE_MAP = {
management: {
KillHouse: ROUTE_SLAUGHTER_DISPENSERS_MANAGEMENT,
AdminX: ROUTE_ADMINX_DISPENSERS_MANAGEMENT,
SuperAdmin: ROUTE_SUPER_ADMIN_DISPENSERS_MANAGEMENT,
AdminXX: ROUTE_ADMINX_DISPENSERS_MANAGEMENT_V2,
AdminXXX: ROUTE_ADMINX_DELEGATES_MANAGEMENT,
default: ROUTE_PROVINCE_DISPENSERS_MANAGEMENT,
},
killhouses: {
KillHouse: ROUTE_SLAUGHTER_DISPENSERS_KILLHOUSES,
AdminX: ROUTE_ADMINX_DISPENSERS_KILLHOUSES,
SuperAdmin: ROUTE_SUPER_ADMIN_DISPENSERS_KILLHOUSES,
default: ROUTE_PROVINCE_DISPENSERS_KILLHOUSES,
},
stewards: {
KillHouse: ROUTE_SLAUGHTER_DISPENSERS_STEWARDS,
AdminX: ROUTE_ADMINX_DISPENSERS_STEWARDS,
SuperAdmin: ROUTE_SUPER_ADMIN_DISPENSERS_STEWARDS,
default: ROUTE_PROVINCE_DISPENSERS_STEWARDS,
},
inventory: {
AdminX: ROUTE_ADMINX_DISPENSERS_INVENTORY,
SuperAdmin: ROUTE_SUPER_ADMIN_DISPENSERS_INVENTORY,
CityPoultry: ROUTE_CITY_DISPENSERS_INVENTORY,
ProvinceSupervisor: ROUTE_PROVINCE_SUPERVISOR_DISPENSERS_INVENTORY,
default: ROUTE_PROVINCE_DISPENSERS_INVENTORY,
},
distribution: {
AdminX: ROUTE_ADMINX_SALE_DESTRIBUTION_DETAILS,
SuperAdmin: ROUTE_SUPER_ADMIN_DESTRIBUTION_DETAILS,
ProvinceOperator: ROUTE_PROVINCE_SALE_DESTRIBUTION_DETAILS,
CityPoultry: ROUTE_CITY_REQUEST_DISTRIBUTION,
ProvinceSupervisor: ROUTE_PROVINCE_SUPERVISOR_REQUEST_DISTRIBUTION,
default: ROUTE_PROVINCE_TRANSACTIONS,
},
transactions: {
AdminX: ROUTE_ADMINX_TRANSACTIONS,
SuperAdmin: ROUTE_SUPER_ADMIN_TRANSACTIONS,
ProvinceFinancial: ROUTE_PROVINCE_FINANCIAL_TRANSACTIONS,
CityPoultry: ROUTE_CITY_REQUEST_TRANSACTIONS,
ProvinceSupervisor: ROUTE_PROVINCE_SUPERVISOR_REQUEST_TRANSACTIONS,
default: ROUTE_PROVINCE_TRANSACTIONS,
},
stock: {
AdminX: ROUTE_ADMINX_DISPENSERS_STOCK,
SuperAdmin: ROUTE_SUPER_ADMIN_DISPENSERS_STOCK,
ProvinceSupervisor: ROUTE_PROVINCE_SUPERVISOR_DISPENSERS_STOCK,
},
};
const getRoute = (routeType, role) => {
const routeMap = ROUTE_MAP[routeType];
return routeMap[role] || routeMap.default || null;
};
const isActive = (pathname, route) => (pathname === route ? "true" : null);
export const SlaughterHouseDispensersOperations = () => {
const { pathname } = useLocation();
const role = getRoleFromUrl();
const isKillHouse = role === "KillHouse";
const isAdminX = role === "AdminX";
const hasStockAccess = [
"AdminX",
"SuperAdmin",
"ProvinceSupervisor",
].includes(role);
const managementRoute = getRoute("management", role);
const managementRouteV2 = getRoute("management", "AdminXX");
const managementRouteDelegates = getRoute("management", "AdminXXX");
const killhousesRoute = getRoute("killhouses", role);
const stewardsRoute = getRoute("stewards", role);
const inventoryRoute = getRoute("inventory", role);
const distributionRoute = getRoute("distribution", role);
const transactionsRoute = getRoute("transactions", role);
const stockRoute = getRoute("stock", role);
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
style={{ placeContent: "baseline" }}
>
{isAdminX && (
<Grid container direction="column" style={{ width: "100%" }}>
<Grid container gap={SPACING.SMALL} justifyContent="center">
<NavLink
to={managementRouteDelegates}
active={isActive(pathname, managementRouteDelegates)}
>
<LinkItem
icon={<BadgeIcon fontSize="large" sx={{ color: "#244CCC" }} />}
title="مدیریت نمایندگان"
description="مدیریت نمایندگان"
/>
</NavLink>
<NavLink
to={managementRouteV2}
active={isActive(pathname, managementRouteV2)}
>
<LinkItem
icon={<VscPerson size={30} color="#244CCC" />}
title="مدیریت توزیع کنندگان دوم"
description="مدیریت توزیع کنندگان دوم"
/>
</NavLink>
<NavLink
to={managementRoute}
active={isActive(pathname, managementRoute)}
>
<LinkItem
icon={<VscPerson size={30} color="#244CCC" />}
title="مدیریت توزیع کنندگان"
description="مدیریت توزیع کنندگان"
/>
</NavLink>
{!isKillHouse && (
<NavLink
to={killhousesRoute}
active={isActive(pathname, killhousesRoute)}
>
<LinkItem
icon={<FaStore size={30} color="#244CCC" />}
title="مدیریت کشتارگاه ها"
description="مدیریت کشتارگاه ها"
/>
</NavLink>
)}
{!isKillHouse && (
<NavLink
to={stewardsRoute}
active={isActive(pathname, stewardsRoute)}
>
<LinkItem
icon={<VscBroadcast size={30} color="#244CCC" />}
title="مدیریت مباشرین"
description="مدیریت مباشرین"
/>
</NavLink>
)}
</Grid>
</Grid>
)}
<Grid container direction="column" style={{ width: "100%" }}>
<Grid container gap={SPACING.SMALL} justifyContent="center">
{!isKillHouse && inventoryRoute && (
<NavLink
to={inventoryRoute}
active={isActive(pathname, inventoryRoute)}
>
<LinkItem
icon={<FaStore size={30} color="#244CCC" />}
title="توزیع/فروش کشتارگاه"
description="توزیع/فروش کشتارگاه"
/>
</NavLink>
)}
{!isKillHouse && distributionRoute && (
<NavLink
to={distributionRoute}
active={isActive(pathname, distributionRoute)}
>
<LinkItem
icon={<FaMoneyBill size={30} color="#244CCC" />}
title=" توزیع/فروش مباشر"
description=" توزیع/فروش مباشر"
/>
</NavLink>
)}
{!isKillHouse && transactionsRoute && (
<NavLink
to={transactionsRoute}
active={isActive(pathname, transactionsRoute)}
>
<LinkItem
icon={<FaWarehouse size={30} color="#244CCC" />}
title="مدیریت انبار صنوف"
description="تراکنش ها"
/>
</NavLink>
)}
{hasStockAccess && stockRoute && (
<NavLink to={stockRoute} active={isActive(pathname, stockRoute)}>
<LinkItem
icon={<FaWarehouse size={30} color="#244CCC" />}
title="مانده انبار"
description="مانده انبار"
/>
</NavLink>
)}
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,264 @@
import React, { useContext } from "react";
import { useFormik } from "formik";
import * as yup from "yup";
import { useDispatch } from "react-redux";
import { Button, TextField, Typography, Box } from "@mui/material";
import PersonIcon from "@mui/icons-material/Person";
import PublicIcon from "@mui/icons-material/Public";
import BadgeIcon from "@mui/icons-material/Badge";
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
import AccountBoxIcon from "@mui/icons-material/AccountBox";
import BusinessIcon from "@mui/icons-material/Business";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterHouseEditDispenserService } from "../../services/slaughter-house-submit-dispenser-service";
const InfoBox = ({ icon: Icon, label, value, iconSx }) => (
<Box
display="flex"
alignItems={iconSx ? "flex-start" : "center"}
gap={1}
px={1.5}
py={0.5}
bgcolor="#f5f5f5"
borderRadius={1}
>
<Icon color="action" sx={iconSx} />
<Box>
<Typography variant="caption" color="text.secondary">
{label}
</Typography>
<Typography variant="body1">{value || "-"}</Typography>
</Box>
</Box>
);
const getValidationSchema = () =>
yup.object({
mobile: yup
.string()
.required("شماره همراه الزامی است")
.matches(/^09\d{9}$/, "شماره تلفن باید با 09 شروع شود و 11 رقم باشد"),
});
const DispenserForm = ({ formik, userInfo }) => {
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
{userInfo && (
<Grid container spacing={2} xs={12} mb={2}>
<Grid item xs={12}>
<Typography
variant="subtitle2"
color="text.secondary"
gutterBottom
sx={{ mb: 1 }}
>
اطلاعات شخصی
</Typography>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="کد ملی"
value={formik.values.nationalId}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={PersonIcon}
label="نام پدر"
value={userInfo.fatherName}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={CalendarTodayIcon}
label="تاریخ تولد"
value={userInfo.birthday}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={AccountBoxIcon}
label="جنسیت"
value={userInfo.gender ? "مرد" : "زن"}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="شماره شناسنامه"
value={userInfo.nationalCode}
/>
</Grid>
</Grid>
)}
<Grid container spacing={2} xs={12}>
<Grid item xs={12} md={4}>
<InfoBox
icon={PersonIcon}
label="نام"
value={formik.values.first_name}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={PersonIcon}
label="نام خانوادگی"
value={formik.values.last_name}
/>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox icon={PublicIcon} label="شهر" value={formik.values.city} />
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={BusinessIcon}
label="نوع توزیع کننده"
value={
formik.values.dispenser_type === "inductor"
? "واسطه"
: formik.values.dispenser_type === "salesman"
? "فروشنده"
: formik.values.dispenser_type === "driver"
? "راننده"
: formik.values.dispenser_type
}
/>
</Grid>
{formik.values.dispenser_type === "driver" && (
<>
<Grid item xs={12} md={4}>
<InfoBox
icon={BusinessIcon}
label="نوع خودرو"
value={formik.values.driver_car_type}
/>
</Grid>
{formik.values.pelak && (
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="پلاک خودرو"
value={formik.values.pelak}
/>
</Grid>
)}
</>
)}
<Grid item xs={12} md={4}>
<InfoBox
icon={BusinessIcon}
label="سقف محدودیت"
value={formik.values.limitation_amount || 0}
/>
</Grid>
<Grid item xs={12} md={4}>
<TextField
label="شماره همراه"
variant="outlined"
fullWidth
id="mobile"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={Boolean(formik.errors.mobile)}
helperText={formik.errors.mobile}
inputProps={{ maxLength: 11 }}
/>
</Grid>
</Grid>
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={!formik.isValid}
>
ویرایش
</Button>
</Grid>
</Grid>
</form>
);
};
export const AllDispensersEditForm = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const nationalId = item?.user?.nationalId || item?.user?.nationalCode;
// Extract userInfo from item.user object
const userInfo = item?.user
? {
nationalCode: item.user.nationalId || item.user.nationalCode || null,
fatherName: item.user.fatherName || null,
birthday: item.user.birthday || null,
gender: item.user.gender,
identityNo: item.user.identityNo || null,
identitySeries: item.user.identitySeries || null,
identitySerial: item.user.identitySerial || null,
}
: null;
const formik = useFormik({
initialValues: {
first_name: item?.user?.firstName || "",
last_name: item?.user?.lastName || "",
mobile: item?.user?.mobile || "",
city: item?.user?.cityName || "",
national_id: nationalId || "",
dispenser_type: item?.dispenserType || "inductor",
limitation_amount: item?.limitationAmount || 0,
driver_car_type: item?.car || "",
pelak: item?.pelak || "",
},
enableReinitialize: true,
validationSchema: getValidationSchema(),
onSubmit: (values) => {
const submitData = {
key: item?.key,
mobile: values.mobile,
};
dispatch(slaughterHouseEditDispenserService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
},
});
return <DispenserForm formik={formik} userInfo={userInfo} />;
};

View File

@@ -0,0 +1,79 @@
import { IconButton, Popover, Typography, Button } from "@mui/material";
import React, { useState } from "react";
import EditIcon from "@mui/icons-material/Edit";
import TuneIcon from "@mui/icons-material/Tune";
import { useDispatch } from "react-redux";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AllDispensersEditForm } from "./AllDispensersEditForm";
export const AllDispensersOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div style={{ padding: "10px" }}>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش توزیع کننده",
content: (
<AllDispensersEditForm
item={item}
updateTable={updateTable}
/>
),
size: 620,
})
);
}}
startIcon={<EditIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
ویرایش
</Typography>
</Button>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,162 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDispenserService } from "../../services/slaughter-get-dispenser-service";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { AllDispensersOperations } from "./AllDispensersOperations";
export const AllDispensersTab = () => {
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 isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDispenserService({
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.user?.firstName || "-",
item?.user?.lastName || "-",
item?.user?.mobile || "-",
item?.user?.cityName || "-",
<AllDispensersOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDispenserService({
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="کل توزیع کنندگان"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,135 @@
import React, { useContext, useState } from "react";
import {
Button,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterEditDispenserInfoService } from "../../services/slaughter-edit-dispenser-info";
export const DispenserInfoLimitationForm = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasLimitation, setHasLimitation] = useState(item?.limitation || false);
const [governmentalValue, setGovernmentalValue] = useState(
item?.governmentalLimitationWeight ||
0
);
const [freeValue, setFreeValue] = useState(
item?.freeLimitationWeight || 0
);
const handleSubmit = (e) => {
e.preventDefault();
const submitData = {
key: item?.key,
limitation: hasLimitation,
governmental_limitation_weight: hasLimitation
? Number(governmentalValue)
: 0,
free_limitation_weight: hasLimitation ? Number(freeValue) : 0,
};
dispatch(slaughterEditDispenserInfoService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
};
return (
<form onSubmit={handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid container item xs={12} alignItems="center" gap={1}>
<Typography variant="body2" color="text.secondary">
اطلاعات توزیع کننده:
</Typography>
<Typography variant="h6" mb={0.75}>
{item?.firstName} {item?.lastName}
</Typography>
</Grid>
<Grid item xs={12} mb={1}>
<FormControlLabel
control={
<Checkbox
checked={hasLimitation}
onChange={(e) => setHasLimitation(e.target.checked)}
color="primary"
/>
}
label="محدودیت فروش روزانه"
/>
</Grid>
{hasLimitation && (
<>
<Grid item xs={12}>
<TextField
label="حداکثر فروش دولتی (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={governmentalValue}
onChange={(e) => setGovernmentalValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="حداکثر فروش آزاد (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={freeValue}
onChange={(e) => setFreeValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
</>
)}
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={
hasLimitation && governmentalValue === 0 && freeValue === 0
}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,140 @@
import { IconButton, Popover, Typography, Button } from "@mui/material";
import React, { useState, useContext } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import ToggleOffIcon from "@mui/icons-material/ToggleOff";
import BlockIcon from "@mui/icons-material/Block";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterEditDispenserInfoService } from "../../services/slaughter-edit-dispenser-info";
import { DispenserInfoLimitationForm } from "./DispenserInfoLimitationForm";
export const DispenserInfoOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const isActive = item?.active;
const handleToggleActive = () => {
handleClose();
dispatch(
slaughterEditDispenserInfoService({
key: item?.key,
active: !isActive,
})
).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",
});
if (updateTable) {
updateTable();
}
}
});
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div
style={{
padding: "10px",
display: "flex",
flexDirection: "column",
gap: "8px",
}}
>
<Button
color={isActive ? "error" : "success"}
size="small"
onClick={handleToggleActive}
startIcon={
isActive ? (
<ToggleOffIcon fontSize="small" />
) : (
<ToggleOnIcon fontSize="small" />
)
}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
{isActive ? "غیرفعال کردن" : "فعال کردن"}
</Typography>
</Button>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تنظیم محدودیت فروش",
content: (
<DispenserInfoLimitationForm
item={item}
updateTable={updateTable}
/>
),
size: 400,
})
);
}}
startIcon={<BlockIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
تنظیم محدودیت
</Typography>
</Button>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDispenserInfoService } from "../../services/slaughter-get-dispenser-info";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DispenserInfoOperations } from "./DispenserInfoOperations";
export const KillHouseDispensersTab = () => {
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 isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDispenserInfoService({
type: "KillHouse",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const killhouseDisplay =
item?.killHouse?.name && item?.killHouse?.mobile
? `${item.killHouse.name} (${item.killHouse.mobile})`
: item?.killHouse?.name
? item.killHouse.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || "-",
item?.lastName || "-",
item?.mobile || "-",
item?.city || "-",
killhouseDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DispenserInfoOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDispenserInfoService({
type: "KillHouse",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="توزیع کنندگان کشتارگاه‌ها"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"کشتارگاه",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,30 @@
import React, { useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs, Box } from "@mui/material";
import { AllDispensersTab } from "./AllDispensersTab";
import { KillHouseDispensersTab } from "./KillHouseDispensersTab";
import { StewardDispensersTab } from "./StewardDispensersTab";
export const SlaughterHouseDispensersV2 = () => {
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<Grid container xs={12} justifyContent="center" alignItems="center" gap={2}>
<Tabs value={value} onChange={handleChange}>
<Tab label="کل توزیع کنندگان" value={0} />
<Tab label="توزیع کنندگان کشتارگاه‌ها" value={1} />
<Tab label="توزیع کنندگان مباشرین" value={2} />
</Tabs>
<Box sx={{ width: "100%", mt: 2 }}>
{value === 0 && <AllDispensersTab />}
{value === 1 && <KillHouseDispensersTab />}
{value === 2 && <StewardDispensersTab />}
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,199 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch } from "react-redux";
import { slaughterGetDispenserInfoService } from "../../services/slaughter-get-dispenser-info";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { DispenserInfoOperations } from "./DispenserInfoOperations";
export const StewardDispensersTab = () => {
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 isFirstMount = useRef(true);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDispenserInfoService({
type: "Steward",
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active;
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
key={`limitation-${item?.key || i}`}
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const stewardDisplay =
item?.steward?.name && item?.steward?.user?.mobile
? `${item.steward.name} (${item.steward.user.mobile})`
: item?.steward?.name
? item.steward.name
: "-";
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.firstName || "-",
item?.lastName || "-",
item?.mobile || "-",
item?.city || "-",
stewardDisplay,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
<Chip
key={`status-${item?.key || i}`}
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>,
<DispenserInfoOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
const updateTableData = () => {
fetchApiData(page);
};
useEffect(() => {
fetchApiData(1);
}, []);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDispenserInfoService({
type: "Steward",
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
return (
<Box>
<Grid container gap={SPACING.SMALL} mb={2}>
<form onSubmit={handleSubmit} style={{ width: "100%" }}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12}>
<ResponsiveTable
title="توزیع کنندگان مباشرین"
columns={[
"ردیف",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"مباشر",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,351 @@
import React, { useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
Button,
IconButton,
Tab,
Tabs,
TextField,
Tooltip,
} from "@mui/material";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch } from "react-redux";
import axios from "axios";
import { SlaughterHouseSubmitDispenser } from "../slaughter-house-submit-dispenser/SlaughterHouseSubmitDispenser";
import { SlaughterHouseDispensersOperation } from "../slaughter-house-dispensers-operation/SlaughterHouseDispensersOperation";
import { slaughterGetDispenserDashboard } from "../../services/slaughter-house-get-dispenser-dashboard";
import {
ROUTE_ADMINX_DISPENSER_DETAILS,
ROUTE_PROVINCE_DISPENSER_DETAILS,
ROUTE_SLAUGHTER_DISPENSER_DETAILS,
ROUTE_SUPER_ADMIN_DISPENSER_DETAILS,
} from "../../../../routes/routes";
import { useNavigate } from "react-router-dom";
import { RiSearchLine } from "react-icons/ri";
import VisibilityIcon from "@mui/icons-material/Visibility";
export const SlaughterHouseDispensers = () => {
const dispatch = useDispatch();
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
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 [value, setValue] = useState(0);
const [dashboardData, setDashboardData] = useState();
const navigate = useNavigate();
const handleChange = (event, newValue) => {
setValue(newValue);
};
const fetchApiData = async (page) => {
let response;
dispatch(LOADING_START());
response = await axios.get(
`dispenser/?search=filter&value=${textValue}&role=${getRoleFromUrl()}&page=${page}&page_size=${perPage}`
);
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(() => {
fetchApiData(1);
}, [value]);
const getDispenserType = (item) => {
let type = "";
switch (item?.dispenserType) {
case "inductor":
type = "واسطه";
break;
case "salesman":
type = "فروشنده";
break;
case "driver":
type = `راننده - ${item?.car} (${item?.pelak})`;
break;
default:
break;
}
return type;
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + (perPage * page) / 2 + 1,
getDispenserType(item),
item?.user?.fullname,
item?.user?.mobile,
item?.user?.city?.cityName,
item?.killHouse?.name,
item?.limitationAmount?.toLocaleString(),
item?.active ? "فعال" : "غیر فعال",
item?.allocationsInfo?.numberOfAllocations?.toLocaleString(),
item?.allocationsInfo?.totalWeight?.toLocaleString(),
item?.allocationsInfo?.totalQuantity?.toLocaleString(),
item?.allocationsInfo?.numberOfTodayAllocations?.toLocaleString(),
item?.allocationsInfo?.totalTodayQuantity?.toLocaleString(),
item?.allocationsInfo?.totalTodayWeight?.toLocaleString(),
value === 0 ? (
<SlaughterHouseDispensersOperation
key={i}
item={item}
updateTable={updateTable}
/>
) : (
<Tooltip key={i} title="جزئیات" placement="left">
<IconButton
color="success"
onClick={() => {
navigate(
getRoleFromUrl() === "KillHouse"
? `${ROUTE_SLAUGHTER_DISPENSER_DETAILS}/${item.key}`
: getRoleFromUrl() === "AdminX"
? `${ROUTE_ADMINX_DISPENSER_DETAILS}/${item.key}`
: getRoleFromUrl() === "SuperAdmin"
? `${ROUTE_SUPER_ADMIN_DISPENSER_DETAILS}/${item.key}`
: `${ROUTE_PROVINCE_DISPENSER_DETAILS}/${item.key}`
);
}}
>
<VisibilityIcon />
</IconButton>
</Tooltip>
),
];
});
setTableData(d);
}, [data, value]);
useEffect(() => {
fetchApiData(1);
}, [perPage]);
useEffect(() => {
if (value === 1) {
dispatch(slaughterGetDispenserDashboard()).then((r) => {
setDashboardData(r.payload.data);
});
}
}, [value]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`dispenser/?role=${getRoleFromUrl()}&search=filter&value=${textValue}&page=${1}&page_size=${perPage}`
);
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}>
<Tabs value={value} onChange={handleChange}>
<Tab label="توزیع کنندگان" value={0} />
<Tab label="جزئیات پخش" value={1} />
</Tabs>
{value === 0 && (
<>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
title: "ثبت توزیع کننده جدید",
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterHouseSubmitDispenser
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت توزیع کننده
</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="مدیریت توزیع کنندگان"
/>
</>
)}
{value === 1 && (
<>
<Grid
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid container xs={12} justifyContent="start" alignItems="center">
<ResponsiveTable
noPagination
data={[
[
dashboardData?.numberOfDispensers?.toLocaleString(),
dashboardData?.numberOfAllocations?.toLocaleString(),
dashboardData?.totalWeight?.toLocaleString(),
dashboardData?.totalQuantity?.toLocaleString(),
dashboardData?.numberOfTodayAllocations?.toLocaleString(),
dashboardData?.totalTodayQuantity?.toLocaleString(),
dashboardData?.totalTodayWeight?.toLocaleString(),
],
]}
columns={[
"تعداد توزیع کنندگان",
"تعداد کل تخصیصات",
"وزن تخصیصات کل",
"حجم کل",
"تخصیصات امروز",
"حجم تخصیصات امروز",
"وزن امروز",
]}
title={"خلاصه اطلاعات"}
/>
</Grid>
<Grid mt={2}>
<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,216 @@
import {
Button,
FormControl,
InputLabel,
MenuItem,
Select,
TextField,
Typography,
} from "@mui/material";
import { useFormik } from "formik";
import React, { useContext, useEffect } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { ImageUpload } from "../../../../components/image-upload/ImageUpload";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import { fixBase64 } from "../../../../utils/toBase64";
import { PropTypes } from "prop-types";
import { useDispatch } from "react-redux";
import { slaughterNewComplaint } from "../../services/slaughter-new-complaint";
import { AppContext } from "../../../../contexts/AppContext";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { slaughterGetRegisteredComplaints } from "../../services/slaughter-get-registered-complaints";
import { slaughterGetComplaints } from "../../services/slaughter-get-complaints";
export const SlaughterHouseNewComplaint = ({ barKey, role }) => {
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const [topic, setTopic] = React.useState(10);
const [complaintImages, setComplaintImages] = React.useState([]);
const [complaintImagesBase64, setComplaintImagesBase64] = React.useState([]);
const complaintImageHandler = (imageList, addUpdateIndex) => {
setComplaintImages(imageList);
setComplaintImagesBase64(imageList.map((img) => fixBase64(img.data_url)));
};
const isFormValid = (topic) => {
switch (topic) {
case 10:
return (
formik2.isValid && formik.isValid && complaintImagesBase64.length
);
default:
return formik.isValid && complaintImagesBase64.length;
}
};
const handleChange = (event) => {
setTopic(event.target.value);
};
const formik = useFormik({
initialValues: {
description: "",
},
validationSchema: Yup.object({
description: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را پر کنید!"),
}),
});
const formik2 = useFormik({
initialValues: {
looses: "",
},
validationSchema: Yup.object({
looses: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
formik2.validateForm();
}, []);
return (
<Grid
container
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">موضوع</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={topic}
label="موضوع"
onChange={handleChange}
>
<MenuItem value={10}>مغایرت درصد تلفات با واقعیت</MenuItem>
<MenuItem value={20}>دیگر</MenuItem>
</Select>
</FormControl>
{topic === 10 && (
<TextField
id="looses"
label="درصد تلفات"
variant="outlined"
sx={{ width: "100%", height: "100%" }}
value={formik2.values.looses}
error={
formik2.touched.looses ? Boolean(formik2.errors.looses) : null
}
onChange={formik2.handleChange}
onBlur={formik2.handleBlur}
helperText={
formik2.touched.looses && Boolean(formik2.errors.looses)
? formik2.errors.looses
: null
}
/>
)}
<TextField
id="description"
label="توضیحات"
multiline
rows={5}
variant="outlined"
sx={{ width: "100%", height: "100%" }}
value={formik.values.description}
error={
formik.touched.description
? Boolean(formik.errors.description)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.description && Boolean(formik.errors.description)
? formik.errors.description
: null
}
/>
<Typography>پیوست تصویر</Typography>
<Grid mb={SPACING.SMALL}>
<ImageUpload
id="image"
onChange={complaintImageHandler}
images={complaintImages}
maxNumber={4}
title={"بارگذاری سند"}
value={formik.values.image}
error={formik.touched.image ? Boolean(formik.errors.image) : null}
onBlur={formik.handleBlur}
helperText={
formik.touched.image && Boolean(formik.errors.image)
? formik.errors.image
: null
}
/>
</Grid>
</Grid>
<Button
fullWidth
variant="contained"
disabled={!isFormValid(topic)}
onClick={() => {
dispatch(LOADING_START());
dispatch(
slaughterNewComplaint({
bar_key: barKey,
image: complaintImagesBase64,
title: topic === 10 ? " مغایرت درصد تلفات با واقعیت" : "دیگر",
description: formik.values.description,
percent: formik2.values?.looses ? formik2.values?.looses : null,
role: role,
})
).then((r) => {
dispatch(LOADING_END());
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است",
severity: "error",
});
} else {
dispatch(slaughterGetComplaints());
dispatch(slaughterGetRegisteredComplaints());
dispatch(DRAWER({ right: false, bottom: false, content: null }));
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد!",
severity: "success",
});
}
});
}}
>
ثبت اطلاعات
</Button>
</Grid>
);
};
SlaughterHouseNewComplaint.propTypes = {
barKey: PropTypes.any,
role: PropTypes.any,
};

View File

@@ -0,0 +1,311 @@
import React, { useContext, useEffect, useState } from "react";
import { useFormik } from "formik";
import * as Yup from "yup";
import {
TextField,
Button,
RadioGroup,
FormControlLabel,
Radio,
FormControl,
FormLabel,
Box,
MenuItem,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import {
slaughterHouseEditDispenserService,
slaughterHouseSubmitDispenserService,
} from "../../services/slaughter-house-submit-dispenser-service";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { provinceGetCitiesService } from "../../../province/services/province-get-cities";
import { CarPelak } from "../../../../components/car-pelak/CarPelak";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
const validationSchema = Yup.object({
mobile: Yup.string()
.required("این فیلد اجباری است!")
.min(11, "شماره موبایل باید 11 رقم باشد")
.max(11, "شماره موبایل باید 11 رقم باشد")
.matches(/^09\d{9}$/, "شماره موبایل باید با 09 شروع شود و 11 رقم باشد"),
first_name: Yup.string().required("این فیلد اجباریست!"),
last_name: Yup.string().required("این فیلد اجباریست!"),
city: Yup.string().required("این فیلد اجباریست!"),
national_id: Yup.string()
.matches(/^\d{10}$/, "کد ملی ده رقمی است!")
.required("این فیلد اجباریست!"),
dispenser_type: Yup.string().required("این فیلد اجباریست!"),
limitation_amount: Yup.number()
.min(0, "عدد مثبت وارد کنید!")
.required("سقف محدودیت اجباری است"),
driver_car_type: Yup.string().when("dispenser_type", {
is: "driver",
then: Yup.string().required("نوع خودرو اجباری است!"),
}),
});
export const SlaughterHouseSubmitDispenser = ({
updateTable,
isEdit,
item,
}) => {
const dispatch = useDispatch();
const { provinceGetCities } = useSelector((state) => state.provinceSlice);
const [driverPelak, setDriverPelak] = useState([]);
const carPelakHandleChange = (pelak1, pelak2, pelak3, pelak4) => {
setDriverPelak([pelak1, pelak2, pelak3, pelak4]);
};
const [openNotif] = useContext(AppContext);
useEffect(() => {
dispatch(provinceGetCitiesService());
}, []);
const formik = useFormik({
initialValues: {
mobile: item?.user?.mobile ? item?.user?.mobile : "",
first_name: item?.user?.firstName ? item?.user?.firstName : "",
last_name: item?.user?.lastName ? item?.user?.lastName : "",
city: item?.user?.city?.cityName ? item?.user?.city?.cityName : "",
national_id: item?.user?.nationalId ? item?.user?.nationalId : "",
dispenser_type: item?.dispenserType ? item?.dispenserType : "inductor",
limitation_amount: item?.limitation_amount ? item?.limitation_amount : 0,
driver_car_type: item?.car ? item?.car : "",
},
validationSchema,
onSubmit: (values) => {
if (!isEdit) {
dispatch(
slaughterHouseSubmitDispenserService({
mobile: values.mobile,
first_name: values.first_name,
last_name: values.last_name,
city: values.city,
national_id: values.national_id,
dispenser_type: values.dispenser_type,
limitation_amount: values.limitation_amount,
role: getRoleFromUrl(),
pelak:
values.dispenser_type === "driver"
? driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3]
: null,
car: values.driver_car_type ? values.driver_car_type : null,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
} else {
dispatch(
slaughterHouseEditDispenserService({
type: "update-profile",
dispenser_key: item?.key,
mobile: values.mobile,
first_name: values.first_name,
last_name: values.last_name,
city: values.city,
national_id: values.national_id,
dispenser_type: values.dispenser_type,
limitation_amount: values.limitation_amount,
role: getRoleFromUrl(),
pelak:
values.dispenser_type === "driver"
? driverPelak[0] +
" " +
driverPelak[1] +
" " +
driverPelak[2] +
" " +
driverPelak[3]
: null,
car: values.driver_car_type ? values.driver_car_type : null,
})
).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(DRAWER({ right: false, bottom: false, content: null }));
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}
},
});
return (
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ display: "flex", flexDirection: "column", gap: 2 }}
>
<TextField
fullWidth
label="نام"
name="first_name"
value={formik.values.first_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.first_name && Boolean(formik.errors.first_name)}
helperText={formik.touched.first_name && formik.errors.first_name}
/>
<TextField
fullWidth
label="نام خانوادگی"
name="last_name"
value={formik.values.last_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.last_name && Boolean(formik.errors.last_name)}
helperText={formik.touched.last_name && formik.errors.last_name}
/>
<TextField
fullWidth
label="موبایل"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.mobile && Boolean(formik.errors.mobile)}
helperText={formik.touched.mobile && formik.errors.mobile}
/>
<TextField
select
label="شهر"
variant="outlined"
fullWidth
id="city"
name="city"
value={formik.values.city}
onChange={formik.handleChange}
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
>
{provinceGetCities?.map((city) => (
<MenuItem key={city.id} value={city.name}>
{city.name}
</MenuItem>
))}
</TextField>
<TextField
fullWidth
label="کد ملی"
name="national_id"
value={formik.values.national_id}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.national_id && Boolean(formik.errors.national_id)}
helperText={formik.touched.national_id && formik.errors.national_id}
/>
<FormControl component="fieldset">
<FormLabel component="legend">ماهیت</FormLabel>
<RadioGroup
name="dispenser_type"
value={formik.values.dispenser_type}
onChange={formik.handleChange}
>
<FormControlLabel
value="inductor"
control={<Radio />}
label="واسطه"
/>
<FormControlLabel
value="salesman"
control={<Radio />}
label="فروشنده"
/>
<FormControlLabel value="driver" control={<Radio />} label="راننده" />
</RadioGroup>
</FormControl>
{formik.values.dispenser_type === "driver" && (
<>
<TextField
fullWidth
label="نوع خودرو"
name="driver_car_type"
value={formik.values.driver_car_type}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.driver_car_type &&
Boolean(formik.errors.driver_car_type)
}
helperText={
formik.touched.driver_car_type && formik.errors.driver_car_type
}
/>
<Grid item xs={12} alignItems="center" justifyContent={"center"}>
<CarPelak
width="100%"
handleChange={carPelakHandleChange}
pelakInitial={isEdit ? item?.pelak : ""}
/>
</Grid>
</>
)}
<TextField
fullWidth
label="سقف محدودیت"
name="limitation_amount"
type="number"
value={formik.values.limitation_amount}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.limitation_amount &&
Boolean(formik.errors.limitation_amount)
}
helperText={
formik.touched.limitation_amount && formik.errors.limitation_amount
}
/>
<Button
color="primary"
variant="contained"
type="submit"
disabled={
formik.values.dispenser_type === "driver" ? !driverPelak[1] : false
}
>
ثبت
</Button>
</Box>
);
};

View File

@@ -0,0 +1,237 @@
import React, { useContext, useEffect, useState } from "react";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { Button, IconButton, Tooltip } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { formatJustDate } from "../../../../utils/formatTime";
import ShowImage from "../../../../components/show-image/ShowImage";
import { SlaughterHouseVetBarsOperation } from "../../../slaughter-house-vet/components/slaughter-house-vet-bars-operation/SlaughterHouseVetBarsOperation";
import SystemUpdateAltIcon from "@mui/icons-material/SystemUpdateAlt";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
import { CheckCleanceCode } from "../../../../components/check-clearance-code/ChechClearanceCode";
import { SlaughterFreeBarsAlivesOperations } from "../slaughter-free-bars-alives-operations/SlaughterFreeBarsAlivesOperations";
import { AppContext } from "../../../../contexts/AppContext";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterInventoryFreeBuyBarsAlives = ({
title,
barState,
fetchDashboardData,
withDate,
selectedDate1,
selectedDate2,
searchValue,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
useEffect(() => {
dispatch(
slaughterGetProfile({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedSubUser?.key]);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(10);
const [page, setPage] = useState(1);
const [tableData, setTableData] = useState([]);
const fetchApiData = async (page) => {
let response;
dispatch(LOADING_START());
try {
response = await axios.get(
`kill_house_free_bar/?type=live&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&page=${page}&page_size=${perPage}&bar_state=${barState}&date_type=input${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ""
}${searchValue ? `&search=filter&value=${searchValue}` : ""}`
);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
};
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
const updateTable = () => {
fetchApiData(page !== 0 ? page : 1);
fetchDashboardData();
};
useEffect(() => {
fetchApiData(1);
}, [
perPage,
withDate,
selectedDate1,
selectedDate2,
searchValue,
selectedSubUser?.key,
]);
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.barCode || "-",
item?.registerType === "automatic" ? "سیستمی" : "دستی",
formatJustDate(item.createDate),
`${item?.killHouse?.name} (${item?.killHouse?.killHouseOperator?.user?.mobile})`,
item?.exclusiveKiller
? `${item?.exclusiveKiller?.name} (${item?.exclusiveKiller?.killHouseOperator?.user?.mobile})`
: "-",
item.buyType === "live" ? "مرغ زنده" : "لاشه",
item.poultryName,
`${item.province}/${item.city}`,
<CheckCleanceCode key={i} clearanceCode={item.barClearanceCode} />,
item.quantity.toLocaleString(),
item.liveWeight.toLocaleString(),
formatJustDate(item.date),
item.numberOfCarcasses.toLocaleString(),
item.weightOfCarcasses.toLocaleString(),
item?.weightLoss ? item?.weightLoss + "%" : "-",
<ShowImage key={i} src={item.barImage} />,
<>
{getRoleFromUrl() === "KillHouse" ? (
<SlaughterFreeBarsAlivesOperations
key={item.key}
item={item}
inventoryKey={item?.key}
updateTable={updateTable}
barState={barState}
/>
) : (
<Button
variant="outlined"
disabled={item?.killHouseVetState !== "pending"}
size="small"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تایید / رد",
content: (
<SlaughterHouseVetBarsOperation
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
تایید / رد
</Button>
)}
</>,
];
});
setTableData(d);
}, [data, page, perPage]);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
return (
<Grid container justifyContent="flex-end" mt={2} mb={2}>
<Grid
container
mt={SPACING.MEDIUM}
alignItems="end"
gap={2}
justifyContent="flex-end"
>
<ResponsiveTable
operation={
<Grid>
<Tooltip title="خروجی اکسل" placement="top">
<IconButton
color="primary"
onClick={() => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "فایل اکسل در حال دانلود می باشد، این علمیات ممکن است زمان بر باشد لطفا صبر کنید.",
severity: "success",
});
const link = `${
axios.defaults.baseURL
}kill_house_free_bar_excel/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}&type=live&bar_state=${barState}${
withDate
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ""
}`;
window.location.href = link;
}}
>
<SystemUpdateAltIcon />
</IconButton>
</Tooltip>
</Grid>
}
data={tableData}
columns={[
"ردیف",
"کد بار",
"نوع بار",
"تاریخ خرید",
"خریدار",
"کشتارکن",
"محصول",
"فروشنده",
"استان/شهر",
"کدقرنطینه",
"حجم زنده",
"وزن زنده (کیلوگرم)",
"تاریخ ورود به انبار",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"درصد افت",
"بارنامه",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={title}
/>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,525 @@
import React, { useContext, useEffect, useState } from "react";
import moment from "moment";
import axios from "axios";
import { useDispatch, useSelector } from "react-redux";
import { Button, Checkbox, Tab, Tabs, TextField, Tooltip } from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import {
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { SlaughterFreeBarsOperations } from "../slaughter-free-bars-operations/SlaughterFreeBarsOperations";
import { formatJustDate } from "../../../../utils/formatTime";
import ShowImage from "../../../../components/show-image/ShowImage";
import { SlaughterSubmitFreeBar } from "../slaughter-submit-free-bar/SlaughterSubmitFreeBar";
import { SlaughterHouseVetBarsOperation } from "../../../slaughter-house-vet/components/slaughter-house-vet-bars-operation/SlaughterHouseVetBarsOperation";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
import { vetFarmGetOutProvinceDashboard } from "../../../vet-farm/services/vet-farm-get-out-province-dashboard";
import ToggleOffOutlinedIcon from "@mui/icons-material/ToggleOffOutlined";
import ToggleOnIcon from "@mui/icons-material/ToggleOn";
import { SlaughterInventoryFreeBuyBarsAlives } from "../slaughter-inventory-free-buy-bars-alives/SlaughterInventoryFreeBuyBarsAlives";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterInventoryFreeBuyBars = () => {
const dispatch = useDispatch();
const [activeTab, setActiveTab] = useState(0);
const handleTabChange = (event, newValue) => {
setActiveTab(newValue);
};
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, []);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
useEffect(() => {
dispatch(
slaughterGetProfile({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
}, [selectedSubUser?.key, dispatch]);
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 [withDate, setWithDate] = useState(false);
const [searchValue, setSearchValue] = useState("");
const fetchApiData = async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`kill_house_free_bar/?type=${
activeTab === 0 ? "live" : "carcass"
}&dashboard=true&search=filter&value=${searchValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ""
}&page=${page}&page_size=${perPage}&date_type=input`
);
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 = () => {
setPage(1);
fetchApiData(1);
fetchDashboardData();
};
const fetchDashboardData = () => {
dispatch(
vetFarmGetOutProvinceDashboard({
search: "filter",
role: getRoleFromUrl(),
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
value: searchValue,
type: activeTab === 0 ? "live" : "carcass",
...(withDate && {
date1: selectedDate1,
date2: selectedDate2,
}),
})
).then((r) => {
setDashboardData(r.payload.data);
});
};
useEffect(() => {
fetchApiData(page !== 0 ? page : 1);
fetchDashboardData();
}, [
withDate,
selectedDate1,
selectedDate2,
perPage,
activeTab,
searchValue,
selectedSubUser?.key,
]);
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.barCode || "-",
formatJustDate(item.createDate),
`${item?.killHouse?.name} (${item?.killHouse?.killHouseOperator?.user?.mobile})`,
item?.exclusiveKiller
? `${item?.exclusiveKiller?.name} (${item?.exclusiveKiller?.killHouseOperator?.user?.mobile})`
: "-",
item.buyType === "live" ? "مرغ زنده" : "لاشه",
item.poultryName,
`${item.province}/${item.city}`,
item.barClearanceCode,
item.numberOfCarcasses.toLocaleString(),
item.weightOfCarcasses.toLocaleString(),
<ShowImage key={i} src={item.barImage} />,
<>
{getRoleFromUrl() === "KillHouse" ? (
<SlaughterFreeBarsOperations
key={item.key}
item={item}
inventoryKey={item?.key}
updateTable={updateTable}
type="carcass"
/>
) : (
<Button
variant="outlined"
disabled={item?.killHouseVetState !== "pending"}
size="small"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "تایید / رد",
content: (
<SlaughterHouseVetBarsOperation
item={item}
updateTable={updateTable}
/>
),
})
);
}}
>
تایید / رد
</Button>
)}
</>,
];
});
setTableData(d);
}, [data]);
const handleSubmit = async (event) => {
event.preventDefault();
setSearchValue(textValue);
};
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const [dashboardData, setDashboardData] = useState([]);
return (
<Grid container direction="column" flexWrap="nowrap">
<Grid container justifyContent="center" alignItems="center">
<Tabs
scrollButtons="auto"
variant="scrollable"
allowScrollButtonsMobile
value={activeTab}
onChange={handleTabChange}
>
<Tab label="زنده" />
<Tab label="لاشه" />
</Tabs>
</Grid>
{activeTab === 0 && (
<Grid
mt={2}
container
xs={12}
justifyContent="start"
alignItems="center"
gap={2}
>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "5px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
alignItems="center"
>
<Checkbox
icon={<ToggleOffOutlinedIcon />}
checkedIcon={<ToggleOnIcon />}
checked={withDate}
onChange={() => {
setWithDate(!withDate);
fetchApiData(1);
}}
color="primary"
size="large"
/>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
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 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</form>
</Grid>
</Grid>
</Grid>
)}
<Grid
container
mt={SPACING.MEDIUM}
alignItems="center"
justifyContent="center"
gap={2}
>
<Grid
container
alignItems="center"
gap={SPACING.SMALL}
justifyContent="start"
xs={12}
>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت اطلاعات خرید",
content: (
<SlaughterSubmitFreeBar
inventoryKey={"slaughterGetInventoryStockData?.key"}
selectedDate={selectedDate1}
updateTable={updateTable}
/>
),
})
);
}}
>
ثبت اطلاعات خرید
</Button>
</Grid>
<Grid container mb={4} isDashboard xs={12}>
{activeTab === 0 ? (
<ResponsiveTable
noPagination
columns={[
"تعداد کل بارهای زنده",
"حجم کل بارهای زنده",
"وزن کل بارهای زنده",
"تعداد کل بارها وارد شده به انبار",
"حجم کل بارهای زنده وارد شده به انبار",
"وزن کل بارهای زنده وارد شده به انبار",
"وزن لاشه بارهای وارد شده به انبار",
"تعداد کل بارها وارد نشده به انبار",
"حجم کل بارهای زنده وارد نشده به انبار",
"وزن کل بارهای زنده وارد نشده به انبار",
]}
data={[
[
dashboardData?.totalBars?.toLocaleString(),
dashboardData?.totalBarsQuantity?.toLocaleString(),
dashboardData?.totalBarsLiveWeight?.toLocaleString(),
dashboardData?.enteredTotalBars?.toLocaleString(),
dashboardData?.enteredTotalBarsQuantity?.toLocaleString(),
dashboardData?.enteredTotalBarsLiveWeight?.toLocaleString(),
dashboardData?.enteredTotalBarsWeightOfCarcasses?.toLocaleString(),
dashboardData?.notEnteredTotalBars?.toLocaleString(),
dashboardData?.notEnteredTotalBarsQuantity?.toLocaleString(),
dashboardData?.notEnteredTotalBarsLiveWeight?.toLocaleString(),
],
]}
customColors={[
{
name: "وزن لاشه بارهای وارد شده به انبار",
color: "green",
},
]}
title={"خلاصه اطلاعات"}
/>
) : (
<ResponsiveTable
noPagination
isDashboard
columns={["تعداد کل بارها", "حجم لاشه", "وزن لاشه"]}
data={[
[
dashboardData?.totalBars?.toLocaleString(),
dashboardData?.totalBarsNumberOfCarcasses?.toLocaleString(),
dashboardData?.totalBarsWeightOfCarcasses?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
/>
)}
</Grid>
{activeTab === 0 ? (
<>
<SlaughterInventoryFreeBuyBarsAlives
title={"بار زنده در انتظار ورود به انبار (خارج استان)"}
barState={"notentered"}
fetchDashboardData={fetchDashboardData}
searchValue={searchValue}
withDate={withDate}
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
/>
<SlaughterInventoryFreeBuyBarsAlives
title={"بار زنده وارد شده به انبار (خارج استان)"}
barState={"entered"}
fetchDashboardData={fetchDashboardData}
searchValue={searchValue}
withDate={withDate}
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
/>
</>
) : (
<>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
value={textValue}
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"));
}}
disabled={!withDate}
/>
</Grid>
<Grid>
<DatePicker
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField style={{ width: "160px" }} {...params} />
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
disabled={!withDate}
/>
</Grid>
<Tooltip title="خروجی اکسل">
<Button
color="success"
onClick={() => {
const link = `${
axios.defaults.baseURL
}kill_house_free_bar_excel/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&key=${userKey}${
withDate
? `&date1=${selectedDate1}&date2=${selectedDate2}`
: ""
}&type=${
activeTab === 0 ? "live" : "carcass"
}&search=filter&value=${searchValue}&date_type=input`;
window.location.href = link;
}}
>
<RiFileExcel2Fill size={32} />
</Button>
</Tooltip>
</Grid>
</form>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"کد بار",
"تاریخ خرید",
"خریدار",
"کشتارکن",
"محصول",
"فروشنده",
"استان/شهر",
"کدقرنطینه",
"حجم لاشه",
"وزن لاشه (کیلوگرم)",
"بارنامه",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بار لاشه خرید خارج استان"
/>
</>
)}
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,337 @@
import React, { useEffect, useState, useCallback } from "react";
import {
Button,
Checkbox,
FormControl,
FormControlLabel,
InputLabel,
IconButton,
MenuItem,
Select,
TextField,
Tooltip,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import { SlaughterSubmitRealInventory } from "../slaughter-submit-real-inventory/SlaughterSubmitRealInventory";
import SettingsIcon from "@mui/icons-material/Settings";
import { formatJustDate } from "../../../../utils/formatTime";
import { RiSearchLine } from "react-icons/ri";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterInventoryInProvinceBars = ({ type }) => {
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [selectedDate2, setSelectedDate2] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const dispatch = useDispatch();
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [withDate, setWithDate] = useState(type === "entered" ? true : false);
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 [quota, setQuota] = useState("all");
const fetchApiData = useCallback(
async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`bars_for_kill_house/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
withDate
? `&date1=${selectedDate1}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&date2=${selectedDate2}`
: ``
}&page=${page}&page_size=${perPage}&type=${type}&quota=${quota}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
},
[
textValue,
withDate,
selectedDate1,
selectedDate2,
perPage,
type,
quota,
dispatch,
setData,
selectedSubUser?.key,
setTotalRows,
]
);
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?.barCode,
formatJustDate(item?.poultryRequest?.date),
`${item?.poultryRequest?.poultryName} (${item?.poultryRequest?.poultryMobile}) - ${item?.poultryRequest?.poultryUserName}`,
item?.poultryRequest?.poultryCity,
`${item?.killhouseUser?.killer ? "کشتارکن" : "کشتارگاه"} ${
item?.killhouseUser?.name
} (${item?.killhouseUser?.killHouseOperator?.user?.mobile})`,
item?.poultryRequest?.poultryReqOrderCode,
item?.freezing ? "انجماد" : item?.export ? "صادرات" : "عادی",
item?.killer
? `${item?.killer?.name} (${item?.killer?.killHouseOperator?.user?.mobile})`
: "-",
item?.poultryRequest?.freeSaleInProvince ? "آزاد" : "دولتی",
item?.poultryRequest?.chickenBreed,
`${item?.addCar?.driver?.driverName}/${item?.addCar?.driver?.typeCar}`,
item?.addCar?.driver?.healthCode
? item?.addCar?.driver?.healthCode
: "-",
item?.clearanceCode ? item?.clearanceCode : "-",
item?.acceptedRealQuantity?.toLocaleString(),
item?.acceptedRealWeight?.toLocaleString(),
item?.weightInfo?.state,
item?.wareHouseAcceptedRealQuantity.toLocaleString(),
item?.wareHouseAcceptedRealWeight.toLocaleString(),
item?.weightInfo?.weightLoss
? item?.weightInfo?.weightLoss?.toFixed(2) + "%"
: 0 + "%",
item?.weightInfo?.inputLoss
? item?.weightInfo?.inputLoss?.toFixed(2) + "%"
: 0 + "%",
item?.dateOfWareHouse ? formatJustDate(item?.dateOfWareHouse) : "-",
<Tooltip title="ورود بار به انبار" key={i}>
<IconButton
size={"small"}
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ثبت موجودی واقعی",
content: (
<SlaughterSubmitRealInventory
updateTable={updateTable}
item={item}
/>
),
})
);
}}
>
<SettingsIcon fontSize="small" />
</IconButton>
</Tooltip>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [fetchApiData]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`bars_for_kill_house/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}&type=${type}&quota=${quota}`
);
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}
>
<Grid
container
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "10px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
>
{type === "notentered" && (
<Grid>
<FormControlLabel
control={
<Checkbox
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
/>
}
/>
</Grid>
)}
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
<Grid>
<FormControl size="small" style={{ minWidth: 150 }}>
<InputLabel>نوع فروش</InputLabel>
<Select
value={quota}
onChange={(e) => setQuota(e.target.value)}
label="نوع فروش"
>
<MenuItem value="all">همه</MenuItem>
<MenuItem value="governmental">دولتی</MenuItem>
<MenuItem value="free">آزاد</MenuItem>
</Select>
</FormControl>
</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>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"کدبار",
"تاریخ کشتار",
"مرغدار",
"شهر مرغدار",
"خریدار",
"کدسفارش",
"کشتار",
"کشتارکن",
"فروش",
"نژاد",
"راننده/نوع خودرو",
"کدبهداشتی حمل و نقل",
"کدرهگیری سامانه قرنطینه",
"حجم بار (قطعه)",
"وزن بار (کیلوگرم)",
"وضعیت",
"حجم لاشه",
"وزن لاشه",
"درصد افت در لحظه",
"درصد افت ورود به انبار",
"تاریخ ورود به انبار",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title={
type === "entered"
? "وارد شده به انبار (کشتار داخل استان)"
: "در انتظار ورود به انبار (کشتار داخل استان)"
}
/>
</Grid>
);
};

View File

@@ -0,0 +1,125 @@
import { VscFolderActive, VscNewFolder } from "react-icons/vsc";
import { useLocation } from "react-router-dom";
import { Grid } from "../../../../components/grid/Grid";
import LinkItem from "../../../../components/link-item/LinkItem";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { SPACING } from "../../../../data/spacing";
import {
ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_IN_PROVINCE,
ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_OUT_PROVINCE,
ROUTE_SLAUGHTER_INVENTORY_STOCK,
ROUTE_SLAUGHTER_OUT_PROVINCE_BUY,
ROUTE_SLAUGHTER_SEGMENTATION,
} from "../../../../routes/routes";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch, useSelector } from "react-redux";
import { useEffect } from "react";
// import moment from "moment";
export const SlaughterInventoryOperation = () => {
const dispatch = useDispatch();
const { pathname } = useLocation();
const { profile } = useSelector((state) => state.slaughterSlice);
useEffect(() => {
if (!profile) {
dispatch(LOADING_START());
dispatch(slaughterGetProfile()).then((r) => {
dispatch(LOADING_END());
});
}
}, []);
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
style={{ placeContent: "baseline" }}
>
<NavLink
to={ROUTE_SLAUGHTER_INVENTORY_STOCK}
active={pathname === ROUTE_SLAUGHTER_INVENTORY_STOCK ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="ورود به انبار"
/>
</NavLink>
<NavLink
to={ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_IN_PROVINCE}
active={
pathname === ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_IN_PROVINCE
? "true"
: null
}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="فروش داخل استان"
/>
</NavLink>
<NavLink
to={ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_OUT_PROVINCE}
active={
pathname === ROUTE_SLAUGHTER_INVENTORY_SELL_CARCASS_OUT_PROVINCE
? "true"
: null
}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="فروش به خارج استان"
/>
</NavLink>
<NavLink
to={ROUTE_SLAUGHTER_OUT_PROVINCE_BUY}
active={pathname === ROUTE_SLAUGHTER_OUT_PROVINCE_BUY ? "true" : null}
>
<LinkItem
icon={<VscFolderActive size={30} color="#244CCC" />}
title="خرید خارج از استان"
description="درخواست های در انتظار عملیات وارد کردن اطلاعات بارهای دریافتی"
/>
</NavLink>
<NavLink
to={ROUTE_SLAUGHTER_SEGMENTATION}
active={pathname === ROUTE_SLAUGHTER_SEGMENTATION ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="قطعه بندی"
/>
</NavLink>
{/* <NavLink
to={`${ROUTE_SLAUGHTER_AGENT_SHARE}/${
profile?.killHouse[0]?.key
}/${moment(new Date()).format("YYYY-MM-DD")}`}
active={
pathname ===
`${ROUTE_SLAUGHTER_AGENT_SHARE}/${
profile?.killHouse[0]?.key
}/${moment(new Date()).format("YYYY-MM-DD")}`
? "true"
: null
}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="سهم بندی اتوماتیک مباشرین"
/>
</NavLink> */}
</Grid>
);
};

View File

@@ -0,0 +1,121 @@
import React, { useEffect } from "react";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SlaughterShowProducts } from "../slaughter-show-products/SlaughterShowProducts";
import { useSelector, useDispatch } from "react-redux";
import { SPACING } from "../../../../data/spacing";
import { Button } from "@mui/material";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { SlaughterSubmitOperations } from "../slaughter-submit-operations/SlaughterSubmitOperations";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterInventorySummary = ({ priceInfo }) => {
const { distributionInfo, slaughterProducts } = useSelector(
(state) => state.slaughterSlice
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const dispatch = useDispatch();
const updateTable = () => {
dispatch(
fetchSlaughterBroadcastAndProducts({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
};
useEffect(() => {
updateTable();
}, [selectedSubUser?.key]);
return (
<Grid container width="100%" gap={SPACING.SMALL} mb={2}>
<Button
disabled={!slaughterProducts}
variant="contained"
color="primary"
size="large"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ثبت عملیات",
size: window.innerWidth <= 600 ? "auto" : 450,
content: (
<SlaughterSubmitOperations
updateTable={updateTable}
priceInfo={priceInfo}
slaughterProducts={slaughterProducts}
/>
),
})
);
}}
>
ثبت عملیات
</Button>
<SlaughterShowProducts />
<ResponsiveTable
title="اطلاعات پخش"
noPagination
isDashboard
data={[
[
distributionInfo?.totalGovernmentalInputWeight?.toLocaleString(),
distributionInfo?.totalFreeInputWeight?.toLocaleString(),
distributionInfo?.totalGovernmentalOutputWeight?.toLocaleString(),
distributionInfo?.totalFreeOutputWeight?.toLocaleString(),
distributionInfo?.totalKillHouseAllocationsWeight?.toLocaleString(),
distributionInfo?.totalKillHouseFreeSale_barCarcassesWeight?.toLocaleString(),
distributionInfo?.segmentationsWeight?.toLocaleString(),
distributionInfo?.coldHouseAllocationsWeight?.toLocaleString(),
distributionInfo?.totalGovernmentalRemainWeight?.toLocaleString(),
distributionInfo?.totalFreeRemainWeight?.toLocaleString(),
],
]}
columns={[
"وزن دولتی (کیلوگرم)",
"وزن آزاد (کیلوگرم)",
"فروش دولتی (کیلوگرم)",
"فروش آزاد (کیلوگرم)",
"فروش و توزیع داخل استان (کیلوگرم)",
"فروش و توزیع خارج استان (کیلوگرم)",
"وزن قطعه بندی (کیلوگرم)",
"وزن انجماد (کیلوگرم)",
"مانده دولتی (کیلوگرم)",
" مانده آزاد (کیلوگرم)",
]}
/>
<ResponsiveTable
title="تعهدات"
noPagination
isDashboard
data={[
[
distributionInfo?.totalCommitmentSellingInProvinceGovernmentalWeight?.toLocaleString(),
distributionInfo?.totalSellingInProvinceGovernmentalWeight?.toLocaleString(),
distributionInfo?.totalCommitmentSellingInProvinceGovernmentalRemainWeight?.toLocaleString(),
distributionInfo?.totalCommitmentSellingInProvinceFreeWeight?.toLocaleString(),
distributionInfo?.totalSellingInProvinceFreeWeight?.toLocaleString(),
distributionInfo?.totalCommitmentSellingInProvinceFreeRemainWeight?.toLocaleString(),
],
]}
columns={[
"تعهد دولتی توزیع داخل استان",
"توزیع دولتی داخل استان",
"باقیمانده تعهد دولتی توزیع داخل استان",
"تعهد آزاد توزیع داخل استان",
"توزیع آزاد داخل استان",
"باقیمانده تعهد آزاد توزیع داخل استان",
]}
allColors={{ color: "green", text: "#332a3d" }}
/>
</Grid>
);
};

View File

@@ -0,0 +1,116 @@
import React, { useEffect, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import { Tab, Tabs } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import { SlaughterShowProducts } from "../slaughter-show-products/SlaughterShowProducts";
import { SlaughterInventoryInProvinceBars } from "../slaughter-inventory-in-province-bars/SlaughterInventoryInProvinceBars";
import { SlaughterInventoryFreeBuyBars } from "../slaughter-inventory-free-buy-bars/SlaughterInventoryFreeBuyBars";
import { slaughterGetBarsInfo } from "../../services/slaughter-get-distribution-info";
import { useDispatch } from "react-redux";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
import { useSelector } from "react-redux";
export const SlaughterInventory = () => {
const [value, setValue] = useState("0");
const [barsInfo, setBarsInfo] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleChange = (event, newValue) => {
setValue(newValue);
};
const dispatch = useDispatch();
useEffect(() => {
dispatch(
slaughterGetBarsInfo({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setBarsInfo(r.payload.data);
});
}, [dispatch, selectedSubUser?.key]);
return (
<Grid container xs={12} justifyContent="end" alignItems="center">
<Grid container width="100%" isDashboard>
<SlaughterShowProducts />
</Grid>
<Grid
container
xs={12}
justifyContent="center"
alignItems="center"
mt={2}
>
<Tabs
value={value}
onChange={handleChange}
textColor="secondary"
indicatorColor="secondary"
aria-label="secondary tabs example"
>
<Tab value="0" label="بارهای داخل استان" />
<Tab value="1" label="خریدهای خارج استان" />
</Tabs>
</Grid>
{value === "0" && (
<Grid container xs={12} justifyContent="center" alignItems="center">
<Grid container justifyContent="start" alignItems="center" xs={12}>
<ResponsiveTable
title="اطلاعات کلی بارها"
noPagination
data={[
[
barsInfo?.totalBars?.toLocaleString(),
barsInfo?.totalBarsQuantity?.toLocaleString(),
barsInfo?.totalBarsWeight?.toLocaleString(),
barsInfo?.totalEnteredBars?.toLocaleString(),
barsInfo?.totalEnteredBarsWeight?.toLocaleString(),
barsInfo?.totalEnteredBarsCarcasses?.toLocaleString(),
barsInfo?.totalEnteredBarsCarcassesWeight?.toLocaleString(),
barsInfo?.totalNotEnteredBars?.toLocaleString(),
barsInfo?.totalNotEnteredBarsQuantity?.toLocaleString(),
barsInfo?.totalNotEnteredKillHouseRequestsWeight?.toLocaleString(),
],
]}
columns={[
"تعداد کل بارها",
"حجم کل بارها (قطعه)",
"وزن کل بارها (کیلوگرم)",
"تعداد کل بارهای وارد شده",
"وزن کل بارهای وارد شده",
"حجم کل لاشه وارد شده (قطعه)",
"وزن کل لاشه وارد شده (کیلوگرم)",
"تعداد کل بارهای وارد نشده",
"حجم کل بار وارد نشده (قطعه)",
"وزن کل بار وارد نشده (کیلوگرم)",
]}
allColors={{ color: "#f3bda3", text: "#332a3d" }}
/>
<Grid container mt={SPACING.MEDIUM} mb={SPACING.MEDIUM}>
<SlaughterInventoryInProvinceBars type="notentered" />
</Grid>
<Grid container mt={SPACING.MEDIUM} mb={SPACING.MEDIUM}>
<SlaughterInventoryInProvinceBars type="entered" />
</Grid>
</Grid>
</Grid>
)}
{value === "1" && (
<Grid container mt={SPACING.MEDIUM} mb={SPACING.MEDIUM}>
<SlaughterInventoryFreeBuyBars />
</Grid>
)}
</Grid>
);
};

View File

@@ -0,0 +1,656 @@
import { useContext, useEffect, useState, useMemo, useCallback } from "react";
import { AppContext } from "../../../../contexts/AppContext";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import {
Box,
TextField,
Typography,
Button,
Tooltip,
FormControlLabel,
Checkbox,
} from "@mui/material";
import moment from "moment";
import { SPACING } from "../../../../data/spacing";
import { DatePicker } from "@mui/x-date-pickers";
import {
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { useDispatch, useSelector } from "react-redux";
import { format } from "date-fns-jalali";
import axios from "axios";
import { formatTime } from "../../../../utils/formatTime";
import ShowImage from "../../../../components/show-image/ShowImage";
import { RiFileExcel2Fill, RiSearchLine } from "react-icons/ri";
import { VetFarmOperationOptions } from "../../../vet-farm/components/vet-farm-operations-options/VetFarmOperationOptions";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { VetFarmEditTrafficCode } from "../../../vet-farm/components/vet-farm-edit-traffic-code/VetFarmEditTrafficCode";
import { vetFarmGetFinishedBarsOverview } from "../../../vet-farm/services/vet-farm-get-bars-overview";
import { Grid } from "../../../../components/grid/Grid";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
// Constants
const ROLES = {
KILL_HOUSE: "KillHouse",
ADMIN_X: "AdminX",
VET_FARM: "VetFarm",
PROVINCE_OPERATOR: "ProvinceOperator",
KILL_HOUSE_VET: "KillHouseVet",
SUPER_ADMIN: "SuperAdmin",
SUPPORTER: "Supporter",
};
const KILL_TYPES = {
FREEZING: "انجماد",
EXPORT: "صادرات",
NORMAL: "عادی",
};
const ITEM_STATES = {
WAREHOUSE: "ورود به انبار",
REGISTERED: "ثبت اطلاعات بار",
DELETED: "حذف شده",
ACCEPTED: "تایید تخلیه",
PENDING: "در انتظار تخلیه",
};
const QUARANTINE_STATES = {
CONTRADICTION: "مغایرت کد رهگیری",
NO_CLEARANCE: "فاقد کد رهگیری",
MERGE: "ادغام",
NOT_APPROVED: "عدم تایید راهداری",
};
const DEFAULT_PER_PAGE = 10;
const DEFAULT_PAGE = 1;
// Helper functions
const formatDate = (date) => {
if (!date) return "-";
return format(new Date(date), "yyyy/MM/dd");
};
const formatCurrency = (value) => {
return value ? `${value.toLocaleString()}` : "-";
};
const formatNumber = (value) => {
return value ? value.toLocaleString() : "-";
};
const formatNumberFixed = (value, decimals = 2) => {
return value ? value.toFixed(decimals).toLocaleString() : "-";
};
const formatUserInfo = (name, mobile) => {
return name && mobile ? `${name} (${mobile})` : "-";
};
const getKillType = (item) => {
if (item?.poultryRequest?.freezing) return KILL_TYPES.FREEZING;
if (item?.poultryRequest?.export) return KILL_TYPES.EXPORT;
return KILL_TYPES.NORMAL;
};
const getItemState = (item) => {
if (item?.wareHouseConfirmation) {
return ITEM_STATES.WAREHOUSE;
}
if (item?.assignmentStateArchive !== "pending") {
return ITEM_STATES.REGISTERED;
}
if (item.trash === true) {
return ITEM_STATES.DELETED;
}
if (item.vetState === "accepted") {
return ITEM_STATES.ACCEPTED;
}
if (item.vetState === "pending") {
return ITEM_STATES.PENDING;
}
return "";
};
const getQuarantineInfo = (item) => {
if (item?.quarantineQuantity) {
return item.quarantineQuantity;
}
if (item?.quarantineCodeState) {
const stateMap = {
contradiction: QUARANTINE_STATES.CONTRADICTION,
noclearance: QUARANTINE_STATES.NO_CLEARANCE,
merge: QUARANTINE_STATES.MERGE,
};
return stateMap[item.quarantineCodeState] || QUARANTINE_STATES.NOT_APPROVED;
}
return "-";
};
const buildApiUrl = (params) => {
const {
textValue,
role,
date1,
date2,
page,
perPage,
roleKey,
withoutBarDocument,
} = params;
const baseUrl = "kill_house_assignment_information/";
const queryParams = new URLSearchParams({
search: "filter",
value: textValue || "",
role: role || "",
date1: date1 || "",
date2: date2 || "",
page: page || DEFAULT_PAGE,
page_size: perPage || DEFAULT_PER_PAGE,
without_bar_document: withoutBarDocument ? "true" : "false",
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseUrl}?${queryParams.toString()}`;
};
const buildExcelUrl = (params) => {
const { baseURL, date1, date2, role, roleKey, userKey, textValue } = params;
const queryParams = new URLSearchParams({
start: date1 || "",
end: date2 || "",
state: "completed",
role: role || "",
key: userKey || "",
search: "filter",
value: textValue || "",
});
if (roleKey) {
queryParams.append("role_key", roleKey);
}
return `${baseURL}bar_excel/?${queryParams.toString()}`;
};
const isTrafficCodeEditable = (role, item) => {
if (!item?.killer?.key) {
return false;
}
const editableRoles = [
ROLES.KILL_HOUSE,
ROLES.ADMIN_X,
ROLES.VET_FARM,
ROLES.PROVINCE_OPERATOR,
ROLES.KILL_HOUSE_VET,
ROLES.SUPER_ADMIN,
ROLES.SUPPORTER,
];
return editableRoles.includes(role);
};
const canShowDocumentCheckbox = (role) => {
return (
role === ROLES.SUPER_ADMIN ||
role === ROLES.SUPPORTER ||
role === ROLES.ADMIN_X
);
};
export const SlaughterManageBars = () => {
const [, , selectedDate1, setSelectedDate1, selectedDate2, setSelectedDate2] =
useContext(AppContext);
// Redux
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const userKey = useSelector((state) => state.userSlice.userProfile.key);
const dispatch = useDispatch();
// State
const [hasDocumentState, setHasDocumentState] = useState(false);
const [data, setData] = useState([]);
const [totalRows, setTotalRows] = useState(0);
const [perPage, setPerPage] = useState(DEFAULT_PER_PAGE);
const [textValue, setTextValue] = useState("");
const [page, setPage] = useState(DEFAULT_PAGE);
const [tableData, setTableData] = useState([]);
const [dashboardData, setDashboardData] = useState([]);
// Memoized values
const currentRole = useMemo(() => getRoleFromUrl(), []);
const roleKey = useMemo(
() => (checkPathStartsWith("slaughter") ? selectedSubUser?.key || "" : ""),
[selectedSubUser?.key]
);
const showDocumentCheckbox = useMemo(
() => canShowDocumentCheckbox(currentRole),
[currentRole]
);
// Initialize dates
useEffect(() => {
const currentDate = moment(new Date()).format("YYYY-MM-DD");
setSelectedDate1(currentDate);
setSelectedDate2(currentDate);
}, [setSelectedDate1, setSelectedDate2]);
// Fetch API data
const fetchApiData = useCallback(
async (pageNumber = page) => {
dispatch(LOADING_START());
try {
const url = buildApiUrl({
textValue,
role: currentRole,
date1: selectedDate1,
date2: selectedDate2,
page: pageNumber || DEFAULT_PAGE,
perPage,
roleKey,
withoutBarDocument: hasDocumentState,
});
const response = await axios.get(url);
setData(response.data.results);
setTotalRows(response.data.count);
} catch (error) {
console.error("Error fetching data:", error);
} finally {
dispatch(LOADING_END());
}
},
[
textValue,
currentRole,
selectedDate1,
selectedDate2,
perPage,
roleKey,
hasDocumentState,
page,
dispatch,
]
);
// Fetch dashboard data
const fetchDashboardData = useCallback(() => {
dispatch(
vetFarmGetFinishedBarsOverview({
selectedDate1,
selectedDate2,
textValue,
hasDocumentState,
roleKey,
})
).then((r) => {
setDashboardData(r.payload.data);
});
}, [
selectedDate1,
selectedDate2,
textValue,
hasDocumentState,
roleKey,
dispatch,
]);
// Handlers
const handlePageChange = (newPage) => {
fetchApiData(newPage);
setPage(newPage);
};
const handlePerRowsChange = (newPerRows) => {
setPerPage(newPerRows);
setPage(DEFAULT_PAGE);
};
const handleSubmit = async (event) => {
event.preventDefault();
await fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
};
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const handleDateChange1 = (date) => {
if (date) {
setSelectedDate1(moment(date).format("YYYY-MM-DD"));
}
};
const handleDateChange2 = (date) => {
if (date) {
setSelectedDate2(moment(date).format("YYYY-MM-DD"));
}
};
const handleChangeDocumentState = () => {
setHasDocumentState(!hasDocumentState);
};
// Initial data fetch
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// Refetch when filters change
useEffect(() => {
fetchApiData(DEFAULT_PAGE);
fetchDashboardData();
setPage(DEFAULT_PAGE);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedDate1, selectedDate2, hasDocumentState, perPage, roleKey]);
// Transform data to table format
useEffect(() => {
const transformedData = data?.map((item, i) => {
const rowNumber =
page === DEFAULT_PAGE ? i + 1 : i + perPage * (page - 1) + 1;
const textColor = item?.trash ? "red" : "black";
return [
rowNumber,
<VetFarmOperationOptions
key={`operation-${i}`}
item={item}
updateTable={fetchApiData}
isComplete
/>,
<Typography
key={`state-${i}`}
style={{ fontSize: "13px", color: textColor }}
>
{getItemState(item)}
</Typography>,
<Typography
key={`barcode-${i}`}
style={{ fontSize: "13px", color: textColor }}
>
{item.barCode}
</Typography>,
formatTime(item.createDate),
getKillType(item),
<Typography
key={`doc-status-${i}`}
variant="body2"
color={item?.barDocumentStatus?.isError ? "error" : "primary"}
>
{item?.barDocumentStatus?.title || "-"}
</Typography>,
<ShowImage
key={`image-${i}`}
src={item?.assignmentInfo?.imageWithBar}
/>,
formatNumber(item.acceptedRealQuantity),
formatNumber(item?.acceptedRealWeight),
formatNumber(item?.weightInfo?.finalIndexWeight),
formatUserInfo(
item.killhouseUser?.name,
item.killhouseUser?.killHouseOperator?.user?.mobile
),
item?.killer
? formatUserInfo(
item.killer?.name,
item.killer?.killHouseOperator?.user?.mobile
)
: "-",
formatUserInfo(
item.poultryRequest?.poultry?.user?.fullname,
item.poultryRequest?.poultry?.user?.mobile
),
item.poultryRequest?.poultry?.unitName || "-",
item?.poultryRequest?.age || "-",
formatNumber(item.quantity),
formatNumber(item?.weightInfo?.weight),
<Grid key={`traffic-${i}`}>
<VetFarmEditTrafficCode
updateTable={fetchApiData}
killHouseRequestKey={item.key}
trafficCode={item?.trafficCode}
isEditable={isTrafficCodeEditable(currentRole, item)}
/>
</Grid>,
formatCurrency(item?.amount),
item?.clearanceCode || "-",
getQuarantineInfo(item),
`${item.addCar?.driver?.typeCar || ""} ${
item.addCar?.driver?.pelak || ""
}`.trim() || "-",
formatUserInfo(
item.addCar?.driver?.driverName,
item.addCar?.driver?.driverMobile
),
item.poultryRequest?.chickenBreed || "-",
formatNumber(item?.weightInfo?.indexWeight),
formatCurrency(item?.poultryRequest?.amount),
formatCurrency(item?.weightInfo?.killHousePrice),
item?.vetFarm?.vet?.user?.fullname
? `${item.vetFarm.vet.user.fullname}(${item.vetFarm.vet.user.mobile})`
: "فاقد دامپزشک",
item.killPlace || "-",
item.poultryRequest?.poultry?.address?.city?.name || "-",
formatDate(item?.poultryRequest?.sendDate),
item?.poultryRequest?.orderCode || "-",
formatNumber(item?.wareHouseAcceptedRealQuantity),
formatNumber(item?.wareHouseAcceptedRealWeight),
formatNumber(item?.weightLoss),
];
});
setTableData(transformedData || []);
}, [data, page, perPage, currentRole, fetchApiData]);
// Table columns
const dashboardColumns = [
"تعداد بارها",
"حجم بارها",
"وزن بارها",
"میانگین وزن",
"کمترین سن ",
"بیشترین سن ",
"میانگین سنی",
"بارهای دارای کد قرنطینه",
"حجم بارهای دارای کد قرنطینه",
"بارهای احراز شده از قرنطینه",
"حجم بارهای احراز شده از قرنطینه",
"بارهای فاقد کد قرنطینه",
"حجم بارهای فاقد کد قرنطینه",
"بارهایی که در قرنطینه و رصدیار اختلاف دارند",
"بارهای تکمیل شده کشتارگاه",
"حجم نهایی در کشتارگاه",
"وزن نهایی در کشتارگاه",
"تعداد بار ورودی به انبار",
"حجم لاشه های انبار",
"وزن لاشه های انبار",
"درصد افت بار انبار",
];
const tableColumns = [
"ردیف",
"عملیات",
"وضعیت",
"کدبار",
"تاریخ ثبت خودرو",
"نوع کشتار",
"وضعیت سند",
"سند (بارنامه)",
"تعداد نهایی",
"وزن نهایی بار (کیلوگرم)",
"میانگین وزن نهایی (کیلوگرم)",
"خریدار",
"کشتارکن اختصاصی",
"مشخصات مرغدار",
"نام فارم",
"سن مرغ",
"تعداد اولیه",
"وزن اولیه بار (کیلوگرم)",
"کد بهداشتی حمل و نقل",
"قیمت مرغ زنده‌ی بار",
"کدرهگیری سامانه قرنطینه",
"تعداد در قرنطینه",
"ماشین",
"راننده",
"نژاد",
"میانگین وزن اولیه (کیلوگرم)",
"قیمت مرغدار",
"قیمت کشتارگاه",
"دامپزشک فارم",
"محل کشتار",
"شهر",
"تاریخ کشتار",
"کدسفارش کشتار",
"حجم لاشه",
"وزن لاشه",
"درصد افت",
];
// Dashboard row data
const renderDashboardCell = (value, isBlue = false) => (
<Typography variant="caption" style={isBlue ? { color: "blue" } : {}}>
{formatNumber(value)}
</Typography>
);
const dashboardRow = [
renderDashboardCell(dashboardData?.lenKillRequest, true),
renderDashboardCell(dashboardData?.killRequestQuantity),
renderDashboardCell(dashboardData?.killRequestWeight, true),
<Typography key="avg-weight" variant="caption">
{dashboardData?.avgWeight || "-"}
</Typography>,
renderDashboardCell(dashboardData?.minAge, true),
renderDashboardCell(dashboardData?.maxAge),
renderDashboardCell(dashboardData?.avgAge, true),
renderDashboardCell(dashboardData?.lenKillRequestHasCode),
renderDashboardCell(dashboardData?.quantityOfKillRequestHasCode, true),
renderDashboardCell(dashboardData?.lenKillRequestHasQuarantine),
renderDashboardCell(
dashboardData?.quantityOfKillRequestHasQuarantine,
true
),
renderDashboardCell(dashboardData?.lenKillRequestHasNotCode),
renderDashboardCell(dashboardData?.quantityOfKillRequestHasNotCode, true),
renderDashboardCell(dashboardData?.differenceBar),
renderDashboardCell(dashboardData?.lenCompleteWithKillHouse, true),
renderDashboardCell(dashboardData?.quantityFinalKillHouse),
renderDashboardCell(dashboardData?.weightFinalKillHouse, true),
renderDashboardCell(dashboardData?.wareHouseBars),
renderDashboardCell(dashboardData?.wareHouseBarsQuantity, true),
renderDashboardCell(dashboardData?.wareHouseBarsWeight),
<Typography key="weight-lose" variant="caption" style={{ color: "blue" }}>
{formatNumberFixed(dashboardData?.wareHouseBarsWeightLose)}
</Typography>,
];
const excelUrl = buildExcelUrl({
baseURL: axios.defaults.baseURL,
date1: selectedDate1,
date2: selectedDate2,
role: currentRole,
roleKey,
userKey,
textValue,
});
return (
<Box width="100%" mt={2}>
<Grid
display="flex"
alignItems="center"
justifyContent="center"
direction="row"
gap={SPACING.SMALL}
>
<Grid container justifyContent="center" gap={SPACING.SMALL} xs={12}>
<Grid container alignItems="center" gap={SPACING.SMALL} xs={12}>
<Grid style={{ width: "150px" }}>
<DatePicker
label="از تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate1}
onChange={handleDateChange1}
/>
</Grid>
<Grid style={{ width: "150px" }}>
<DatePicker
label="تا تاریخ"
renderInput={(params) => <TextField {...params} />}
value={selectedDate2}
onChange={handleDateChange2}
/>
</Grid>
<form onSubmit={handleSubmit}>
<TextField
autoComplete="off"
size="small"
label="جستجو"
variant="outlined"
style={{ width: 250 }}
onChange={handleTextChange}
value={textValue}
/>
<Button type="submit" endIcon={<RiSearchLine />}>
جستجو
</Button>
</form>
{!!data?.length && (
<Grid>
<Tooltip title="خروجی اکسل">
<a href={excelUrl} rel="noreferrer">
<Button color="success">
<RiFileExcel2Fill size={32} />
</Button>
</a>
</Tooltip>
</Grid>
)}
{showDocumentCheckbox && (
<FormControlLabel
control={
<Checkbox
checked={hasDocumentState}
onChange={handleChangeDocumentState}
name="document-checkbox"
/>
}
label="بدون وضعیت سند"
/>
)}
</Grid>
<Grid container mt={2} mb={4} isDashboard xs={12}>
<ResponsiveTable
title="خلاصه اطلاعات"
noPagination
isDashboard
columns={dashboardColumns}
data={[dashboardRow]}
/>
</Grid>
<ResponsiveTable
data={tableData}
columns={tableColumns}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="بارهای تکمیل شده"
/>
</Grid>
</Grid>
</Box>
);
};

View File

@@ -0,0 +1,142 @@
import { Button, Card, IconButton } from "@mui/material";
import { useEffect, useState } from "react";
import { AdvancedTable } from "../../../../components/advanced-table/AdvancedTable";
import { useDispatch, useSelector } from "react-redux";
import { slaughterGetCars } from "../../services/slaughter-get-cars";
import DeleteIcon from "@mui/icons-material/Delete";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteCar } from "../../services/slaughter-delete-car";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { SlaughterNewCar } from "../slaughter-new-car/SlaughterNewCar";
import { AppContext } from "../../../../contexts/AppContext";
import { useContext } from "react";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
export const SlaughterManageCars = () => {
const [dataTable, setDataTable] = useState([]);
const { slaughterHouseCars, profile } = useSelector(
(state) => state.slaughterSlice
);
const [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
useEffect(() => {
dispatch(slaughterGetProfile());
dispatch(slaughterGetCars());
}, []);
useEffect(() => {
const filteredData = slaughterHouseCars;
const d = filteredData?.map((item, i) => {
return [
i + 1,
item.typeCar,
item.pelak,
item.capocity,
parseInt(item.healthCode),
// Number(item.healthCode),
item.driverName,
item.driverMobile,
// @todo make onclick delete the car
<IconButton
key={i}
aria-label="delete"
color="error"
onClick={() => {
dispatch(LOADING_START());
dispatch(
slaughterDeleteCar({
id: item.id,
})
).then((r) => {
if (r.error) {
if (r.error.message.includes("403")) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "امکان حذف بدلیل تخصیص بار فعال به خودرو وجود ندارد!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
}
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
dispatch(slaughterGetCars());
dispatch(LOADING_END());
});
}}
>
<DeleteIcon />
</IconButton>,
];
});
setDataTable(d);
}, [slaughterHouseCars]);
const [tableDataCol] = useState([
"ردیف",
"مدل خودرو",
"پلاک",
"ظرفیت",
"کد بهداشتی",
"نام راننده",
"موبایل راننده",
"حذف",
]);
return (
<>
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={SPACING.SMALL}
xs={12}
>
<Grid>
<Button
variant="contained"
onClick={() => {
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
content: (
<SlaughterNewCar
cars={slaughterHouseCars}
killHouseKey={profile?.killHouse[0]?.key}
/>
),
title: "ثبت خودرو جدید",
})
);
}}
>
ثبت خودرو جدید
</Button>
</Grid>
<Card sx={{ width: "100%" }}>
<AdvancedTable columns={tableDataCol} data={dataTable} />
</Card>
</Grid>
</>
);
};

View File

@@ -0,0 +1,249 @@
import React, { useEffect, useState, useRef } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { BackButton } from "../../../../components/back-button/BackButton";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterManageDelegatesForm } from "./SlaughterManageDelegatesForm";
import { SlaughterManageDelegatesOperations } from "./SlaughterManageDelegatesOperations";
import { slaughterGetDelegatesInfoService } from "../../services/slaughter-get-delegates-info";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { formatJustDate } from "../../../../utils/formatTime";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const SlaughterManageDelegates = () => {
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 isFirstMount = useRef(true);
const isSteward = getRoleFromUrl() === "Steward";
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDelegatesInfoService({
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.trash !== undefined ? !item.trash : null;
const statusBadge =
isActive === null ? (
"-"
) : (
<Chip
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>
);
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const killhouseDisplay =
item?.killHouse?.name && item?.killHouse?.mobile
? `${item.killHouse.name} (${item.killHouse.mobile})`
: item?.killHouse?.name
? item.killHouse.name
: "-";
const stewardDisplay =
item?.steward?.name && item?.steward?.user?.mobile
? `${item.steward.name} (${item.steward.user.mobile})`
: item?.steward?.name
? item.steward.name
: "-";
const displayValue = isSteward ? stewardDisplay : killhouseDisplay;
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.createDate ? formatJustDate(item.createDate) : "-",
item?.firstName || "-",
item?.lastName || "-",
item?.mobile || "-",
item?.city || "-",
displayValue,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
statusBadge,
<SlaughterManageDelegatesOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
}, [selectedSubUser?.key]);
useEffect(() => {
if (isFirstMount.current) {
isFirstMount.current = false;
return;
}
fetchApiData(1);
setPage(1);
}, [perPage]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDelegatesInfoService({
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handleOpenModal = () => {
dispatch(
OPEN_MODAL({
title: "ثبت نماینده جدید",
content: <SlaughterManageDelegatesForm updateTable={updateTableData} />,
size: 300,
})
);
};
return (
<Box display="flex" justifyContent="center">
<Grid container xs={12} sm={12} md={10} lg={10}>
<Grid item xs={12}>
<BackButton />
</Grid>
<Grid container item gap={SPACING.SMALL}>
<Button variant="contained" color="primary" onClick={handleOpenModal}>
ثبت نماینده جدید
</Button>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12} mt={2}>
<ResponsiveTable
title="نمایندگان"
columns={[
"ردیف",
"تاریخ ایجاد",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
isSteward ? "مباشر" : "کشتارگاه",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Grid>
</Box>
);
};
export default SlaughterManageDelegates;

View File

@@ -0,0 +1,165 @@
import React, { useContext } from "react";
import { useFormik } from "formik";
import * as yup from "yup";
import { useDispatch } from "react-redux";
import { Button, TextField } from "@mui/material";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import {
slaughterSubmitRepresentativeService,
slaughterEditRepresentativeService,
} from "../../services/slaughter-submit-delegate";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
const getValidationSchema = () =>
yup.object({
first_name: yup.string().required("نام الزامی است"),
last_name: yup.string().required("نام خانوادگی الزامی است"),
mobile: yup
.string()
.required("شماره همراه الزامی است")
.matches(/^09\d{9}$/, "شماره تلفن باید با 09 شروع شود و 11 رقم باشد"),
city: yup.string().required("شهر الزامی است"),
});
export const SlaughterManageDelegatesForm = ({ updateTable, item, isEdit }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const formik = useFormik({
initialValues: {
first_name: item?.first_name || item?.firstName || "",
last_name: item?.last_name || item?.lastName || "",
mobile: item?.mobile || "",
city: item?.city || "",
},
enableReinitialize: true,
validationSchema: getValidationSchema(),
onSubmit: (values) => {
const submitData = isEdit
? {
key: item?.key,
first_name: values.first_name,
last_name: values.last_name,
mobile: values.mobile,
city: values.city,
}
: {
first_name: values.first_name,
last_name: values.last_name,
mobile: values.mobile,
city: values.city,
role: getRoleFromUrl(),
};
const service = isEdit
? slaughterEditRepresentativeService
: slaughterSubmitRepresentativeService;
dispatch(service(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
},
});
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid item xs={12}>
<TextField
label="نام"
variant="outlined"
fullWidth
id="first_name"
name="first_name"
value={formik.values.first_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={
formik.touched.first_name && Boolean(formik.errors.first_name)
}
helperText={formik.touched.first_name && formik.errors.first_name}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="نام خانوادگی"
variant="outlined"
fullWidth
id="last_name"
name="last_name"
value={formik.values.last_name}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.last_name && Boolean(formik.errors.last_name)}
helperText={formik.touched.last_name && formik.errors.last_name}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="شماره همراه"
variant="outlined"
fullWidth
id="mobile"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.mobile && Boolean(formik.errors.mobile)}
helperText={formik.touched.mobile && formik.errors.mobile}
inputProps={{ maxLength: 11 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="شهر"
variant="outlined"
fullWidth
id="city"
name="city"
value={formik.values.city}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.city && Boolean(formik.errors.city)}
helperText={formik.touched.city && formik.errors.city}
/>
</Grid>
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={!formik.isValid}
>
{isEdit ? "ویرایش" : "ثبت"}
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,133 @@
import React, { useContext, useState } from "react";
import {
Button,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterEditRepresentativeService } from "../../services/slaughter-submit-delegate";
export const SlaughterManageDelegatesLimitationForm = ({
item,
updateTable,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasLimitation, setHasLimitation] = useState(item?.limitation || false);
const [governmentalValue, setGovernmentalValue] = useState(
item?.governmentalLimitationWeight || 0
);
const [freeValue, setFreeValue] = useState(item?.freeLimitationWeight || 0);
const handleSubmit = (e) => {
e.preventDefault();
const submitData = {
key: item?.key,
limitation: hasLimitation,
governmental_limitation_weight: hasLimitation
? Number(governmentalValue)
: 0,
free_limitation_weight: hasLimitation ? Number(freeValue) : 0,
};
dispatch(slaughterEditRepresentativeService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
};
return (
<form onSubmit={handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid container item xs={12} alignItems="center" gap={1}>
<Typography variant="body2" color="text.secondary">
اطلاعات نماینده:
</Typography>
<Typography variant="h6" mb={0.75}>
{item?.firstName} {item?.lastName}
</Typography>
</Grid>
<Grid item xs={12} mb={1}>
<FormControlLabel
control={
<Checkbox
checked={hasLimitation}
onChange={(e) => setHasLimitation(e.target.checked)}
color="primary"
/>
}
label="محدودیت فروش روزانه"
/>
</Grid>
{hasLimitation && (
<>
<Grid item xs={12}>
<TextField
label="حداکثر فروش دولتی (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={governmentalValue}
onChange={(e) => setGovernmentalValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="حداکثر فروش آزاد (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={freeValue}
onChange={(e) => setFreeValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
</>
)}
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={
hasLimitation && governmentalValue === 0 && freeValue === 0
}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,114 @@
import { IconButton, Popover, Typography, Button, Box } from "@mui/material";
import React, { useState } from "react";
import EditIcon from "@mui/icons-material/Edit";
import BlockIcon from "@mui/icons-material/Block";
import TuneIcon from "@mui/icons-material/Tune";
import { useDispatch } from "react-redux";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterManageDelegatesForm } from "./SlaughterManageDelegatesForm";
import { SlaughterManageDelegatesLimitationForm } from "./SlaughterManageDelegatesLimitationForm";
export const SlaughterManageDelegatesOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<Box
style={{
padding: "10px",
display: "flex",
flexDirection: "column",
gap: "8px",
}}
>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش نماینده",
content: (
<SlaughterManageDelegatesForm
item={item}
updateTable={updateTable}
isEdit
/>
),
size: 300,
})
);
}}
startIcon={<EditIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
ویرایش
</Typography>
</Button>
<Button
color="primary"
size="small"
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تنظیم محدودیت فروش",
content: (
<SlaughterManageDelegatesLimitationForm
item={item}
updateTable={updateTable}
/>
),
size: 400,
})
);
}}
startIcon={<BlockIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
تنظیم محدودیت
</Typography>
</Button>
</Box>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,253 @@
import React, { useEffect, useState } from "react";
import { Box, Button, Grid, TextField, Chip } from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import { BackButton } from "../../../../components/back-button/BackButton";
import { OPEN_MODAL, CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterManageDispensersForm } from "./SlaughterManageDispensersForm";
import { slaughterGetDispenserInfoService } from "../../services/slaughter-get-dispenser-info";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { RiSearchLine } from "react-icons/ri";
import { formatJustDate } from "../../../../utils/formatTime";
import { SlaughterManageDispensersOperations } from "./SlaughterManageDispensersOperations";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const SlaughterManageDispensers = () => {
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 isSteward = getRoleFromUrl() === "Steward";
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleTextChange = (e) => setTextValue(e.target.value);
const fetchApiData = async (pageNum) => {
const response = await dispatch(
slaughterGetDispenserInfoService({
search: "filter",
value: textValue,
page: pageNum,
page_size: perPage,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handlePageChange = (pageNum) => {
fetchApiData(pageNum);
setPage(pageNum);
};
const handlePerRowsChange = (perRows) => {
setPerPage(Number(perRows));
setPage(1);
};
const updateTableData = () => {
fetchApiData(page !== 0 ? page : 1);
};
useEffect(() => {
if (!data || !Array.isArray(data)) {
setTableData([]);
return;
}
const d = data.map((item, i) => {
const isActive = item?.active !== undefined ? item.active : null;
const statusBadge =
isActive === null ? (
"-"
) : (
<Chip
label={isActive ? "فعال" : "غیرفعال"}
color={isActive ? "success" : "error"}
size="small"
sx={{ minWidth: 80 }}
/>
);
const hasLimitation = item?.limitation;
const limitationBadge = (
<Chip
label={hasLimitation ? "دارد" : "ندارد"}
color={hasLimitation ? "warning" : "default"}
size="small"
sx={{ minWidth: 60 }}
/>
);
const killhouseDisplay =
item?.killHouse?.name && item?.killHouse?.mobile
? `${item.killHouse.name} (${item.killHouse.mobile})`
: item?.killHouse?.name
? item.killHouse.name
: "-";
const stewardDisplay =
item?.steward?.name && item?.steward?.user?.mobile
? `${item.steward.name} (${item.steward.user.mobile})`
: item?.steward?.name
? item.steward.name
: "-";
const displayValue = isSteward ? stewardDisplay : killhouseDisplay;
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.createDate ? formatJustDate(item.createDate) : "-",
item?.nationalId || "-",
item?.firstName || "-",
item?.lastName || "-",
item?.mobile || "-",
item?.city || "-",
item?.province || "-",
displayValue,
limitationBadge,
item?.governmentalLimitationWeight || 0,
item?.freeLimitationWeight || 0,
statusBadge,
<SlaughterManageDispensersOperations
key={`operations-${item?.key || i}`}
item={item}
updateTable={updateTableData}
/>,
];
});
setTableData(d);
}, [data, page, perPage]);
useEffect(() => {
fetchApiData(1);
setPage(1);
}, [perPage, selectedSubUser?.key]);
useEffect(() => {
fetchApiData(1);
}, [selectedSubUser?.key]);
const handleSubmit = async (e) => {
e.preventDefault();
setPage(1);
const response = await dispatch(
slaughterGetDispenserInfoService({
search: "filter",
value: textValue,
page: 1,
page_size: perPage,
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
);
if (response.payload.error) {
console.error("Error fetching data:", response.payload.error);
setData([]);
setTotalRows(0);
} else {
setData(response.payload.data?.results || []);
const count = Number(response.payload.data?.count) || 0;
setTotalRows(count);
}
};
const handleOpenModal = () => {
dispatch(
OPEN_MODAL({
title: "ثبت توزیع کننده جدید",
content: (
<SlaughterManageDispensersForm
onClose={() => dispatch(CLOSE_MODAL())}
updateTable={updateTableData}
/>
),
size: 300,
})
);
};
return (
<Box display="flex" justifyContent="center">
<Grid container xs={12} sm={12} md={10} lg={10}>
<Grid item xs={12}>
<BackButton />
</Grid>
<Grid container item gap={SPACING.SMALL}>
<Button variant="contained" color="primary" onClick={handleOpenModal}>
ثبت توزیع کننده جدید
</Button>
<form onSubmit={handleSubmit}>
<Grid container alignItems="center" gap={SPACING.SMALL}>
<TextField
size="small"
autoComplete="off"
label="جستجو"
variant="outlined"
style={{ width: 200 }}
value={textValue}
onChange={handleTextChange}
/>
<Button
type="submit"
onClick={handleSubmit}
endIcon={<RiSearchLine />}
>
جستجو
</Button>
</Grid>
</form>
</Grid>
<Grid item xs={12} mt={2}>
<ResponsiveTable
title="توزیع کنندگان"
columns={[
"ردیف",
"تاریخ ایجاد",
"کد ملی",
"نام",
"نام خانوادگی",
"شماره همراه",
"شهر",
"استان",
isSteward ? "مباشر" : "کشتارگاه",
"محدودیت فروش",
"حداکثر فروش دولتی",
"حداکثر فروش آزاد",
"وضعیت",
"عملیات",
]}
customWidth={"100%"}
data={tableData}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
/>
</Grid>
</Grid>
</Box>
);
};
export default SlaughterManageDispensers;

View File

@@ -0,0 +1,577 @@
import React, {
useContext,
useEffect,
useState,
useCallback,
useRef,
} from "react";
import { useFormik } from "formik";
import * as yup from "yup";
import { useDispatch, useSelector } from "react-redux";
import { Button, TextField, Typography, Box } from "@mui/material";
import PersonIcon from "@mui/icons-material/Person";
import PublicIcon from "@mui/icons-material/Public";
import BadgeIcon from "@mui/icons-material/Badge";
import CalendarTodayIcon from "@mui/icons-material/CalendarToday";
import AccountBoxIcon from "@mui/icons-material/AccountBox";
import BusinessIcon from "@mui/icons-material/Business";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterGetDispenserUserInfoService } from "../../services/slaughter-get-dispenser-user-info";
import { provinceGetCitiesService } from "../../../province/services/province-get-cities";
import {
slaughterHouseSubmitDispenserService,
slaughterHouseEditDispenserService,
} from "../../services/slaughter-house-submit-dispenser-service";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const InfoBox = ({ icon: Icon, label, value, iconSx }) => (
<Box
display="flex"
alignItems={iconSx ? "flex-start" : "center"}
gap={1}
px={1.5}
py={0.5}
bgcolor="#f5f5f5"
borderRadius={1}
>
<Icon color="action" sx={iconSx} />
<Box>
<Typography variant="caption" color="text.secondary">
{label}
</Typography>
<Typography variant="body1">{value || "-"}</Typography>
</Box>
</Box>
);
const getValidationSchema = () =>
yup.object({
mobile: yup
.string()
.required("شماره همراه الزامی است")
.matches(/^09\d{9}$/, "شماره تلفن باید با 09 شروع شود و 11 رقم باشد"),
});
const DispenserForm = ({ formik, userInfo }) => {
return (
<form onSubmit={formik.handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid item xs={12}>
<Typography
variant="h6"
style={{
fontSize: "16px",
}}
gutterBottom
>
اطلاعات توزیع کننده
</Typography>
</Grid>
{/* Additional User Info Section */}
{userInfo && (
<Grid container spacing={2} xs={12} mb={2}>
<Grid item xs={12}>
<Typography
variant="subtitle2"
color="text.secondary"
gutterBottom
sx={{ mb: 1 }}
>
اطلاعات شخصی
</Typography>
</Grid>
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="کد ملی"
value={userInfo.nationalCode || formik.values.national_id}
/>
</Grid>
{userInfo.fatherName && (
<Grid item xs={12} md={4}>
<InfoBox
icon={PersonIcon}
label="نام پدر"
value={userInfo.fatherName}
/>
</Grid>
)}
{userInfo.birthDate && (
<Grid item xs={12} md={4}>
<InfoBox
icon={CalendarTodayIcon}
label="تاریخ تولد"
value={userInfo.birthDate}
/>
</Grid>
)}
{userInfo.gender !== undefined && (
<Grid item xs={12} md={4}>
<InfoBox
icon={AccountBoxIcon}
label="جنسیت"
value={userInfo.gender ? "مرد" : "زن"}
/>
</Grid>
)}
{userInfo.identityNo && userInfo.identityNo !== "0" && (
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="شماره شناسنامه"
value={userInfo.identityNo}
/>
</Grid>
)}
{userInfo.identitySeries && (
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="سری شناسنامه"
value={userInfo.identitySeries}
/>
</Grid>
)}
{userInfo.identitySerial && (
<Grid item xs={12} md={4}>
<InfoBox
icon={BadgeIcon}
label="سریال شناسنامه"
value={userInfo.identitySerial}
/>
</Grid>
)}
</Grid>
)}
<Grid container spacing={2} xs={12}>
<Grid item xs={12} md={6}>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid item xs={12}>
<InfoBox
icon={PersonIcon}
label="نام"
value={formik.values.first_name}
/>
</Grid>
<Grid item xs={12}>
<InfoBox
icon={PersonIcon}
label="نام خانوادگی"
value={formik.values.last_name}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="شماره همراه"
variant="outlined"
fullWidth
id="mobile"
name="mobile"
value={formik.values.mobile}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={Boolean(formik.errors.mobile)}
helperText={formik.errors.mobile}
inputProps={{ maxLength: 11 }}
/>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} md={6}>
<Grid container direction="column" gap={SPACING.SMALL}>
<Grid item xs={12}>
<InfoBox
icon={PublicIcon}
label="شهر"
value={formik.values.city}
/>
</Grid>
<Grid item xs={12}>
<InfoBox
icon={BusinessIcon}
label="نوع توزیع کننده"
value={
formik.values.dispenser_type === "inductor"
? "واسطه"
: formik.values.dispenser_type === "salesman"
? "فروشنده"
: formik.values.dispenser_type === "driver"
? "راننده"
: formik.values.dispenser_type
}
/>
</Grid>
{formik.values.dispenser_type === "driver" && (
<>
<Grid item xs={12}>
<InfoBox
icon={BusinessIcon}
label="نوع خودرو"
value={formik.values.driver_car_type}
/>
</Grid>
{formik.values.pelak && (
<Grid item xs={12}>
<InfoBox
icon={BadgeIcon}
label="پلاک خودرو"
value={formik.values.pelak}
/>
</Grid>
)}
</>
)}
<Grid item xs={12}>
<InfoBox
icon={BusinessIcon}
label="سقف محدودیت"
value={formik.values.limitation_amount || 0}
/>
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={!formik.isValid}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};
const InquiryForm = ({ onInquiry, nationalCode, setNationalCode }) => {
return (
<Grid container gap={SPACING.SMALL} p={2}>
<Grid item xs={12}>
<TextField
label="کد ملی"
variant="outlined"
fullWidth
value={nationalCode}
onChange={(e) => setNationalCode(e.target.value)}
placeholder="کد ملی 10 رقمی را وارد کنید"
inputProps={{ maxLength: 10 }}
/>
</Grid>
<Grid item xs={12}>
<Button
color="primary"
fullWidth
variant="contained"
onClick={onInquiry}
disabled={!nationalCode || nationalCode.length !== 10}
>
استعلام
</Button>
</Grid>
</Grid>
);
};
export const SlaughterManageDispensersForm = ({
onClose,
updateTable,
dispenser,
initialUserData,
initialUserInfo,
initialNationalCode,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [nationalCode, setNationalCode] = useState(
initialNationalCode ||
dispenser?.user?.nationalId ||
dispenser?.national_id ||
""
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [userData, setUserData] = useState(
initialUserData ||
(dispenser
? {
national_id: dispenser?.user?.nationalId || "",
first_name: dispenser?.user?.firstName || "",
last_name: dispenser?.user?.lastName || "",
city: dispenser?.user?.city?.cityName || "",
mobile: dispenser?.user?.mobile || "",
dispenser_type: dispenser?.dispenserType || "inductor",
limitation_amount: dispenser?.limitation_amount || 0,
driver_car_type: dispenser?.car || "",
pelak: dispenser?.pelak || "",
}
: null)
);
const [userFound, setUserFound] = useState(dispenser ? true : false); // eslint-disable-line no-unused-vars
const [cities, setCities] = useState([]);
const [userInfo, setUserInfo] = useState(initialUserInfo || null);
const sizeUpdatedRef = useRef(!!initialUserData);
useEffect(() => {
dispatch(provinceGetCitiesService()).then((r) => {
setCities(r.payload.data || []);
});
}, [dispatch]);
useEffect(() => {
if (userData && !sizeUpdatedRef.current && !dispenser) {
sizeUpdatedRef.current = true;
const isDesktop = window.innerWidth > 600;
const preservedUserData = userData;
const preservedUserInfo = userInfo;
const preservedNationalCode = nationalCode;
dispatch(
OPEN_MODAL({
title: "ثبت توزیع کننده جدید",
content: (
<SlaughterManageDispensersForm
onClose={onClose}
updateTable={updateTable}
initialUserData={preservedUserData}
initialUserInfo={preservedUserInfo}
initialNationalCode={preservedNationalCode}
/>
),
size: isDesktop ? 600 : 300,
})
);
}
}, [
userData,
dispatch,
onClose,
updateTable,
dispenser,
userInfo,
nationalCode,
]);
const handleInquiry = useCallback(() => {
if (!nationalCode || nationalCode.length !== 10) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "لطفا کد ملی 10 رقمی معتبر وارد کنید",
severity: "error",
});
return;
}
dispatch(
slaughterGetDispenserUserInfoService({
national_code: nationalCode,
role_key:
checkPathStartsWith("slaughter") || checkPathStartsWith("steward")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
if (r.payload.error) {
setUserFound(false);
setUserInfo(null);
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else if (r.payload.data) {
const response = r.payload.data;
if (response.status === true && response.data) {
const userDataFromApi = response.data;
setUserFound(true);
setUserInfo(userDataFromApi);
console.log(userDataFromApi);
setUserData({
national_id: userDataFromApi.nationalCode || nationalCode,
first_name: userDataFromApi.firstName || "",
last_name: userDataFromApi.lastName || "",
city: userDataFromApi.city || "",
mobile: "", // Mobile is not in API response, user must enter it
dispenser_type: "inductor",
limitation_amount: 0,
});
openNotif({
vertical: "top",
horizontal: "center",
msg: "اطلاعات با موفقیت دریافت شد",
severity: "success",
});
} else if (response.status === false) {
setUserFound(false);
setUserInfo(null);
openNotif({
vertical: "top",
horizontal: "center",
msg: response.errorDescription || "خطا در دریافت اطلاعات",
severity: "error",
});
} else {
const userDataFromApi = response.data || response;
if (userDataFromApi && userDataFromApi.nationalCode) {
setUserFound(true);
setUserInfo(userDataFromApi);
setUserData({
national_id: userDataFromApi.nationalCode || nationalCode,
first_name: userDataFromApi.firstName || "",
last_name: userDataFromApi.lastName || "",
city: userDataFromApi.city || "",
mobile: "",
dispenser_type: "inductor",
limitation_amount: 0,
});
openNotif({
vertical: "top",
horizontal: "center",
msg: "اطلاعات با موفقیت دریافت شد",
severity: "success",
});
} else {
setUserFound(false);
setUserInfo(null);
openNotif({
vertical: "top",
horizontal: "center",
msg: "خطا در دریافت اطلاعات",
severity: "error",
});
}
}
}
});
}, [dispatch, nationalCode, openNotif, selectedSubUser]);
const formik = useFormik({
initialValues: {
national_id:
userData?.national_id ||
userData?.nationalCode ||
userData?.nationalId ||
"",
first_name: userData?.first_name || "",
last_name: userData?.last_name || "",
city: userData?.city || "",
mobile: userData?.mobile || "",
dispenser_type: userData?.dispenser_type || "inductor",
limitation_amount: userData?.limitation_amount || 0,
driver_car_type: userData?.driverCarType || userData?.car || "",
pelak: userData?.pelak || "",
},
validationSchema: getValidationSchema(),
enableReinitialize: true,
onSubmit: (values) => {
const currentUserInfo = userInfo;
const submitData = {
nationalCode: values.national_id || "",
firstName: values.first_name || "",
lastName: values.last_name || "",
fatherName: currentUserInfo?.fatherName || null,
gender:
currentUserInfo?.gender !== undefined ? currentUserInfo.gender : null,
isLive:
currentUserInfo?.isLive !== undefined ? currentUserInfo.isLive : true,
identityNo: currentUserInfo?.identityNo || null,
birthDate: currentUserInfo?.birthDate || null,
city: values.city || currentUserInfo?.city || "",
mobile: values.mobile,
role: getRoleFromUrl(),
};
if (dispenser?.key) {
// Edit mode
dispatch(
slaughterHouseEditDispenserService({
type: "update-profile",
dispenser_key: dispenser.key,
...submitData,
})
).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
} else {
// Create mode
dispatch(slaughterHouseSubmitDispenserService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
}
},
});
if (!userData && !dispenser) {
return (
<InquiryForm
onInquiry={handleInquiry}
nationalCode={nationalCode}
setNationalCode={setNationalCode}
/>
);
}
return <DispenserForm formik={formik} cities={cities} userInfo={userInfo} />;
};

View File

@@ -0,0 +1,133 @@
import React, { useContext, useState } from "react";
import {
Button,
TextField,
Typography,
Checkbox,
FormControlLabel,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { CLOSE_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { slaughterEditDispenserInfoService } from "../../services/slaughter-edit-dispenser-info";
export const SlaughterManageDispensersLimitationForm = ({
item,
updateTable,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [hasLimitation, setHasLimitation] = useState(item?.limitation || false);
const [governmentalValue, setGovernmentalValue] = useState(
item?.governmentalLimitationWeight || 0
);
const [freeValue, setFreeValue] = useState(item?.freeLimitationWeight || 0);
const handleSubmit = (e) => {
e.preventDefault();
const submitData = {
key: item?.key,
limitation: hasLimitation,
governmental_limitation_weight: hasLimitation
? Number(governmentalValue)
: 0,
free_limitation_weight: hasLimitation ? Number(freeValue) : 0,
};
dispatch(slaughterEditDispenserInfoService(submitData)).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",
});
if (updateTable) {
updateTable();
}
dispatch(CLOSE_MODAL());
}
});
};
return (
<form onSubmit={handleSubmit}>
<Grid container gap={SPACING.SMALL} p={2}>
<Grid container item xs={12} alignItems="center" gap={1}>
<Typography variant="body2" color="text.secondary">
اطلاعات توزیع کننده:
</Typography>
<Typography variant="h6" mb={0.75}>
{item?.firstName} {item?.lastName}
</Typography>
</Grid>
<Grid item xs={12} mb={1}>
<FormControlLabel
control={
<Checkbox
checked={hasLimitation}
onChange={(e) => setHasLimitation(e.target.checked)}
color="primary"
/>
}
label="محدودیت فروش روزانه"
/>
</Grid>
{hasLimitation && (
<>
<Grid item xs={12}>
<TextField
label="حداکثر فروش دولتی (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={governmentalValue}
onChange={(e) => setGovernmentalValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
<Grid item xs={12}>
<TextField
label="حداکثر فروش آزاد (کیلوگرم)"
variant="outlined"
fullWidth
type="number"
value={freeValue}
onChange={(e) => setFreeValue(e.target.value)}
inputProps={{ min: 0 }}
/>
</Grid>
</>
)}
<Grid item xs={12} mt={2}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={
hasLimitation && governmentalValue === 0 && freeValue === 0
}
>
ثبت
</Button>
</Grid>
</Grid>
</form>
);
};

View File

@@ -0,0 +1,82 @@
import { IconButton, Popover, Typography, Button } from "@mui/material";
import React, { useState } from "react";
import TuneIcon from "@mui/icons-material/Tune";
import BlockIcon from "@mui/icons-material/Block";
import { useDispatch } from "react-redux";
import { OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { SlaughterManageDispensersLimitationForm } from "./SlaughterManageDispensersLimitationForm";
export const SlaughterManageDispensersOperations = ({ item, updateTable }) => {
const [anchorEl, setAnchorEl] = useState(null);
const dispatch = useDispatch();
const handleClick = (event) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const open = Boolean(anchorEl);
const id = open ? "popover" : undefined;
const handleOpenLimitationModal = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تنظیم محدودیت فروش",
content: (
<SlaughterManageDispensersLimitationForm
item={item}
updateTable={updateTable}
/>
),
size: 400,
})
);
};
return (
<div>
<IconButton
aria-describedby={id}
variant="contained"
color="primary"
onClick={handleClick}
size="small"
>
<TuneIcon fontSize="small" />
</IconButton>
<Popover
anchorOrigin={{
vertical: "bottom",
horizontal: "right",
}}
transformOrigin={{
vertical: "top",
horizontal: "left",
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
>
<div style={{ padding: "10px" }}>
<Button
color="primary"
size="small"
onClick={handleOpenLimitationModal}
startIcon={<BlockIcon fontSize="small" />}
sx={{ textTransform: "none", userSelect: "text" }}
>
<Typography variant="body2" sx={{ userSelect: "text" }}>
تنظیم محدودیت
</Typography>
</Button>
</div>
</Popover>
</div>
);
};

View File

@@ -0,0 +1,266 @@
import React, { useContext, useState } from "react";
import {
Button,
IconButton,
List,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
Typography,
} from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import EditIcon from "@mui/icons-material/Edit";
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
import DeleteIcon from "@mui/icons-material/Delete";
import { Grid } from "../../../../components/grid/Grid";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { SPACING } from "../../../../data/spacing";
import { useDispatch } from "react-redux";
import { slaughterInventoryFinalSubmitService } from "../../services/slaughter-inventory-final-submit";
import { slaughterDeleteAllocatedService } from "../../services/salughter-delete-allocated";
import { SlaughterAllocateToGuild } from "../slaughter-allocate-to-guild/SlaughterAllocateToGuild";
import { SlaughterAllocateForFreezing } from "../slaughter-allocate-for-freezing/SlaughterAllocateForFreezing";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
export const SlaughterManageInventoryAllocationOperations = ({
fetchApiData,
item,
priceInfo,
remainWeight,
}) => {
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 [openNotif] = useContext(AppContext);
const dispatch = useDispatch();
const handleEdit = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص",
content:
item?.allocationType === "ColdHouse" ? (
<SlaughterAllocateForFreezing
fetchApiData={fetchApiData}
editData={item}
priceInfo={priceInfo}
remainWeight={remainWeight}
/>
) : (
<SlaughterAllocateToGuild
fetchApiData={fetchApiData}
editData={item}
priceInfo={priceInfo}
remainWeight={remainWeight}
/>
),
})
);
};
const handleFinalSubmit = () => {
handleClose();
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: [item?.key],
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
fetchApiData(1);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
};
const handleDelete = () => {
handleClose();
dispatch(
OPEN_MODAL({
title: "آیا مطمئن هستید؟",
content: (
<Grid container spacing={2}>
<Grid item>
<Button
variant="contained"
color="error"
onClick={() => {
dispatch(
slaughterDeleteAllocatedService({
steward_allocation_key: item.key,
})
).then(() => {
dispatch(CLOSE_MODAL());
dispatch(fetchSlaughterBroadcastAndProducts());
fetchApiData(1);
});
}}
>
تایید
</Button>
</Grid>
<Grid item>
<Button
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
};
const options = [
{
key: "edit",
label: "ویرایش",
icon: EditIcon,
color: "primary.main",
action: handleEdit,
},
];
if (!item?.registrationCode) {
options.push({
key: "finalSubmit",
label: "تایید نهایی",
icon: CheckCircleOutlineIcon,
color: "info.main",
action: handleFinalSubmit,
});
}
if (!item?.registrationCode) {
options.push({
key: "delete",
label: "حذف",
icon: DeleteIcon,
color: "error.main",
action: handleDelete,
});
}
return (
<Grid container>
<IconButton
size="small"
disabled={item?.registrationCode}
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}
>
<List sx={{ minWidth: 160, p: 0.5 }}>
{options.map((option) => {
const IconComponent = option.icon;
return (
<ListItemButton
key={option.key}
onClick={option.action}
sx={{
borderRadius: 1,
mb: 0.25,
py: 0.5,
"&:last-of-type": { mb: 0 },
}}
>
<ListItemIcon sx={{ minWidth: 32, color: option.color }}>
<IconComponent fontSize="small" />
</ListItemIcon>
<ListItemText
primary={option.label}
primaryTypographyProps={{
sx: {
color: option.color,
fontSize: "0.82rem",
fontWeight: 600,
},
}}
/>
</ListItemButton>
);
})}
</List>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,191 @@
import { Button, Typography } from "@mui/material";
import { useContext } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Grid } from "../../../../components/grid/Grid";
import { AppContext } from "../../../../contexts/AppContext";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteAllocatedService } from "../../services/salughter-delete-allocated";
import { slaughterGetUpdatedInventoryStock } from "../../services/salughter-get-updated-inventory-stock";
import { slaughterManageInventoryAllocationsService } from "../../services/salughter-manage-inventory-allocations";
import { slaughterGetKillhouseGuildsService } from "../../services/slaughter-get-killhouse-guilds";
import { slaughterGetKillhouseStewardsService } from "../../services/slaughter-get-killhouse-stewards";
import { SlaughterAddSteward } from "../slaughter-add-steward/SlaughterAddSteward";
import { SubmitAuthCode } from "../submit-auth-code/SubmitAuthCode";
import { slaughterInventoryFinalSubmitService } from "../../services/slaughter-inventory-final-submit";
import { SPACING } from "../../../../data/spacing";
export const SlaughterManageInventoryAllocationOperationsTest = ({
item,
disabled,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [, , selectedDate1] = useContext(AppContext);
const { inventorySelectedKillHouse } = useSelector(
(state) => state.slaughterSlice
);
return (
<Grid container direction="column">
{item.systemRegistrationCode && (
<Button
size="small"
disabled={
item.finalRegistration || item.loggedRegistrationCode || disabled
}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "کداحراز",
content: <SubmitAuthCode item={item} />,
})
);
}}
>
ثبت کداحراز
</Button>
)}
{!item.systemRegistrationCode && (
<Button
size="small"
disabled={item.finalRegistration}
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: [item.key],
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
dispatch(
slaughterManageInventoryAllocationsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
}}
>
تایید
</Button>
<Button
fullWidth
color="error"
variant="contained"
onClick={() => {
dispatch(CLOSE_MODAL());
}}
>
لغو
</Button>
</Grid>
</Grid>
),
})
);
}}
>
ارسال کداحراز
</Button>
)}
{!item.systemRegistrationCode && (
<>
<Button
size="small"
disabled={item.finalRegistration}
onClick={() => {
dispatch(
OPEN_MODAL({
title: "ویرایش تخصیص",
content: (
<SlaughterAddSteward
item={item}
quantity={item.numberOfCarcasses}
weight={item.weightOfCarcasses}
stewardKey={item.key}
/>
),
})
);
}}
>
ویرایش
</Button>
<Button
size="small"
disabled={item.finalRegistration}
color="error"
onClick={() => {
dispatch(
slaughterDeleteAllocatedService({
steward_allocation_key: item.key,
})
).then((r) => {
dispatch(
slaughterGetKillhouseStewardsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetUpdatedInventoryStock({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterManageInventoryAllocationsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
dispatch(
slaughterGetKillhouseGuildsService({
kill_house_key: inventorySelectedKillHouse,
date: selectedDate1,
})
);
});
}}
>
حذف
</Button>
</>
)}
</Grid>
);
};

View File

@@ -0,0 +1,3 @@
export const SlaughterMorgueBroadcastManagement = () => {
return null;
};

View File

@@ -0,0 +1,47 @@
import { VscNewFolder } from "react-icons/vsc";
import { useLocation } from "react-router-dom";
import { Grid } from "../../../../components/grid/Grid";
import LinkItem from "../../../../components/link-item/LinkItem";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { SPACING } from "../../../../data/spacing";
import {
ROUTE_SLAUGHTER_MORGUE_BROADCAST_MANAGEMENT,
ROUTE_SLAUGHTER_MORGUE_STOCK,
} from "../../../../routes/routes";
export const SlaughterMorgueOperation = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
justifyContent="center"
style={{ placeContent: "baseline" }}
>
<NavLink
to={ROUTE_SLAUGHTER_MORGUE_STOCK}
active={pathname === ROUTE_SLAUGHTER_MORGUE_STOCK ? "true" : null}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="موجودی انبار"
/>
</NavLink>
<NavLink
to={ROUTE_SLAUGHTER_MORGUE_BROADCAST_MANAGEMENT}
active={
pathname === ROUTE_SLAUGHTER_MORGUE_BROADCAST_MANAGEMENT
? "true"
: null
}
>
<LinkItem
icon={<VscNewFolder size={30} color="#244CCC" />}
title="مدیریت پخش"
/>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,3 @@
export const SlaughterMorgueStock = () => {
return null;
};

View File

@@ -0,0 +1,221 @@
import React, { useEffect, useState } from "react";
import {
Checkbox,
FormControlLabel,
Tab,
Tabs,
TextField,
} from "@mui/material";
import { useDispatch, useSelector } from "react-redux";
import moment from "moment";
import { DatePicker } from "@mui/x-date-pickers";
import {
liveStockGetInventoryData,
liveStockGetInventoryDataDashboard,
} from "../../../live-stock-support/services/live-stock-get-inventory-data";
import { Grid } from "../../../../components/grid/Grid";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { SPACING } from "../../../../data/spacing";
import { SlaughterColdHouseBars } from "../slaughter-cold-house-bars/SlaughterColdHouseBars";
import { useParams } from "react-router-dom";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterMorgueView = () => {
const dispatch = useDispatch();
const [tableData, setTableData] = useState([]);
const [tableDataDashboard, setTableDataDashboard] = useState([]);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const { key } = useParams();
const [withDate, setWithDate] = useState(false);
const [selectedDate1, setSelectedDate1] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const [selectedDate2, setSelectedDate2] = useState(
moment(new Date()).format("YYYY-MM-DD")
);
const getDashboardsData = () => {
dispatch(
liveStockGetInventoryData({
dashboard: true,
cold_house_key: key,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setTableData(r.payload.data);
});
dispatch(
liveStockGetInventoryDataDashboard({
date1: withDate ? selectedDate1 : null,
date2: withDate ? selectedDate2 : null,
cold_house_key: key,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then((r) => {
setTableDataDashboard(r.payload.data);
});
};
useEffect(() => {
getDashboardsData();
}, [dispatch, withDate, selectedDate1, selectedDate2, selectedSubUser?.key]);
const [value, setValue] = useState(0);
const handleChange = (event, newValue) => {
setValue(newValue);
};
return (
<>
<Grid container direction="column" justifyContent="center" xs={12}>
<Grid container justifyContent="space-between" gap={SPACING.SMALL}>
<ResponsiveTable
noPagination
columns={["وزن کل", "وزن خارج شده", "وزن باقیمانده"]}
data={[
[
tableData?.totalInputWeight?.toLocaleString(),
tableData?.totalAllocatedWeight?.toLocaleString(),
tableData?.totalRemainWeight?.toLocaleString(),
],
]}
title="موجودی سردخانه"
isDashboard
/>
<ResponsiveTable
noPagination
columns={[
"تعداد کل بارهای وارد شده",
"وزن کل بارهای وارد شده",
"تعداد کل بارهای خارج شده",
"وزن کل بارهای خارج شده",
]}
data={[
[
tableDataDashboard?.totalInputBars?.toLocaleString(),
tableDataDashboard?.totalInputBarsWeight?.toLocaleString(),
tableDataDashboard?.totalOutputBars?.toLocaleString(),
tableDataDashboard?.totalOutputBarsWeight?.toLocaleString(),
],
]}
title={"خلاصه اطلاعات"}
isDashboard
/>
</Grid>
<Grid
container
alignItems="center"
justifyContent="space-between"
gap={2}
paddingTop={2}
mb={1}
width="100%"
>
<Grid
container
gap={1}
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "10px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
>
<Grid>
<FormControlLabel
control={
<Checkbox
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
/>
}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</Grid>
</Grid>
<Grid container xs={12} justifyContent="center">
<Tabs
sx={{ mb: 2 }}
value={value}
onChange={handleChange}
aria-label="simple tabs example"
>
<Tab value={0} label="بارهای خارج شده" />
<Tab value={1} label="بارهای وارد شده" />
</Tabs>
{value === 0 && (
<SlaughterColdHouseBars
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
title="بارهای خارج شده"
type={"output"}
withDate={withDate}
coldHouseKey={key}
getDashboardsData={getDashboardsData}
remainWeight={tableData?.totalRemainWeight}
/>
)}
{value === 1 && (
<SlaughterColdHouseBars
selectedDate1={selectedDate1}
selectedDate2={selectedDate2}
title="بارهای وارد شده"
type={"input"}
withDate={withDate}
coldHouseKey={key}
getDashboardsData={getDashboardsData}
/>
)}
</Grid>
</Grid>
</>
);
};

View File

@@ -0,0 +1,374 @@
import { Button, Grid, IconButton, TextField, Typography } from "@mui/material";
import { SPACING } from "../../../../data/spacing";
import SendIcon from "@mui/icons-material/Send";
import { useFormik } from "formik";
import { Yup } from "../../../../lib/yup/yup";
import { useEffect } from "react";
import {
DRAWER,
LOADING_END,
LOADING_START,
} from "../../../../lib/redux/slices/appSlice";
import { slaughterNewCar } from "../../services/slaughter-new-car";
import { useDispatch } from "react-redux";
import { slaughterGetCars } from "../../services/slaughter-get-cars";
import { AppContext } from "../../../../contexts/AppContext";
import { useContext } from "react";
import SearchIcon from "@mui/icons-material/Search";
import { slaughterGetDriverByHealthCode } from "../../services/slaughter-get-driver-by-health-code";
import { useState } from "react";
export const SlaughterNewCar = ({ cars, killHouseKey }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [userExist, setUserExist] = useState(false);
const [userData, setUserData] = useState();
const [userKey, setUserKey] = useState();
const [userChecked, setUserChecked] = useState(false);
const formik = useFormik({
initialValues: {
driver_name: "",
driver_mobile: "",
type_car: "ایسوزو",
type_weight: "سنگین",
capocity: "",
health_code: "",
pelak1: "",
pelak2: "الف",
pelak3: "",
pelak4: "",
// name: "",
},
validationSchema: Yup.object({
driver_name: Yup.string()
.matches(
/^[ض‌ص‌ث‌ق‌ف‌غ‌ع‌ه‌خ‌خ‌ح‌ج‌چ‌ش‌س‌ی‌ب‌ل‌ا‌ت‌ن‌ن‌م‌ک‌گ‌ظ‌ط‌ز‌ر‌ذ‌د‌و‌پ‌آ‌ژ ]+$/,
"فقط حروف فارسی وارد کنید"
)
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
driver_mobile: Yup.string()
.test("len", "شماره تلفن باید با 0 شروع شود", (val, context) => {
return context.originalValue && context.originalValue.startsWith("0");
})
.test("len", "شماره تماس 11 رقم باید باشد", (val, context) => {
if (context.originalValue) {
return context.originalValue.length === 11;
}
})
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
type_weight: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
capocity: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
health_code: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
pelak1: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
pelak2: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
pelak3: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
pelak4: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
name: Yup.string().typeError("لطفا فیلد را به درستی وارد کنید!"),
}),
});
useEffect(() => {
formik.validateForm();
formik2.validateForm();
}, []);
const formik2 = useFormik({
initialValues: {
health_code_check: "",
},
validationSchema: Yup.object({
health_code_check: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا فیلد را به درستی وارد کنید!"),
}),
});
return (
<Grid>
<Grid container gap={SPACING.MEDIUM} direction="column">
<Grid>
<Grid container gap={SPACING.SMALL} justifyContent="left">
{!userChecked && (
<>
<Typography>ثبت با کد بهداشتی</Typography>
<Grid display="flex" width={1}>
<TextField
fullWidth
id="health_code_check"
label="کد بهداشتی"
variant="outlined"
value={formik2.values.health_code_check}
error={
formik2.touched.health_code_check
? Boolean(formik2.errors.health_code_check)
: null
}
onChange={formik2.handleChange}
onBlur={formik2.handleBlur}
helperText={
formik2.touched.health_code_check &&
Boolean(formik2.errors.health_code_check)
? formik2.errors.health_code_check
: null
}
/>
<IconButton
disabled={!formik2.isValid}
aria-label="delete"
color="primary"
onClick={() => {
if (formik2.values.health_code_check) {
dispatch(LOADING_START());
dispatch(
slaughterGetDriverByHealthCode(
formik2.values.health_code_check
)
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
setUserChecked(true);
if (r.payload.data[0]) {
setUserData(r.payload.data[0]);
setUserExist(true);
setUserKey(r.payload.data[0]?.key);
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "راننده پیدا نشد!",
severity: "error",
});
dispatch(
DRAWER({
right: false,
bottom: false,
content: null,
})
);
setUserExist(false);
}
}
dispatch(LOADING_END());
});
}
}}
>
<SearchIcon />
</IconButton>
</Grid>
</>
)}
{userChecked && (
<>
{userExist ? (
<>
<Grid
container
gap={SPACING.SMALL}
direction="column"
flex="1"
height="100%"
justifyContent="space-between"
>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
نام راننده:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{userData.driverName}
</Typography>{" "}
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
موبایل:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{userData.driverMobile}
</Typography>
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
خودرو:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{userData.typeCar}
</Typography>{" "}
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
پلاک:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{userData.pelak}
</Typography>{" "}
</Grid>
<Grid display="flex">
<Typography
variant="body1"
mr={SPACING.TINY}
color={(prop) => prop.palette.grey["A700"]}
>
ظرفیت:
</Typography>
<Typography mr={SPACING.TINY} fontWeight="bold">
{userData.capocity}
</Typography>{" "}
</Grid>
</Grid>
</>
) : (
<>
<Typography>خودرو وجود ندارد!</Typography>
</>
)}
<Button
fullWidth
color="primary"
variant="contained"
disabled={userKey ? !formik2.isValid : !formik.isValid}
startIcon={<SendIcon />}
onClick={() => {
dispatch(LOADING_START());
if (userKey) {
dispatch(
slaughterNewCar({
driver_key: userKey,
kill_house_key: killHouseKey,
})
).then((r) => {
if (r.error) {
if (r.error.message.includes("403")) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "این خودرو قبلا برای این کشتارگاه ثبت شده است!",
severity: "error",
});
} else {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
}
} else {
dispatch(slaughterGetCars()).then((r) => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
DRAWER({
right: false,
bottom: false,
content: null,
})
);
});
}
dispatch(LOADING_END());
});
} else {
dispatch(
slaughterNewCar({
driver_name: formik.values.driver_name,
driver_mobile: formik.values.driver_mobile,
type_car: formik.values.type_car,
capocity: formik.values.capocity,
weight_without_load: "0",
health_code: formik.values.health_code,
pelak1: formik.values.pelak1,
pelak2: formik.values.pelak2,
pelak3: formik.values.pelak3,
pelak4: formik.values.pelak4,
// name: formik.values.name,
})
).then((r) => {
if (r.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: "مشکلی پیش آمده است!",
severity: "error",
});
} else {
dispatch(slaughterGetCars()).then((r) => {
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(
DRAWER({
right: false,
bottom: false,
content: null,
})
);
});
}
dispatch(LOADING_END());
});
}
}}
>
ثبت اطلاعات
</Button>
</>
)}
</Grid>
</Grid>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,414 @@
import React, { forwardRef } from "react";
import { PropTypes } from "prop-types";
import { useSystemName } from "../../../../utils/getSystemName";
import logo from "../../../../assets/images/logo.png";
import { useCeoName } from "../../../../utils/getCeoName";
const styles = {
page: {
width: "210mm",
margin: "0 auto",
display: "flex",
flexDirection: "column",
position: "relative",
direction: "rtl",
fontFamily: "nazanin",
fontWeight: "bold",
},
container: {
width: "95%",
alignSelf: "center",
pageBreakInside: "avoid",
},
p: {
fontFamily: "nazanin",
fontWeight: "bold",
pAlign: "justify",
},
span: {
fontFamily: "nazanin",
fontWeight: "bold",
pAlign: "justify",
},
invoiceTable: {
width: "100%",
borderCollapse: "collapse",
alignSelf: "center",
marginBottom: "2px",
},
tableCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 11,
},
tableCellMobile: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 10,
},
tableInnerCell: {
border: "1px solid #000",
pAlign: "left",
textAlign: "center",
fontSize: 8,
whiteSpace: "nowrap",
},
tableHeader: {
backgroundColor: "rgba(211, 211, 211, 0.3)",
pageBreakAfter: "auto",
},
headerRow: {
backgroundColor: "rgba(211, 211, 211, 0.3)",
color: "black",
pageBreakInside: "avoid",
pageBreakAfter: "auto",
},
logo: {
width: "60px",
height: "auto",
marginBottom: "10px",
},
contentContainer: {
display: "flex",
justifyContent: "space-between",
marginTop: "20px",
marginLeft: "100px",
marginRight: "30px",
},
contentInLine: {
display: "flex",
flexDirection: "column",
alignItems: "center",
},
mainTitle: {
fontFamily: "nazanin",
fontSize: 11,
pAlign: "center",
fontWeight: "bolder",
},
signature: {
display: "flex",
flexDirection: "column",
alignItems: "flex-end",
marginLeft: "20px",
},
watermarkContainer: {
position: "fixed",
top: 450,
left: 0,
right: 30,
bottom: 0,
justifyContent: "center",
alignItems: "center",
opacity: 0.2,
zIndex: -1,
},
watermarkp: {
fontSize: 100,
fontWeight: "bolder",
color: "grey",
transform: "rotate(-45deg)",
left: "50%",
},
title: {
fontSize: 12,
fontWeight: "bolder",
pAlign: "center",
},
titleTopic: {
marginTop: "10px",
fontSize: 12,
fontWeight: "bolder",
pAlign: "center",
},
firsttitle: {
fontSize: 14,
fontWeight: "bolder",
marginLeft: "40px",
pAlign: "center",
},
title2: {
fontSize: 10,
marginBottom: 10,
pAlign: "center",
},
options: {
marginLeft: "50px",
padding: "10px",
marginTop: "15px",
display: "flex",
flexDirection: "column",
alignItems: "flex-start",
},
divider: {
width: "100%",
height: "2px",
backgroundColor: "red",
marginBottom: 15,
},
pTitleContainer: {
pAlign: "center",
margin: "15px",
textAlign: "justify",
textJustify: "inter-word",
},
tableHeaderCell: {
backgroundColor: "rgba(211, 211, 211, 0.3)",
fontSize: 10,
border: "1px solid #000",
padding: "4px",
textAlign: "center",
fontWeight: "bold",
},
footer: {
pageBreakAfter: "always",
position: "fixed",
left: 0,
bottom: 0,
width: "100%",
},
};
const SlaughterNewFactorFile = forwardRef((props, ref) => {
const { item } = props;
// const { date } = props;
const systemName = useSystemName();
const ceoName = useCeoName();
return (
<div style={styles.page} ref={ref}>
<div style={styles.contentContainer}>
<div style={styles.contentInLine}>
<img alt="logo" src={logo} style={styles.logo} />
<span style={styles.mainTitle}>
اتحادیه سراسری تعاونیهای کشاورزی پرورش دهندگان مرغ گوشتی ایران
</span>
<span style={styles.title}>
اتحادیه شرکت های تعاونی کشاورزی مرغداران {" "} {systemName}
</span>
</div>
<div>
<span style={styles.firsttitle}>بسمه تعالی</span>
</div>
<div style={styles.options}>
<span style={styles.title}>شماره:</span>
<span style={styles.titleTopic}>تاریخ:</span>
</div>
</div>
<hr style={styles.divider} />
<strong style={{ fontSize: "20px", textAlign: "center" }}>
فاکتور فروش
</strong>
<br />
<div style={styles.container}>
<table
style={{
...styles.invoiceTable,
marginBottom: "5px",
}}
>
<tbody>
<tr>
<td
style={{
...styles.tableCell,
width: "3%",
backgroundColor: "grey",
}}
>
<span
style={{
writingMode: "vertical-lr",
textOrientation: "mixed",
fontSize: 14,
fontWeight: "bold",
color: "white",
}}
>
خریدار{" "}
</span>
</td>
<td
style={{
...styles.tableCell,
width: "90%",
textAlign: "right",
}}
>
{" "}
{" "}
{
item?.provinceCheckInfo?.killHouseAssignment?.killHouseRequest
?.killhouseUser?.killHouseOperator?.user?.fullname
}
</td>
</tr>
</tbody>
</table>
<table
style={{
...styles.invoiceTable,
marginBottom: "5px",
}}
>
<tbody>
<tr>
<td
style={{
...styles.tableCell,
width: "3%",
backgroundColor: "grey",
}}
>
<span
style={{
writingMode: "vertical-lr",
textOrientation: "mixed",
fontSize: 14,
fontWeight: "bold",
color: "white",
}}
>
فروشنده{" "}
</span>
</td>
<td
style={{
...styles.tableCell,
width: "90%",
textAlign: "right",
}}
>
{" "}
{" "}
اتحادیه شرکت های تعاونی کشاورزی مرغداران {" "} {systemName}
</td>
</tr>
</tbody>
</table>
<table style={styles.invoiceTable}>
<tr>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th style={styles.tableHeaderCell}>ردیف</th>
<th style={styles.tableHeaderCell}>شرح</th>
<th style={styles.tableHeaderCell}>وزن (کیلوگرم)</th>
<th style={styles.tableHeaderCell}>
قیمت مرغ زنده (کیلوگرم)
</th>
<th style={styles.tableHeaderCell}>قیمت کل (ریال)</th>
</tr>
</thead>
<tbody>
<tr>
<td style={styles.tableCell}>1</td>
<td style={styles.tableCell}>
بار شماره
{
item?.provinceCheckInfo?.killHouseAssignment
?.killHouseRequest?.barCode
}
مرغداری :{" "}
{`${item?.poultryRequest?.poultryName} (${item?.poultryRequest?.poultryUserMobile})`}
</td>
<td style={styles.tableCell}>
{item?.provinceFactorToKillHouse.netWeight?.toLocaleString()}
</td>
<td style={styles.tableCell}>
{item?.factorFee?.toLocaleString() + " ﷼"}
</td>
<td style={styles.tableCell}>
{item?.provinceFactorToKillHouse?.totalFactorAmount?.toLocaleString() +
" ﷼"}
</td>
</tr>
</tbody>
</table>
</tr>
<tr>
<table style={styles.invoiceTable}>
<thead style={styles.tableHeader}>
<tr style={styles.headerRow}>
<th colSpan={4} style={styles.tableHeaderCell}>
جمع فاکتور
</th>
<th style={styles.tableHeaderCell}>
{item?.provinceFactorToKillHouse?.totalFactorAmount?.toLocaleString() +
" ﷼"}
</th>
</tr>
</thead>
<tbody style={styles.tableCell}>
<tr>
<td colSpan={3}></td>
<td style={styles.tableCell}>تخفیف</td>
<td style={styles.tableCell}>0</td>
</tr>
<tr>
<td colSpan={3}></td>
<td style={styles.tableCell}>مالیات</td>
<td style={styles.tableCell}>0</td>
</tr>
<tr>
<td colSpan={3}></td>
<td style={styles.tableCell}>عوارض</td>
<td style={styles.tableCell}>0</td>
</tr>
<tr>
<td style={styles.tableCell} colSpan={3}>
مبلغ قابل پرداخت (ریال)
</td>
<td colSpan={2} style={styles.tableCell}>
{item?.provinceFactorToKillHouse?.totalFactorAmount?.toLocaleString() +
" ﷼"}
</td>
</tr>
</tbody>
</table>
</tr>
</table>
<div style={{ flexDirection: "row", marginTop: "20px" }}>
<div style={styles.signature}>
<div style={styles.contentInLine}>
<span style={styles.title}>{ceoName}</span>
<span style={styles.title}>
مدیرعامل اتحادیه مرغداران{" "}
{systemName}
</span>
</div>
</div>
</div>
<div style={styles.watermarkContainer}>
<p style={styles.watermarkp}>سامانه رصدیار </p>
</div>
{/* <div style={styles.footer}>
<div style={styles.divider} />
<p style={{ fontSize: 12, padding: "10px", marginRight: "10px" }}>
{getAddressContent(systemName)}
</p>
</div> */}
</div>
</div>
);
});
SlaughterNewFactorFile.displayName = "SlaughterNewFactorFile";
export default SlaughterNewFactorFile;
SlaughterNewFactorFile.propTypes = {
item: PropTypes.any,
};

View File

@@ -0,0 +1,517 @@
import {
Button,
Checkbox,
FormControl,
FormControlLabel,
FormHelperText,
Grid,
InputLabel,
ListItem,
ListItemIcon,
ListItemText,
MenuItem,
Select,
TextField,
Tooltip,
Typography,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import DoneIcon from "@mui/icons-material/Done";
import { useEffect, useState, useContext } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useFormik } from "formik";
import moment from "moment";
import { SPACING } from "../../../../data/spacing";
import { Yup } from "../../../../lib/yup/yup";
import {
CLOSE_MODAL,
DRAWER,
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { AppContext } from "../../../../contexts/AppContext";
import { DialogAlert } from "../../../../components/dialog-alert/DialogAlert";
import { NumberInput } from "../../../../components/number-format-custom/NumberFormatCustom";
import useUserProfile from "../../../authentication/hooks/useUserProfile";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { slaughterNewRequest } from "../../services/slaughter-new-request";
import { slaughterGetRequests } from "../../services/salughter-get-requests";
import { slaughterGetProfile } from "../../services/slaughter-get-profile";
import { slaughterGetKillerKillhousesService } from "../../services/slaughter-get-killers-killhouses";
import { provinceGetPricing } from "../../../province/services/province-get-pricing";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
const TIME_RANGES = [
"12 - 14",
"14 - 16",
"16 - 18",
"18 - 20",
"20 - 22",
"22 - 24",
];
const CHICKEN_BREEDS = [
"آرین",
"راس",
"آربراکرز (آپلاس)",
"کاب",
"هوبارد",
"ترکیبی",
"وارداتی",
];
const DEFAULT_INDEX_WEIGHT = 2.7;
const PENALTY_MULTIPLIER = 1000;
const SMS_COST = "5000 تومان";
const getInitialValues = (selectedSubUser) => ({
capacity: "",
recieveTime: "",
selectedKillhouse: selectedSubUser?.key || "",
selectedKillerKillhouse: "",
race: CHICKEN_BREEDS[0],
sellType: {
cash: true,
haveTime: false,
},
weightType: {
under2AndHalf: false,
over2AndHalf: false,
},
recieveDate: moment().format("YYYY-MM-DD hh:mm:ss"),
isAccepted: getRoleFromUrl() === "ProvinceOperator",
indexWeight: DEFAULT_INDEX_WEIGHT,
});
const getValidationSchema = (selectedSubUser) =>
Yup.object({
capacity: Yup.number()
.required("این فیلد اجباری است!")
.typeError("لطفا عدد وارد کنید!"),
selectedKillhouse: Yup.string().required("این فیلد اجباری است!"),
selectedKillerKillhouse: selectedSubUser?.killer
? Yup.string().required("این فیلد اجباری است!")
: Yup.string(),
recieveTime: Yup.string()
.required("این فیلد اجباری است!")
.typeError("لطفا وزن را وارد کنید!"),
sellType: Yup.object()
.test("sellType", "نحوه فروش را انتخاب کنید!", (val, context) => {
return (
context.originalValue &&
Object.values(context.originalValue).some((item) => item === true)
);
})
.required("این فیلد اجباری است!"),
isAccepted: Yup.boolean()
.test("req", "باید تعهد نامه را بپذیرید!", (val, context) => {
return context.originalValue && context.originalValue === true;
})
.required("این فیلد اجباری است!"),
});
export const SlaughterNewRequestForm = () => {
const [openNotif, , selectedDate1, , selectedDate2] = useContext(AppContext);
const dispatch = useDispatch();
const [, userProfile] = useUserProfile();
const { profile, slaughterGetKillerKillhouses } = useSelector(
(state) => state.slaughterSlice
);
const selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const [checkedSms, setCheckedSms] = useState(true);
const [checkedBreedAndWeight, setCheckedBreedAndWeight] = useState(false);
const formik = useFormik({
initialValues: getInitialValues(selectedSubUser),
validationSchema: getValidationSchema(selectedSubUser),
});
const penaltyPrice = formik.values.capacity * PENALTY_MULTIPLIER;
const dialogContent = (
<>
<Typography variant="body1">
اینجانب {userProfile.fullname} موافقت خود را نسبت به موارد ذکر شده اعلام
می نمایم.
</Typography>
<ListItem>
<ListItemIcon>
<DoneIcon />
</ListItemIcon>
<ListItemText
primary={`بر اساس این توافق نامه در صورت لغو کشتار ${formik.values.capacity} قطعه
مرغ ${penaltyPrice} ریال جریمه خواهم شد.`}
/>
</ListItem>
</>
);
useEffect(() => {
dispatch(
slaughterGetKillerKillhousesService({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(LOADING_START());
dispatch(
slaughterGetProfile({
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
).then(() => {
dispatch(LOADING_END());
});
dispatch(provinceGetPricing());
formik.validateForm();
}, [selectedSubUser?.key]);
const handleSubmit = async () => {
dispatch(LOADING_START());
const result = await dispatch(
slaughterNewRequest({
kill_capacity: formik.values.capacity,
recive_time: formik.values.recieveTime,
recive_date: formik.values.recieveDate,
low_weight: formik.values.weightType.under2AndHalf,
high_weight: formik.values.weightType.over2AndHalf,
Index_weight: checkedBreedAndWeight
? formik.values.indexWeight
: DEFAULT_INDEX_WEIGHT,
chicken_breed: checkedBreedAndWeight
? formik.values.race
: "تعیین نشده",
cash: formik.values.sellType.cash,
credit: formik.values.sellType.haveTime,
sms_payment: checkedSms,
kill_house_key: formik.values.selectedKillhouse,
killer_kill_house_key: formik.values.selectedKillerKillhouse || null,
role: getRoleFromUrl(),
})
);
dispatch(LOADING_END());
if (result.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: result.payload.error,
severity: "error",
});
return;
}
dispatch(
slaughterGetRequests({
selectedDate1,
selectedDate2,
role_key: checkPathStartsWith("slaughter")
? selectedSubUser?.key || ""
: "",
})
);
dispatch(
OPEN_MODAL({
title: result.payload.data.result,
content: (
<Button
variant="contained"
onClick={() => dispatch(CLOSE_MODAL())}
color="primary"
>
تایید
</Button>
),
})
);
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
dispatch(DRAWER({ right: false, bottom: false, content: null }));
};
const isProvinceOperator = getRoleFromUrl() === "ProvinceOperator";
return (
<Grid
className="slaughter-new-request-form"
container
gap={SPACING.SMALL}
direction="column"
flex="1"
justifyContent="space-between"
>
<Grid container direction="column" gap={SPACING.SMALL}>
{!selectedSubUser?.key && (
<FormControl
fullWidth
error={
formik.errors.selectedKillhouse &&
formik.touched.selectedKillhouse
}
>
<InputLabel>
{selectedSubUser?.killer ? "کشتارکن" : "محل کشتار"} را انتخاب کنید
</InputLabel>
<Select
label="محل کشتار را انتخاب کنید"
id="selectedKillhouse"
name="selectedKillhouse"
value={formik.values.selectedKillhouse}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
>
{profile?.killHouse?.map((option) => (
<MenuItem key={option.key} value={option.key}>
{option.name}
</MenuItem>
))}
</Select>
{formik.errors.selectedKillhouse &&
formik.touched.selectedKillhouse && (
<FormHelperText>
{formik.errors.selectedKillhouse}
</FormHelperText>
)}
</FormControl>
)}
{selectedSubUser?.killer && (
<FormControl
fullWidth
error={
formik.errors.selectedKillerKillhouse &&
formik.touched.selectedKillerKillhouse
}
>
<InputLabel>محل کشتار را انتخاب کنید</InputLabel>
<Select
label="محل کشتار را انتخاب کنید"
id="selectedKillerKillhouse"
name="selectedKillerKillhouse"
value={formik.values.selectedKillerKillhouse}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
>
{slaughterGetKillerKillhouses?.map((option) => (
<MenuItem key={option.key} value={option.key}>
{option.name}
</MenuItem>
))}
</Select>
{formik.errors.selectedKillerKillhouse &&
formik.touched.selectedKillerKillhouse && (
<FormHelperText>
{formik.errors.selectedKillerKillhouse}
</FormHelperText>
)}
</FormControl>
)}
<Grid>
<NumberInput
allowLeadingZeros
thousandSeparator=","
id="capacity"
fullWidth
label="حجم کشتار را در روز به قطعه وارد کنید"
variant="outlined"
value={formik.values.capacity}
error={
formik.touched.capacity ? Boolean(formik.errors.capacity) : null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.capacity && Boolean(formik.errors.capacity)
? formik.errors.capacity
: null
}
/>
</Grid>
<Grid>
<FormControl fullWidth sx={{ minWidth: 210 }}>
<InputLabel>بازه زمانی دریافت مرغ مرغدار</InputLabel>
<Select
id="recieveTime"
value={formik.values.recieveTime}
label="بازه زمانی دریافت مرغ مرغدار"
onChange={(e) => {
formik.setFieldValue("recieveTime", e.target.value);
}}
>
{TIME_RANGES.map((range) => (
<MenuItem key={range} value={range}>
{range}
</MenuItem>
))}
</Select>
</FormControl>
</Grid>
<Grid>
<DatePicker
label="تاریخ کشتار"
id="recieveDate"
renderInput={(params) => <TextField {...params} />}
value={formik.values.recieveDate}
error={
formik.touched.recieveDate
? Boolean(formik.errors.recieveDate)
: null
}
onChange={(e) => {
formik.setFieldValue(
"recieveDate",
moment(e).format("YYYY-MM-DD hh:mm:ss")
);
}}
onBlur={formik.handleBlur}
helperText={
formik.touched.recieveDate && Boolean(formik.errors.recieveDate)
? formik.errors.recieveDate
: null
}
/>
</Grid>
</Grid>
<FormControlLabel
control={
<Checkbox
checked={checkedBreedAndWeight}
onChange={(e) => setCheckedBreedAndWeight(e.target.checked)}
color="primary"
/>
}
label="تعیین نژاد/وزن مرغ"
/>
{checkedBreedAndWeight && (
<Grid container direction="column" gap={SPACING.TINY}>
<FormControl fullWidth>
<InputLabel id="race-select-label">نژاد مرغ</InputLabel>
<Select
labelId="race-select-label"
id="race"
label="نژاد مرغ"
value={formik.values.race}
error={formik.touched.race ? Boolean(formik.errors.race) : null}
onChange={(e) => {
formik.setFieldValue("race", e.target.value);
}}
onBlur={formik.handleBlur}
>
{CHICKEN_BREEDS.map((breed) => (
<MenuItem key={breed} value={breed}>
{breed}
</MenuItem>
))}
</Select>
<FormHelperText>
{formik.touched.race && Boolean(formik.errors.race)
? formik.errors.race
: null}
</FormHelperText>
</FormControl>
<Grid>
<NumberInput
allowLeadingZeros
thousandSeparator=","
id="indexWeight"
fullWidth
label="وزن مرغ"
variant="outlined"
value={formik.values.indexWeight}
error={
formik.touched.indexWeight
? Boolean(formik.errors.indexWeight)
: null
}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
helperText={
formik.touched.indexWeight && Boolean(formik.errors.indexWeight)
? formik.errors.indexWeight
: null
}
/>
</Grid>
</Grid>
)}
<Grid>
{!isProvinceOperator && (
<DialogAlert
title="تعهد نامه"
content={dialogContent}
actions={
<Grid container justifyContent="end" gap={SPACING.TINY}>
<Button
variant="outlined"
color="error"
onClick={() => formik.setFieldValue("isAccepted", false)}
>
رد
</Button>
<Button
variant="contained"
color="success"
onClick={() => formik.setFieldValue("isAccepted", true)}
>
موافقم
</Button>
</Grid>
}
btnTitle="با تعهد نامه موافق هستم!"
isAccepted={formik.values.isAccepted}
/>
)}
<Grid>
<FormControlLabel
style={{ fontSize: "10px" }}
control={
<Checkbox
checked={checkedSms}
onChange={(e) => setCheckedSms(e.target.checked)}
/>
}
label={
<Tooltip title={SMS_COST} arrow>
<Typography variant="caption">
مایل به دریافت پیامک اطلاع رسانی هستم!
</Typography>
</Tooltip>
}
/>
</Grid>
</Grid>
<Grid container mt={SPACING.SMALL} justifyContent="center">
<Button
fullWidth
variant="contained"
disabled={!formik.isValid}
size="large"
onClick={handleSubmit}
>
ثبت درخواست
</Button>
</Grid>
</Grid>
);
};

View File

@@ -0,0 +1,121 @@
import {
Box,
Button,
Typography,
Stack,
Divider,
TextField,
} from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { format } from "date-fns-jalali";
export const SlaughterRecieptBackModal = ({ handleSubmit, item }) => {
const validationSchema = Yup.object({
message: Yup.string().required("پیام الزامی است"),
});
const formik = useFormik({
initialValues: {
message: "",
},
validationSchema,
onSubmit: (values) => {
handleSubmit(values);
},
});
return (
<Grid container gap={SPACING.SMALL} direction="column">
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ p: 2, minWidth: 300 }}
>
<Grid container spacing={2} sx={{ mb: 3 }}>
<Grid item xs={6}>
<Stack spacing={1}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">کدبار:</Typography>
<Typography variant="body2">{item?.barCode || "-"}</Typography>
</Stack>
<Stack direction="row" spacing={1}>
<Typography variant="body2">تاریخ کشتار:</Typography>
<Typography variant="body2">
{item?.poultryRequest.sendDate
? format(
new Date(item?.poultryRequest.sendDate),
"yyyy/MM/dd"
)
: "-"}
</Typography>
</Stack>
<Stack direction="row" spacing={1}>
<Typography variant="body2">خریدار:</Typography>
<Typography variant="body2">{`${item.killhouseUser?.name}(${item.killhouseUser?.killHouseOperator?.user?.mobile})`}</Typography>
</Stack>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack spacing={1}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">مرغدار:</Typography>
<Typography variant="body2">{`${item.poultryRequest?.poultry?.unitName}`}</Typography>
</Stack>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2"> کد سفارش:</Typography>
<Typography variant="body2">
{item?.poultryRequest.orderCode}
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">تعداد اولیه:</Typography>
<Typography variant="body2">
{item.quantity?.toLocaleString()} (قطعه)
</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">وزن :</Typography>
<Typography variant="body2">
{item?.weightInfo?.weight?.toLocaleString()} (کیلوگرم)
</Typography>
</Stack>
</Grid>
</Grid>
<Divider sx={{ mt: 1, mb: 2 }} />
<TextField
name="message"
label="پیام (اجباری)"
multiline
rows={4}
value={formik.values.message}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.message && Boolean(formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
variant="outlined"
fullWidth
sx={{ mb: 2 }}
/>
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
ثبت
</Button>
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,152 @@
import {
Box,
Button,
FormControlLabel,
Radio,
RadioGroup,
TextField,
FormHelperText,
FormControl,
FormLabel,
Typography,
Stack,
Divider,
} from "@mui/material";
import { useFormik } from "formik";
import * as Yup from "yup";
import { SPACING } from "../../../../data/spacing";
import { Grid } from "../../../../components/grid/Grid";
import { format } from "date-fns-jalali";
export const SlaughterNoneRecieptModal = ({ handleSubmit, item }) => {
const validationSchema = Yup.object({
state: Yup.string().required("لطفا یک گزینه را انتخاب کنید"),
message: Yup.string().required("پیام الزامی است"),
});
const formik = useFormik({
initialValues: {
state: "accepted",
message: "",
},
validationSchema,
onSubmit: (values) => {
handleSubmit(values);
},
});
return (
<Grid container gap={SPACING.SMALL} direction="column">
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ p: 2, minWidth: 300 }}
>
<Grid container spacing={2} sx={{ mb: 3 }}>
<Grid item xs={6}>
<Stack spacing={1}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">کدبار:</Typography>
<Typography>{item?.barCode || "-"}</Typography>
</Stack>
<Stack direction="row" spacing={1}>
<Typography variant="body2">تاریخ کشتار:</Typography>
<Typography>
{item?.poultryRequest.sendDate
? format(
new Date(item?.poultryRequest.sendDate),
"yyyy/MM/dd"
)
: "-"}
</Typography>
</Stack>
<Stack spacing={1}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">مرغدار:</Typography>
<Typography>{`${item.poultryRequest?.poultry?.unitName}`}</Typography>
</Stack>
</Stack>
</Stack>
</Grid>
<Grid item xs={6}>
<Stack direction="row" spacing={2}>
<Typography variant="body2">خریدار:</Typography>
<Typography>{`${item.killhouseUser?.name} (${item.killhouseUser?.killHouseOperator?.user?.mobile})`}</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2"> کد سفارش:</Typography>
<Typography>{item?.poultryRequest.orderCode}</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">تعداد اولیه:</Typography>
<Typography>{item.quantity?.toLocaleString()} (قطعه)</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography variant="body2">وزن :</Typography>
<Typography>
{item?.weightInfo?.weight?.toLocaleString()} (کیلوگرم)
</Typography>
</Stack>
</Grid>
</Grid>
<Divider sx={{ mt: 1, mb: 2 }} />
<FormControl
component="fieldset"
error={formik.touched.state && Boolean(formik.errors.state)}
fullWidth
sx={{ mb: 2 }}
>
<FormLabel component="legend">وضعیت</FormLabel>
<RadioGroup
row
name="state"
value={formik.values.state}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
>
<FormControlLabel
value="accepted"
control={<Radio />}
label="تایید"
/>
<FormControlLabel value="rejected" control={<Radio />} label="رد" />
</RadioGroup>
{formik.touched.state && formik.errors.state && (
<FormHelperText>{formik.errors.state}</FormHelperText>
)}
</FormControl>
<TextField
name="message"
label="پیام (اجباری)"
multiline
rows={4}
value={formik.values.message}
onChange={formik.handleChange}
onBlur={formik.handleBlur}
error={formik.touched.message && Boolean(formik.errors.message)}
helperText={formik.touched.message && formik.errors.message}
variant="outlined"
fullWidth
sx={{ mb: 2 }}
/>
<Button
fullWidth
variant="contained"
color="primary"
type="submit"
disabled={!formik.isValid || formik.isSubmitting}
>
ثبت
</Button>
</Box>
</Grid>
);
};

View File

@@ -0,0 +1,193 @@
import { useContext, useState } from "react";
import { Grid } from "../../../../components/grid/Grid";
import {
Button,
IconButton,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import TuneIcon from "@mui/icons-material/Tune";
import ReceiptLongIcon from "@mui/icons-material/ReceiptLong";
import { CLOSE_MODAL, OPEN_MODAL } from "../../../../lib/redux/slices/appSlice";
import { useDispatch } from "react-redux";
import { slaughterNoneRecieptService } from "../../services/slaughter-none-reciept-operation";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { AppContext } from "../../../../contexts/AppContext";
import { SlaughterNoneRecieptModal } from "../slaughter-none-reciept-modal/SlaughterNoneRecieptModal";
import RestoreIcon from "@mui/icons-material/Restore";
import { SlaughterRecieptBackModal } from "../slaughter-none-reciept-back-modal/SlaughterRecieptBackModal";
import { slaughterNoneBackRecieptService } from "../../services/slaughter-none-back-reciept";
export const SlaughterNoneRecieptOperation = ({ item, updateTable }) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
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 handleSubmit = (values) => {
dispatch(
slaughterNoneRecieptService({
key: item?.key,
role: getRoleFromUrl(),
state: values.state,
message: values.message,
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
handleClose();
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
};
const handleBackSubmit = (values) => {
dispatch(
slaughterNoneBackRecieptService({
key: item?.key,
non_receipt: false,
non_receipt_return: true,
non_receipt_return_message: values.message,
})
).then((r) => {
dispatch(CLOSE_MODAL());
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.error,
severity: "error",
});
} else {
handleClose();
updateTable();
openNotif({
vertical: "top",
horizontal: "center",
msg: "عملیات با موفقیت انجام شد.",
severity: "success",
});
}
});
};
return (
<Grid>
<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}
>
<Grid
style={{ padding: "10px", width: "200px" }}
container
direction="column"
alignItems="flex-start"
>
{["ProvinceOperator", "SuperAdmin", "AdminX"].includes(
getRoleFromUrl()
) && (
<Tooltip
title={"تایید / رد عدم وصول کشتارگاه"}
placement="left-start"
>
<Button
// variant="outlined"
disabled={item?.nonReceiptState !== "pending"}
size="small"
color="primary"
startIcon={<ReceiptLongIcon fontSize="small" />}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "تایید / رد عدم وصول کشتارگاه",
content: (
<SlaughterNoneRecieptModal
handleSubmit={handleSubmit}
item={item}
/>
),
})
);
}}
>
<Typography variant="body2" textAlign="left" fontWeight={600}>
تایید / رد عدم وصول کشتارگاه
</Typography>
</Button>
</Tooltip>
)}
<Tooltip title={"برگشت عدم وصول"} placement="left-start">
<Button
// variant="outlined"
disabled={item?.registrationCode}
size="small"
color="error"
startIcon={<RestoreIcon fontSize="small" />}
onClick={() => {
handleClose();
dispatch(
OPEN_MODAL({
title: "برگشت عدم وصول",
content: (
<SlaughterRecieptBackModal
handleSubmit={handleBackSubmit}
item={item}
/>
),
})
);
}}
>
<Typography variant="body2" textAlign="left" fontWeight={600}>
برگشت عدم وصول
</Typography>
</Button>
</Tooltip>
</Grid>
</Popover>
</Grid>
);
};

View File

@@ -0,0 +1,36 @@
import { Grid } from "../../../../components/grid/Grid";
import { SPACING } from "../../../../data/spacing";
import { NavLink } from "../../../../components/nav-link/NavLink";
import { useLocation } from "react-router-dom";
import { Button } from "@mui/material";
import { ROUTE_SLAUGHTER_CAR_MANAGEMENT } from "../../../../routes/routes";
export const SlaughterOperations = () => {
const { pathname } = useLocation();
return (
<Grid
container
gap={SPACING.SMALL}
p={SPACING.SMALL}
direction={{ xs: "column", md: "row" }}
>
{/* <NavLink
to={ROUTE_SLAUGHTER_ADD_CAR}
active={pathname === ROUTE_SLAUGHTER_ADD_CAR ? "true" : null}
>
<Button variant="text" color="inherit">
ثبت خودرو جدید
</Button>
</NavLink> */}
<NavLink
to={ROUTE_SLAUGHTER_CAR_MANAGEMENT}
active={pathname === ROUTE_SLAUGHTER_CAR_MANAGEMENT ? "true" : null}
>
<Button variant="text" color="inherit">
مدیریت خودروها
</Button>
</NavLink>
</Grid>
);
};

View File

@@ -0,0 +1,304 @@
import React, { useEffect, useState, useCallback } from "react";
import {
Button,
Checkbox,
FormControlLabel,
IconButton,
TextField,
Tooltip,
} from "@mui/material";
import { DatePicker } from "@mui/x-date-pickers";
import moment from "moment";
import { useDispatch, useSelector } from "react-redux";
import axios from "axios";
import {
LOADING_END,
LOADING_START,
OPEN_MODAL,
} from "../../../../lib/redux/slices/appSlice";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import ResponsiveTable from "../../../../components/responsive-table/ResponsiveTable";
import { Grid } from "../../../../components/grid/Grid";
import VisibilityIcon from "@mui/icons-material/Visibility";
import { formatJustDate } from "../../../../utils/formatTime";
import { RiSearchLine } from "react-icons/ri";
import { checkPathStartsWith } from "../../../../utils/checkPathStartsWith";
export const SlaughterOrders = () => {
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 selectedSubUser = useSelector(
(state) => state.userSlice.selectedSubUser
);
const handleTextChange = (event) => {
setTextValue(event.target.value);
};
const [withDate, setWithDate] = useState(true);
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 fetchApiData = useCallback(
async (page) => {
dispatch(LOADING_START());
const response = await axios.get(
`orders_for_kill_house/?search=filter&value=${textValue}&role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${page}&page_size=${perPage}`
);
dispatch(LOADING_END());
setData(response.data.results);
setTotalRows(response.data.count);
},
[
textValue,
withDate,
selectedDate1,
selectedDate2,
perPage,
setData,
setTotalRows,
page,
dispatch,
selectedSubUser?.key,
]
);
const handlePageChange = (page) => {
fetchApiData(page);
setPage(page);
};
const handlePerRowsChange = (perRows) => {
setPerPage(perRows);
setPage(1);
};
useEffect(() => {
const d = data?.map((item, i) => {
return [
page === 1 ? i + 1 : i + perPage * (page - 1) + 1,
item?.orderCode,
formatJustDate(item?.date),
item?.customerName,
item?.customerMobile,
item?.customerCity,
item?.productType,
item?.quantity?.toLocaleString(),
item?.weight?.toLocaleString(),
item?.status,
item?.deliveryDate ? formatJustDate(item?.deliveryDate) : "-",
<Tooltip title="مشاهده جزئیات" key={i}>
<IconButton
size={"small"}
color="primary"
onClick={() => {
dispatch(
OPEN_MODAL({
title: "جزئیات سفارش",
content: (
<Grid container gap={2}>
<Grid xs={12}>
<strong>کد سفارش:</strong> {item?.orderCode}
</Grid>
<Grid xs={12}>
<strong>مشتری:</strong> {item?.customerName}
</Grid>
<Grid xs={12}>
<strong>تلفن:</strong> {item?.customerMobile}
</Grid>
<Grid xs={12}>
<strong>شهر:</strong> {item?.customerCity}
</Grid>
<Grid xs={12}>
<strong>نوع محصول:</strong> {item?.productType}
</Grid>
<Grid xs={12}>
<strong>حجم:</strong> {item?.quantity?.toLocaleString()}{" "}
قطعه
</Grid>
<Grid xs={12}>
<strong>وزن:</strong> {item?.weight?.toLocaleString()}{" "}
کیلوگرم
</Grid>
<Grid xs={12}>
<strong>وضعیت:</strong> {item?.status}
</Grid>
<Grid xs={12}>
<strong>تاریخ ثبت:</strong> {formatJustDate(item?.date)}
</Grid>
{item?.deliveryDate && (
<Grid xs={12}>
<strong>تاریخ تحویل:</strong>{" "}
{formatJustDate(item?.deliveryDate)}
</Grid>
)}
</Grid>
),
})
);
}}
>
<VisibilityIcon fontSize="small" />
</IconButton>
</Tooltip>,
];
});
setTableData(d);
}, [data]);
useEffect(() => {
fetchApiData(1);
}, [selectedSubUser?.key]);
const handleSubmit = async (event) => {
event.preventDefault();
dispatch(LOADING_START());
try {
const response = await axios.get(
`orders_for_kill_house/?role=${getRoleFromUrl()}${
checkPathStartsWith("slaughter")
? `&role_key=${selectedSubUser?.key}`
: ""
}&search=filter&value=${textValue}${
withDate ? `&date1=${selectedDate1}&date2=${selectedDate2}` : ``
}&page=${1}&page_size=${perPage}`
);
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}
>
<Grid
container
style={{
borderStyle: "solid",
borderWidth: "1px",
padding: "10px",
borderRadius: "15px",
borderColor: "gray",
justifyContent: "left",
}}
>
<Grid>
<FormControlLabel
control={
<Checkbox
checked={withDate}
onChange={() => setWithDate(!withDate)}
color="primary"
/>
}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="از تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate1}
onChange={(e) => {
setSelectedDate1(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
<Grid>
<DatePicker
disabled={!withDate}
label="تا تاریخ"
id="date"
renderInput={(params) => (
<TextField
size="small"
style={{ width: "160px" }}
{...params}
/>
)}
value={selectedDate2}
onChange={(e) => {
setSelectedDate2(moment(e).format("YYYY-MM-DD"));
}}
/>
</Grid>
</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>
</Grid>
<ResponsiveTable
data={tableData}
columns={[
"ردیف",
"کد سفارش",
"تاریخ ثبت",
"نام مشتری",
"شماره تماس",
"شهر",
"نوع محصول",
"حجم (قطعه)",
"وزن (کیلوگرم)",
"وضعیت",
"تاریخ تحویل",
"عملیات",
]}
handlePageChange={handlePageChange}
totalRows={totalRows}
page={page}
perPage={perPage}
handlePerRowsChange={handlePerRowsChange}
title="سفارشات کشتارگاه"
/>
</Grid>
);
};

View File

@@ -0,0 +1,6 @@
import React from "react";
import { SlaughterFreeBuyBars } from "../slaughter-free-buy-bars/SlaughterFreeBuyBars";
export const SlaughterOutProvinceBars = () => {
return <SlaughterFreeBuyBars isBarManagemen />;
};

View File

@@ -0,0 +1,101 @@
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 { slaughterEditOutOfProvinceService } from "../../services/slaughterEditOutOfProvinceService";
import { getRoleFromUrl } from "../../../../utils/getRoleFromUrl";
import { Done } from "@mui/icons-material";
export const SlaughterOutProvinceRegistrationCodeInput = ({
item,
fetchApiData,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [registrationCode, setRegistrationCode] = useState(
item?.loggedRegistrationCode || ""
);
const handleSubmit = () => {
dispatch(
slaughterEditOutOfProvinceService({
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,
quota: item?.quota,
sale_type: item?.saleType,
...(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,196 @@
import React, { useContext, useState } from "react";
import {
IconButton,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Popover,
Tooltip,
Typography,
} from "@mui/material";
import { useDispatch } from "react-redux";
import { AppContext } from "../../../../contexts/AppContext";
import { DRAWER } from "../../../../lib/redux/slices/appSlice";
import { slaughterDeleteOutOfProvinceSell } from "../../services/slaughter-delete-out-province-sell";
import { SlaughterSubmitOutProvinceSell } from "../slaughter-submit-out-province-sell/SlaughterSubmitOutProvinceSell";
import { fetchSlaughterBroadcastAndProducts } from "../../services/handle-fetch-slaughter-products";
import { slaughterResendOutProvinceRegistrationCodeService } from "../../services/slaughter-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 SlaughterOutProvinceSalesOperations = ({
item,
updateTable,
fetchApiData,
page,
}) => {
const dispatch = useDispatch();
const [openNotif] = useContext(AppContext);
const [anchorEl, setAnchorEl] = useState(null);
const openPopover = (event) => {
setAnchorEl(event.currentTarget);
};
const closePopover = () => {
setAnchorEl(null);
};
const handleEdit = () => {
closePopover();
dispatch(
DRAWER({
right: !(window.innerWidth <= 600),
bottom: window.innerWidth <= 600,
title: "ویرایش فروش خارج از استان",
content: (
<SlaughterSubmitOutProvinceSell
fetchItems={updateTable}
isEdit={true}
item={item}
/>
),
})
);
};
const handleDelete = () => {
closePopover();
dispatch(slaughterDeleteOutOfProvinceSell(item?.key)).then((r) => {
if (r.payload.error) {
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "error",
});
} else {
updateTable();
dispatch(fetchSlaughterBroadcastAndProducts());
openNotif({
vertical: "top",
horizontal: "center",
msg: r.payload.data.result,
severity: "success",
});
}
});
};
const handleResend = () => {
closePopover();
dispatch(
slaughterResendOutProvinceRegistrationCodeService({
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}
>
<List sx={{ py: 1, minWidth: 130 }}>
<Tooltip title="ویرایش" placement="left-start">
<ListItem disablePadding>
<ListItemButton onClick={handleEdit}>
<ListItemIcon sx={{ minWidth: 36 }}>
<EditIcon color="primary" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="primary">
ویرایش
</Typography>
}
/>
</ListItemButton>
</ListItem>
</Tooltip>
<Tooltip title="حذف" placement="left-start">
<ListItem disablePadding>
<ListItemButton onClick={handleDelete}>
<ListItemIcon sx={{ minWidth: 36 }}>
<DeleteIcon color="error" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="error">
حذف
</Typography>
}
/>
</ListItemButton>
</ListItem>
</Tooltip>
{item?.systemRegistrationCode &&
item?.registrationCode &&
!item?.loggedRegistrationCode && (
<Tooltip title="ارسال مجدد کد" placement="left-start">
<ListItem disablePadding>
<ListItemButton onClick={handleResend}>
<ListItemIcon sx={{ minWidth: 36 }}>
<SendIcon color="success" />
</ListItemIcon>
<ListItemText
primary={
<Typography variant="body2" color="success">
ارسال مجدد کد
</Typography>
}
/>
</ListItemButton>
</ListItem>
</Tooltip>
)}
</List>
</Popover>
</div>
);
};

Some files were not shown because too many files have changed in this diff Show More