[] => {
+ const tableColumns: ColumnDef<
+ LogisticPurchasePerSupplierReport['rows'][0]
+ >[] = [
+ {
+ id: 'no',
+ header: 'No',
+ cell: (props) => props.row.index + 1,
+ footer: () => Total
,
+ },
+
+ {
+ id: 'received_date',
+ header: 'Tanggal Terima',
+ accessorKey: 'receive_date',
+ cell: (props) => {
+ const value = props.row.original.receive_date;
+ return formatDate(value, 'DD MMM YYYY');
+ },
+ },
+ {
+ id: 'po_date',
+ header: 'Tanggal PO',
+ accessorKey: 'po_date',
+ cell: (props) => {
+ const value = props.row.original.po_date;
+ return formatDate(value, 'DD MMM YYYY');
+ },
+ },
+ {
+ id: 'po_number',
+ header: 'No. Referensi',
+ accessorKey: 'po_number',
+ cell: (props) => {
+ const value = props.row.original.po_number;
+ return value || '-';
+ },
+ },
+ {
+ id: 'product_name',
+ header: 'Nama Produk',
+ accessorKey: 'product.name',
+ cell: (props) => {
+ const product = props.row.original.product;
+ return product?.name || '-';
+ },
+ },
+ {
+ id: 'destination_warehouse',
+ header: 'Tujuan',
+ accessorKey: 'warehouse.name',
+ cell: (props) => {
+ const warehouse = props.row.original.warehouse;
+ return warehouse?.name || '-';
+ },
+ },
+ {
+ id: 'qty',
+ header: 'QTY',
+ accessorKey: 'qty',
+ cell: (props) => {
+ const value = props.row.original.qty;
+ return {formatNumber(value)}
;
+ },
+ footer: () => (
+
+ {formatNumber(summary.total_qty) || '-'}
+
+ ),
+ },
+ {
+ id: 'price',
+ header: 'Harga Beli (Rp)',
+ accessorKey: 'unit_price',
+ cell: (props) => {
+ const value = props.row.original.unit_price;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(summary.total_unit_price) || '-'}
+
+ ),
+ },
+ {
+ id: 'purchase_amount',
+ header: 'Value Harga Beli (Rp)',
+ accessorKey: 'purchase_value',
+ cell: (props) => {
+ const value = props.row.original.purchase_value;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(summary.total_purchase_value) || '-'}
+
+ ),
+ },
+ {
+ id: 'transport',
+ header: 'Transport (Rp)',
+ accessorKey: 'transport_unit_price',
+ cell: (props) => {
+ const value = props.row.original.transport_unit_price;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(summary.total_transport_unit_price) || '-'}
+
+ ),
+ },
+ {
+ id: 'value_transport',
+ header: 'Value Transport (Rp)',
+ accessorKey: 'transport_value',
+ cell: (props) => {
+ const value = props.row.original.transport_value;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(summary.total_transport_value) || '-'}
+
+ ),
+ },
+ {
+ id: 'total',
+ header: 'Jumlah (Rp)',
+ accessorKey: 'total_amount',
+ cell: (props) => {
+ const value = props.row.original.total_amount;
+ return {formatCurrency(value)}
;
+ },
+ footer: () => (
+
+ {formatCurrency(summary.total_amount) || '-'}
+
+ ),
+ },
+ {
+ id: 'expedition_vendor_name',
+ header: 'Ekspedisi',
+ accessorKey: 'expedition',
+ cell: (props) => {
+ const value = props.row.original.expedition;
+ return value || '-';
+ },
+ },
+ {
+ id: 'travel_number',
+ header: 'Surat Jalan',
+ accessorKey: 'delivery_number',
+ cell: (props) => {
+ const value = props.row.original.delivery_number;
+ return value || '-';
+ },
+ },
+ ];
+ return tableColumns;
+ };
+
+ return (
+
+
+
+
+
+
+ Export
+
+ }
+ align='end'
+ >
+
+
+
+
+
+ (tableFilterState.area_id || [])
+ .map(String)
+ .includes(String(opt.value))
+ )}
+ onChange={areaChangeHandler}
+ isLoading={isLoadingAreas}
+ isClearable
+ />
+
+ (tableFilterState.supplier_id || [])
+ .map(String)
+ .includes(String(opt.value))
+ )}
+ onChange={supplierChangeHandler}
+ isLoading={isLoadingSuppliers}
+ isClearable
+ />
+
+ (tableFilterState.product_id || [])
+ .map(String)
+ .includes(String(opt.value))
+ )}
+ onChange={productChangeHandler}
+ isLoading={isLoadingProducts}
+ isClearable
+ />
+
+
+
+ (tableFilterState.product_category_id || [])
+ .map(String)
+ .includes(String(opt.value))
+ )}
+ onChange={productCategoryChangeHandler}
+ isLoading={isLoadingProductCategories}
+ isClearable
+ />
+
+ option.value === tableFilterState.filter_by
+ ) || null
+ }
+ onChange={dataTypeChangeHandler}
+ isLoading={false}
+ isClearable={false}
+ />
+ option.value === tableFilterState.sort_by
+ ) || null
+ }
+ onChange={sortByHandler}
+ isLoading={false}
+ isClearable={false}
+ />
+
+
+
+
+
+
+
+ {!isSubmitted ? (
+
+ Silakan pilih filter dan klik tombol Submit untuk menampilkan data.
+
+ ) : isLoading ? (
+
+
+
+ ) : data.length === 0 ? (
+
+ Tidak ada data yang dapat ditampilkan...
+
+ ) : (
+ data.map((supplierReport) => {
+ const summary = supplierReport.summary || {
+ total_qty: 0,
+ total_unit_price: 0,
+ total_purchase_value: 0,
+ total_transport_unit_price: 0,
+ total_transport_value: 0,
+ total_amount: 0,
+ };
+
+ const totalPurchase = summary.total_amount;
+ const tableColumns = getTableColumns(summary);
+
+ return (
+
+ 0}
+ className={{
+ containerClassName: 'w-full',
+ tableWrapperClassName: 'overflow-x-auto mt-4',
+ tableClassName: 'w-full table-auto text-sm',
+ headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
+ headerColumnClassName:
+ 'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200',
+ bodyRowClassName:
+ 'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200',
+ bodyColumnClassName:
+ 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
+ tableFooterClassName:
+ 'bg-gray-100 font-semibold border border-gray-200',
+ footerRowClassName: 'border-t-2 border-gray-300',
+ footerColumnClassName:
+ 'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
+ paginationClassName: 'hidden',
+ }}
+ />
+
+ );
+ })
+ )}
+
+ {meta && data.length > 0 && (
+
+ )}
+
+ );
+};
+
+export default PurchasesPerSupplierTab;
diff --git a/src/config/constant.ts b/src/config/constant.ts
index 96fc8401..196afc18 100644
--- a/src/config/constant.ts
+++ b/src/config/constant.ts
@@ -45,6 +45,17 @@ export const MAIN_DRAWER_LINKS: SidebarMenuItem[] = [
link: '/closing',
icon: 'heroicons-outline:presentation-chart-bar',
},
+ {
+ text: 'Laporan',
+ link: '/report',
+ icon: 'mdi:chart-box-outline',
+ submenu: [
+ {
+ text: 'Logistik & Persediaan',
+ link: '/report/logistic-stock',
+ },
+ ],
+ },
{
text: 'Persediaan',
link: '/inventory',
diff --git a/src/services/api/report/logistic-stock.ts b/src/services/api/report/logistic-stock.ts
new file mode 100644
index 00000000..13ac5032
--- /dev/null
+++ b/src/services/api/report/logistic-stock.ts
@@ -0,0 +1,54 @@
+import { BaseApiService } from '@/services/api/base';
+import { BaseApiResponse } from '@/types/api/api-general';
+import { LogisticPurchasePerSupplierReport } from '@/types/api/report/logistic-stock';
+
+export class LogisticApiService extends BaseApiService<
+ LogisticPurchasePerSupplierReport,
+ unknown,
+ unknown
+> {
+ constructor(basePath: string) {
+ super(basePath);
+ }
+
+ async getLogisticPurchasePerSupplierReport(
+ area_id?: string,
+ supplier_id?: string,
+ product_id?: string,
+ product_category_id?: string,
+ received_date?: string,
+ po_date?: string,
+ start_date?: string,
+ end_date?: string,
+ sort_by?: string,
+ filter_by?: string,
+ page?: number,
+ limit?: number
+ ): Promise | undefined> {
+ return await this.customRequest<
+ BaseApiResponse
+ >(`purchase-supplier`, {
+ method: 'GET',
+ params: {
+ area_id: area_id,
+ supplier_id: supplier_id,
+ product_id: product_id,
+ product_category_id: product_category_id,
+ received_date: received_date,
+ po_date: po_date,
+ start_date: start_date,
+ end_date: end_date,
+ sort_by: sort_by,
+ filter_by: filter_by,
+ page: page,
+ limit: limit,
+ },
+ });
+ }
+}
+
+export const LogisticApi = new LogisticApiService('reports');
+
+// export const LogisticApi = new LogisticApiService(
+// 'http://localhost:4010/api/reports/logistics'
+// );
diff --git a/src/types/api/report/logistic-stock.d.ts b/src/types/api/report/logistic-stock.d.ts
new file mode 100644
index 00000000..e5f0f2c6
--- /dev/null
+++ b/src/types/api/report/logistic-stock.d.ts
@@ -0,0 +1,35 @@
+import { BaseMetadata } from '@/types/api/api-general';
+import { Supplier } from '@/types/api/supplier/supplier';
+import { Product } from '@/types/api/product/product';
+import { Warehouse } from '@/types/api/warehouse/warehouse';
+
+export type LogisticPurchasePerSupplierReportRow = {
+ receive_date: string;
+ po_date: string;
+ po_number: string;
+ product: Product;
+ warehouse: Warehouse;
+ qty: number;
+ unit_price: number;
+ purchase_value: number;
+ transport_unit_price: number;
+ transport_value: number;
+ total_amount: number;
+ expedition: string;
+ delivery_number: string;
+};
+
+export type LogisticPurchasePerSupplierSummary = {
+ total_qty: number;
+ total_unit_price: number;
+ total_purchase_value: number;
+ total_transport_unit_price: number;
+ total_transport_value: number;
+ total_amount: number;
+};
+
+export type LogisticPurchasePerSupplierReport = BaseMetadata & {
+ supplier: Supplier;
+ rows: LogisticPurchasePerSupplierReportRow[];
+ summary: LogisticPurchasePerSupplierSummary;
+};