feat(FE-62,65): add inventory movement management with API and form validation

This commit is contained in:
rstubryan
2025-10-08 15:26:45 +07:00
parent 1968761b5d
commit 7ceb25ea71
4 changed files with 262 additions and 109 deletions
@@ -0,0 +1,67 @@
import * as Yup from 'yup';
export const MovementFormSchema = Yup.object({
alasan_transfer: Yup.string()
.required('Alasan Transfer wajib diisi!'),
tanggal_transfer: Yup.date()
.required('Tanggal Transfer wajib diisi!')
.typeError('Tanggal Transfer tidak valid!'),
warehouse_asal: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
warehouse_asal_id: Yup.number()
.required('Gudang Asal wajib diisi!'),
warehouse_tujuan: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
warehouse_tujuan_id: Yup.number()
.required('Gudang Tujuan wajib diisi!'),
alasan: Yup.string()
.required('Alasan wajib diisi!'),
product: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
product_id: Yup.array()
.of(Yup.number()).min(1, 'Pilih minimal 1 produk')
.required('Produk wajib diisi!'),
qty_product: Yup.array()
.of(Yup.number().min(1, 'Kuantitas minimal 1'))
.min(1, 'Pilih minimal 1 produk')
.required('Kuantitas wajib diisi!'),
ekspedisi: Yup.array().of(
Yup.object({
product: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
product_id: Yup.number()
.required('Produk wajib diisi!'),
qty: Yup.number().min(1, 'Kuantitas minimal 1')
.required('Kuantitas wajib diisi!'),
supplier: Yup.object({
value: Yup.number().min(1).required(),
label: Yup.string().required(),
}).nullable(),
supplier_id: Yup.number()
.required('Supplier wajib diisi!'),
plat_nomor: Yup.string()
.required('Plat Nomor wajib diisi!'),
no_surat_jalan: Yup.string()
.required('No Surat Jalan wajib diisi!'),
dokumen: Yup.mixed()
.required('Dokumen wajib diisi!'),
biaya_ekspedisi: Yup.number()
.min(0, 'Biaya Ekspedisi minimal 0')
.required('Biaya Ekspedisi wajib diisi!'),
nama_sopir: Yup.string()
.required('Nama Sopir wajib diisi!'),
})
).min(1, 'Pilih minimal 1 ekspedisi').required('Ekspedisi wajib diisi!'),
});
export const UpdateMovementFormSchema = MovementFormSchema;
export type MovementFormValues = Yup.InferType<typeof MovementFormSchema>;
+132 -109
View File
@@ -1,122 +1,145 @@
export const MAIN_DRAWER_LINKS = [
{
title: 'Dashboard',
link: '/dashboard',
icon: 'gg:chart',
},
{
title: 'Dashboard',
link: '/dashboard',
icon: 'gg:chart',
},
{
title: 'Master Data',
link: '/master-data',
icon: 'majesticons:data-line',
submenu: [
{
title: 'Product',
link: '/master-data/product',
icon: 'fluent-mdl2:product-variant',
},
{
title: 'Product Category',
link: '/master-data/product-category',
icon: 'carbon:categories',
},
{
title: 'Bank',
link: '/master-data/bank',
icon: 'mdi:bank-outline',
},
{
title: 'Area',
link: '/master-data/area',
icon: 'majesticons:map-marker-area-line',
},
{
title: 'Location',
link: '/master-data/location',
icon: 'mingcute:location-line',
},
{
title: 'Kandang',
link: '/master-data/kandang',
icon: 'mdi:farm-home-outline',
},
{
title: 'Warehouse',
link: '/master-data/warehouse',
icon: 'hugeicons:warehouse',
},
{
title: 'Customer',
link: '/master-data/customer',
icon: 'ix:customer',
},
{
title: 'UOM',
link: '/master-data/uom',
icon: 'lsicon:measure-outline',
},
{
title: 'Non-Stock',
link: '/master-data/nonstock',
icon: 'fluent:box-32-regular',
},
{
title: 'FCR',
link: '/master-data/FCR',
icon: 'fluent:food-chicken-leg-16-regular',
},
{
title: 'Supplier',
link: '/master-data/supplier',
icon: 'material-symbols:add-business-outline-rounded',
},
],
},
{
title: 'Persediaan',
link: '/inventory',
icon: 'mdi:warehouse',
submenu: [
{
title: 'Product',
link: '/inventory/product',
icon: 'mdi:package-variant-closed',
},
{
title: 'Penyesuaian Stok',
link: '/inventory/adjustment',
icon: 'mdi:database-edit',
},
{
title: 'Transfer Stok',
link: '/inventory/movement',
icon: 'mdi:swap-horizontal',
},
],
},
{
title: 'Master Data',
link: '/master-data',
icon: 'majesticons:data-line',
submenu: [
{
title: 'Product',
link: '/master-data/product',
icon: 'fluent-mdl2:product-variant',
},
{
title: 'Product Category',
link: '/master-data/product-category',
icon: 'carbon:categories',
},
{
title: 'Bank',
link: '/master-data/bank',
icon: 'mdi:bank-outline',
},
{
title: 'Area',
link: '/master-data/area',
icon: 'majesticons:map-marker-area-line',
},
{
title: 'Location',
link: '/master-data/location',
icon: 'mingcute:location-line',
},
{
title: 'Kandang',
link: '/master-data/kandang',
icon: 'mdi:farm-home-outline',
},
{
title: 'Warehouse',
link: '/master-data/warehouse',
icon: 'hugeicons:warehouse',
},
{
title: 'Customer',
link: '/master-data/customer',
icon: 'ix:customer',
},
{
title: 'UOM',
link: '/master-data/uom',
icon: 'lsicon:measure-outline',
},
{
title: 'Non-Stock',
link: '/master-data/nonstock',
icon: 'fluent:box-32-regular',
},
{
title: 'FCR',
link: '/master-data/FCR',
icon: 'fluent:food-chicken-leg-16-regular',
},
{
title: 'Supplier',
link: '/master-data/supplier',
icon: 'material-symbols:add-business-outline-rounded',
},
],
},
] as const;
export const ROWS_OPTIONS = [
{
label: '10',
value: 10,
},
{
label: '20',
value: 20,
},
{
label: '50',
value: 50,
},
{
label: '100',
value: 100,
},
{
label: '10',
value: 10,
},
{
label: '20',
value: 20,
},
{
label: '50',
value: 50,
},
{
label: '100',
value: 100,
},
];
export const WAREHOUSE_TYPE_OPTIONS = [
{
label: 'AREA',
value: 'AREA',
},
{
label: 'LOKASI',
value: 'LOKASI',
},
{
label: 'KANDANG',
value: 'KANDANG',
},
{
label: 'AREA',
value: 'AREA',
},
{
label: 'LOKASI',
value: 'LOKASI',
},
{
label: 'KANDANG',
value: 'KANDANG',
},
];
export const PRODUCT_FLAG_OPTIONS = [
{ label: 'DOC', value: 'DOC' },
{ label: 'PAKAN', value: 'PAKAN' },
{ label: 'PRE-STARTER', value: 'PRE-STARTER' },
{ label: 'STARTER', value: 'STARTER' },
{ label: 'FINISHER', value: 'FINISHER' },
{ label: 'OVK', value: 'OVK' },
{ label: 'OBAT', value: 'OBAT' },
{ label: 'VITAMIN', value: 'VITAMIN' },
{ label: 'KIMIA', value: 'KIMIA' },
{label: 'DOC', value: 'DOC'},
{label: 'PAKAN', value: 'PAKAN'},
{label: 'PRE-STARTER', value: 'PRE-STARTER'},
{label: 'STARTER', value: 'STARTER'},
{label: 'FINISHER', value: 'FINISHER'},
{label: 'OVK', value: 'OVK'},
{label: 'OBAT', value: 'OBAT'},
{label: 'VITAMIN', value: 'VITAMIN'},
{label: 'KIMIA', value: 'KIMIA'},
];
+12
View File
@@ -0,0 +1,12 @@
import {
CreateMovementPayload,
Movement,
UpdateMovementPayload,
} from "@/types/api/inventory/movement";
import {BaseApiService} from "@/services/api/base";
export const MovementApi = new BaseApiService<
Movement,
CreateMovementPayload,
UpdateMovementPayload
>('/inventory/movements');
+51
View File
@@ -0,0 +1,51 @@
import {BaseMetadata} from '@/types/api/api-general';
import {Product} from "@/types/api/master-data/product";
import {Supplier} from "@/types/api/master-data/supplier";
import {Warehouse} from "@/types/api/master-data/warehouse";
export type BaseMovement = {
id: number;
alasan_transfer: string;
tanggal_transfer: string;
warehouse_asal: Warehouse;
warehouse_tujuan: Warehouse;
product: Array<{
product: Product;
qty_product: number;
}>;
ekspedisi: Array<{
product_id: number;
qty: number;
supplier: Supplier;
plat_nomor: string;
no_surat_jalan: string;
dokumen: string;
biaya_ekspedisi: number;
nama_sopir: string;
}>;
name: string;
};
export type Movement = BaseMetadata & BaseMovement;
export type CreateMovementPayload = {
alasan: string;
warehouse_asal_id: number;
warehouse_tujuan_id: number;
product: Array<{
product_id: number;
qty_product: number;
}>;
ekspedisi: Array<{
product_id: number;
qty: number;
supplier_id: number;
plat_nomor: string;
no_surat_jalan: string;
dokumen: string;
biaya_ekspedisi: number;
nama_sopir: string;
}>;
}
export type UpdateMovementPayload = CreateMovementPayload;