refactor(FE-361,363): Refactor purchases-per-supplier report to use API

This commit is contained in:
rstubryan
2025-12-13 10:55:15 +07:00
parent 67b5187d39
commit bd4c51cb04
3 changed files with 267 additions and 210 deletions
@@ -1,23 +1,69 @@
import { useState, useMemo } from 'react'; import { useState, useMemo, useCallback } from 'react';
import { ChangeEventHandler } from 'react';
import useSWR from 'swr';
import Card from '@/components/Card'; import Card from '@/components/Card';
import SelectInput, { useSelect } from '@/components/input/SelectInput'; import SelectInput, {
useSelect,
OptionType,
} from '@/components/input/SelectInput';
import DateInput from '@/components/input/DateInput'; import DateInput from '@/components/input/DateInput';
import { AreaApi } from '@/services/api/master-data'; import { AreaApi } from '@/services/api/master-data';
import { SupplierApi } from '@/services/api/master-data'; import { SupplierApi } from '@/services/api/master-data';
import { ProductApi } from '@/services/api/master-data'; import { ProductApi } from '@/services/api/master-data';
import { LogisticService } from '@/services/api/logistic';
import Table from '@/components/Table'; import Table from '@/components/Table';
import { ColumnDef } from '@tanstack/react-table'; import { ColumnDef } from '@tanstack/react-table';
import { formatCurrency, formatDate } from '@/lib/helper'; import { formatCurrency, formatDate } from '@/lib/helper';
import {
SupplierWithItems,
LogisticPurchasePerSupplierItems,
} from '@/types/api/report/logistic-stock';
import { isResponseSuccess } from '@/lib/api-helper';
import { useTableFilter } from '@/services/hooks/useTableFilter';
interface Totals {
totalQty: number;
totalPrice: number;
totalPurchaseAmount: number;
totalTransport: number;
totalValueTransport: number;
totalJumlah: number;
}
const PurchasesPerSupplierTab = () => { const PurchasesPerSupplierTab = () => {
const [selectedArea, setSelectedArea] = useState<number | null>(null);
const [selectedSupplier, setSelectedSupplier] = useState<number | null>(null);
const [selectedProduct, setSelectedProduct] = useState<number | null>(null);
const [dataType, setDataType] = useState<'received_date' | 'po_date'>( const [dataType, setDataType] = useState<'received_date' | 'po_date'>(
'received_date' 'received_date'
); );
const [startDate, setStartDate] = useState<string | null>(null);
const [endDate, setEndDate] = useState<string | null>(null); // ===== TABLE FILTER STATE =====
const {
state: tableFilterState,
updateFilter,
toQueryString: getTableFilterQueryString,
} = useTableFilter({
initial: {
area_id: '',
supplier_id: '',
product_id: '',
received_date: '',
po_date: '',
start_date: '',
end_date: '',
},
paramMap: {
page: 'page',
pageSize: 'limit',
},
});
const updateDataType = useCallback(
(newDataType: 'received_date' | 'po_date') => {
setDataType(newDataType);
updateFilter('received_date', '');
updateFilter('po_date', '');
},
[updateFilter]
);
const { options: areaOptions, isLoadingOptions: isLoadingAreas } = useSelect( const { options: areaOptions, isLoadingOptions: isLoadingAreas } = useSelect(
AreaApi.basePath, AreaApi.basePath,
@@ -40,147 +86,77 @@ const PurchasesPerSupplierTab = () => {
[] []
); );
const data = useMemo( const areaChangeHandler = useCallback(
() => [ (val: OptionType | OptionType[] | null) => {
{ const newVal = val as OptionType;
id: 1, updateFilter('area_id', newVal?.value ? String(newVal.value) : '');
supplier: 'PT. RAJAWALI MITRA PAKANINDO',
items: [
{
id: 1,
received_date: '2025-09-26',
po_date: '2025-09-30',
po_number: 'PO-MBU-00670',
product_name: 'KAPORIT BESAR (TCCA) 200 GR @1 KG',
destination_warehouse: 'GUDANG CIMARAGAS 1',
qty: 5,
price: 45000,
purchase_amount: 225000,
transport: 0,
value_transport: 0,
total: 225000,
expedition_vendor_name: 'PT. RAJAWALI MITRA PAKANINDO',
travel_number: '-',
}, },
{ [updateFilter]
id: 2,
received_date: '2025-09-30',
po_date: '2025-09-30',
po_number: 'PO-MBU-00670',
product_name: 'KAPORIT BESAR (TCCA) 200 GR @1 KG',
destination_warehouse: 'GUDANG CIMARAGAS 2',
qty: 5,
price: 45000,
purchase_amount: 225000,
transport: 0,
value_transport: 0,
total: 225000,
expedition_vendor_name: 'PT. RAJAWALI MITRA PAKANINDO',
travel_number: '-',
},
{
id: 3,
received_date: '2025-09-30',
po_date: '2025-09-30',
po_number: 'PO-MBU-00670',
product_name: 'KAPORIT BESAR (TCCA) 200 GR @1 KG',
destination_warehouse: 'GUDANG CIMARAGAS 3',
qty: 5,
price: 45000,
purchase_amount: 225000,
transport: 0,
value_transport: 0,
total: 225000,
expedition_vendor_name: 'PT. RAJAWALI MITRA PAKANINDO',
travel_number: '-',
},
{
id: 4,
received_date: '2025-09-30',
po_date: '2025-09-30',
po_number: 'PO-MBU-00670',
product_name: 'KAPORIT BESAR (TCCA) 200 GR @1 KG',
destination_warehouse: 'GUDANG CIMARAGAS 4',
qty: 5,
price: 45000,
purchase_amount: 225000,
transport: 0,
value_transport: 0,
total: 225000,
expedition_vendor_name: 'PT. RAJAWALI MITRA PAKANINDO',
travel_number: '-',
},
{
id: 5,
received_date: '2025-09-04',
po_date: '2025-09-30',
po_number: 'PO-MBU-00606',
product_name: 'DESINFEKTAN C 100 @20L',
destination_warehouse: 'GUDANG MANDALAWANGI 1',
qty: 1,
price: 800000,
purchase_amount: 800000,
transport: 0,
value_transport: 0,
total: 800000,
expedition_vendor_name: 'PT. RAJAWALI MITRA PAKANINDO',
travel_number: '-',
},
],
},
{
id: 2,
supplier: 'Supplier B',
items: [
{
id: 6,
received_date: '2024-01-18',
po_date: '2024-01-13',
po_number: 'PO-2024-004',
product_name: 'Produk D',
destination_warehouse: 'Gudang Pusat',
qty: 200,
price: 25000,
purchase_amount: 5000000,
transport: 200000,
value_transport: 200000,
total: 5200000,
expedition_vendor_name: 'Ekspedisi GHI',
travel_number: 'SJ-004',
},
],
},
{
id: 3,
supplier: 'Supplier C',
items: [
{
id: 7,
received_date: '2024-01-20',
po_date: '2024-01-15',
po_number: 'PO-2024-006',
product_name: 'Produk F',
destination_warehouse: 'Gudang Cabang',
qty: 80,
price: 55000,
purchase_amount: 4400000,
transport: 80000,
value_transport: 80000,
total: 4480000,
expedition_vendor_name: 'Ekspedisi MNO',
travel_number: 'SJ-006',
},
],
},
],
[]
); );
// TODO START const supplierChangeHandler = useCallback(
// eslint-disable-next-line @typescript-eslint/no-explicit-any (val: OptionType | OptionType[] | null) => {
const getTableColumns = (totals: any) => { const newVal = val as OptionType;
// eslint-disable-next-line @typescript-eslint/no-explicit-any updateFilter('supplier_id', newVal?.value ? String(newVal.value) : '');
const tableColumns: ColumnDef<any>[] = [ },
[updateFilter]
);
const productChangeHandler = useCallback(
(val: OptionType | OptionType[] | null) => {
const newVal = val as OptionType;
updateFilter('product_id', newVal?.value ? String(newVal.value) : '');
},
[updateFilter]
);
const dataTypeChangeHandler = useCallback(
(val: OptionType | OptionType[] | null) => {
const newVal = val as OptionType;
updateDataType(
(newVal?.value as 'received_date' | 'po_date') || 'received_date'
);
},
[updateDataType]
);
const startDateChangeHandler = useCallback<
ChangeEventHandler<HTMLInputElement>
>(
(e) => {
const val = e.target.value;
updateFilter('start_date', val || '');
if (val && dataType) {
updateFilter(dataType, val);
}
},
[updateFilter, dataType]
);
const endDateChangeHandler = useCallback<
ChangeEventHandler<HTMLInputElement>
>(
(e) => {
const val = e.target.value;
updateFilter('end_date', val || '');
},
[updateFilter]
);
// ===== DATA FETCHING =====
const { data: response, isLoading } = useSWR(
`${LogisticService.basePath}/purchase-supplier${getTableFilterQueryString()}`,
LogisticService.getAllFetcher
);
const data: SupplierWithItems[] = isResponseSuccess(response)
? (response?.data as unknown as SupplierWithItems[])
: [];
const getTableColumns = (
totals: Totals
): ColumnDef<LogisticPurchasePerSupplierItems>[] => {
const tableColumns: ColumnDef<LogisticPurchasePerSupplierItems>[] = [
{ {
id: 'no', id: 'no',
header: 'No', header: 'No',
@@ -191,38 +167,53 @@ const PurchasesPerSupplierTab = () => {
id: 'received_date', id: 'received_date',
header: 'Tanggal Terima', header: 'Tanggal Terima',
accessorKey: 'received_date', accessorKey: 'received_date',
cell: (props) => formatDate(props.getValue() as string, 'DD MMM YYYY'), cell: (props) => {
const value = props.row.original.received_date;
return formatDate(value, 'DD MMM YYYY');
},
}, },
{ {
id: 'po_date', id: 'po_date',
header: 'Tanggal PO', header: 'Tanggal PO',
accessorKey: 'po_date', accessorKey: 'po_date',
cell: (props) => formatDate(props.getValue() as string, 'DD MMM YYYY'), cell: (props) => {
const value = props.row.original.po_date;
return formatDate(value, 'DD MMM YYYY');
},
}, },
{ {
id: 'po_number', id: 'po_number',
header: 'No. Referensi', header: 'No. Referensi',
accessorKey: 'po_number', accessorKey: 'po_number',
cell: (props) => props.getValue() || '-', cell: (props) => {
const value = props.row.original.po_number;
return value || '-';
},
}, },
{ {
id: 'product_name', id: 'product_name',
header: 'Nama Produk', header: 'Nama Produk',
accessorKey: 'product_name', accessorKey: 'product.name',
cell: (props) => props.getValue() || '-', cell: (props) => {
const product = props.row.original.product;
return product?.name || '-';
},
}, },
{ {
id: 'destination_warehouse', id: 'destination_warehouse',
header: 'Tujuan', header: 'Tujuan',
accessorKey: 'destination_warehouse', accessorKey: 'destination_warehouse',
cell: (props) => props.getValue() || '-', cell: (props) => {
const value = props.row.original.destination_warehouse;
return value || '-';
},
}, },
{ {
id: 'qty', id: 'qty',
header: 'QTY', header: 'QTY',
accessorKey: 'qty', accessorKey: 'qty',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.row.original.qty;
return <div className='text-right'>{value.toLocaleString()}</div>; return <div className='text-right'>{value.toLocaleString()}</div>;
}, },
footer: () => ( footer: () => (
@@ -236,7 +227,7 @@ const PurchasesPerSupplierTab = () => {
header: 'Harga Beli (Rp)', header: 'Harga Beli (Rp)',
accessorKey: 'price', accessorKey: 'price',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.row.original.price;
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => ( footer: () => (
@@ -248,9 +239,9 @@ const PurchasesPerSupplierTab = () => {
{ {
id: 'purchase_amount', id: 'purchase_amount',
header: 'Value Harga Beli (Rp)', header: 'Value Harga Beli (Rp)',
accessorKey: 'purchase_amount',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const item = props.row.original;
const value = (item.price || 0) * (item.qty || 0);
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => ( footer: () => (
@@ -262,9 +253,9 @@ const PurchasesPerSupplierTab = () => {
{ {
id: 'transport', id: 'transport',
header: 'Transport (Rp)', header: 'Transport (Rp)',
accessorKey: 'transport', accessorKey: 'transport_per_item',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.row.original.transport_per_item;
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => ( footer: () => (
@@ -276,9 +267,9 @@ const PurchasesPerSupplierTab = () => {
{ {
id: 'value_transport', id: 'value_transport',
header: 'Value Transport (Rp)', header: 'Value Transport (Rp)',
accessorKey: 'value_transport', accessorKey: 'transport_total',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const value = props.row.original.transport_total;
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => ( footer: () => (
@@ -290,9 +281,10 @@ const PurchasesPerSupplierTab = () => {
{ {
id: 'total', id: 'total',
header: 'Jumlah (Rp)', header: 'Jumlah (Rp)',
accessorKey: 'total',
cell: (props) => { cell: (props) => {
const value = props.getValue() as number; const item = props.row.original;
const value =
(item.price || 0) * (item.qty || 0) + (item.transport_total || 0);
return <div className='text-right'>{formatCurrency(value)}</div>; return <div className='text-right'>{formatCurrency(value)}</div>;
}, },
footer: () => ( footer: () => (
@@ -305,66 +297,88 @@ const PurchasesPerSupplierTab = () => {
id: 'expedition_vendor_name', id: 'expedition_vendor_name',
header: 'Ekspedisi', header: 'Ekspedisi',
accessorKey: 'expedition_vendor_name', accessorKey: 'expedition_vendor_name',
cell: (props) => props.getValue() || '-', cell: (props) => {
const value = props.row.original.expedition_vendor_name;
return value || '-';
},
}, },
{ {
id: 'travel_number', id: 'travel_number',
header: 'Surat Jalan', header: 'Surat Jalan',
accessorKey: 'travel_number', accessorKey: 'travel_number',
cell: (props) => props.getValue() || '-', cell: (props) => {
const value = props.row.original.travel_number;
return value || '-';
},
}, },
]; ];
return tableColumns; return tableColumns;
}; };
return ( return (
<> <div className='w-full p-0 sm:p-4'>
<Card <Card
subtitle='Laporan > Rekapitulasi Pembelian Per Supplier' subtitle='Laporan > Rekapitulasi Pembelian Per Supplier'
className={{ wrapper: 'w-full', body: 'p-1!' }} className={{ wrapper: 'w-full', body: 'p-1!' }}
> >
<div className='grid grid-cols-1 md:grid-cols-3 gap-4'> <div className='grid grid-cols-12 gap-4'>
{/* TODO START */} {/* TODO START */}
<SelectInput <SelectInput
label='Area' label='Area'
placeholder='Pilih Area' placeholder='Pilih Area'
options={areaOptions} options={areaOptions}
value={ value={
areaOptions.find((option) => option.value === selectedArea) || tableFilterState.area_id
null ? areaOptions.find(
(option) =>
option.value === Number(tableFilterState.area_id)
) || null
: null
} }
// @ts-expect-error TS2345 onChange={areaChangeHandler}
onChange={(val) => setSelectedArea(val?.value || null)}
isLoading={isLoadingAreas} isLoading={isLoadingAreas}
isClearable isClearable
className={{
wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
<SelectInput <SelectInput
label='Supplier' label='Supplier'
placeholder='Pilih Supplier' placeholder='Pilih Supplier'
options={supplierOptions} options={supplierOptions}
value={ value={
supplierOptions.find( tableFilterState.supplier_id
(option) => option.value === selectedSupplier ? supplierOptions.find(
(option) =>
option.value === Number(tableFilterState.supplier_id)
) || null ) || null
: null
} }
// @ts-expect-error TS2345 onChange={supplierChangeHandler}
onChange={(val) => setSelectedSupplier(val?.value || null)}
isLoading={isLoadingSuppliers} isLoading={isLoadingSuppliers}
isClearable isClearable
className={{
wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
<SelectInput <SelectInput
label='Produk' label='Produk'
placeholder='Pilih Produk' placeholder='Pilih Produk'
options={productOptions} options={productOptions}
value={ value={
productOptions.find( tableFilterState.product_id
(option) => option.value === selectedProduct ? productOptions.find(
(option) =>
option.value === Number(tableFilterState.product_id)
) || null ) || null
: null
} }
// @ts-expect-error TS2345 onChange={productChangeHandler}
onChange={(val) => setSelectedProduct(val?.value || null)}
isLoading={isLoadingProducts} isLoading={isLoadingProducts}
isClearable isClearable
className={{
wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
<SelectInput <SelectInput
label='Tipe Data' label='Tipe Data'
@@ -374,31 +388,39 @@ const PurchasesPerSupplierTab = () => {
dataTypeOptions?.find((option) => option.value === dataType) || dataTypeOptions?.find((option) => option.value === dataType) ||
null null
} }
// @ts-expect-error TS2345 onChange={dataTypeChangeHandler}
onChange={(val) => setDataType(val?.value || 'received_date')}
isLoading={false} isLoading={false}
isClearable={false} isClearable={false}
className={{
wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
<DateInput <DateInput
label='Tanggal Awal' label='Tanggal Awal'
name='start_date'
placeholder='Pilih Tanggal Awal' placeholder='Pilih Tanggal Awal'
// @ts-expect-error TS2345 value={tableFilterState.start_date}
value={startDate} onChange={startDateChangeHandler}
// @ts-expect-error TS2345 className={{
onChange={setStartDate} wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
<DateInput <DateInput
label='Tanggal Akhir' label='Tanggal Akhir'
name='end_date'
placeholder='Pilih Tanggal Akhir' placeholder='Pilih Tanggal Akhir'
// @ts-expect-error TS2345 value={tableFilterState.end_date}
value={endDate} onChange={endDateChangeHandler}
// @ts-expect-error TS2345 className={{
onChange={setEndDate} wrapper: 'col-span-12 sm:col-span-4',
}}
/> />
{/* TODO END */} {/* TODO END */}
</div> </div>
{data.length === 0 ? ( {isLoading ? (
<div className='mt-6 text-center text-gray-500'>Memuat data...</div>
) : data.length === 0 ? (
<div className='mt-6 text-center text-gray-500'> <div className='mt-6 text-center text-gray-500'>
Tidak ada data untuk ditampilkan. Tidak ada data untuk ditampilkan.
</div> </div>
@@ -409,30 +431,30 @@ const PurchasesPerSupplierTab = () => {
0 0
); );
const totalPrice = supplier.items.reduce( const totalPrice = supplier.items.reduce(
(sum, item) => sum + (item.price || 0), (sum, item) => sum + (item.price || 0) * (item.qty || 0),
0
);
const totalPurchaseAmount = supplier.items.reduce(
(sum, item) => sum + (item.purchase_amount || 0),
0 0
); );
const totalTransport = supplier.items.reduce( const totalTransport = supplier.items.reduce(
(sum, item) => sum + (item.transport || 0), (sum, item) =>
sum + (item.transport_per_item || 0) * (item.qty || 0),
0 0
); );
const totalValueTransport = supplier.items.reduce( const totalValueTransport = supplier.items.reduce(
(sum, item) => sum + (item.value_transport || 0), (sum, item) => sum + (item.transport_total || 0),
0 0
); );
const totalJumlah = supplier.items.reduce( const totalJumlah = supplier.items.reduce(
(sum, item) => sum + (item.total || 0), (sum, item) =>
sum +
(item.price || 0) * (item.qty || 0) +
(item.transport_total || 0),
0 0
); );
const totals = { const totals = {
totalQty, totalQty,
totalPrice, totalPrice,
totalPurchaseAmount, totalPurchaseAmount: totalPrice,
totalTransport, totalTransport,
totalValueTransport, totalValueTransport,
totalJumlah, totalJumlah,
@@ -444,7 +466,7 @@ const PurchasesPerSupplierTab = () => {
return ( return (
<Card <Card
key={supplier.id} key={supplier.id}
title={supplier.supplier} title={supplier.supplier.name}
subtitle={`Total Pembelian: ${formatCurrency(totalPurchase)}`} subtitle={`Total Pembelian: ${formatCurrency(totalPurchase)}`}
className={{ wrapper: 'w-full' }} className={{ wrapper: 'w-full' }}
collapsible={true} collapsible={true}
@@ -455,7 +477,7 @@ const PurchasesPerSupplierTab = () => {
pageSize={10} pageSize={10}
renderFooter={supplier.items.length > 0} renderFooter={supplier.items.length > 0}
className={{ className={{
containerClassName: 'mb-0!', containerClassName: 'w-full',
tableWrapperClassName: 'overflow-x-auto mt-4', tableWrapperClassName: 'overflow-x-auto mt-4',
tableClassName: 'w-full table-auto text-sm', tableClassName: 'w-full table-auto text-sm',
headerRowClassName: 'border-b border-b-gray-200 bg-gray-50', headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
@@ -470,7 +492,6 @@ const PurchasesPerSupplierTab = () => {
footerRowClassName: 'border-t-2 border-gray-300', footerRowClassName: 'border-t-2 border-gray-300',
footerColumnClassName: footerColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap', 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
paginationClassName: 'hidden',
}} }}
/> />
</Card> </Card>
@@ -478,7 +499,7 @@ const PurchasesPerSupplierTab = () => {
}) })
)} )}
</Card> </Card>
</> </div>
); );
}; };
+36 -1
View File
@@ -2,6 +2,12 @@ import { BaseApiService } from '@/services/api/base';
import { BaseApiResponse } from '@/types/api/api-general'; import { BaseApiResponse } from '@/types/api/api-general';
import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock'; import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock';
const baseLogisticApi = new BaseApiService<
LogisticPurchasePerSupplierReport,
unknown,
unknown
>('/reports');
export class LogisticApi extends BaseApiService< export class LogisticApi extends BaseApiService<
LogisticPurchasePerSupplierReport, LogisticPurchasePerSupplierReport,
unknown, unknown,
@@ -37,4 +43,33 @@ export class LogisticApi extends BaseApiService<
} }
} }
export const LogisticService = new LogisticApi('/reports'); export const LogisticService = {
basePath: baseLogisticApi.basePath,
header: baseLogisticApi.header,
getAllFetcher: baseLogisticApi.getAllFetcher.bind(baseLogisticApi),
getSingle: baseLogisticApi.getSingle.bind(baseLogisticApi),
create: baseLogisticApi.create.bind(baseLogisticApi),
update: baseLogisticApi.update.bind(baseLogisticApi),
delete: baseLogisticApi.delete.bind(baseLogisticApi),
customRequest: baseLogisticApi.customRequest.bind(baseLogisticApi),
// Custom method for specific endpoint
getLogisticStockReport: (
area_id?: number,
supplier_id?: number,
product_id?: number,
received_date?: string,
po_date?: string,
start_date?: string,
end_date?: string
) =>
new LogisticApi().getLogisticStockReport(
area_id,
supplier_id,
product_id,
received_date,
po_date,
start_date,
end_date
),
};
+6 -5
View File
@@ -1,7 +1,7 @@
import { BaseMetadata } from '@/types/api/api-general'; import { BaseMetadata } from '@/types/api/api-general';
import { Supplier } from '@/types/api/supplier/supplier'; import { Supplier } from '@/types/api/supplier/supplier';
import { Product } from '@/types/api/product/product'; import { Product } from '@/types/api/product/product';
import { Area } from '@types/api/area/area'; import { Area } from '@/types/api/area/area';
export type LogisticPurchasePerSupplierItems = { export type LogisticPurchasePerSupplierItems = {
id: number; id: number;
@@ -20,11 +20,12 @@ export type LogisticPurchasePerSupplierItems = {
travel_number: string; travel_number: string;
}; };
export type BaseLogisticPurchasePerSupplierReport = { export type SupplierWithItems = {
id: number; id: number;
supplier: Supplier; supplier: Supplier;
items: LogisticStockItems[]; items: LogisticPurchasePerSupplierItems[];
}; };
export type LogisticPurchasePerSupplierReport = BaseMetadata & export type LogisticPurchasePerSupplierReport = BaseMetadata & {
BaseLogisticStockReport; data: SupplierWithItems[];
};