mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
Merge branch 'feat/FE/US-334/TASK-344-345-slicing-and-integrate-expedition-hpp-report-table' into 'feat/FE/US-334/expedition-hpp-report'
[FEAT/FE][US#334/TASK-344-345] Slicing and Integrate API Expedition HPP Report Table See merge request mbugroup/lti-web-client!105
This commit is contained in:
@@ -24,6 +24,11 @@ const ClosingDetailPage = () => {
|
|||||||
() => ClosingApi.getPenjualan(Number(closingId))
|
() => ClosingApi.getPenjualan(Number(closingId))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { data: hppEkspedisiData, isLoading: isLoadingHppEkspedisi } = useSWR(
|
||||||
|
closingId ? `hpp-ekspedisi-${closingId}` : null,
|
||||||
|
() => ClosingApi.getHppEkspedisi(Number(closingId))
|
||||||
|
);
|
||||||
|
|
||||||
if (!closingId) {
|
if (!closingId) {
|
||||||
router.back();
|
router.back();
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@ const ClosingDetailPage = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const isLoading = isLoadingClosing || isLoadingSales;
|
const isLoading = isLoadingClosing || isLoadingSales || isLoadingHppEkspedisi;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-full p-4 flex flex-row justify-center'>
|
<div className='w-full p-4 flex flex-row justify-center'>
|
||||||
@@ -50,6 +55,11 @@ const ClosingDetailPage = () => {
|
|||||||
id={Number(closingId)}
|
id={Number(closingId)}
|
||||||
initialValue={closing.data}
|
initialValue={closing.data}
|
||||||
salesData={isResponseSuccess(salesData) ? salesData.data : undefined}
|
salesData={isResponseSuccess(salesData) ? salesData.data : undefined}
|
||||||
|
hppExpeditionData={
|
||||||
|
isResponseSuccess(hppEkspedisiData)
|
||||||
|
? hppEkspedisiData.data
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -0,0 +1,114 @@
|
|||||||
|
import React, { ReactNode, useState, useRef } from 'react';
|
||||||
|
|
||||||
|
import { cn } from '@/lib/helper';
|
||||||
|
|
||||||
|
export interface DropdownProps {
|
||||||
|
trigger: ReactNode;
|
||||||
|
children: ReactNode;
|
||||||
|
className?: {
|
||||||
|
wrapper?: string;
|
||||||
|
trigger?: string;
|
||||||
|
content?: string;
|
||||||
|
};
|
||||||
|
align?: 'start' | 'center' | 'end';
|
||||||
|
direction?: 'top' | 'bottom' | 'left' | 'right';
|
||||||
|
hover?: boolean;
|
||||||
|
defaultOpen?: boolean;
|
||||||
|
open?: boolean;
|
||||||
|
close?: boolean;
|
||||||
|
controlled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dropdown = ({
|
||||||
|
trigger,
|
||||||
|
children,
|
||||||
|
className,
|
||||||
|
align,
|
||||||
|
direction,
|
||||||
|
hover,
|
||||||
|
defaultOpen = false,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
controlled = false,
|
||||||
|
}: DropdownProps) => {
|
||||||
|
const [isOpen, setIsOpen] = useState(defaultOpen);
|
||||||
|
const dropdownRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const toggleDropdown = () => {
|
||||||
|
if (!controlled) {
|
||||||
|
const newState = !isOpen;
|
||||||
|
setIsOpen(newState);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getWrapperClasses = () => {
|
||||||
|
const openState = controlled ? open : isOpen;
|
||||||
|
|
||||||
|
return cn(
|
||||||
|
'dropdown',
|
||||||
|
{
|
||||||
|
'dropdown-start': align === 'start',
|
||||||
|
'dropdown-center': align === 'center',
|
||||||
|
'dropdown-end': align === 'end',
|
||||||
|
'dropdown-top': direction === 'top',
|
||||||
|
'dropdown-bottom': direction === 'bottom',
|
||||||
|
'dropdown-left': direction === 'left',
|
||||||
|
'dropdown-right': direction === 'right',
|
||||||
|
'dropdown-hover': hover,
|
||||||
|
'dropdown-open': openState && !close,
|
||||||
|
'dropdown-close': close,
|
||||||
|
},
|
||||||
|
className?.wrapper
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getTriggerClasses = () => {
|
||||||
|
return cn(className?.trigger);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getContentClasses = () => {
|
||||||
|
return cn(
|
||||||
|
'dropdown-content z-[9999] shadow-sm bg-base-100 rounded-box',
|
||||||
|
className?.content
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (controlled) {
|
||||||
|
return (
|
||||||
|
<div className={getWrapperClasses()}>
|
||||||
|
{trigger}
|
||||||
|
{open && !close && (
|
||||||
|
<div tabIndex={-1} className={getContentClasses()}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={dropdownRef} className={getWrapperClasses()}>
|
||||||
|
<div
|
||||||
|
tabIndex={0}
|
||||||
|
role='button'
|
||||||
|
className={getTriggerClasses()}
|
||||||
|
onClick={toggleDropdown}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
toggleDropdown();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{trigger}
|
||||||
|
</div>
|
||||||
|
{!close && (
|
||||||
|
<div tabIndex={-1} className={getContentClasses()}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dropdown;
|
||||||
@@ -7,7 +7,7 @@ import { Icon } from '@iconify/react';
|
|||||||
import Menu from '@/components/menu/Menu';
|
import Menu from '@/components/menu/Menu';
|
||||||
import MenuItem from '@/components/menu/MenuItem';
|
import MenuItem from '@/components/menu/MenuItem';
|
||||||
import Button from '@/components/Button';
|
import Button from '@/components/Button';
|
||||||
import Dropdown from '@/components/dropdown/Dropdown';
|
import Dropdown from '@/components/Dropdown';
|
||||||
|
|
||||||
import { useAuth } from '@/services/hooks/useAuth';
|
import { useAuth } from '@/services/hooks/useAuth';
|
||||||
import { AuthApi } from '@/services/api/auth';
|
import { AuthApi } from '@/services/api/auth';
|
||||||
@@ -54,7 +54,8 @@ const Navbar = ({ title, toggleSidebar }: NavbarProps) => {
|
|||||||
|
|
||||||
<div className='flex gap-2'>
|
<div className='flex gap-2'>
|
||||||
<Dropdown
|
<Dropdown
|
||||||
position='bottom-end'
|
align='end'
|
||||||
|
direction='bottom'
|
||||||
trigger={
|
trigger={
|
||||||
<div className='btn btn-ghost btn-circle avatar'>
|
<div className='btn btn-ghost btn-circle avatar'>
|
||||||
<div className='w-10 rounded-full border flex justify-center items-center'>
|
<div className='w-10 rounded-full border flex justify-center items-center'>
|
||||||
@@ -62,9 +63,11 @@ const Navbar = ({ title, toggleSidebar }: NavbarProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
contentClassName='w-52 mt-3'
|
className={{
|
||||||
|
content: 'w-52 mt-3',
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<Menu className='p-2 bg-base-100 shadow rounded-box menu-sm'>
|
<Menu>
|
||||||
<MenuItem title='Logout' onClick={logoutClickHandler} />
|
<MenuItem title='Logout' onClick={logoutClickHandler} />
|
||||||
</Menu>
|
</Menu>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
'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;
|
|
||||||
@@ -10,22 +10,26 @@ import ClosingGeneralInformationTable from '@/components/pages/closing/ClosingGe
|
|||||||
import {
|
import {
|
||||||
ClosingGeneralInformation,
|
ClosingGeneralInformation,
|
||||||
BaseClosingSales,
|
BaseClosingSales,
|
||||||
|
ClosingHppExpedition,
|
||||||
} from '@/types/api/closing';
|
} from '@/types/api/closing';
|
||||||
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
|
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
|
||||||
import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent';
|
import ClosingSapronakCalculationTabContent from '@/components/pages/closing/ClosingSapronakCalculationTabContent';
|
||||||
import ClosingOverheadTabContent from '@/components/pages/closing/ClosingOverheadTabContent';
|
import ClosingOverheadTabContent from '@/components/pages/closing/ClosingOverheadTabContent';
|
||||||
import SalesReportTable from './sale/SalesReportTable';
|
import SalesReportTable from './sale/SalesReportTable';
|
||||||
|
import HppExpeditionReportTable from './hpp-ekspedisi/HppExpeditionReportTable';
|
||||||
|
|
||||||
interface ClosingDetailProps {
|
interface ClosingDetailProps {
|
||||||
id: number;
|
id: number;
|
||||||
initialValue?: ClosingGeneralInformation;
|
initialValue?: ClosingGeneralInformation;
|
||||||
salesData?: BaseClosingSales;
|
salesData?: BaseClosingSales;
|
||||||
|
hppExpeditionData?: ClosingHppExpedition;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ClosingDetail: React.FC<ClosingDetailProps> = ({
|
const ClosingDetail: React.FC<ClosingDetailProps> = ({
|
||||||
id,
|
id,
|
||||||
initialValue,
|
initialValue,
|
||||||
salesData,
|
salesData,
|
||||||
|
hppExpeditionData,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeTab, setActiveTab] = useState<string>('sapronak');
|
const [activeTab, setActiveTab] = useState<string>('sapronak');
|
||||||
|
|
||||||
@@ -54,7 +58,7 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
|
|||||||
{
|
{
|
||||||
id: 'hppEkspedisi',
|
id: 'hppEkspedisi',
|
||||||
label: 'HPP Ekspedisi',
|
label: 'HPP Ekspedisi',
|
||||||
content: 'HPP Ekspedisi',
|
content: <HppExpeditionReportTable initialValues={hppExpeditionData} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'dataProduksi',
|
id: 'dataProduksi',
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import React, { useMemo } from 'react';
|
||||||
|
import { ColumnDef } from '@tanstack/react-table';
|
||||||
|
import Table from '@/components/Table';
|
||||||
|
import Card from '@/components/Card';
|
||||||
|
import { formatCurrency } from '@/lib/helper';
|
||||||
|
import { BaseHppExpedition, BaseExpeditionCost } from '@/types/api/closing';
|
||||||
|
|
||||||
|
interface HppExpeditionReportTableProps {
|
||||||
|
type?: 'detail';
|
||||||
|
initialValues?: BaseHppExpedition;
|
||||||
|
}
|
||||||
|
|
||||||
|
const HppExpeditionReportTable = ({
|
||||||
|
type = 'detail',
|
||||||
|
initialValues,
|
||||||
|
}: HppExpeditionReportTableProps) => {
|
||||||
|
const costOfRevenueExpeditionData: BaseExpeditionCost[] = useMemo(() => {
|
||||||
|
return initialValues?.expedition_costs || [];
|
||||||
|
}, [initialValues]);
|
||||||
|
|
||||||
|
const totals = useMemo(() => {
|
||||||
|
const totalHpp = initialValues?.total_hpp_amount || 0;
|
||||||
|
|
||||||
|
return {
|
||||||
|
totalHpp,
|
||||||
|
};
|
||||||
|
}, [initialValues]);
|
||||||
|
|
||||||
|
const costOfRevenueExpeditionColumns: ColumnDef<BaseExpeditionCost>[] =
|
||||||
|
useMemo(
|
||||||
|
() => [
|
||||||
|
{
|
||||||
|
id: 'id',
|
||||||
|
accessorKey: 'id',
|
||||||
|
header: 'No',
|
||||||
|
cell: (props) => {
|
||||||
|
return <div>{props.row.index + 1}</div>;
|
||||||
|
},
|
||||||
|
footer: () => (
|
||||||
|
<div className='font-semibold text-gray-900'>
|
||||||
|
Total HPP Ekspedisi
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'expedition_vendor_name',
|
||||||
|
accessorKey: 'expedition_vendor_name',
|
||||||
|
header: 'Nama Ekspedisi',
|
||||||
|
cell: (props) => props.getValue() || '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hpp_amount',
|
||||||
|
accessorKey: 'hpp_amount',
|
||||||
|
header: 'HPP Ekspedisi',
|
||||||
|
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.totalHpp)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
[totals]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<section className='w-full'>
|
||||||
|
<div className='p-4'>
|
||||||
|
<h2 className='text-xl font-semibold mb-4'>HPP Ekspedisi</h2>
|
||||||
|
<Card
|
||||||
|
className={{
|
||||||
|
wrapper: 'w-full bg-base-100',
|
||||||
|
body: 'p-0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Table
|
||||||
|
data={costOfRevenueExpeditionData}
|
||||||
|
columns={costOfRevenueExpeditionColumns}
|
||||||
|
renderFooter={costOfRevenueExpeditionData.length > 0}
|
||||||
|
className={{
|
||||||
|
tableWrapperClassName: 'overflow-x-auto',
|
||||||
|
tableClassName: 'w-full table-auto text-sm',
|
||||||
|
headerRowClassName: 'border-b border-b-gray-200',
|
||||||
|
headerColumnClassName:
|
||||||
|
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end whitespace-nowrap',
|
||||||
|
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',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default HppExpeditionReportTable;
|
||||||
@@ -8,7 +8,9 @@ import {
|
|||||||
ClosingOutgoingSapronak,
|
ClosingOutgoingSapronak,
|
||||||
ClosingOverhead,
|
ClosingOverhead,
|
||||||
ClosingSapronakCalculation,
|
ClosingSapronakCalculation,
|
||||||
|
ClosingHppExpedition,
|
||||||
} from '@/types/api/closing';
|
} from '@/types/api/closing';
|
||||||
|
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
||||||
import { BaseApiResponse } from '@/types/api/api-general';
|
import { BaseApiResponse } from '@/types/api/api-general';
|
||||||
import {
|
import {
|
||||||
dummyGetAllFetcher,
|
dummyGetAllFetcher,
|
||||||
@@ -19,7 +21,6 @@ import {
|
|||||||
dummyGetPerhitunganSapronak,
|
dummyGetPerhitunganSapronak,
|
||||||
dummyGetOverhead,
|
dummyGetOverhead,
|
||||||
} from '@/dummy/closing.dummy';
|
} from '@/dummy/closing.dummy';
|
||||||
import { httpClient, httpClientFetcher } from '@/services/http/client';
|
|
||||||
import { ClosingSales } from '@/types/api/closing';
|
import { ClosingSales } from '@/types/api/closing';
|
||||||
|
|
||||||
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
||||||
@@ -194,6 +195,24 @@ export class ClosingApiService extends BaseApiService<Closing, null, null> {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getHppEkspedisi(
|
||||||
|
id: number
|
||||||
|
): Promise<BaseApiResponse<ClosingHppExpedition> | undefined> {
|
||||||
|
try {
|
||||||
|
const getHppEkspedisiPath = `${this.basePath}/${id}/expedition-hpp`;
|
||||||
|
const getHppEkspedisiRes =
|
||||||
|
await httpClient<BaseApiResponse<ClosingHppExpedition>>(
|
||||||
|
getHppEkspedisiPath
|
||||||
|
);
|
||||||
|
return getHppEkspedisiRes;
|
||||||
|
} catch (error) {
|
||||||
|
if (axios.isAxiosError<BaseApiResponse<ClosingHppExpedition>>(error)) {
|
||||||
|
return error.response?.data;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ClosingApi = new ClosingApiService('/closings');
|
export const ClosingApi = new ClosingApiService('/closings');
|
||||||
|
|||||||
Vendored
+14
-1
@@ -78,6 +78,7 @@ export type ClosingIncomingSapronak = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type ClosingOutgoingSapronak = ClosingIncomingSapronak;
|
export type ClosingOutgoingSapronak = ClosingIncomingSapronak;
|
||||||
|
export type ClosingSales = BaseMetadata & BaseClosingSales;
|
||||||
|
|
||||||
// ====== PERHITUNGAN SAPRONAK ======
|
// ====== PERHITUNGAN SAPRONAK ======
|
||||||
|
|
||||||
@@ -141,4 +142,16 @@ export type OverheadTotal = {
|
|||||||
actual_total_amount: number;
|
actual_total_amount: number;
|
||||||
cost_per_bird: number;
|
cost_per_bird: number;
|
||||||
};
|
};
|
||||||
export type ClosingSales = BaseMetadata & BaseClosingSales;
|
|
||||||
|
export type BaseExpeditionCost = {
|
||||||
|
id: number;
|
||||||
|
expedition_vendor_name: string;
|
||||||
|
hpp_amount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type BaseHppExpedition = {
|
||||||
|
expedition_costs: BaseExpeditionCost[];
|
||||||
|
total_hpp_amount: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ClosingHppExpedition = BaseMetadata & BaseHppExpedition;
|
||||||
|
|||||||
Reference in New Issue
Block a user