From c6a0c542aad1dcc2c4dc8c5361bfa95a6d9b01cd Mon Sep 17 00:00:00 2001 From: rstubryan Date: Thu, 16 Oct 2025 14:33:49 +0700 Subject: [PATCH] refactor(FE-62,63,65): refactor Movement and ProductWarehouse APIs, update MovementForm schema, and enhance MovementTable functionality --- .../inventory/movement/MovementTable.tsx | 622 ++++-------------- .../movement/form/MovementForm.schema.ts | 53 +- .../movement/form/useMovementFormHandlers.ts | 80 ++- src/components/table/TableRowOptions.tsx | 11 +- src/services/api/inventory.ts | 13 +- src/types/api/inventory/movement.d.ts | 32 +- .../api/inventory/product-warehouse.d.ts | 22 + 7 files changed, 277 insertions(+), 556 deletions(-) create mode 100644 src/types/api/inventory/product-warehouse.d.ts diff --git a/src/components/pages/inventory/movement/MovementTable.tsx b/src/components/pages/inventory/movement/MovementTable.tsx index a00f7111..e0bc9541 100644 --- a/src/components/pages/inventory/movement/MovementTable.tsx +++ b/src/components/pages/inventory/movement/MovementTable.tsx @@ -1,463 +1,56 @@ 'use client'; -import { useState, useMemo } from 'react'; +import { useState } from 'react'; +import useSWR from 'swr'; import { SortingState } from '@tanstack/react-table'; -import { Icon } from '@iconify/react'; + import Table from '@/components/Table'; import { useModal } from '@/components/Modal'; import ConfirmationModal from '@/components/modal/ConfirmationModal'; -import { ROWS_OPTIONS } from '@/config/constant'; import { Movement } from '@/types/api/inventory/movement'; +import { MovementApi } from '@/services/api/inventory'; +import { cn } from '@/lib/helper'; +import { isResponseSuccess } from '@/lib/api-helper'; +import { useTableFilter } from '@/services/hooks/useTableFilter'; +import { ROWS_OPTIONS } from '@/config/constant'; import { TableToolbar } from '@/components/table/TableToolbar'; import { TableRowSizeSelector } from '@/components/table/TableRowSizeSelector'; -import { TableRowOptions } from '@/components/table/TableRowOptions'; import { OptionType } from '@/components/input/SelectInput'; -import Button from '@/components/Button'; -import { cn } from '@/lib/helper'; - -// Dummy data -const baseMetadata = { - created_user: { - id: 1, - id_user: 1, - email: 'user@example.com', - name: 'User', - }, - created_at: '2024-06-01T00:00:00Z', - updated_at: '2024-06-01T00:00:00Z', -}; - -const dummyMovements: Movement[] = [ - { - ...baseMetadata, - id: 1, - transfer_reason: 'Restock', - transfer_date: '2024-06-01', - source_warehouse: { - ...baseMetadata, - id: 1, - name: 'Warehouse A', - type: 'AREA', - area: { id: 1, name: 'Area 1' }, - }, - destination_warehouse: { - ...baseMetadata, - id: 2, - name: 'Warehouse B', - type: 'AREA', - area: { id: 2, name: 'Area 2' }, - }, - products: [ - { - product: { - ...baseMetadata, - id: 1, - name: 'Product X', - brand: 'Brand X', - sku: 'SKU-X', - product_price: 10000, - selling_price: 12000, - tax: 10, - expiry_period: 365, - uom: { - ...baseMetadata, - id: 1, - name: 'PCS', - }, - product_category: { - ...baseMetadata, - id: 1, - code: 'CAT-1', - name: 'Category 1', - }, - suppliers: [], - flags: [], - }, - product_qty: 10, - }, - ], - deliveries: [ - { - delivery_cost: 50000, - delivery_cost_per_item: 5000, - document: 'doc1.pdf', - driver_name: 'Andi', - vehicle_plate: 'B 1234 CD', - supplier: { - ...baseMetadata, - id: 1, - name: 'Supplier 1', - alias: 'S1', - category: 'General', - pic: 'PIC 1', - type: 'Type 1', - hatchery: 'Hatchery 1', - phone: '08123456789', - email: 'supplier1@example.com', - address: 'Address 1', - npwp: '1234567890123456', - account_number: '1234567890', - balance: 0, - due_date: 30, - }, - products: [ - { - product: { - ...baseMetadata, - id: 1, - name: 'Product X', - brand: 'Brand X', - sku: 'SKU-X', - product_price: 10000, - selling_price: 12000, - tax: 10, - expiry_period: 365, - uom: { - ...baseMetadata, - id: 1, - name: 'PCS', - }, - product_category: { - ...baseMetadata, - id: 1, - code: 'CAT-1', - name: 'Category 1', - }, - suppliers: [], - flags: [], - }, - product_qty: 10, - }, - ], - }, - ], - }, - { - ...baseMetadata, - id: 2, - transfer_reason: 'Mutasi Stok', - transfer_date: '2024-06-02', - source_warehouse: { - ...baseMetadata, - id: 2, - name: 'Warehouse B', - type: 'AREA', - area: { id: 2, name: 'Area 2' }, - }, - destination_warehouse: { - ...baseMetadata, - id: 3, - name: 'Warehouse C', - type: 'AREA', - area: { id: 3, name: 'Area 3' }, - }, - products: [ - { - product: { - ...baseMetadata, - id: 2, - name: 'Product Y', - brand: 'Brand Y', - sku: 'SKU-Y', - product_price: 20000, - selling_price: 25000, - tax: 5, - expiry_period: 180, - uom: { - ...baseMetadata, - id: 2, - name: 'BOX', - }, - product_category: { - ...baseMetadata, - id: 2, - code: 'CAT-2', - name: 'Category 2', - }, - suppliers: [], - flags: [], - }, - product_qty: 5, - }, - ], - deliveries: [ - { - delivery_cost: 60000, - delivery_cost_per_item: 12000, - document: 'doc2.pdf', - driver_name: 'Budi', - vehicle_plate: 'D 5678 EF', - supplier: { - ...baseMetadata, - id: 2, - name: 'Supplier 2', - alias: 'S2', - category: 'Special', - pic: 'PIC 2', - type: 'Type 2', - hatchery: 'Hatchery 2', - phone: '08123456780', - email: 'supplier2@example.com', - address: 'Address 2', - npwp: '1234567890123457', - account_number: '1234567891', - balance: 1000, - due_date: 15, - }, - products: [ - { - product: { - ...baseMetadata, - id: 2, - name: 'Product Y', - brand: 'Brand Y', - sku: 'SKU-Y', - product_price: 20000, - selling_price: 25000, - tax: 5, - expiry_period: 180, - uom: { - ...baseMetadata, - id: 2, - name: 'BOX', - }, - product_category: { - ...baseMetadata, - id: 2, - code: 'CAT-2', - name: 'Category 2', - }, - suppliers: [], - flags: [], - }, - product_qty: 5, - }, - ], - }, - ], - }, - { - ...baseMetadata, - id: 3, - transfer_reason: 'Pengembalian', - transfer_date: '2024-06-03', - source_warehouse: { - ...baseMetadata, - id: 3, - name: 'Warehouse C', - type: 'AREA', - area: { id: 3, name: 'Area 3' }, - }, - destination_warehouse: { - ...baseMetadata, - id: 1, - name: 'Warehouse A', - type: 'AREA', - area: { id: 1, name: 'Area 1' }, - }, - products: [ - { - product: { - ...baseMetadata, - id: 3, - name: 'Product Z', - brand: 'Brand Z', - sku: 'SKU-Z', - product_price: 15000, - selling_price: 18000, - tax: 8, - expiry_period: 90, - uom: { - ...baseMetadata, - id: 3, - name: 'KG', - }, - product_category: { - ...baseMetadata, - id: 3, - code: 'CAT-3', - name: 'Category 3', - }, - suppliers: [], - flags: [], - }, - product_qty: 8, - }, - ], - deliveries: [ - { - delivery_cost: 40000, - delivery_cost_per_item: 5000, - document: 'doc3.pdf', - driver_name: 'Cici', - vehicle_plate: 'F 9101 GH', - supplier: { - ...baseMetadata, - id: 3, - name: 'Supplier 3', - alias: 'S3', - category: 'Return', - pic: 'PIC 3', - type: 'Type 3', - hatchery: 'Hatchery 3', - phone: '08123456781', - email: 'supplier3@example.com', - address: 'Address 3', - npwp: '1234567890123458', - account_number: '1234567892', - balance: 500, - due_date: 10, - }, - products: [ - { - product: { - ...baseMetadata, - id: 3, - name: 'Product Z', - brand: 'Brand Z', - sku: 'SKU-Z', - product_price: 15000, - selling_price: 18000, - tax: 8, - expiry_period: 90, - uom: { - ...baseMetadata, - id: 3, - name: 'KG', - }, - product_category: { - ...baseMetadata, - id: 3, - code: 'CAT-3', - name: 'Category 3', - }, - suppliers: [], - flags: [], - }, - product_qty: 8, - }, - ], - }, - ], - }, - { - ...baseMetadata, - id: 4, - transfer_reason: 'Transfer Internal', - transfer_date: '2024-06-04', - source_warehouse: { - ...baseMetadata, - id: 4, - name: 'Warehouse D', - type: 'AREA', - area: { id: 4, name: 'Area 4' }, - }, - destination_warehouse: { - ...baseMetadata, - id: 5, - name: 'Warehouse E', - type: 'AREA', - area: { id: 5, name: 'Area 5' }, - }, - products: [ - { - product: { - ...baseMetadata, - id: 4, - name: 'Product A', - brand: 'Brand A', - sku: 'SKU-A', - product_price: 5000, - selling_price: 7000, - tax: 0, - expiry_period: 60, - uom: { - ...baseMetadata, - id: 4, - name: 'LITER', - }, - product_category: { - ...baseMetadata, - id: 4, - code: 'CAT-4', - name: 'Category 4', - }, - suppliers: [], - flags: [], - }, - product_qty: 20, - }, - ], - deliveries: [ - { - delivery_cost: 30000, - delivery_cost_per_item: 1500, - document: 'doc4.pdf', - driver_name: 'Dedi', - vehicle_plate: 'H 2345 IJ', - supplier: { - ...baseMetadata, - id: 4, - name: 'Supplier 4', - alias: 'S4', - category: 'Internal', - pic: 'PIC 4', - type: 'Type 4', - hatchery: 'Hatchery 4', - phone: '08123456782', - email: 'supplier4@example.com', - address: 'Address 4', - npwp: '1234567890123459', - account_number: '1234567893', - balance: 200, - due_date: 20, - }, - products: [ - { - product: { - ...baseMetadata, - id: 4, - name: 'Product A', - brand: 'Brand A', - sku: 'SKU-A', - product_price: 5000, - selling_price: 7000, - tax: 0, - expiry_period: 60, - uom: { - ...baseMetadata, - id: 4, - name: 'LITER', - }, - product_category: { - ...baseMetadata, - id: 4, - code: 'CAT-4', - name: 'Category 4', - }, - suppliers: [], - flags: [], - }, - product_qty: 20, - }, - ], - }, - ], - }, -]; +import RowDropdownOptions from '@/components/table/RowDropdownOptions'; +import RowCollapseOptions from '@/components/table/RowCollapseOptions'; +import { TableRowOptions } from '@/components/table/TableRowOptions'; const MovementTable = () => { - const [search, setSearch] = useState(''); - const [page, setPage] = useState(1); - const [pageSize, setPageSize] = useState(10); + const { + state: tableFilterState, + updateFilter, + setPage, + setPageSize, + toQueryString: getTableFilterQueryString, + } = useTableFilter({ + initial: { search: '' }, + paramMap: { page: 'page', pageSize: 'limit' }, + }); + const [sorting, setSorting] = useState([]); - const [, setSelectedMovement] = useState(undefined); + const [selectedMovement, setSelectedMovement] = useState< + Movement | undefined + >(undefined); const [isDeleteLoading, setIsDeleteLoading] = useState(false); const deleteModal = useModal(); + const { + data: movements, + isLoading, + mutate: refreshMovements, + } = useSWR( + `${MovementApi.basePath}${getTableFilterQueryString()}`, + MovementApi.getAllFetcher + ); + const searchChangeHandler = (e: React.ChangeEvent) => { - setSearch(e.target.value); + updateFilter('search', e.target.value); setPage(1); }; @@ -469,17 +62,15 @@ const MovementTable = () => { const confirmationModalDeleteClickHandler = async () => { setIsDeleteLoading(true); - setTimeout(() => { - setIsDeleteLoading(false); + try { + await MovementApi.delete(selectedMovement?.id as number); + refreshMovements(); deleteModal.closeModal(); - }, 1000); + } finally { + setIsDeleteLoading(false); + } }; - const paginatedData = useMemo(() => { - const start = (page - 1) * pageSize; - return dummyMovements.slice(start, start + pageSize); - }, [page, pageSize]); - return (
@@ -489,85 +80,118 @@ const MovementTable = () => { label: 'Tambah Movement', }} search={{ - value: search, + value: tableFilterState.search, onChange: searchChangeHandler, placeholder: 'Cari Movement', }} />
- + data={isResponseSuccess(movements) ? movements?.data : []} columns={[ { header: '#', - cell: (props) => pageSize * (page - 1) + props.row.index + 1, - }, - { - accessorKey: 'source_warehouse', - header: 'Gudang Asal', - cell: (props) => props.row.original.source_warehouse.name, - }, - { - accessorKey: 'destination_warehouse', - header: 'Gudang Tujuan', - cell: (props) => props.row.original.destination_warehouse.name, - }, - { - accessorKey: 'products', - header: 'Nama Produk', cell: (props) => - props.row.original.products - .map((p) => p.product.name) - .join(', '), + tableFilterState.pageSize * (tableFilterState.page - 1) + + props.row.index + + 1, + }, + { + accessorFn: (row) => row.source_warehouse?.name, + header: 'Gudang Asal', + }, + { + accessorFn: (row) => row.destination_warehouse?.name, + header: 'Gudang Tujuan', }, { accessorKey: 'transfer_reason', header: 'Catatan', }, { - accessorKey: 'delivery_cost', - header: 'Biaya Pengiriman', + accessorKey: 'transfer_date', + header: 'Tanggal', cell: (props) => - props.row.original.deliveries - .reduce((sum, d) => sum + d.delivery_cost, 0) - .toLocaleString('id-ID'), + new Date(props.row.original.transfer_date).toLocaleDateString( + 'id-ID' + ), }, { - id: 'actions', - cell: (props) => ( -
- - { - setSelectedMovement(props.row.original); - deleteModal.openModal(); - }} - /> -
- ), + accessorFn: (row) => { + const totalCost = row.deliveries?.reduce( + (sum, d) => sum + (d.shipping_cost_total || 0), + 0 + ); + return totalCost?.toLocaleString('id-ID'); + }, + header: 'Biaya Pengiriman', + }, + { + header: 'Aksi', + cell: (props) => { + const currentPageSize = + props.table.getPaginationRowModel().rows.length; + const currentPageRows = + props.table.getPaginationRowModel().flatRows; + const currentRowRelativeIndex = + currentPageRows.findIndex((r) => r.id === props.row.id) + 1; + + const isLast2Rows = currentRowRelativeIndex > currentPageSize - 2; + + const deleteClickHandler = () => { + setSelectedMovement(props.row.original); + deleteModal.openModal(); + }; + + return ( + <> + {currentPageSize > 2 && ( + + + + )} + + {currentPageSize <= 2 && ( + + + + )} + + ); + }, }, ]} - pageSize={pageSize} - page={page} - totalItems={dummyMovements.length} + pageSize={tableFilterState.pageSize} + page={isResponseSuccess(movements) ? movements?.meta?.page : 0} + totalItems={ + isResponseSuccess(movements) ? movements?.meta?.total_results : 0 + } onPageChange={setPage} - isLoading={false} + isLoading={isLoading} sorting={sorting} setSorting={setSorting} className={{ containerClassName: cn({ - 'mb-20': paginatedData.length === 0, + 'mb-20': + isResponseSuccess(movements) && movements?.data?.length === 0, }), tableWrapperClassName: 'overflow-x-auto min-h-full!', tableClassName: 'font-inter w-full table-auto min-h-full!', diff --git a/src/components/pages/inventory/movement/form/MovementForm.schema.ts b/src/components/pages/inventory/movement/form/MovementForm.schema.ts index cb0d228d..148a7dce 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.schema.ts +++ b/src/components/pages/inventory/movement/form/MovementForm.schema.ts @@ -13,7 +13,7 @@ export type ProductSchema = { export type DeliverySchema = { delivery_cost: number; delivery_cost_per_item?: number | undefined; - document: string | File; + document?: File | string | null; driver_name: string; vehicle_plate: string; supplier: { @@ -64,23 +64,15 @@ const DeliveryObjectSchema: Yup.ObjectSchema = Yup.object({ .transform((value) => (isNaN(value) ? undefined : value)) .min(0, 'Biaya per item minimal 0!') .typeError('Biaya per item harus berupa angka!'), - document: Yup.mixed() - .required('Dokumen wajib diisi!') - .test( - 'fileType', - 'Mohon upload file berformat PDF atau JPEG/JPG.', - (value) => - typeof value === 'string' || - (value instanceof File && - ['application/pdf', 'image/jpeg', 'image/jpg'].includes(value.type)) - ) - .test( - 'fileSize', - 'Ukuran dokumen maksimal 2 MB!', - (value) => - typeof value === 'string' || - (value instanceof File && value.size <= 2 * 1024 * 1024) - ), + document_index: Yup.number().optional(), + document: Yup.mixed() + .nullable() + .test('fileSize', 'Ukuran dokumen maksimal 2 MB', (value) => { + if (!value) return true; + if (typeof value === 'string') return true; + if (value instanceof File) return value.size <= 2 * 1024 * 1024; + return false; + }), driver_name: Yup.string().required('Nama sopir wajib diisi!'), vehicle_plate: Yup.string().required('Plat nomor wajib diisi!'), supplier: Yup.object({ @@ -145,24 +137,25 @@ export const getMovementFormInitialValues = ( : null, destination_warehouse_id: initialValues?.destination_warehouse?.id ?? 0, products: - initialValues?.products?.map((p) => ({ - product: { value: p.product.id, label: p.product.name }, - product_id: p.product.id, - product_qty: p.product_qty, + initialValues?.details?.map((p) => ({ + product: { value: p.product_id, label: '' }, + product_id: p.product_id, + product_qty: p.quantity, })) ?? [], deliveries: initialValues?.deliveries?.map((d) => ({ - delivery_cost: d.delivery_cost, - delivery_cost_per_item: d.delivery_cost_per_item, - document: d.document, + delivery_cost: d.shipping_cost_total, + delivery_cost_per_item: d.shipping_cost_item, + document_index: 0, + document: d.document_path || null, driver_name: d.driver_name, vehicle_plate: d.vehicle_plate, supplier: { value: d.supplier.id, label: d.supplier.name }, - supplier_id: d.supplier.id, - products: d.products.map((p) => ({ - product: { value: p.product.id, label: p.product.name }, - product_id: p.product.id, - product_qty: p.product_qty, + supplier_id: d.supplier_id, + products: d.items.map((p) => ({ + product: { value: 0, label: '' }, + product_id: 0, + product_qty: p.quantity, })), })) ?? [], }); diff --git a/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts b/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts index 0b6b0962..1894b1a7 100644 --- a/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts +++ b/src/components/pages/inventory/movement/form/useMovementFormHandlers.ts @@ -8,7 +8,6 @@ import { UpdateMovementPayload, } from '@/types/api/inventory/movement'; import { isResponseError } from '@/lib/api-helper'; -import { containsFile, toFormData } from '@/lib/form-data'; export const useMovementFormHandlers = (initialValuesId?: number) => { const router = useRouter(); @@ -17,13 +16,44 @@ export const useMovementFormHandlers = (initialValuesId?: number) => { const [isDeleteLoading, setIsDeleteLoading] = useState(false); const createMovementHandler = useCallback( - async (payload: CreateMovementPayload) => { - const finalPayload = containsFile(payload) - ? (toFormData(payload) as unknown as CreateMovementPayload) - : payload; + async (payload: CreateMovementPayload, documents: File[] = []) => { + console.log('=== CREATE HANDLER DEBUG ==='); + console.log('1. Received payload:', payload); + console.log('2. Documents count:', documents.length); + + let finalPayload: CreateMovementPayload | FormData; + + if (documents.length > 0) { + // Ada dokumen: kirim sebagai FormData dengan "data" field + console.log('3. Creating FormData (has documents)'); + const formData = new FormData(); + formData.append('data', JSON.stringify(payload)); + documents.forEach((file, index) => { + formData.append(`documents[${index}]`, file); + }); + + console.log('4. FormData entries:'); + for (const [key, value] of formData.entries()) { + if (value instanceof File) { + console.log(` ${key}: [File] ${value.name} (${value.size} bytes)`); + } else { + console.log(` ${key}: ${value}`); + } + } + + finalPayload = formData as unknown as CreateMovementPayload; + } else { + // Tidak ada dokumen: kirim sebagai JSON biasa + console.log('3. Sending as JSON (no documents)'); + console.log('4. Payload:', JSON.stringify(payload, null, 2)); + finalPayload = payload; + } + + console.log('=== END CREATE HANDLER DEBUG ==='); const res = await MovementApi.create(finalPayload); if (isResponseError(res)) { + console.error('API Error:', res); setMovementFormErrorMessage(res.message); return; } @@ -34,13 +64,45 @@ export const useMovementFormHandlers = (initialValuesId?: number) => { ); const updateMovementHandler = useCallback( - async (movementId: number, payload: UpdateMovementPayload) => { - const finalPayload = containsFile(payload) - ? (toFormData(payload) as unknown as UpdateMovementPayload) - : payload; + async (movementId: number, payload: UpdateMovementPayload, documents: File[] = []) => { + console.log('=== UPDATE HANDLER DEBUG ==='); + console.log('1. Received payload:', payload); + console.log('2. Movement ID:', movementId); + console.log('3. Documents count:', documents.length); + + let finalPayload: UpdateMovementPayload | FormData; + + if (documents.length > 0) { + // Ada dokumen: kirim sebagai FormData dengan "data" field + console.log('4. Creating FormData (has documents)'); + const formData = new FormData(); + formData.append('data', JSON.stringify(payload)); + documents.forEach((file, index) => { + formData.append(`documents[${index}]`, file); + }); + + console.log('5. FormData entries:'); + for (const [key, value] of formData.entries()) { + if (value instanceof File) { + console.log(` ${key}: [File] ${value.name} (${value.size} bytes)`); + } else { + console.log(` ${key}: ${value}`); + } + } + + finalPayload = formData as unknown as UpdateMovementPayload; + } else { + // Tidak ada dokumen: kirim sebagai JSON biasa + console.log('4. Sending as JSON (no documents)'); + console.log('5. Payload:', JSON.stringify(payload, null, 2)); + finalPayload = payload; + } + + console.log('=== END UPDATE HANDLER DEBUG ==='); const res = await MovementApi.update(movementId, finalPayload); if (res?.status === 'error') { + console.error('API Error:', res); setMovementFormErrorMessage(res.message); return; } diff --git a/src/components/table/TableRowOptions.tsx b/src/components/table/TableRowOptions.tsx index 61332b4f..e34f2ad4 100644 --- a/src/components/table/TableRowOptions.tsx +++ b/src/components/table/TableRowOptions.tsx @@ -7,6 +7,7 @@ interface TableRowOptionsProps { recordId: string | number; basePath: string; onDelete?: () => void; + queryParam?: string; } export const TableRowOptions = ({ @@ -14,6 +15,7 @@ export const TableRowOptions = ({ recordId, basePath, onDelete, + queryParam = 'id', }: TableRowOptionsProps) => (
{onDelete && ( @@ -51,9 +53,10 @@ export const TableRowOptions = ({ className='text-error hover:text-inherit justify-start text-sm' > Delete diff --git a/src/services/api/inventory.ts b/src/services/api/inventory.ts index cf799442..ec58f6f2 100644 --- a/src/services/api/inventory.ts +++ b/src/services/api/inventory.ts @@ -1,4 +1,9 @@ import { BaseApiService } from '@/services/api/base'; +import { + CreateProductWarehousePayload, + ProductWarehouse, + UpdateProductWarehousePayload, +} from '@/types/api/inventory/product-warehouse'; import { CreateMovementPayload, Movement, @@ -9,11 +14,17 @@ import { InventoryAdjustment, } from '@/types/api/inventory/adjustment'; +export const ProductWarehouseApi = new BaseApiService< + ProductWarehouse, + CreateProductWarehousePayload, + UpdateProductWarehousePayload +>('/inventory/product-warehouses'); + export const MovementApi = new BaseApiService< Movement, CreateMovementPayload, UpdateMovementPayload ->('/inventory/movements'); +>('/inventory/transfers'); export const inventoryAdjustmentApi = new BaseApiService< InventoryAdjustment, diff --git a/src/types/api/inventory/movement.d.ts b/src/types/api/inventory/movement.d.ts index 11da41a5..9e156a1e 100644 --- a/src/types/api/inventory/movement.d.ts +++ b/src/types/api/inventory/movement.d.ts @@ -1,5 +1,4 @@ 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'; @@ -9,20 +8,27 @@ export type BaseMovement = { transfer_date: string; source_warehouse: Warehouse; destination_warehouse: Warehouse; - products: { - product: Product; - product_qty: number; + details: { + id: number; + product_id: number; + quantity: number; + before_quantity: number; + after_quantity: number; }[]; deliveries: { - delivery_cost: number; - delivery_cost_per_item: number; - document: string; - driver_name: string; - vehicle_plate: string; + id: number; + supplier_id: number; supplier: Supplier; - products: { - product: Product; - product_qty: number; + vehicle_plate: string; + driver_name: string; + document_number: string; + document_path: string; + shipping_cost_item: number; + shipping_cost_total: number; + items: { + id: number; + stock_transfer_detail_id: number; + quantity: number; }[]; }[]; }; @@ -41,7 +47,7 @@ export type CreateMovementPayload = { deliveries: { delivery_cost: number; delivery_cost_per_item: number; - document: string | File; + document_index?: number; driver_name: string; vehicle_plate: string; supplier_id: number; diff --git a/src/types/api/inventory/product-warehouse.d.ts b/src/types/api/inventory/product-warehouse.d.ts new file mode 100644 index 00000000..eda8d1b8 --- /dev/null +++ b/src/types/api/inventory/product-warehouse.d.ts @@ -0,0 +1,22 @@ +import { BaseMetadata } from '@/types/api/api-general'; +import { Warehouse } from '@/types/api/master-data/warehouse'; +import { Product } from '@/types/api/master-data/product'; + +export type BaseProductWarehouse = { + id: number; + product_id: number; + warehouse_id: number; + quantity: number; + product: Product; + warehouse: Warehouse; +}; + +export type ProductWarehouse = BaseMetadata & BaseProductWarehouse; + +export type CreateProductWarehousePayload = { + product_id: number; + warehouse_id: number; + quantity: number; +}; + +export type UpdateProductWarehousePayload = CreateProductWarehousePayload;