mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
fix(FE): resolve conflit merge development
This commit is contained in:
@@ -4,6 +4,7 @@ import { useRouter, useSearchParams } from 'next/navigation';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import ClosingDetail from '@/components/pages/closing/ClosingDetail';
|
||||
import SalesReportTable from '@/components/pages/closing/sale/SalesReportTable';
|
||||
|
||||
import { ClosingApi } from '@/services/api/closing';
|
||||
import { isResponseError, isResponseSuccess } from '@/lib/api-helper';
|
||||
@@ -19,6 +20,11 @@ const ClosingDetailPage = () => {
|
||||
(id: number) => ClosingApi.getGeneralInfo(id)
|
||||
);
|
||||
|
||||
const { data: salesReport, isLoading: isLoadingSalesReport } = useSWR(
|
||||
closingId,
|
||||
(id: number) => ClosingApi.getPenjualan(id)
|
||||
);
|
||||
|
||||
if (!closingId) {
|
||||
router.back();
|
||||
|
||||
@@ -43,6 +49,9 @@ const ClosingDetailPage = () => {
|
||||
{!isLoadingClosing && isResponseSuccess(closing) && (
|
||||
<ClosingDetail id={Number(closingId)} initialValue={closing.data} />
|
||||
)}
|
||||
{!isLoadingSalesReport && isResponseSuccess(salesReport) && (
|
||||
<SalesReportTable type='detail' initialValues={salesReport.data} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -52,6 +52,7 @@ export default function ProjectFlockLayout({
|
||||
closeOnBackdropClick={isDetail ? true : false}
|
||||
onBackdropClick={handleBackdropClick}
|
||||
variant='right'
|
||||
zIndex='99999'
|
||||
sidebarContent={isOpen && <div className=''>{children}</div>}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -54,7 +54,7 @@ const FloatingActionsButton = ({
|
||||
<div
|
||||
className={cn(
|
||||
`absolute ${positionStyles} inset-x-1/2 -translate-x-1/2 z-50`,
|
||||
'mx-auto w-full max-w-lg sm:mx-0 bg-base-300 p-4 rounded-xl shadow-md transition-all duration-300 transform',
|
||||
'mx-auto w-full max-w-sm sm:mx-0 bg-base-300 p-4 rounded-xl shadow-md transition-all duration-300 transform',
|
||||
'bg-slate-950 backdrop-blur-md'
|
||||
)}
|
||||
>
|
||||
|
||||
+13
-12
@@ -7,6 +7,7 @@ import { Icon } from '@iconify/react';
|
||||
import Menu from '@/components/menu/Menu';
|
||||
import MenuItem from '@/components/menu/MenuItem';
|
||||
import Button from '@/components/Button';
|
||||
import Dropdown from '@/components/dropdown/Dropdown';
|
||||
|
||||
import { useAuth } from '@/services/hooks/useAuth';
|
||||
import { AuthApi } from '@/services/api/auth';
|
||||
@@ -52,21 +53,21 @@ const Navbar = ({ title, toggleSidebar }: NavbarProps) => {
|
||||
</div>
|
||||
|
||||
<div className='flex gap-2'>
|
||||
<div className='dropdown dropdown-end'>
|
||||
<div
|
||||
tabIndex={0}
|
||||
role='button'
|
||||
className='btn btn-ghost btn-circle avatar'
|
||||
>
|
||||
<div className='w-10 rounded-full border grid place-items-center'>
|
||||
<Icon icon='uil:user' width={40} height={40} />
|
||||
<Dropdown
|
||||
position='bottom-end'
|
||||
trigger={
|
||||
<div className='btn btn-ghost btn-circle avatar'>
|
||||
<div className='w-10 rounded-full border flex justify-center items-center'>
|
||||
<Icon icon='uil:user' width={40} height={40} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Menu className='dropdown-content w-52 mt-3 p-2 bg-base-100 shadow rounded-box menu-sm'>
|
||||
}
|
||||
contentClassName='w-52 mt-3'
|
||||
>
|
||||
<Menu className='p-2 bg-base-100 shadow rounded-box menu-sm'>
|
||||
<MenuItem title='Logout' onClick={logoutClickHandler} />
|
||||
</Menu>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
'use client';
|
||||
|
||||
import { ReactNode, useRef, useEffect, useState } from 'react';
|
||||
import { cn } from '@/lib/helper';
|
||||
|
||||
interface DropdownProps {
|
||||
trigger: ReactNode;
|
||||
children: ReactNode;
|
||||
position?:
|
||||
| 'top'
|
||||
| 'bottom'
|
||||
| 'left'
|
||||
| 'right'
|
||||
| 'top-start'
|
||||
| 'top-end'
|
||||
| 'bottom-start'
|
||||
| 'bottom-end'
|
||||
| 'left-start'
|
||||
| 'left-end'
|
||||
| 'right-start'
|
||||
| 'right-end';
|
||||
align?: 'start' | 'center' | 'end';
|
||||
hover?: boolean;
|
||||
className?: string;
|
||||
contentClassName?: string;
|
||||
}
|
||||
|
||||
const Dropdown = ({
|
||||
trigger,
|
||||
children,
|
||||
position = 'bottom',
|
||||
align = 'start',
|
||||
hover = false,
|
||||
className,
|
||||
contentClassName,
|
||||
}: DropdownProps) => {
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Handle click outside to close dropdown
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent) => {
|
||||
if (
|
||||
dropdownRef.current &&
|
||||
!dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
setIsOpen(false);
|
||||
}
|
||||
};
|
||||
|
||||
if (isOpen) {
|
||||
document.addEventListener('mousedown', handleClickOutside);
|
||||
}
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside);
|
||||
};
|
||||
}, [isOpen]);
|
||||
|
||||
// Build position classes
|
||||
const getPositionClasses = () => {
|
||||
const classes: string[] = [];
|
||||
|
||||
// Handle combined positions like 'top-start'
|
||||
if (position.includes('-')) {
|
||||
const [pos, al] = position.split('-');
|
||||
classes.push(`dropdown-${pos}`);
|
||||
classes.push(`dropdown-${al}`);
|
||||
} else {
|
||||
classes.push(`dropdown-${position}`);
|
||||
if (align !== 'start') {
|
||||
classes.push(`dropdown-${align}`);
|
||||
}
|
||||
}
|
||||
|
||||
return classes.join(' ');
|
||||
};
|
||||
|
||||
const handleToggle = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// alert('clicked');
|
||||
setIsOpen(!isOpen);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={cn(
|
||||
'dropdown',
|
||||
getPositionClasses(),
|
||||
hover && 'dropdown-hover',
|
||||
isOpen && 'dropdown-open',
|
||||
className
|
||||
)}
|
||||
>
|
||||
{/* Trigger Button */}
|
||||
<div onClick={handleToggle} className='cursor-pointer'>
|
||||
{trigger}
|
||||
</div>
|
||||
|
||||
{/* Dropdown Content - Only render when open */}
|
||||
{isOpen && (
|
||||
<div
|
||||
tabIndex={-1}
|
||||
className={cn('dropdown-content z-[10]', contentClassName)}
|
||||
onClick={() => setIsOpen(false)} // Close on item click
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Dropdown;
|
||||
@@ -0,0 +1,285 @@
|
||||
'use client';
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { ColumnDef } from '@tanstack/react-table';
|
||||
import Table from '@/components/Table';
|
||||
import Card from '@/components/Card';
|
||||
import Badge from '@/components/Badge';
|
||||
import { formatCurrency, formatNumber, formatDate } from '@/lib/helper';
|
||||
import { BaseClosingSales, BaseSales } from '@/types/api/closing';
|
||||
import { Product } from '@/types/api/master-data/product';
|
||||
import { Customer } from '@/types/api/master-data/customer';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
|
||||
interface SalesReportTableProps {
|
||||
type?: 'detail';
|
||||
initialValues?: BaseClosingSales;
|
||||
}
|
||||
|
||||
const SalesReportTable = ({
|
||||
type = 'detail',
|
||||
initialValues,
|
||||
}: SalesReportTableProps) => {
|
||||
const salesData: BaseSales[] = useMemo(() => {
|
||||
return initialValues?.sales || [];
|
||||
}, [initialValues]);
|
||||
|
||||
const totals = useMemo(() => {
|
||||
if (salesData.length === 0) {
|
||||
return {
|
||||
totalQuantity: 0,
|
||||
totalWeight: 0,
|
||||
avgWeight: 0,
|
||||
avgPricePartner: 0,
|
||||
totalPartner: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const totalQuantity = salesData.reduce(
|
||||
(sum, item) => sum + (item.qty || 0),
|
||||
0
|
||||
);
|
||||
const totalWeight = salesData.reduce(
|
||||
(sum, item) => sum + (item.weight || 0),
|
||||
0
|
||||
);
|
||||
const avgWeight = totalQuantity > 0 ? totalWeight / totalQuantity : 0;
|
||||
|
||||
const validPriceItems = salesData.filter(
|
||||
(item) => item.price != null && item.price > 0
|
||||
);
|
||||
const avgPricePartner =
|
||||
validPriceItems.length > 0
|
||||
? validPriceItems.reduce((sum, item) => sum + item.price, 0) /
|
||||
validPriceItems.length
|
||||
: 0;
|
||||
|
||||
const totalPartner = salesData.reduce(
|
||||
(sum, item) => sum + (item.total_price || 0),
|
||||
0
|
||||
);
|
||||
|
||||
return {
|
||||
totalQuantity,
|
||||
totalWeight,
|
||||
avgWeight,
|
||||
avgPricePartner,
|
||||
totalPartner,
|
||||
};
|
||||
}, [salesData]);
|
||||
|
||||
const salesColumns: ColumnDef<BaseSales>[] = useMemo(
|
||||
() => [
|
||||
{
|
||||
id: 'realization_date',
|
||||
accessorKey: 'realization_date',
|
||||
header: 'Tanggal Realisasi',
|
||||
cell: (props) => {
|
||||
const date = props.row.original.realization_date;
|
||||
return date ? formatDate(date, 'DD MMM YYYY') : '-';
|
||||
},
|
||||
footer: () => (
|
||||
<div className='font-semibold text-gray-900'>Total Penjualan</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'age',
|
||||
accessorKey: 'age',
|
||||
header: 'Umur',
|
||||
cell: (props) => props.getValue() || '-',
|
||||
},
|
||||
{
|
||||
id: 'do_number',
|
||||
accessorKey: 'do_number',
|
||||
header: 'No. DO',
|
||||
cell: (props) => props.getValue() || '-',
|
||||
},
|
||||
{
|
||||
id: 'product',
|
||||
accessorKey: 'product',
|
||||
header: 'Produk',
|
||||
cell: (props) => {
|
||||
const product = props.getValue() as Product;
|
||||
return product?.name || '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'customer',
|
||||
accessorKey: 'customer',
|
||||
header: 'Customer',
|
||||
cell: (props) => {
|
||||
const customer = props.getValue() as Customer;
|
||||
return customer?.name || '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'jumlah',
|
||||
header: 'Jumlah',
|
||||
columns: [
|
||||
{
|
||||
id: 'qty',
|
||||
accessorKey: 'qty',
|
||||
header: 'Kuantitas',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-left'>{formatNumber(value)}</div>;
|
||||
},
|
||||
footer: () => (
|
||||
<div className='text-left font-semibold text-gray-900'>
|
||||
{formatNumber(totals.totalQuantity)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'weight',
|
||||
accessorKey: 'weight',
|
||||
header: 'Kg',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-left'>{formatNumber(value)}</div>;
|
||||
},
|
||||
footer: () => (
|
||||
<div className='text-left font-semibold text-gray-900'>
|
||||
{formatNumber(totals.totalWeight)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 'avg_weight',
|
||||
accessorKey: 'avg_weight',
|
||||
header: 'AVG (Kg)',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-left'>{formatNumber(value)}</div>;
|
||||
},
|
||||
footer: () => (
|
||||
<div className='text-left font-semibold text-gray-900'>
|
||||
{formatNumber(totals.avgWeight)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'price_partner',
|
||||
accessorKey: 'price',
|
||||
header: 'Harga Mitra (Rp)',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||
},
|
||||
footer: () => (
|
||||
<div className='text-right font-semibold text-gray-900'>
|
||||
{formatCurrency(totals.avgPricePartner)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'total_mitra',
|
||||
accessorKey: 'total_price',
|
||||
header: 'Total Mitra (Rp)',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||
},
|
||||
footer: () => (
|
||||
<div className='text-right font-semibold text-gray-900'>
|
||||
{formatCurrency(totals.totalPartner)}
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: 'price_act',
|
||||
accessorKey: 'price',
|
||||
header: 'Harga Act (Rp)',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'total_act',
|
||||
accessorKey: 'total_price',
|
||||
header: 'Total Act (Rp)',
|
||||
cell: (props) => {
|
||||
const value = props.getValue() as number;
|
||||
return <div className='text-right'>{formatCurrency(value)}</div>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'kandang',
|
||||
accessorKey: 'kandang',
|
||||
header: 'Kandang',
|
||||
cell: (props) => {
|
||||
const kandang = props.getValue() as Kandang;
|
||||
return kandang?.name || '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'payment_status',
|
||||
accessorKey: 'payment_status',
|
||||
header: 'Status Pembayaran',
|
||||
cell: (props) => {
|
||||
const status = props.getValue() as string;
|
||||
const getStatusColor = (status: string) => {
|
||||
if (!status) return 'neutral';
|
||||
switch (status.toLowerCase()) {
|
||||
case 'paid':
|
||||
return 'success';
|
||||
case 'tempo':
|
||||
return 'warning';
|
||||
default:
|
||||
return 'neutral';
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Badge variant='soft' size='sm' color={getStatusColor(status)}>
|
||||
{status || '-'}
|
||||
</Badge>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<section className='w-full'>
|
||||
<div className='p-4'>
|
||||
<h2 className='text-xl font-semibold mb-4'>Penjualan</h2>
|
||||
<Card
|
||||
className={{
|
||||
wrapper: 'w-full bg-base-100',
|
||||
body: 'p-0',
|
||||
}}
|
||||
>
|
||||
<Table
|
||||
data={salesData}
|
||||
columns={salesColumns}
|
||||
renderFooter={salesData.length > 0}
|
||||
className={{
|
||||
tableWrapperClassName: 'overflow-x-auto',
|
||||
tableClassName: 'w-full table-auto text-sm',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end whitespace-nowrap border-l border-l-gray-200 border-r border-r-gray-200 border-t border-t-gray-200 border-gray-200 border-b-0',
|
||||
bodyRowClassName:
|
||||
'hover:bg-gray-50 transition-colors border-b border-gray-200 first:border-t first:border-t-gray-200 border-l border-l-gray-200 border-r 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',
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SalesReportTable;
|
||||
@@ -207,7 +207,7 @@ const ExpenseRealizationContent = ({
|
||||
let expenseGrandTotal = 0;
|
||||
|
||||
kandangExpense.pengajuans?.forEach(
|
||||
(item) => (expenseGrandTotal += item.total_price)
|
||||
(item) => (expenseGrandTotal += item.price)
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -238,7 +238,7 @@ const ExpenseRealizationContent = ({
|
||||
<tr key={pengajuanIdx}>
|
||||
<td>{pengajuanItem.nonstock.name}</td>
|
||||
<td>{pengajuanItem.qty}</td>
|
||||
<td>{formatCurrency(pengajuanItem.total_price)}</td>
|
||||
<td>{formatCurrency(pengajuanItem.price)}</td>
|
||||
<td className='w-xs'>{pengajuanItem.note ?? '-'}</td>
|
||||
</tr>
|
||||
)
|
||||
@@ -269,7 +269,7 @@ const ExpenseRealizationContent = ({
|
||||
let expenseGrandTotal = 0;
|
||||
|
||||
kandangExpense.realisasi?.forEach(
|
||||
(item) => (expenseGrandTotal += item.total_price)
|
||||
(item) => (expenseGrandTotal += item.price)
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -300,7 +300,7 @@ const ExpenseRealizationContent = ({
|
||||
<tr key={realisasiIdx}>
|
||||
<td>{realisasiItem.nonstock.name}</td>
|
||||
<td>{realisasiItem.qty}</td>
|
||||
<td>{formatCurrency(realisasiItem.total_price)}</td>
|
||||
<td>{formatCurrency(realisasiItem.price)}</td>
|
||||
<td className='w-xs'>{realisasiItem.note ?? '-'}</td>
|
||||
</tr>
|
||||
)
|
||||
|
||||
@@ -402,7 +402,10 @@ const ExpenseRequestContent = ({
|
||||
<th>Tanggal Transaksi</th>
|
||||
<th>:</th>
|
||||
<td>
|
||||
{formatDate(initialValues?.expense_date, 'DD MMMM YYYY')}
|
||||
{formatDate(
|
||||
initialValues?.transaction_date,
|
||||
'DD MMMM YYYY'
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -529,7 +532,7 @@ const ExpenseRequestContent = ({
|
||||
let expenseGrandTotal = 0;
|
||||
|
||||
kandangExpense.pengajuans?.forEach(
|
||||
(item) => (expenseGrandTotal += item.total_price)
|
||||
(item) => (expenseGrandTotal += item.price)
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -550,7 +553,7 @@ const ExpenseRequestContent = ({
|
||||
<tr>
|
||||
<th>Nonstock</th>
|
||||
<th>Total Kuantitas</th>
|
||||
<th>Total Biaya</th>
|
||||
<th>Harga Satuan</th>
|
||||
<th>Catatan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -560,9 +563,7 @@ const ExpenseRequestContent = ({
|
||||
<tr key={pengajuanIdx}>
|
||||
<td>{pengajuanItem.nonstock.name}</td>
|
||||
<td>{pengajuanItem.qty}</td>
|
||||
<td>
|
||||
{formatCurrency(pengajuanItem.total_price)}
|
||||
</td>
|
||||
<td>{formatCurrency(pengajuanItem.price)}</td>
|
||||
<td className='w-xs'>
|
||||
{pengajuanItem.note ?? '-'}
|
||||
</td>
|
||||
|
||||
@@ -263,11 +263,11 @@ const ExpensesTable = () => {
|
||||
},
|
||||
},
|
||||
{
|
||||
accessorKey: 'expense_date',
|
||||
accessorKey: 'transaction_date',
|
||||
header: 'Tanggal Pengajuan',
|
||||
cell: (props) =>
|
||||
props.row.original.expense_date
|
||||
? formatDate(props.row.original.expense_date, 'DD MMM YYYY')
|
||||
props.row.original.transaction_date
|
||||
? formatDate(props.row.original.transaction_date, 'DD MMM YYYY')
|
||||
: '-',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -27,7 +27,7 @@ type ExpenseRealizationFormSchemaType = {
|
||||
label: string;
|
||||
};
|
||||
quantity?: number;
|
||||
total_cost?: number;
|
||||
price?: number;
|
||||
notes?: string;
|
||||
}[];
|
||||
}[];
|
||||
@@ -82,7 +82,7 @@ export const ExpenseRealizationFormSchema: Yup.ObjectSchema<ExpenseRealizationFo
|
||||
label: Yup.string().required(),
|
||||
}).required('Nonstock wajib diisi!'),
|
||||
quantity: Yup.number().required('Total kuantitas wajib diisi!'),
|
||||
total_cost: Yup.number().required('Total biaya wajib diisi!'),
|
||||
price: Yup.number().required('Harga satuan wajib diisi!'),
|
||||
notes: Yup.string(),
|
||||
})
|
||||
)
|
||||
@@ -155,7 +155,7 @@ export const getExpenseRealizationFormInitialValues = (
|
||||
label: realisasiItem.nonstock.name,
|
||||
},
|
||||
quantity: realisasiItem.qty,
|
||||
total_cost: realisasiItem.total_price,
|
||||
price: realisasiItem.price,
|
||||
notes: realisasiItem.note,
|
||||
};
|
||||
})
|
||||
@@ -166,7 +166,7 @@ export const getExpenseRealizationFormInitialValues = (
|
||||
label: expenseItem.nonstock.name,
|
||||
},
|
||||
quantity: expenseItem.qty,
|
||||
total_cost: expenseItem.total_price,
|
||||
price: expenseItem.price,
|
||||
notes: expenseItem.note,
|
||||
}))
|
||||
: [];
|
||||
|
||||
@@ -98,15 +98,10 @@ const ExpenseRealizationForm = ({
|
||||
|
||||
values.realizations.forEach((realization) => {
|
||||
realization.cost_items.forEach((costItem) => {
|
||||
const unitPrice =
|
||||
parseFloat(String(costItem.total_cost)) /
|
||||
parseFloat(String(costItem.quantity));
|
||||
|
||||
const realizationItem = {
|
||||
expense_nonstock_id: costItem.nonstock?.value as number,
|
||||
qty: parseFloat(String(costItem.quantity)) as number,
|
||||
unit_price: unitPrice,
|
||||
total_price: parseFloat(String(costItem.total_cost)) as number,
|
||||
price: parseFloat(String(costItem.price)) as number,
|
||||
notes: costItem.notes ?? '',
|
||||
};
|
||||
|
||||
@@ -177,7 +172,7 @@ const ExpenseRealizationForm = ({
|
||||
{
|
||||
nonstock: undefined,
|
||||
quantity: undefined,
|
||||
total_cost: undefined,
|
||||
price: undefined,
|
||||
notes: '',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -48,7 +48,7 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
|
||||
};
|
||||
|
||||
const isExpenseRepeaterInputError = (
|
||||
column: 'nonstock' | 'quantity' | 'total_cost' | 'notes',
|
||||
column: 'nonstock' | 'quantity' | 'price' | 'notes',
|
||||
kandangExpenseIdx: number,
|
||||
expenseIdx: number
|
||||
) => {
|
||||
@@ -112,7 +112,7 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
|
||||
<tr>
|
||||
<th>Nonstock</th>
|
||||
<th>Total Kuantitas</th>
|
||||
<th>Total Biaya</th>
|
||||
<th>Harga Satuan</th>
|
||||
<th>Catatan</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -163,17 +163,17 @@ const ExpenseRealizationKandangDetailExpense: React.FC<
|
||||
|
||||
<td className='p-2'>
|
||||
<NumberInput
|
||||
name={`realizations[${kandangExpenseIdx}].cost_items[${expenseIdx}].total_cost`}
|
||||
placeholder='Masukkan Total Biaya'
|
||||
name={`realizations[${kandangExpenseIdx}].cost_items[${expenseIdx}].price`}
|
||||
placeholder='Masukkan Harga Satuan'
|
||||
value={
|
||||
formik.values.realizations[
|
||||
kandangExpenseIdx
|
||||
].cost_items[expenseIdx].total_cost ?? ''
|
||||
].cost_items[expenseIdx].price ?? ''
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={isExpenseRepeaterInputError(
|
||||
'total_cost',
|
||||
'price',
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)}
|
||||
|
||||
@@ -20,7 +20,7 @@ type ExpenseFormSchemaType = {
|
||||
existing_documents?: { id: number; name: string; url: string }[];
|
||||
deleted_documents?: number[];
|
||||
documents?: File[];
|
||||
cost_per_kandangs: {
|
||||
expense_nonstocks: {
|
||||
kandang_id: number;
|
||||
cost_items: {
|
||||
nonstock?: {
|
||||
@@ -28,7 +28,7 @@ type ExpenseFormSchemaType = {
|
||||
label: string;
|
||||
};
|
||||
quantity?: number;
|
||||
total_cost?: number;
|
||||
price?: number;
|
||||
notes?: string;
|
||||
}[];
|
||||
}[];
|
||||
@@ -74,7 +74,7 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema<ExpenseFormSchemaType> =
|
||||
|
||||
documents: Yup.array().of(Yup.mixed<File>().required()).optional(),
|
||||
|
||||
cost_per_kandangs: Yup.array()
|
||||
expense_nonstocks: Yup.array()
|
||||
.of(
|
||||
Yup.object({
|
||||
kandang_id: Yup.number().min(1, 'Wajib memilih kandang!').required(),
|
||||
@@ -86,7 +86,7 @@ export const ExpenseRequestFormSchema: Yup.ObjectSchema<ExpenseFormSchemaType> =
|
||||
label: Yup.string().required(),
|
||||
}).required('Nonstock wajib diisi!'),
|
||||
quantity: Yup.number().required('Total kuantitas wajib diisi!'),
|
||||
total_cost: Yup.number().required('Total biaya wajib diisi!'),
|
||||
price: Yup.number().required('Harga satuan wajib diisi!'),
|
||||
notes: Yup.string(),
|
||||
})
|
||||
)
|
||||
@@ -128,8 +128,8 @@ export const getExpenseFormInitialValues = (
|
||||
label: initialValues.location.name,
|
||||
}
|
||||
: undefined,
|
||||
transaction_date: initialValues?.expense_date
|
||||
? formatDate(initialValues.expense_date, 'YYYY-MM-DD')
|
||||
transaction_date: initialValues?.transaction_date
|
||||
? formatDate(initialValues.transaction_date, 'YYYY-MM-DD')
|
||||
: undefined,
|
||||
kandangs: initialValues?.kandangs.map((kandang) => ({
|
||||
id: kandang.kandang_id,
|
||||
@@ -148,7 +148,7 @@ export const getExpenseFormInitialValues = (
|
||||
})),
|
||||
deleted_documents: [],
|
||||
documents: [],
|
||||
cost_per_kandangs: initialValues?.kandangs
|
||||
expense_nonstocks: initialValues?.kandangs
|
||||
? initialValues.kandangs.map((kandangExpense) => ({
|
||||
kandang_id: kandangExpense.kandang_id,
|
||||
cost_items: kandangExpense.pengajuans
|
||||
@@ -158,7 +158,7 @@ export const getExpenseFormInitialValues = (
|
||||
label: expenseItem.nonstock.name,
|
||||
},
|
||||
quantity: expenseItem.qty,
|
||||
total_cost: expenseItem.total_price,
|
||||
price: expenseItem.price,
|
||||
notes: expenseItem.note,
|
||||
}))
|
||||
: [],
|
||||
|
||||
@@ -110,12 +110,12 @@ const ExpenseRequestForm = ({
|
||||
transaction_date: values?.transaction_date as string,
|
||||
supplier_id: values.supplier?.value as number,
|
||||
documents: values.documents as File[],
|
||||
cost_per_kandangs: values.cost_per_kandangs.map((costPerKandang) => ({
|
||||
kandang_id: costPerKandang.kandang_id,
|
||||
cost_items: costPerKandang.cost_items.map((costItem) => ({
|
||||
expense_nonstocks: values.expense_nonstocks.map((expenseNonstock) => ({
|
||||
kandang_id: expenseNonstock.kandang_id,
|
||||
cost_items: expenseNonstock.cost_items.map((costItem) => ({
|
||||
nonstock_id: costItem.nonstock?.value as number,
|
||||
quantity: parseFloat(String(costItem.quantity)) as number,
|
||||
total_cost: parseFloat(String(costItem.total_cost)) as number,
|
||||
price: parseFloat(String(costItem.price)) as number,
|
||||
notes: costItem.notes ?? '',
|
||||
})),
|
||||
})),
|
||||
@@ -132,13 +132,13 @@ const ExpenseRequestForm = ({
|
||||
transaction_date: values?.transaction_date as string,
|
||||
supplier_id: values.supplier?.value as number,
|
||||
documents: values.documents as File[],
|
||||
cost_per_kandang: values.cost_per_kandangs.map(
|
||||
(costPerKandang) => ({
|
||||
kandang_id: costPerKandang.kandang_id,
|
||||
cost_items: costPerKandang.cost_items.map((costItem) => ({
|
||||
expense_nonstocks: values.expense_nonstocks.map(
|
||||
(expenseNonstock) => ({
|
||||
kandang_id: expenseNonstock.kandang_id,
|
||||
cost_items: expenseNonstock.cost_items.map((costItem) => ({
|
||||
nonstock_id: costItem.nonstock?.value as number,
|
||||
quantity: parseFloat(String(costItem.quantity)) as number,
|
||||
total_cost: parseFloat(String(costItem.total_cost)) as number,
|
||||
price: parseFloat(String(costItem.price)) as number,
|
||||
notes: costItem.notes ?? '',
|
||||
})),
|
||||
})
|
||||
@@ -179,53 +179,54 @@ const ExpenseRequestForm = ({
|
||||
formik.setFieldValue('location', val);
|
||||
|
||||
formik.setFieldValue('kandangs', []);
|
||||
formik.setFieldValue('cost_per_kandangs', []);
|
||||
formik.setFieldValue('expense_nonstocks', []);
|
||||
};
|
||||
|
||||
const kandangsChangeHandler = (kandangs: { id: number; name: string }[]) => {
|
||||
formik.setFieldTouched('kandangs', true);
|
||||
formik.setFieldValue('kandangs', kandangs);
|
||||
|
||||
const newCostPerKandangs = [...(formik.values.cost_per_kandangs ?? [])];
|
||||
const newExpenseNonstocks = [...(formik.values.expense_nonstocks ?? [])];
|
||||
|
||||
// add new cost_per_kandangs
|
||||
// add new expense_nonstocks
|
||||
kandangs.forEach((kandangItem) => {
|
||||
const isKandangExistInCostPerKandangs = newCostPerKandangs.find(
|
||||
(costPerKandangItem) => costPerKandangItem.kandang_id === kandangItem.id
|
||||
const isKandangExistInExpenseNonstocks = newExpenseNonstocks.find(
|
||||
(expenseNonstockItem) =>
|
||||
expenseNonstockItem.kandang_id === kandangItem.id
|
||||
);
|
||||
|
||||
if (isKandangExistInCostPerKandangs) return;
|
||||
if (isKandangExistInExpenseNonstocks) return;
|
||||
|
||||
newCostPerKandangs.push({
|
||||
newExpenseNonstocks.push({
|
||||
kandang_id: kandangItem.id,
|
||||
cost_items: [
|
||||
{
|
||||
nonstock: undefined,
|
||||
quantity: undefined,
|
||||
total_cost: undefined,
|
||||
price: undefined,
|
||||
notes: '',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
// prune cost_per_kandangs
|
||||
// prune expense_nonstocks
|
||||
const kandangIds = new Set(kandangs.map((kandang) => kandang.id));
|
||||
const deletedCostPerKandangsIdx: number[] = [];
|
||||
const deletedExpenseNonstocksIdx: number[] = [];
|
||||
|
||||
newCostPerKandangs.forEach((costPerKandang, idx) => {
|
||||
const isCostPerKandangValid = kandangIds.has(costPerKandang.kandang_id);
|
||||
newExpenseNonstocks.forEach((expenseNonstock, idx) => {
|
||||
const isExpenseNonstockValid = kandangIds.has(expenseNonstock.kandang_id);
|
||||
|
||||
if (!isCostPerKandangValid) {
|
||||
deletedCostPerKandangsIdx.push(idx);
|
||||
if (!isExpenseNonstockValid) {
|
||||
deletedExpenseNonstocksIdx.push(idx);
|
||||
}
|
||||
});
|
||||
|
||||
deletedCostPerKandangsIdx.forEach((deletedCostPerKandangIdx) => {
|
||||
newCostPerKandangs.splice(deletedCostPerKandangIdx, 1);
|
||||
deletedExpenseNonstocksIdx.forEach((deletedExpenseNonstockIdx) => {
|
||||
newExpenseNonstocks.splice(deletedExpenseNonstockIdx, 1);
|
||||
});
|
||||
|
||||
formik.setFieldValue('cost_per_kandangs', newCostPerKandangs);
|
||||
formik.setFieldValue('expense_nonstocks', newExpenseNonstocks);
|
||||
};
|
||||
|
||||
const supplierChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||
|
||||
@@ -41,28 +41,28 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
val: OptionType | OptionType[] | null
|
||||
) => {
|
||||
formik.setFieldTouched(
|
||||
`cost_per_kandangs[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock`,
|
||||
`expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock`,
|
||||
true
|
||||
);
|
||||
formik.setFieldValue(
|
||||
`cost_per_kandangs[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock`,
|
||||
`expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].nonstock`,
|
||||
val
|
||||
);
|
||||
};
|
||||
|
||||
const addExpenseItemHandler = (kandangExpenseIdx: number) => {
|
||||
const newExpensesValue = [
|
||||
...formik.values.cost_per_kandangs[kandangExpenseIdx].cost_items,
|
||||
...formik.values.expense_nonstocks[kandangExpenseIdx].cost_items,
|
||||
{
|
||||
nonstock: undefined,
|
||||
total_cost: undefined,
|
||||
price: undefined,
|
||||
quantity: undefined,
|
||||
notes: '',
|
||||
},
|
||||
];
|
||||
|
||||
formik.setFieldValue(
|
||||
`cost_per_kandangs[${kandangExpenseIdx}].cost_items`,
|
||||
`expense_nonstocks[${kandangExpenseIdx}].cost_items`,
|
||||
newExpensesValue
|
||||
);
|
||||
};
|
||||
@@ -71,28 +71,28 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
kandangExpenseIdx: number,
|
||||
expenseIdx: number
|
||||
) => {
|
||||
const path = `cost_per_kandangs[${kandangExpenseIdx}].cost_items`;
|
||||
const path = `expense_nonstocks[${kandangExpenseIdx}].cost_items`;
|
||||
|
||||
// trims values, errors, and touched at expenseIdx
|
||||
removeArrayItemAndSync(formik, path, expenseIdx);
|
||||
};
|
||||
|
||||
const isExpenseRepeaterInputError = (
|
||||
column: 'nonstock' | 'quantity' | 'total_cost' | 'notes',
|
||||
column: 'nonstock' | 'quantity' | 'price' | 'notes',
|
||||
kandangExpenseIdx: number,
|
||||
expenseIdx: number
|
||||
) => {
|
||||
return (
|
||||
formik.touched.cost_per_kandangs?.[kandangExpenseIdx]?.cost_items?.[
|
||||
formik.touched.expense_nonstocks?.[kandangExpenseIdx]?.cost_items?.[
|
||||
expenseIdx
|
||||
]?.[column] &&
|
||||
Boolean(
|
||||
formik.errors.cost_per_kandangs?.[kandangExpenseIdx] instanceof
|
||||
formik.errors.expense_nonstocks?.[kandangExpenseIdx] instanceof
|
||||
Object &&
|
||||
formik.errors.cost_per_kandangs?.[kandangExpenseIdx].cost_items?.[
|
||||
formik.errors.expense_nonstocks?.[kandangExpenseIdx].cost_items?.[
|
||||
expenseIdx
|
||||
] instanceof Object &&
|
||||
formik.errors.cost_per_kandangs?.[kandangExpenseIdx].cost_items?.[
|
||||
formik.errors.expense_nonstocks?.[kandangExpenseIdx].cost_items?.[
|
||||
expenseIdx
|
||||
]?.[column]
|
||||
)
|
||||
@@ -113,7 +113,7 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
</div>
|
||||
|
||||
<div className='w-full flex flex-col gap-6'>
|
||||
{(formik.values.cost_per_kandangs.length === 0 ||
|
||||
{(formik.values.expense_nonstocks.length === 0 ||
|
||||
!formik.values.supplier?.value) && (
|
||||
<div>
|
||||
<p className='text-sm text-gray-400 text-center'>
|
||||
@@ -122,9 +122,9 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
</div>
|
||||
)}
|
||||
|
||||
{formik.values.cost_per_kandangs.length > 0 &&
|
||||
{formik.values.expense_nonstocks.length > 0 &&
|
||||
formik.values.supplier?.value &&
|
||||
formik.values.cost_per_kandangs.map(
|
||||
formik.values.expense_nonstocks.map(
|
||||
(kandangExpense, kandangExpenseIdx) => {
|
||||
const kandangName = formik.values.kandangs?.find(
|
||||
(kandang) => kandang.id === kandangExpense.kandang_id
|
||||
@@ -147,7 +147,7 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
<tr>
|
||||
<th>Nonstock</th>
|
||||
<th>Total Kuantitas</th>
|
||||
<th>Total Biaya</th>
|
||||
<th>Harga Satuan</th>
|
||||
<th>Catatan</th>
|
||||
{type !== 'detail' && <th>Aksi</th>}
|
||||
</tr>
|
||||
@@ -178,10 +178,10 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
<td className='p-2'>
|
||||
<NumberInput
|
||||
required
|
||||
name={`cost_per_kandangs[${kandangExpenseIdx}].cost_items[${expenseIdx}].quantity`}
|
||||
name={`expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].quantity`}
|
||||
placeholder='Masukkan Total Kuantitas'
|
||||
value={
|
||||
formik.values.cost_per_kandangs[
|
||||
formik.values.expense_nonstocks[
|
||||
kandangExpenseIdx
|
||||
].cost_items[expenseIdx].quantity ?? ''
|
||||
}
|
||||
@@ -198,18 +198,17 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
|
||||
<td className='p-2'>
|
||||
<NumberInput
|
||||
name={`cost_per_kandangs[${kandangExpenseIdx}].cost_items[${expenseIdx}].total_cost`}
|
||||
placeholder='Masukkan Total Biaya'
|
||||
name={`expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].price`}
|
||||
placeholder='Masukkan Harga Satuan'
|
||||
value={
|
||||
formik.values.cost_per_kandangs[
|
||||
formik.values.expense_nonstocks[
|
||||
kandangExpenseIdx
|
||||
].cost_items[expenseIdx].total_cost ??
|
||||
''
|
||||
].cost_items[expenseIdx].price ?? ''
|
||||
}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
isError={isExpenseRepeaterInputError(
|
||||
'total_cost',
|
||||
'price',
|
||||
kandangExpenseIdx,
|
||||
expenseIdx
|
||||
)}
|
||||
@@ -224,10 +223,10 @@ const ExpenseRequestKandangDetailExpense: React.FC<
|
||||
|
||||
<td className='p-2'>
|
||||
<TextInput
|
||||
name={`cost_per_kandangs[${kandangExpenseIdx}].cost_items[${expenseIdx}].notes`}
|
||||
name={`expense_nonstocks[${kandangExpenseIdx}].cost_items[${expenseIdx}].notes`}
|
||||
placeholder='Tuliskan catatan'
|
||||
value={
|
||||
formik.values.cost_per_kandangs[
|
||||
formik.values.expense_nonstocks[
|
||||
kandangExpenseIdx
|
||||
].cost_items[expenseIdx].notes ?? ''
|
||||
}
|
||||
|
||||
@@ -224,7 +224,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
{ label: 'Vendor', value: expense?.supplier.name },
|
||||
{
|
||||
label: 'Tanggal Transaksi',
|
||||
value: formatDate(expense?.expense_date, 'DD MMMM YYYY'),
|
||||
value: formatDate(expense?.transaction_date, 'DD MMMM YYYY'),
|
||||
},
|
||||
{
|
||||
label: 'Tanggal Realisasi',
|
||||
@@ -326,7 +326,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
let expenseRequestTotal = 0;
|
||||
|
||||
kandangExpense.pengajuans?.forEach(
|
||||
(item) => (expenseRequestTotal += item.total_price)
|
||||
(item) => (expenseRequestTotal += item.price)
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -374,7 +374,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
<Text
|
||||
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
||||
>
|
||||
Total Biaya
|
||||
Harga Satuan
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
@@ -424,7 +424,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
]}
|
||||
>
|
||||
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
||||
{formatCurrency(pengajuan.total_price)}
|
||||
{formatCurrency(pengajuan.price)}
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
@@ -484,7 +484,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
let expenseRealizationTotal = 0;
|
||||
|
||||
kandangExpense.realisasi?.forEach(
|
||||
(item) => (expenseRealizationTotal += item.total_price)
|
||||
(item) => (expenseRealizationTotal += item.price)
|
||||
);
|
||||
|
||||
return (
|
||||
@@ -532,7 +532,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
<Text
|
||||
style={ExpensePDFStyle.kandangExpenseHeaderLabelText}
|
||||
>
|
||||
Total Biaya
|
||||
Harga Satuan
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
@@ -582,7 +582,7 @@ const ExpensePDF = ({ expense }: ExpensePDFProps) => {
|
||||
]}
|
||||
>
|
||||
<Text style={ExpensePDFStyle.kandangExpenseLabelText}>
|
||||
{formatCurrency(realisasi.total_price)}
|
||||
{formatCurrency(realisasi.price)}
|
||||
</Text>
|
||||
</View>
|
||||
<View
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
dummyGetOverhead,
|
||||
} from '@/dummy/closing.dummy';
|
||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||
import { ClosingSales } from '@/types/api/closing';
|
||||
|
||||
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
constructor(basePath: string) {
|
||||
@@ -53,6 +54,21 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
return getSingleRes;
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError<BaseApiResponse<Closing>>(error)) {
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async getPenjualan(
|
||||
id: number
|
||||
): Promise<BaseApiResponse<ClosingSales> | undefined> {
|
||||
try {
|
||||
const getPenjualanPath = `${id}/penjualan`;
|
||||
return await this.customRequest<BaseApiResponse<ClosingSales>>(
|
||||
getPenjualanPath
|
||||
);
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError<BaseApiResponse<ClosingSales>>(error)) {
|
||||
return error.response?.data;
|
||||
}
|
||||
return undefined;
|
||||
@@ -178,4 +194,4 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||
}
|
||||
}
|
||||
|
||||
export const ClosingApi = new ClosingApiService('/closing');
|
||||
export const ClosingApi = new ClosingApiService('/closings');
|
||||
|
||||
@@ -492,8 +492,8 @@ export class ExpenseApiService extends BaseApiService<
|
||||
});
|
||||
|
||||
formData.append(
|
||||
'cost_per_kandangs',
|
||||
JSON.stringify(payload.cost_per_kandangs)
|
||||
'expense_nonstocks',
|
||||
JSON.stringify(payload.expense_nonstocks)
|
||||
);
|
||||
|
||||
return formData;
|
||||
@@ -514,8 +514,8 @@ export class ExpenseApiService extends BaseApiService<
|
||||
});
|
||||
|
||||
formData.append(
|
||||
'cost_per_kandang',
|
||||
JSON.stringify(payload.cost_per_kandang)
|
||||
'expense_nonstocks',
|
||||
JSON.stringify(payload.expense_nonstocks)
|
||||
);
|
||||
|
||||
return formData;
|
||||
|
||||
Vendored
+28
-2
@@ -1,9 +1,34 @@
|
||||
import { Area } from '@/types/api/master-data/area';
|
||||
import { Fcr } from '@/types/api/master-data/fcr';
|
||||
import { Flock } from '@/types/api/master-data/flock';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { Location } from '@/types/api/master-data/location';
|
||||
import { BaseApproval, BaseMetadata } from '@/types/api/api-general';
|
||||
import { Kandang } from '@/types/api/master-data/kandang';
|
||||
import { Product } from '@type/api/master-data/product';
|
||||
import { Customer } from '@type/api/master-data/customer';
|
||||
import { BaseMetadata } from '@/types/api/api-general';
|
||||
|
||||
export type BaseSales = {
|
||||
id: number;
|
||||
realization_date: string;
|
||||
age: number;
|
||||
do_number: string;
|
||||
product: Product;
|
||||
customer: Customer;
|
||||
qty: number;
|
||||
weight: number;
|
||||
avg_weight: number;
|
||||
price: number;
|
||||
total_price: number;
|
||||
kandang: Kandang;
|
||||
payment_status: string;
|
||||
};
|
||||
|
||||
export type BaseClosingSales = {
|
||||
project_type: string;
|
||||
flock_id: number;
|
||||
period: number;
|
||||
sales: BaseSales[];
|
||||
};
|
||||
|
||||
export type BaseClosing = {
|
||||
id: number;
|
||||
@@ -116,3 +141,4 @@ export type OverheadTotal = {
|
||||
actual_total_amount: number;
|
||||
cost_per_bird: number;
|
||||
};
|
||||
export type ClosingSales = BaseMetadata & BaseClosingSales;
|
||||
|
||||
Vendored
+14
-20
@@ -18,7 +18,7 @@ export type BaseExpense = {
|
||||
id: number;
|
||||
path: string;
|
||||
}[];
|
||||
expense_date: string;
|
||||
transaction_date: string;
|
||||
realization_date?: string;
|
||||
grand_total: number;
|
||||
location: BaseLocation;
|
||||
@@ -29,28 +29,23 @@ export type BaseExpense = {
|
||||
name: string;
|
||||
pengajuans?: {
|
||||
id: number;
|
||||
expense_id: number;
|
||||
kandang_id: number;
|
||||
nonstock_id: number;
|
||||
qty: number;
|
||||
unit_price: number;
|
||||
total_price: number;
|
||||
price: number;
|
||||
note?: string;
|
||||
nonstock: Pick<BaseNonstock, 'id' | 'name' | 'flags'>;
|
||||
project_flock_kandang: {
|
||||
id: number;
|
||||
kandang_id: number;
|
||||
};
|
||||
created_at: string;
|
||||
}[];
|
||||
realisasi?: {
|
||||
id: number;
|
||||
expense_nonstock_id: number;
|
||||
qty: number;
|
||||
unit_price: number;
|
||||
total_price: number;
|
||||
date: string;
|
||||
price: number;
|
||||
note?: string;
|
||||
nonstock: Pick<BaseNonstock, 'id' | 'name' | 'flags'>;
|
||||
project_flock_kandang: {
|
||||
id: number;
|
||||
kandang_id: number;
|
||||
};
|
||||
created_at: string;
|
||||
}[];
|
||||
}[];
|
||||
total_pengajuan: number;
|
||||
@@ -65,12 +60,12 @@ export type CreateExpensePayload = {
|
||||
transaction_date: string;
|
||||
supplier_id: number;
|
||||
documents: File[];
|
||||
cost_per_kandangs: {
|
||||
expense_nonstocks: {
|
||||
kandang_id: number;
|
||||
cost_items: {
|
||||
nonstock_id: number;
|
||||
quantity: number;
|
||||
total_cost: number;
|
||||
price: number;
|
||||
notes: string;
|
||||
}[];
|
||||
}[];
|
||||
@@ -81,12 +76,12 @@ export type UpdateExpensePayload = {
|
||||
transaction_date: string;
|
||||
supplier_id: number;
|
||||
documents: File[];
|
||||
cost_per_kandang: {
|
||||
expense_nonstocks: {
|
||||
kandang_id: number;
|
||||
cost_items: {
|
||||
nonstock_id: number;
|
||||
quantity: number;
|
||||
total_cost: number;
|
||||
price: number;
|
||||
notes: string;
|
||||
}[];
|
||||
}[];
|
||||
@@ -98,8 +93,7 @@ export type CreateExpenseRealizationPayload = {
|
||||
realizations: {
|
||||
expense_nonstock_id: number;
|
||||
qty: number;
|
||||
unit_price: number;
|
||||
total_price: number;
|
||||
price: number;
|
||||
notes: string;
|
||||
}[];
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user