create invoice
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
VITE_API_BASE_URL = 'https://negareh-api.dev.danakcorp.com'
|
# VITE_API_BASE_URL = 'https://negareh-api.dev.danakcorp.com'
|
||||||
# VITE_API_BASE_URL = 'http://10.86.60.88:4000'
|
VITE_API_BASE_URL = 'http://10.164.213.88:4000'
|
||||||
VITE_TOKEN_NAME = 'negareh_at'
|
VITE_TOKEN_NAME = 'negareh_at'
|
||||||
VITE_REFRESH_TOKEN_NAME = 'negareh_art'
|
VITE_REFRESH_TOKEN_NAME = 'negareh_art'
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, type FC } from "react";
|
import { useState, type FC } from "react";
|
||||||
import Button from "@/components/Button";
|
import Button from "@/components/Button";
|
||||||
import Select from "@/components/Select";
|
import Select from "@/components/Select";
|
||||||
import { TickSquare } from "iconsax-react";
|
import { TickSquare } from "iconsax-react";
|
||||||
@@ -9,13 +9,15 @@ import { extractErrorMessage } from "@/config/func";
|
|||||||
import { useFormik } from "formik";
|
import { useFormik } from "formik";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import { useGetUsers } from "@/pages/user/hooks/useUserData";
|
import { useGetUsers } from "@/pages/user/hooks/useUserData";
|
||||||
import { useGetRequestDetail } from "@/pages/requests/hooks/useRequestData";
|
|
||||||
import { useCreatePreInvoice } from "./hooks/useInvoiceData";
|
|
||||||
import type { CreatePreInvoiceType, CreatePreInvoiceItemType } from "./types/Types";
|
import type { CreatePreInvoiceType, CreatePreInvoiceItemType } from "./types/Types";
|
||||||
import InvoiceItemRow from "./components/InvoiceItemRow";
|
import InvoiceItemRow from "./components/InvoiceItemRow";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import UploadBox from "@/components/UploadBox";
|
import UploadBox from "@/components/UploadBox";
|
||||||
import Textarea from "@/components/Textarea";
|
import Textarea from "@/components/Textarea";
|
||||||
|
import DatePickerComponent from "@/components/DatePicker";
|
||||||
|
import { useCreateInvoice } from "./hooks/useInvoiceData";
|
||||||
|
import { useMultiUpload } from "../uploader/hooks/useUploader";
|
||||||
|
import moment from "moment-jalaali";
|
||||||
|
|
||||||
const createEmptyItem = (): CreatePreInvoiceItemType => ({
|
const createEmptyItem = (): CreatePreInvoiceItemType => ({
|
||||||
productId: "",
|
productId: "",
|
||||||
@@ -26,29 +28,33 @@ const createEmptyItem = (): CreatePreInvoiceItemType => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const CreateInvoice: FC = () => {
|
const CreateInvoice: FC = () => {
|
||||||
|
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const requestId = searchParams.get("requestId") ?? undefined;
|
const requestId = searchParams.get("requestId") ?? undefined;
|
||||||
|
|
||||||
const { mutate: submitInvoice, isPending } = useCreatePreInvoice();
|
const { mutate: submitInvoice, isPending } = useCreateInvoice();
|
||||||
const { data: usersData } = useGetUsers();
|
const { data: usersData } = useGetUsers();
|
||||||
const { data: requestData } = useGetRequestDetail(requestId || "");
|
const multiUpload = useMultiUpload()
|
||||||
|
|
||||||
const [items, setItems] = useState<CreatePreInvoiceItemType[]>([createEmptyItem()]);
|
const [items, setItems] = useState<CreatePreInvoiceItemType[]>([createEmptyItem()]);
|
||||||
const [, setAttachmentFiles] = useState<File[]>([]);
|
const [attachmentFiles, setAttachmentFiles] = useState<File[]>([]);
|
||||||
|
|
||||||
const formik = useFormik<Pick<CreatePreInvoiceType, "userId" | "enableTax" | "approvalDeadline" | "notes">>({
|
const formik = useFormik<Pick<CreatePreInvoiceType, "userId" | "enableTax" | "approvalDeadline" | "paymentMethod" | "notes">>({
|
||||||
initialValues: {
|
initialValues: {
|
||||||
userId: "",
|
userId: "",
|
||||||
enableTax: false,
|
enableTax: false,
|
||||||
approvalDeadline: "",
|
approvalDeadline: "",
|
||||||
|
paymentMethod: "",
|
||||||
notes: "",
|
notes: "",
|
||||||
},
|
},
|
||||||
validationSchema: Yup.object({
|
validationSchema: Yup.object({
|
||||||
userId: Yup.string().required("انتخاب مشتری اجباری است."),
|
userId: !requestId
|
||||||
approvalDeadline: Yup.string().required("نحوه پرداخت اجباری است."),
|
? Yup.string().required("انتخاب مشتری اجباری است.")
|
||||||
|
: Yup.string(),
|
||||||
|
approvalDeadline: Yup.string().required("مهلت تایید اجباری است."),
|
||||||
}),
|
}),
|
||||||
onSubmit: (values) => {
|
onSubmit: async (values) => {
|
||||||
const validItems = items.filter(
|
const validItems = items.filter(
|
||||||
(i) => i.productId && i.quantity > 0 && i.unitPrice >= 0
|
(i) => i.productId && i.quantity > 0 && i.unitPrice >= 0
|
||||||
);
|
);
|
||||||
@@ -58,9 +64,19 @@ const CreateInvoice: FC = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const attachments: { type: string; url: string }[] = [];
|
||||||
|
if (attachmentFiles.length) {
|
||||||
|
const uploadResult = await multiUpload.mutateAsync(attachmentFiles);
|
||||||
|
uploadResult?.data?.forEach((item) => {
|
||||||
|
attachments.push({
|
||||||
|
type: "uploads_attach",
|
||||||
|
url: item.url,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const params: CreatePreInvoiceType = {
|
const params: CreatePreInvoiceType = {
|
||||||
requestId,
|
...(requestId ? { requestId } : { userId: values.userId }),
|
||||||
userId: values.userId,
|
|
||||||
items: validItems.map((i) => ({
|
items: validItems.map((i) => ({
|
||||||
productId: i.productId,
|
productId: i.productId,
|
||||||
quantity: i.quantity,
|
quantity: i.quantity,
|
||||||
@@ -69,8 +85,10 @@ const CreateInvoice: FC = () => {
|
|||||||
description: i.description || "",
|
description: i.description || "",
|
||||||
})),
|
})),
|
||||||
enableTax: values.enableTax,
|
enableTax: values.enableTax,
|
||||||
approvalDeadline: values.approvalDeadline,
|
approvalDeadline: moment(values.approvalDeadline, 'jYYYY/jMM/jDD').format('YYYY-MM-DD'),
|
||||||
|
...(values.paymentMethod ? { paymentMethod: values.paymentMethod } : {}),
|
||||||
notes: values.notes,
|
notes: values.notes,
|
||||||
|
...(attachments.length ? { attachments } : {}),
|
||||||
};
|
};
|
||||||
|
|
||||||
submitInvoice(params, {
|
submitInvoice(params, {
|
||||||
@@ -85,12 +103,6 @@ const CreateInvoice: FC = () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (requestId && requestData?.data?.user?.id) {
|
|
||||||
formik.setFieldValue("userId", String(requestData.data.user.id));
|
|
||||||
}
|
|
||||||
}, [requestId, requestData?.data?.user?.id]);
|
|
||||||
|
|
||||||
const handleItemChange = (
|
const handleItemChange = (
|
||||||
index: number,
|
index: number,
|
||||||
field: keyof CreatePreInvoiceItemType,
|
field: keyof CreatePreInvoiceItemType,
|
||||||
@@ -133,7 +145,7 @@ const CreateInvoice: FC = () => {
|
|||||||
<Button
|
<Button
|
||||||
className="w-fit px-5"
|
className="w-fit px-5"
|
||||||
onClick={() => formik.handleSubmit()}
|
onClick={() => formik.handleSubmit()}
|
||||||
isLoading={isPending}
|
isLoading={isPending || multiUpload.isPending}
|
||||||
>
|
>
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
<TickSquare size={20} color="black" />
|
<TickSquare size={20} color="black" />
|
||||||
@@ -145,22 +157,24 @@ const CreateInvoice: FC = () => {
|
|||||||
<div className="bg-white rounded-3xl p-6 mt-8">
|
<div className="bg-white rounded-3xl p-6 mt-8">
|
||||||
<div className="font-light">اطلاعات پیش فاکتور</div>
|
<div className="font-light">اطلاعات پیش فاکتور</div>
|
||||||
|
|
||||||
<div className="rowTwoInput mt-6">
|
{!requestId && (
|
||||||
<Select
|
<div className="rowTwoInput mt-6">
|
||||||
items={users.map((u) => ({
|
<Select
|
||||||
label: u.firstName && u.lastName ? `${u.firstName} ${u.lastName}` : u.phone,
|
items={users.map((u) => ({
|
||||||
value: String(u.id),
|
label: u.firstName && u.lastName ? `${u.firstName} ${u.lastName}` : u.phone,
|
||||||
}))}
|
value: String(u.id),
|
||||||
label="مشتری"
|
}))}
|
||||||
placeholder="انتخاب"
|
label="مشتری"
|
||||||
value={formik.values.userId}
|
placeholder="انتخاب"
|
||||||
onChange={(e) => formik.setFieldValue("userId", e.target.value)}
|
value={formik.values.userId}
|
||||||
onBlur={formik.handleBlur}
|
onChange={(e) => formik.setFieldValue("userId", e.target.value)}
|
||||||
error_text={
|
onBlur={formik.handleBlur}
|
||||||
formik.errors.userId && formik.touched.userId ? formik.errors.userId : ""
|
error_text={
|
||||||
}
|
formik.errors.userId && formik.touched.userId ? formik.errors.userId : ""
|
||||||
/>
|
}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
{items.map((item, index) => (
|
{items.map((item, index) => (
|
||||||
@@ -192,13 +206,15 @@ const CreateInvoice: FC = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="rowTwoInput mt-6">
|
||||||
<Textarea
|
<DatePickerComponent
|
||||||
label="نحوه پرداخت"
|
label="مهلت تایید"
|
||||||
placeholder="مثال: مبلغ ۷۰ درصد پیش پرداخت / اعتبار ۲ روز"
|
placeholder="انتخاب تاریخ"
|
||||||
value={formik.values.approvalDeadline}
|
defaultValue={formik.values.approvalDeadline || undefined}
|
||||||
onChange={(e) => formik.setFieldValue("approvalDeadline", e.target.value)}
|
onChange={(date) => {
|
||||||
onBlur={formik.handleBlur}
|
formik.setFieldValue("approvalDeadline", date);
|
||||||
|
formik.setFieldTouched("approvalDeadline", true);
|
||||||
|
}}
|
||||||
error_text={
|
error_text={
|
||||||
formik.errors.approvalDeadline && formik.touched.approvalDeadline
|
formik.errors.approvalDeadline && formik.touched.approvalDeadline
|
||||||
? formik.errors.approvalDeadline
|
? formik.errors.approvalDeadline
|
||||||
@@ -207,6 +223,22 @@ const CreateInvoice: FC = () => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6">
|
||||||
|
<Textarea
|
||||||
|
label="روش پرداخت"
|
||||||
|
name="paymentMethod"
|
||||||
|
placeholder="روش پرداخت"
|
||||||
|
value={formik.values.paymentMethod}
|
||||||
|
onChange={(e) => formik.setFieldValue("paymentMethod", e.target.value)}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
error_text={
|
||||||
|
formik.errors.paymentMethod && formik.touched.paymentMethod
|
||||||
|
? formik.errors.paymentMethod
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<Textarea
|
<Textarea
|
||||||
label="توضیحات"
|
label="توضیحات"
|
||||||
@@ -219,7 +251,7 @@ const CreateInvoice: FC = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6">
|
<div className="mt-6">
|
||||||
<UploadBox label="فایل ضمیمه" onChange={setAttachmentFiles} />
|
<UploadBox isMultiple label="فایل ضمیمه" onChange={setAttachmentFiles} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mt-6 bg-[#F5F7FC] h-12 px-4 flex justify-between items-center rounded-xl">
|
<div className="mt-6 bg-[#F5F7FC] h-12 px-4 flex justify-between items-center rounded-xl">
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
||||||
import * as api from "../service/InvoiceService";
|
import * as api from "../service/InvoiceService";
|
||||||
import type { CreatePreInvoiceType } from "../types/Types";
|
|
||||||
|
|
||||||
export const useCreatePreInvoice = () => {
|
export const useCreateInvoice = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
|
||||||
return useMutation({
|
return useMutation({
|
||||||
mutationFn: (params: CreatePreInvoiceType) => api.createPreInvoice(params),
|
mutationFn: api.createInvoice,
|
||||||
onSuccess: () => {
|
onSuccess: () => {
|
||||||
queryClient.invalidateQueries({ queryKey: ["orders-invoiced"] });
|
queryClient.invalidateQueries({ queryKey: ["orders-invoiced"] });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import axios from "@/config/axios";
|
import axios from "@/config/axios";
|
||||||
import type { CreatePreInvoiceType } from "../types/Types";
|
import type { CreatePreInvoiceType } from "../types/Types";
|
||||||
|
|
||||||
export const createPreInvoice = async (params: CreatePreInvoiceType) => {
|
export const createInvoice = async (params: CreatePreInvoiceType) => {
|
||||||
const { data } = await axios.post("/admin/pre-invoice", params);
|
const { data } = await axios.post("/admin/invoice", params);
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,11 +6,18 @@ export type CreatePreInvoiceItemType = {
|
|||||||
description: string;
|
description: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type InvoiceAttachmentType = {
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
|
||||||
export type CreatePreInvoiceType = {
|
export type CreatePreInvoiceType = {
|
||||||
requestId?: string;
|
requestId?: string;
|
||||||
userId: string;
|
userId?: string;
|
||||||
items: CreatePreInvoiceItemType[];
|
items: CreatePreInvoiceItemType[];
|
||||||
enableTax: boolean;
|
enableTax: boolean;
|
||||||
approvalDeadline: string;
|
approvalDeadline: string;
|
||||||
|
paymentMethod?: string;
|
||||||
notes?: string;
|
notes?: string;
|
||||||
|
attachments?: InvoiceAttachmentType[];
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user