mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-21 05:45:46 +00:00
refactor(US-170,174): update recording types and validation schema for daily recording form
This commit is contained in:
@@ -1,16 +1,15 @@
|
||||
import * as Yup from 'yup';
|
||||
import { RECORDING_FLAG_OPTIONS } from '@/config/constant';
|
||||
import {
|
||||
Recording,
|
||||
CreateRecordingPayload,
|
||||
CreateGrowingRecordingPayload,
|
||||
} from '@/types/api/production/recording';
|
||||
|
||||
export const RecordingFormSchema = Yup.object({
|
||||
export const RecordingGrowingFormSchema = Yup.object({
|
||||
project_flock_kandang: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
project_flock_kandang_id: Yup.number()
|
||||
project_flock_kandangs_id: Yup.number()
|
||||
.default(0)
|
||||
.typeError('Project Flock Kandang wajib diisi!')
|
||||
.test(
|
||||
@@ -22,9 +21,13 @@ export const RecordingFormSchema = Yup.object({
|
||||
.test(
|
||||
'not-already-recorded',
|
||||
'Project Flock ini sudah direcord hari ini!',
|
||||
function(value) {
|
||||
const recordedProjectFlockIds = this.options.context?.recordedProjectFlockIds as Set<number>;
|
||||
const formType = this.options.context?.type as 'add' | 'edit' | 'detail';
|
||||
function (value) {
|
||||
const recordedProjectFlockIds = this.options.context
|
||||
?.recordedProjectFlockIds as Set<number>;
|
||||
const formType = this.options.context?.type as
|
||||
| 'add'
|
||||
| 'edit'
|
||||
| 'detail';
|
||||
if (formType !== 'add') return true;
|
||||
if (value && recordedProjectFlockIds?.has(value)) {
|
||||
return false;
|
||||
@@ -35,20 +38,15 @@ export const RecordingFormSchema = Yup.object({
|
||||
body_weights: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
weight: Yup.number()
|
||||
.required('Berat ayam wajib diisi!')
|
||||
.min(1, 'Berat ayam minimal 1 gram!')
|
||||
.typeError('Berat ayam harus berupa angka!'),
|
||||
avg_weight: Yup.number()
|
||||
.required('Berat ayam rata-rata wajib diisi!')
|
||||
.min(1, 'Berat ayam rata-rata minimal 1 gram!')
|
||||
.typeError('Berat ayam rata-rata harus berupa angka!'),
|
||||
qty: Yup.number()
|
||||
.required('Jumlah ayam wajib diisi!')
|
||||
.min(1, 'Jumlah ayam minimal 1 ekor!')
|
||||
.typeError('Jumlah ayam harus berupa angka!')
|
||||
.default(1),
|
||||
average_weight: Yup.number()
|
||||
.optional()
|
||||
.min(0, 'Rata-rata berat tidak boleh negatif!')
|
||||
.typeError('Rata-rata berat harus berupa angka!')
|
||||
.default(0),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data bobot badan!')
|
||||
@@ -60,163 +58,97 @@ export const RecordingFormSchema = Yup.object({
|
||||
.required('Produk wajib diisi!')
|
||||
.min(1, 'Produk wajib diisi!')
|
||||
.typeError('Produk harus berupa angka!'),
|
||||
usage_amount: Yup.number()
|
||||
usage_qty: Yup.number()
|
||||
.required('Jumlah penggunaan wajib diisi!')
|
||||
.min(0, 'Jumlah penggunaan tidak boleh negatif!')
|
||||
.typeError('Jumlah penggunaan harus berupa angka!'),
|
||||
notes: Yup.string().optional(),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data stok!')
|
||||
.required('Data stok wajib diisi!'),
|
||||
depletions: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
total: Yup.number()
|
||||
.required('Jumlah depletions wajib diisi!')
|
||||
.min(1, 'Jumlah depletions minimal 1!')
|
||||
.typeError('Jumlah depletions harus berupa angka!'),
|
||||
notes: Yup.string()
|
||||
.required('Kondisi depletions wajib diisi!')
|
||||
.oneOf(
|
||||
RECORDING_FLAG_OPTIONS.map((option) => option.value),
|
||||
'Kondisi depletions tidak valid!'
|
||||
)
|
||||
.typeError('Kondisi depletions harus berupa teks!')
|
||||
.min(1, 'Kondisi depletions wajib diisi!'),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data depletions!')
|
||||
.required('Data depletions wajib diisi!'),
|
||||
});
|
||||
|
||||
export const UpdateRecordingFormSchema = Yup.object({
|
||||
project_flock_kandang: Yup.object({
|
||||
value: Yup.number().min(1).required(),
|
||||
label: Yup.string().required(),
|
||||
}).nullable(),
|
||||
project_flock_kandang_id: Yup.number()
|
||||
.default(0)
|
||||
.typeError('Project Flock Kandang wajib diisi!')
|
||||
.test(
|
||||
'is-valid-project-flock-kandang',
|
||||
'Project Flock Kandang wajib diisi!',
|
||||
(value) => value !== undefined && value !== null && value > 0
|
||||
)
|
||||
.required('Project Flock Kandang wajib diisi!'),
|
||||
body_weights: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
weight: Yup.number()
|
||||
.required('Berat ayam wajib diisi!')
|
||||
.min(1, 'Berat ayam minimal 1 gram!')
|
||||
.typeError('Berat ayam harus berupa angka!'),
|
||||
qty: Yup.number()
|
||||
.required('Jumlah ayam wajib diisi!')
|
||||
.min(1, 'Jumlah ayam minimal 1 ekor!')
|
||||
.typeError('Jumlah ayam harus berupa angka!')
|
||||
.default(1),
|
||||
average_weight: Yup.number()
|
||||
.optional()
|
||||
.min(0, 'Rata-rata berat tidak boleh negatif!')
|
||||
.typeError('Rata-rata berat harus berupa angka!')
|
||||
.default(0),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data bobot badan!')
|
||||
.required('Data bobot badan wajib diisi!'),
|
||||
stocks: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
product_warehouse_id: Yup.number()
|
||||
.required('Produk wajib diisi!')
|
||||
.min(1, 'Produk wajib diisi!')
|
||||
.typeError('Produk harus berupa angka!'),
|
||||
usage_amount: Yup.number()
|
||||
.required('Jumlah penggunaan wajib diisi!')
|
||||
.min(0, 'Jumlah penggunaan tidak boleh negatif!')
|
||||
.typeError('Jumlah penggunaan harus berupa angka!'),
|
||||
notes: Yup.string().optional(),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data stok!')
|
||||
.required('Data stok wajib diisi!'),
|
||||
depletions: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
total: Yup.number()
|
||||
.required('Produk depletions wajib diisi!')
|
||||
.min(1, 'Produk depletions wajib diisi!')
|
||||
.typeError('Produk depletions harus berupa angka!'),
|
||||
qty: Yup.number()
|
||||
.required('Jumlah depletions wajib diisi!')
|
||||
.min(1, 'Jumlah depletions minimal 1!')
|
||||
.typeError('Jumlah depletions harus berupa angka!'),
|
||||
notes: Yup.string()
|
||||
.required('Kondisi depletions wajib diisi!')
|
||||
.oneOf(
|
||||
RECORDING_FLAG_OPTIONS.map((option) => option.value),
|
||||
'Kondisi depletions tidak valid!'
|
||||
)
|
||||
.typeError('Kondisi depletions harus berupa teks!')
|
||||
.min(1, 'Kondisi depletions wajib diisi!'),
|
||||
})
|
||||
)
|
||||
.min(1, 'Minimal harus ada 1 data depletions!')
|
||||
.required('Data depletions wajib diisi!'),
|
||||
});
|
||||
|
||||
export type RecordingFormValues = Yup.InferType<typeof RecordingFormSchema>;
|
||||
export const UpdateRecordingGrowingFormSchema =
|
||||
RecordingGrowingFormSchema.shape({
|
||||
project_flock_kandangs_id: Yup.number()
|
||||
.default(0)
|
||||
.typeError('Project Flock Kandang wajib diisi!')
|
||||
.test(
|
||||
'is-valid-project-flock-kandang',
|
||||
'Project Flock Kandang wajib diisi!',
|
||||
(value) => value !== undefined && value !== null && value > 0
|
||||
)
|
||||
.required('Project Flock Kandang wajib diisi!'),
|
||||
});
|
||||
|
||||
export type RecordingGrowingFormValues = Yup.InferType<
|
||||
typeof RecordingGrowingFormSchema
|
||||
>;
|
||||
|
||||
type RecordingFormData = Partial<Recording> & {
|
||||
body_weights?: CreateRecordingPayload['body_weights'];
|
||||
stocks?: CreateRecordingPayload['stocks'];
|
||||
depletions?: CreateRecordingPayload['depletions'];
|
||||
body_weights?: CreateGrowingRecordingPayload['body_weights'];
|
||||
stocks?: CreateGrowingRecordingPayload['stocks'];
|
||||
depletions?: CreateGrowingRecordingPayload['depletions'];
|
||||
};
|
||||
|
||||
export const getRecordingFormInitialValues = (
|
||||
export const getRecordingGrowingFormInitialValues = (
|
||||
initialValues?: RecordingFormData
|
||||
): RecordingFormValues => ({
|
||||
project_flock_kandang: initialValues?.project_flock_kandang_id
|
||||
): RecordingGrowingFormValues => ({
|
||||
project_flock_kandang: initialValues?.project_flock_kandangs_id
|
||||
? {
|
||||
value: initialValues.project_flock_kandang_id,
|
||||
label: `Project Flock #${initialValues.project_flock_kandang_id}`,
|
||||
value: initialValues.project_flock_kandangs_id,
|
||||
label: `Project Flock #${initialValues.project_flock_kandangs_id}`,
|
||||
}
|
||||
: null,
|
||||
project_flock_kandang_id: initialValues?.project_flock_kandang_id ?? 0,
|
||||
project_flock_kandangs_id: initialValues?.project_flock_kandangs_id ?? 0,
|
||||
body_weights: initialValues?.body_weights?.map(
|
||||
(bw: NonNullable<CreateRecordingPayload['body_weights']>[0]) => ({
|
||||
weight: bw.weight,
|
||||
(bw: NonNullable<CreateGrowingRecordingPayload['body_weights']>[0]) => ({
|
||||
avg_weight: bw.avg_weight,
|
||||
qty: bw.qty,
|
||||
average_weight: bw.qty > 0 ? Math.round(bw.weight / bw.qty) : 0,
|
||||
})
|
||||
) ?? [
|
||||
{
|
||||
weight: 0,
|
||||
avg_weight: 0,
|
||||
qty: 0,
|
||||
average_weight: 0,
|
||||
},
|
||||
],
|
||||
stocks: initialValues?.stocks?.map(
|
||||
(stock: NonNullable<CreateRecordingPayload['stocks']>[0]) => ({
|
||||
(stock: NonNullable<CreateGrowingRecordingPayload['stocks']>[0]) => ({
|
||||
product_warehouse_id: stock.product_warehouse_id,
|
||||
usage_amount: stock.usage_amount,
|
||||
notes: stock.notes,
|
||||
usage_qty: stock.usage_qty,
|
||||
})
|
||||
) ?? [
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
usage_amount: 0,
|
||||
notes: '',
|
||||
usage_qty: 0,
|
||||
},
|
||||
],
|
||||
depletions: initialValues?.depletions?.map(
|
||||
(depletion: NonNullable<CreateRecordingPayload['depletions']>[0]) => ({
|
||||
(
|
||||
depletion: NonNullable<CreateGrowingRecordingPayload['depletions']>[0]
|
||||
) => ({
|
||||
product_warehouse_id: depletion.product_warehouse_id,
|
||||
total: depletion.total,
|
||||
notes: depletion.notes,
|
||||
qty: depletion.qty,
|
||||
})
|
||||
) ?? [
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
total: 0,
|
||||
notes: '',
|
||||
qty: 0,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useMemo, useState, useEffect, useCallback } from 'react';
|
||||
import { useMemo, useState, useCallback } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import useSWR from 'swr';
|
||||
import { Icon } from '@iconify/react';
|
||||
@@ -14,22 +14,21 @@ import { FormHeader } from '@/components/helper/form/FormHeader';
|
||||
import { FormActions } from '@/components/helper/form/FormActions';
|
||||
import { RecordingApi } from '@/services/api/production';
|
||||
import {
|
||||
CreateRecordingPayload,
|
||||
CreateGrowingRecordingPayload,
|
||||
Recording,
|
||||
} from '@/types/api/production/recording';
|
||||
import { type BaseApiResponse } from '@/types/api/api-general';
|
||||
import {
|
||||
RecordingFormSchema,
|
||||
RecordingFormValues,
|
||||
getRecordingFormInitialValues,
|
||||
UpdateRecordingFormSchema,
|
||||
RecordingGrowingFormSchema,
|
||||
RecordingGrowingFormValues,
|
||||
getRecordingGrowingFormInitialValues,
|
||||
UpdateRecordingGrowingFormSchema,
|
||||
} from './RecordingForm.schema';
|
||||
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
|
||||
import { ProjectFlockApi } from '@/services/api/production';
|
||||
import { LocationApi } from '@/services/api/master-data';
|
||||
import { ProductWarehouseApi } from '@/services/api/inventory';
|
||||
import { isResponseSuccess } from '@/lib/api-helper';
|
||||
import { RECORDING_FLAG_OPTIONS } from '@/config/constant';
|
||||
import { PeriodFlock } from '@/types/api/production/project-flock';
|
||||
import { useModal } from '@/components/Modal';
|
||||
import toast from 'react-hot-toast';
|
||||
@@ -47,13 +46,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const [selectedStocks, setSelectedStocks] = useState<number[]>([]);
|
||||
const [selectedDepletions, setSelectedDepletions] = useState<number[]>([]);
|
||||
|
||||
const [editingAverageIndex, setEditingAverageIndex] = useState<number | null>(
|
||||
null
|
||||
);
|
||||
const [manuallyEditedRows, setManuallyEditedRows] = useState<Set<number>>(
|
||||
new Set()
|
||||
);
|
||||
|
||||
const [locationSearchValue, setLocationSearchValue] = useState('');
|
||||
const [selectedLocation, setSelectedLocation] = useState<OptionType | null>(
|
||||
null
|
||||
@@ -132,7 +124,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const recordedIds = new Set<number>();
|
||||
|
||||
todayRecordings.forEach((recording) => {
|
||||
const recordingDate = recording.record_date?.split('T')[0];
|
||||
const recordingDate = recording.record_datetime?.split('T')[0];
|
||||
|
||||
const isRecordedToday = recordingDate === today;
|
||||
|
||||
@@ -143,7 +135,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
isResponseSuccess(projectFlocks)
|
||||
) {
|
||||
const flockIndex = projectFlocks.data.findIndex(
|
||||
(pf) => pf.id === recording.project_flock_kandang_id
|
||||
(pf) => pf.id === recording.project_flock_kandangs_id
|
||||
);
|
||||
if (
|
||||
flockIndex !== undefined &&
|
||||
@@ -165,7 +157,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
}
|
||||
|
||||
if (isRecordedToday && (isCorrectPeriod || !flockPeriodsData)) {
|
||||
recordedIds.add(recording.project_flock_kandang_id);
|
||||
recordedIds.add(recording.project_flock_kandangs_id);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -283,45 +275,36 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
confirmationModalDeleteClickHandler,
|
||||
} = useRecordingFormHandlers(initialValues?.id);
|
||||
|
||||
const formikInitialValues = useMemo<RecordingFormValues>(
|
||||
() => getRecordingFormInitialValues(initialValues),
|
||||
const formikInitialValues = useMemo<RecordingGrowingFormValues>(
|
||||
() => getRecordingGrowingFormInitialValues(initialValues),
|
||||
[initialValues]
|
||||
);
|
||||
|
||||
const formik = useFormik<RecordingFormValues>({
|
||||
const formik = useFormik<RecordingGrowingFormValues>({
|
||||
initialValues: formikInitialValues,
|
||||
validationSchema:
|
||||
type === 'edit' ? UpdateRecordingFormSchema : RecordingFormSchema,
|
||||
type === 'edit'
|
||||
? UpdateRecordingGrowingFormSchema
|
||||
: RecordingGrowingFormSchema,
|
||||
validateOnChange: true,
|
||||
validateOnBlur: true,
|
||||
onSubmit: async (values) => {
|
||||
const payload: CreateRecordingPayload = {
|
||||
project_flock_kandang_id: values.project_flock_kandang_id,
|
||||
const payload: CreateGrowingRecordingPayload = {
|
||||
project_flock_kandangs_id: values.project_flock_kandangs_id,
|
||||
body_weights: (values.body_weights ?? []).map((bw) => ({
|
||||
weight:
|
||||
typeof bw.weight === 'number'
|
||||
? bw.weight
|
||||
: parseFloat(String(bw.weight)) || 0,
|
||||
qty:
|
||||
typeof bw.qty === 'number'
|
||||
? bw.qty
|
||||
: parseFloat(String(bw.qty)) || 0,
|
||||
avg_weight:
|
||||
typeof bw.avg_weight === 'number'
|
||||
? bw.avg_weight
|
||||
: parseFloat(String(bw.avg_weight)) || 0,
|
||||
qty: bw.qty || 0,
|
||||
})),
|
||||
stocks: (values.stocks ?? []).map((stock) => ({
|
||||
product_warehouse_id: stock.product_warehouse_id,
|
||||
usage_amount:
|
||||
typeof stock.usage_amount === 'number'
|
||||
? stock.usage_amount
|
||||
: parseFloat(String(stock.usage_amount)) || 0,
|
||||
notes: stock.notes || '',
|
||||
usage_qty: stock.usage_qty || 0,
|
||||
})),
|
||||
depletions: (values.depletions ?? []).map((depletion) => ({
|
||||
product_warehouse_id: 1,
|
||||
total:
|
||||
typeof depletion.total === 'number'
|
||||
? depletion.total
|
||||
: parseFloat(String(depletion.total)) || 0,
|
||||
notes: depletion.notes,
|
||||
product_warehouse_id: depletion.product_warehouse_id,
|
||||
qty: depletion.qty || 0,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -375,7 +358,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const stock = formik.values.stocks?.[stockIdx];
|
||||
if (!stock || !stock.product_warehouse_id) return null;
|
||||
const availableStock = getAvailableStock(stock.product_warehouse_id);
|
||||
const requestedUsage = Number(stock.usage_amount) || 0;
|
||||
const requestedUsage = Number(stock.usage_qty) || 0;
|
||||
if (requestedUsage > availableStock) {
|
||||
return `Jumlah pakai melebihi stok tersedia! Maksimal: ${availableStock.toLocaleString('en-US')}`;
|
||||
}
|
||||
@@ -390,7 +373,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const stock = formik.values.stocks?.[stockIdx];
|
||||
if (!stock || !stock.product_warehouse_id) return null;
|
||||
const availableStock = getAvailableStock(stock.product_warehouse_id);
|
||||
const requestedUsage = Number(stock.usage_amount) || 0;
|
||||
const requestedUsage = Number(stock.usage_qty) || 0;
|
||||
const remainingStock = availableStock - requestedUsage;
|
||||
if (requestedUsage > 0) {
|
||||
return (
|
||||
@@ -432,7 +415,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
color={color}
|
||||
size='sm'
|
||||
className={{
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2 py-0.5',
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2',
|
||||
}}
|
||||
>
|
||||
Periode {projectFlock.period}
|
||||
@@ -461,7 +444,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
color='info'
|
||||
size='sm'
|
||||
className={{
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2 py-0.5',
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2',
|
||||
}}
|
||||
>
|
||||
PAKAN
|
||||
@@ -476,7 +459,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
color='secondary'
|
||||
size='sm'
|
||||
className={{
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2 py-0.5',
|
||||
badge: 'whitespace-nowrap font-semibold text-xs px-2',
|
||||
}}
|
||||
>
|
||||
OVK
|
||||
@@ -503,11 +486,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
>(
|
||||
arrayName: T,
|
||||
column: T extends 'body_weights'
|
||||
? keyof RecordingFormValues['body_weights'][0]
|
||||
? keyof RecordingGrowingFormValues['body_weights'][0]
|
||||
: T extends 'stocks'
|
||||
? keyof RecordingFormValues['stocks'][0]
|
||||
? keyof RecordingGrowingFormValues['stocks'][0]
|
||||
: T extends 'depletions'
|
||||
? keyof RecordingFormValues['depletions'][0]
|
||||
? keyof RecordingGrowingFormValues['depletions'][0]
|
||||
: never,
|
||||
idx: number
|
||||
) => {
|
||||
@@ -540,7 +523,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
setSelectedLocation(val as OptionType);
|
||||
formik.setFieldValue('project_flock_kandang', null);
|
||||
formik.setFieldValue('project_flock_kandang_id', 0);
|
||||
formik.setFieldValue('project_flock_kandangs_id', 0);
|
||||
};
|
||||
|
||||
const projectFlockKandangChangeHandler = (
|
||||
@@ -557,9 +540,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
|
||||
formik.setFieldTouched('project_flock_kandang', true);
|
||||
formik.setFieldValue('project_flock_kandang', val);
|
||||
formik.setFieldTouched('project_flock_kandang_id', true);
|
||||
formik.setFieldTouched('project_flock_kandangs_id', true);
|
||||
formik.setFieldValue(
|
||||
'project_flock_kandang_id',
|
||||
'project_flock_kandangs_id',
|
||||
(val as OptionType)?.value || 0
|
||||
);
|
||||
};
|
||||
@@ -629,81 +612,25 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const newBodyWeights = [
|
||||
...(formik.values.body_weights || []),
|
||||
{
|
||||
weight: 0,
|
||||
avg_weight: 0,
|
||||
qty: 1,
|
||||
average_weight: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('body_weights', newBodyWeights);
|
||||
};
|
||||
|
||||
const handleWeightChange = (idx: number, value: number) => {
|
||||
formik.setFieldValue(`body_weights.${idx}.weight`, value);
|
||||
|
||||
setManuallyEditedRows((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(idx);
|
||||
return newSet;
|
||||
});
|
||||
|
||||
const currentWeight = formik.values.body_weights?.[idx];
|
||||
if (currentWeight) {
|
||||
const qty = currentWeight.qty;
|
||||
if (qty > 0 && value > 0) {
|
||||
const averageWeight = parseFloat((value / qty).toFixed(2));
|
||||
formik.setFieldValue(
|
||||
`body_weights.${idx}.average_weight`,
|
||||
averageWeight
|
||||
);
|
||||
} else {
|
||||
formik.setFieldValue(`body_weights.${idx}.average_weight`, 0);
|
||||
}
|
||||
}
|
||||
const handleAvgWeightChange = (idx: number, value: number) => {
|
||||
formik.setFieldValue(`body_weights.${idx}.avg_weight`, value);
|
||||
};
|
||||
|
||||
const handleQtyChange = (idx: number, value: number) => {
|
||||
formik.setFieldValue(`body_weights.${idx}.qty`, value);
|
||||
|
||||
setManuallyEditedRows((prev) => {
|
||||
const newSet = new Set(prev);
|
||||
newSet.delete(idx);
|
||||
return newSet;
|
||||
});
|
||||
|
||||
const currentWeight = formik.values.body_weights?.[idx];
|
||||
if (currentWeight) {
|
||||
const weight = currentWeight.weight;
|
||||
if (value > 0 && weight > 0) {
|
||||
const averageWeight = parseFloat((weight / value).toFixed(2));
|
||||
formik.setFieldValue(
|
||||
`body_weights.${idx}.average_weight`,
|
||||
averageWeight
|
||||
);
|
||||
} else {
|
||||
formik.setFieldValue(`body_weights.${idx}.average_weight`, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleAverageWeightChange = (idx: number, value: number) => {
|
||||
formik.setFieldValue(`body_weights.${idx}.average_weight`, value);
|
||||
|
||||
const currentWeight = formik.values.body_weights?.[idx];
|
||||
if (currentWeight) {
|
||||
const qty = currentWeight.qty;
|
||||
if (qty > 0 && value > 0) {
|
||||
const totalWeight = value * qty;
|
||||
formik.setFieldValue(`body_weights.${idx}.weight`, totalWeight);
|
||||
} else {
|
||||
formik.setFieldValue(`body_weights.${idx}.weight`, 0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const handleWeightChangeWrapper =
|
||||
const handleAvgWeightChangeWrapper =
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = parseFloat(e.target.value) || 0;
|
||||
handleWeightChange(idx, value);
|
||||
handleAvgWeightChange(idx, value);
|
||||
};
|
||||
|
||||
const handleQtyChangeWrapper =
|
||||
@@ -712,19 +639,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
handleQtyChange(idx, value);
|
||||
};
|
||||
|
||||
const handleAverageWeightChangeWrapper =
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setEditingAverageIndex(idx);
|
||||
setManuallyEditedRows((prev) => new Set(prev).add(idx));
|
||||
|
||||
const value = parseFloat(e.target.value) || 0;
|
||||
handleAverageWeightChange(idx, value);
|
||||
};
|
||||
|
||||
const handleAverageWeightBlur = (idx: number) => {
|
||||
setEditingAverageIndex(null);
|
||||
};
|
||||
|
||||
const removeBodyWeight = (idx: number) => {
|
||||
const updatedBodyWeights = formik.values.body_weights?.filter(
|
||||
(_, i) => i !== idx
|
||||
@@ -746,17 +660,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
...(formik.values.stocks || []),
|
||||
{
|
||||
product_warehouse_id: 0,
|
||||
usage_amount: 0,
|
||||
notes: '',
|
||||
usage_qty: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('stocks', newStocks);
|
||||
};
|
||||
|
||||
const handleStockUsageAmountChangeWrapper = useCallback(
|
||||
const handleStockUsageQtyChangeWrapper = useCallback(
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = parseFloat(e.target.value) || 0;
|
||||
formik.setFieldValue(`stocks.${idx}.usage_amount`, value);
|
||||
formik.setFieldValue(`stocks.${idx}.usage_qty`, value);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
@@ -779,17 +692,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
const newDepletions = [
|
||||
...(formik.values.depletions || []),
|
||||
{
|
||||
total: 0,
|
||||
notes: '',
|
||||
product_warehouse_id: 0,
|
||||
qty: 0,
|
||||
},
|
||||
];
|
||||
formik.setFieldValue('depletions', newDepletions);
|
||||
};
|
||||
|
||||
const handleDepletionTotalChangeWrapper = useCallback(
|
||||
const handleDepletionQtyChangeWrapper = useCallback(
|
||||
(idx: number) => (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = parseFloat(e.target.value) || 0;
|
||||
formik.setFieldValue(`depletions.${idx}.total`, value);
|
||||
formik.setFieldValue(`depletions.${idx}.qty`, value);
|
||||
},
|
||||
[formik]
|
||||
);
|
||||
@@ -809,41 +722,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
setSelectedDepletions([]);
|
||||
};
|
||||
|
||||
// ===== EFFECTS =====
|
||||
useEffect(() => {
|
||||
if (formik.values.body_weights && editingAverageIndex === null) {
|
||||
const updatedBodyWeights = formik.values.body_weights.map(
|
||||
(weight, idx) => {
|
||||
if (idx === editingAverageIndex || manuallyEditedRows.has(idx)) {
|
||||
return weight;
|
||||
}
|
||||
return {
|
||||
...weight,
|
||||
average_weight:
|
||||
weight.qty > 0 && weight.weight > 0
|
||||
? parseFloat((weight.weight / weight.qty).toFixed(2))
|
||||
: 0,
|
||||
};
|
||||
}
|
||||
);
|
||||
const hasChanges = updatedBodyWeights.some(
|
||||
(updated, idx) =>
|
||||
idx !== editingAverageIndex &&
|
||||
!manuallyEditedRows.has(idx) &&
|
||||
updated.average_weight !==
|
||||
(formik.values.body_weights[idx]?.average_weight || 0)
|
||||
);
|
||||
if (hasChanges) {
|
||||
formik.setFieldValue('body_weights', updatedBodyWeights, false);
|
||||
}
|
||||
}
|
||||
}, [
|
||||
formik.values.body_weights?.map((w) => w.weight),
|
||||
formik.values.body_weights?.map((w) => w.qty),
|
||||
editingAverageIndex,
|
||||
manuallyEditedRows,
|
||||
]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='w-full'>
|
||||
@@ -889,6 +767,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<div>
|
||||
<SelectInput
|
||||
required
|
||||
key={`project-flock-${formik.values.project_flock_kandangs_id}`}
|
||||
label='Project Flock'
|
||||
value={formik.values.project_flock_kandang ?? undefined}
|
||||
onChange={projectFlockKandangChangeHandler}
|
||||
@@ -896,11 +775,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
onInputChange={setProjectFlockSearchValue}
|
||||
isLoading={isLoadingProjectFlocks}
|
||||
isError={
|
||||
formik.touched.project_flock_kandang_id &&
|
||||
Boolean(formik.errors.project_flock_kandang_id)
|
||||
formik.touched.project_flock_kandangs_id &&
|
||||
Boolean(formik.errors.project_flock_kandangs_id)
|
||||
}
|
||||
errorMessage={
|
||||
formik.errors.project_flock_kandang_id as string
|
||||
formik.errors.project_flock_kandangs_id as string
|
||||
}
|
||||
isDisabled={type === 'detail' || !selectedLocation}
|
||||
placeholder={
|
||||
@@ -911,9 +790,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
isClearable
|
||||
isSearchable
|
||||
startAdornment={
|
||||
formik.values.project_flock_kandang_id
|
||||
formik.values.project_flock_kandangs_id
|
||||
? getProjectFlockBadgeAdornment(
|
||||
formik.values.project_flock_kandang_id
|
||||
formik.values.project_flock_kandangs_id
|
||||
)
|
||||
: undefined
|
||||
}
|
||||
@@ -964,7 +843,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
</th>
|
||||
)}
|
||||
<th>
|
||||
Berat Ayam (gram)
|
||||
Rata-rata Berat Ayam (gram)
|
||||
<span
|
||||
className='tooltip tooltip-error tooltip-bottom z-[9999]'
|
||||
data-tip='required'
|
||||
@@ -981,19 +860,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<span className='text-error'>*</span>
|
||||
</span>
|
||||
</th>
|
||||
<th>
|
||||
Rata-rata Berat Ayam (gram)
|
||||
<span
|
||||
className='tooltip tooltip-info tooltip-bottom z-[9999]'
|
||||
data-tip='Otomatis dihitung: Total Berat ÷ Jumlah Ayam'
|
||||
>
|
||||
<Icon
|
||||
icon='material-symbols:info-outline'
|
||||
width={16}
|
||||
height={16}
|
||||
/>
|
||||
</span>
|
||||
</th>
|
||||
{type !== 'detail' && <th>Action</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -1029,9 +895,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<td>
|
||||
<NumberInput
|
||||
required
|
||||
name={`body_weights.${idx}.weight`}
|
||||
value={bw.weight}
|
||||
onChange={handleWeightChangeWrapper(idx)}
|
||||
name={`body_weights.${idx}.avg_weight`}
|
||||
value={bw.avg_weight}
|
||||
onChange={handleAvgWeightChangeWrapper(idx)}
|
||||
onBlur={formik.handleBlur}
|
||||
decimalScale={2}
|
||||
allowNegative={false}
|
||||
@@ -1039,12 +905,18 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
decimalSeparator='.'
|
||||
inputSuffix='gram'
|
||||
isError={
|
||||
isRepeaterInputError('body_weights', 'weight', idx)
|
||||
.isError
|
||||
isRepeaterInputError(
|
||||
'body_weights',
|
||||
'avg_weight',
|
||||
idx
|
||||
).isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError('body_weights', 'weight', idx)
|
||||
.errorMessage
|
||||
isRepeaterInputError(
|
||||
'body_weights',
|
||||
'avg_weight',
|
||||
idx
|
||||
).errorMessage
|
||||
}
|
||||
readOnly={type === 'detail'}
|
||||
className={{
|
||||
@@ -1077,43 +949,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<NumberInput
|
||||
name={`body_weights.${idx}.average_weight`}
|
||||
value={bw.average_weight || 0}
|
||||
onChange={handleAverageWeightChangeWrapper(idx)}
|
||||
onBlur={(e) => {
|
||||
handleAverageWeightBlur(idx);
|
||||
formik.handleBlur(e);
|
||||
}}
|
||||
decimalScale={2}
|
||||
allowNegative={false}
|
||||
thousandSeparator=','
|
||||
decimalSeparator='.'
|
||||
inputSuffix='gram'
|
||||
isError={
|
||||
isRepeaterInputError(
|
||||
'body_weights',
|
||||
'average_weight',
|
||||
idx
|
||||
).isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError(
|
||||
'body_weights',
|
||||
'average_weight',
|
||||
idx
|
||||
).errorMessage
|
||||
}
|
||||
readOnly={type === 'detail'}
|
||||
className={{
|
||||
wrapper: 'w-full min-w-32',
|
||||
}}
|
||||
/>
|
||||
</td>
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<div className='flex justify-center'>
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
type='button'
|
||||
color='error'
|
||||
@@ -1249,6 +1087,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<td>
|
||||
<SelectInput
|
||||
required
|
||||
key={`stock-product-${idx}-${stock.product_warehouse_id}`}
|
||||
value={
|
||||
unifiedStockProducts.find(
|
||||
(product) =>
|
||||
@@ -1261,21 +1100,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
`stocks.${idx}.product_warehouse_id`,
|
||||
option?.value || 0
|
||||
);
|
||||
|
||||
if (
|
||||
option?.value &&
|
||||
isResponseSuccess(stockProducts)
|
||||
) {
|
||||
const selectedProduct = stockProducts.data.find(
|
||||
(product) => product.id === option.value
|
||||
);
|
||||
if (selectedProduct) {
|
||||
formik.setFieldValue(
|
||||
`stocks.${idx}.notes`,
|
||||
selectedProduct.product.name
|
||||
);
|
||||
}
|
||||
}
|
||||
}}
|
||||
options={unifiedStockProducts}
|
||||
placeholder='Pilih Produk'
|
||||
@@ -1313,27 +1137,21 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<div className='flex flex-col gap-1'>
|
||||
<NumberInput
|
||||
required
|
||||
name={`stocks.${idx}.usage_amount`}
|
||||
value={stock.usage_amount}
|
||||
onChange={handleStockUsageAmountChangeWrapper(idx)}
|
||||
name={`stocks.${idx}.usage_qty`}
|
||||
value={stock.usage_qty}
|
||||
onChange={handleStockUsageQtyChangeWrapper(idx)}
|
||||
onBlur={formik.handleBlur}
|
||||
decimalScale={0}
|
||||
allowNegative={false}
|
||||
thousandSeparator=','
|
||||
decimalSeparator='.'
|
||||
isError={
|
||||
isRepeaterInputError(
|
||||
'stocks',
|
||||
'usage_amount',
|
||||
idx
|
||||
).isError || Boolean(getStockUsageError(idx))
|
||||
isRepeaterInputError('stocks', 'usage_qty', idx)
|
||||
.isError || Boolean(getStockUsageError(idx))
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError(
|
||||
'stocks',
|
||||
'usage_amount',
|
||||
idx
|
||||
).errorMessage ||
|
||||
isRepeaterInputError('stocks', 'usage_qty', idx)
|
||||
.errorMessage ||
|
||||
getStockUsageError(idx) ||
|
||||
undefined
|
||||
}
|
||||
@@ -1348,7 +1166,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
</td>
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<div className='flex justify-center'>
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
type='button'
|
||||
color='error'
|
||||
@@ -1446,7 +1264,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
</span>
|
||||
</th>
|
||||
<th>
|
||||
Total
|
||||
Jumlah
|
||||
<span
|
||||
className='tooltip tooltip-error tooltip-bottom z-[9999]'
|
||||
data-tip='required'
|
||||
@@ -1489,64 +1307,72 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
<td>
|
||||
<SelectInput
|
||||
value={
|
||||
RECORDING_FLAG_OPTIONS.find(
|
||||
(option) => option.value === depletion.notes
|
||||
unifiedStockProducts.find(
|
||||
(product) =>
|
||||
product.value === depletion.product_warehouse_id
|
||||
) || null
|
||||
}
|
||||
onChange={(selectedOption) => {
|
||||
const option = selectedOption as OptionType | null;
|
||||
formik.setFieldValue(
|
||||
`depletions.${idx}.notes`,
|
||||
option?.value || ''
|
||||
`depletions.${idx}.product_warehouse_id`,
|
||||
option?.value || 0
|
||||
);
|
||||
}}
|
||||
options={RECORDING_FLAG_OPTIONS}
|
||||
options={unifiedStockProducts}
|
||||
placeholder='Pilih Kondisi'
|
||||
isLoading={isLoadingStockProducts}
|
||||
isError={
|
||||
isRepeaterInputError('depletions', 'notes', idx)
|
||||
.isError
|
||||
isRepeaterInputError(
|
||||
'depletions',
|
||||
'product_warehouse_id',
|
||||
idx
|
||||
).isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError('depletions', 'notes', idx)
|
||||
.errorMessage
|
||||
isRepeaterInputError(
|
||||
'depletions',
|
||||
'product_warehouse_id',
|
||||
idx
|
||||
).errorMessage
|
||||
}
|
||||
isDisabled={type === 'detail'}
|
||||
className={{
|
||||
wrapper: 'w-full min-w-32',
|
||||
wrapper: 'w-full min-w-48',
|
||||
}}
|
||||
isSearchable={false}
|
||||
isSearchable
|
||||
isClearable={type !== 'detail'}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<NumberInput
|
||||
required
|
||||
name={`depletions.${idx}.total`}
|
||||
value={depletion.total}
|
||||
onChange={handleDepletionTotalChangeWrapper(idx)}
|
||||
name={`depletions.${idx}.qty`}
|
||||
value={depletion.qty}
|
||||
onChange={handleDepletionQtyChangeWrapper(idx)}
|
||||
onBlur={formik.handleBlur}
|
||||
decimalScale={0}
|
||||
allowNegative={false}
|
||||
thousandSeparator=','
|
||||
decimalSeparator='.'
|
||||
isError={
|
||||
isRepeaterInputError('depletions', 'total', idx)
|
||||
isRepeaterInputError('depletions', 'qty', idx)
|
||||
.isError
|
||||
}
|
||||
errorMessage={
|
||||
isRepeaterInputError('depletions', 'total', idx)
|
||||
isRepeaterInputError('depletions', 'qty', idx)
|
||||
.errorMessage
|
||||
}
|
||||
readOnly={type === 'detail'}
|
||||
className={{
|
||||
wrapper: 'w-full min-w-24',
|
||||
}}
|
||||
placeholder='Total'
|
||||
placeholder='Jumlah'
|
||||
/>
|
||||
</td>
|
||||
{type !== 'detail' && (
|
||||
<td>
|
||||
<div className='flex justify-center'>
|
||||
<div className='flex items-center'>
|
||||
<Button
|
||||
type='button'
|
||||
color='error'
|
||||
@@ -1594,7 +1420,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
||||
</Card>
|
||||
|
||||
{/* Action buttons */}
|
||||
<FormActions<RecordingFormValues>
|
||||
<FormActions<RecordingGrowingFormValues>
|
||||
type={type}
|
||||
formik={formik}
|
||||
editUrl={
|
||||
|
||||
+90
-16
@@ -1,46 +1,120 @@
|
||||
import { BaseMetadata, User } from '@/types/api/api-general';
|
||||
|
||||
export type ProductionMetrics = {
|
||||
total_depletion: number;
|
||||
total_depletion_qty: number;
|
||||
cum_depletion_rate: number;
|
||||
daily_gain: number;
|
||||
avg_daily_gain: number;
|
||||
cum_intake: number;
|
||||
fcr_value: number;
|
||||
total_chick: number;
|
||||
total_chick_qty: number;
|
||||
daily_depletion_rate: number;
|
||||
cum_depletion: number;
|
||||
};
|
||||
|
||||
export type BaseRecording = {
|
||||
id: number;
|
||||
project_flock_kandang_id: number;
|
||||
project_flock_kandangs_id: number;
|
||||
record_datetime: string;
|
||||
record_date: string;
|
||||
status: number;
|
||||
ontime: boolean;
|
||||
day: number;
|
||||
created_user: User;
|
||||
created_by: User;
|
||||
} & ProductionMetrics;
|
||||
|
||||
export type Recording = BaseMetadata & BaseRecording;
|
||||
export type RecordingBW = {
|
||||
id: number;
|
||||
recording_id: number;
|
||||
avg_weight: number;
|
||||
qty: number;
|
||||
total_weight: number;
|
||||
};
|
||||
|
||||
export type CreateRecordingPayload = {
|
||||
project_flock_kandang_id: number;
|
||||
export type RecordingDepletion = {
|
||||
id: number;
|
||||
recording_id: number;
|
||||
product_warehouse_id: number;
|
||||
qty: number;
|
||||
};
|
||||
|
||||
export type RecordingStock = {
|
||||
id: number;
|
||||
recording_id: number;
|
||||
product_warehouse_id: number;
|
||||
usage_qty: number;
|
||||
pending_qty: number;
|
||||
};
|
||||
|
||||
export type RecordingEgg = {
|
||||
id: number;
|
||||
recording_id: number;
|
||||
product_warehouse_id: number;
|
||||
qty: number;
|
||||
created_by: User;
|
||||
};
|
||||
|
||||
export type GradingEgg = {
|
||||
id: number;
|
||||
recording_egg_id: number;
|
||||
qty: number;
|
||||
grade: string;
|
||||
created_by: User;
|
||||
};
|
||||
|
||||
export type Recording = BaseMetadata &
|
||||
BaseRecording & {
|
||||
recording_bws?: RecordingBW[];
|
||||
recording_depletions?: RecordingDepletion[];
|
||||
recording_stocks?: RecordingStock[];
|
||||
recording_eggs?: RecordingEgg[];
|
||||
grading_eggs?: GradingEgg[];
|
||||
};
|
||||
|
||||
export type CreateGrowingRecordingPayload = {
|
||||
project_flock_kandangs_id: number;
|
||||
body_weights: {
|
||||
weight: number;
|
||||
avg_weight: number;
|
||||
qty: number;
|
||||
}[];
|
||||
stocks?: {
|
||||
product_warehouse_id: number;
|
||||
usage_amount: number;
|
||||
notes: string;
|
||||
usage_qty: number;
|
||||
pending_qty?: number;
|
||||
}[];
|
||||
depletions?: {
|
||||
product_warehouse_id?: number;
|
||||
total: number;
|
||||
notes: string;
|
||||
product_warehouse_id: number;
|
||||
qty: number;
|
||||
}[];
|
||||
};
|
||||
|
||||
export type CreateLayingRecordingPayload = {
|
||||
project_flock_kandangs_id: number;
|
||||
body_weights: {
|
||||
avg_weight: number;
|
||||
qty: number;
|
||||
}[];
|
||||
stocks?: {
|
||||
product_warehouse_id: number;
|
||||
usage_qty: number;
|
||||
pending_qty?: number;
|
||||
}[];
|
||||
depletions?: {
|
||||
product_warehouse_id: number;
|
||||
qty: number;
|
||||
}[];
|
||||
eggs: {
|
||||
product_warehouse_id: number;
|
||||
qty: number;
|
||||
grading?: {
|
||||
grade: string;
|
||||
qty: number;
|
||||
}[];
|
||||
}[];
|
||||
};
|
||||
|
||||
export type CreateRecordingPayload =
|
||||
| CreateGrowingRecordingPayload
|
||||
| CreateLayingRecordingPayload;
|
||||
|
||||
export type UpdateGrowingRecordingPayload = CreateGrowingRecordingPayload;
|
||||
export type UpdateLayingRecordingPayload = CreateLayingRecordingPayload;
|
||||
|
||||
export type UpdateRecordingPayload = CreateRecordingPayload;
|
||||
|
||||
Reference in New Issue
Block a user