Commit 1748cbbe authored by Rais Aryaguna's avatar Rais Aryaguna

feat: Add new constants and refactor hooks for Bulanan Bupot

- Introduced METODE_POTONG constants for payment methods.
- Updated useGetBulanan hook to normalize data structure and improve readability.
- Refactored useSaveBulanan hook to handle new data structure and API calls.
- Enhanced types in types.ts for better type safety and clarity.
- Updated API utility functions to align with new data structures.
- Improved BulananListView to display additional fields and formatted values.
- Refactored BulananRekamView to include new validation schema and handle form submissions.
- Changed fetcher utility in hitung.ts to use the correct endpoint for calculations.
parent a7813084
......@@ -58,10 +58,10 @@ const {
} = endpoints.masterData;
export function useKodeNegara(): UseKodeNegaraReturn {
const { data, isLoading, error } = useQuery<KodeNegaraResponse, AxiosError>({
const { data, isLoading, error } = useQuery<KodeNegara[], AxiosError>({
queryKey: ['kodeNegara'],
queryFn: async () => {
const response = await fetcher<ApiResponse<KodeNegaraResponse>>(kodeNegara);
const response = await fetcher<ApiResponse<KodeNegara[]>>(kodeNegara);
return validateResponse(response);
},
......@@ -69,7 +69,7 @@ export function useKodeNegara(): UseKodeNegaraReturn {
});
return {
kodeNegara: data?.data || [],
kodeNegara: data || [],
kodeNegaraLoading: isLoading,
kodeNegaraError: error,
};
......
......@@ -24,6 +24,7 @@ import { AuthProvider as FirebaseAuthProvider } from 'src/auth/context/firebase'
import { store, persistor } from 'src/store';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { PersistGate } from 'redux-persist/integration/react';
// ----------------------------------------------------------------------
......@@ -69,6 +70,7 @@ export default function App({ children }: AppProps) {
</SettingsProvider>
</AuthProvider>
</I18nProvider>
<ReactQueryDevtools />
</QueryClientProvider>
</PersistGate>
</Provider>
......
// ----------------------------------------------------------------------
export const JWT_STORAGE_KEY = 'jwt_access_token';
export const X_TOKEN = 'x-token';
import type { AxiosRequestConfig, AxiosInstance } from 'axios';
import axios from 'axios';
import { JWT_STORAGE_KEY } from 'src/auth/context/jwt';
import { JWT_STORAGE_KEY, X_TOKEN } from 'src/auth/context/jwt';
import { CONFIG } from 'src/global-config';
const API_CONFIGS = {
......@@ -43,9 +43,11 @@ const createAxiosInstance = (
config.headers.Authorization = `Basic ${credentials}`;
} else {
// Bearer Token untuk Node API
const accessToken = localStorage.getItem(JWT_STORAGE_KEY);
if (accessToken) {
const accessToken = localStorage.getItem('jwt_access_token');
const xToken = localStorage.getItem('x-token');
if (accessToken && xToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
config.headers['x-token'] = xToken;
}
}
return config;
......@@ -71,8 +73,8 @@ export const axiosnodesandbox = createAxiosInstance(
API_CONFIGS.nodesandbox.name
);
export const axiosApiJava = createAxiosInstance(
API_CONFIGS.apiJava.baseURL,
export const axiosHitung = createAxiosInstance(
`${API_CONFIGS.apiJava.baseURL}:${API_CONFIGS.apiJava.portPPH21}`,
API_CONFIGS.apiJava.name,
{
useBasicAuth: true,
......@@ -103,12 +105,12 @@ export const fetcher = async <T = unknown>(args: FetcherArgs): Promise<T> => {
}
};
export const fetcherJava = async <T = unknown>(args: FetcherArgs): Promise<T> => {
export const fetcherHitung = async <T = unknown>(args: FetcherArgs): Promise<T> => {
try {
const [url, config = {}] = Array.isArray(args) ? args : [args, {}];
const { method = 'GET', ...restConfig } = config;
const res = await axiosApiJava.request<T>({
const res = await axiosHitung.request<T>({
url,
method,
...restConfig,
......
......@@ -16,6 +16,7 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
const { dnId } = useParams();
const { setValue, watch } = useFormContext();
const tanggalPemotongan = watch('tglPemotongan');
const fgKaryawanAsing = watch('fgKaryawanAsing');
const [jumlahKeterangan, setJumlahKeterangan] = useState<number>(0);
const maxKeterangan = 5;
......@@ -60,10 +61,16 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
/>
</Grid>
<Grid size={{ md: 3 }}>
<Field.DatePicker name="thnPajak" label="Tahun Pajak" view="year" format="YYYY" />
<Field.DatePicker
name="thnPajak"
label="Tahun Pajak"
view="year"
format="YYYY"
readOnly
/>
</Grid>
<Grid size={{ md: 3 }}>
<Field.DatePicker name="msPajak" label="Masa Pajak" view="month" format="MM" />
<Field.DatePicker name="msPajak" label="Masa Pajak" view="month" format="MM" readOnly />
</Grid>
{/* NPWP dengan onChange langsung */}
......@@ -73,6 +80,7 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
label="NPWP"
onChange={(e) => {
const value = e.target.value.replace(/\D/g, '').slice(0, 16); // hanya angka, max 16
console.log('🚀 ~ value:', value);
setValue('idDipotong', value, { shouldValidate: true, shouldDirty: true });
setValue('nitku', value.length === 16 ? value + '000000' : value, {
shouldValidate: true,
......@@ -100,11 +108,20 @@ const Identitas = ({ isPengganti }: IdentitasProps) => {
<Field.Autocomplete
name="kodeNegara"
label="Negara"
options={kodeNegara.map((val) => ({ label: val.nama, ...val }))}
options={kodeNegara.map((val) => ({ label: val.nama, value: val.kode }))}
readOnly={!fgKaryawanAsing}
/>
</Grid>
<Grid size={{ md: 6 }}>
<Field.Text name="passport" label="Paspor" />
<Field.Text
name="passport"
label="Paspor"
slotProps={{
input: {
readOnly: !fgKaryawanAsing,
},
}}
/>
</Grid>
</Grid>
......
import { CalculateRounded } from '@mui/icons-material';
import { LoadingButton } from '@mui/lab';
import { Grid } from '@mui/material';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import { useFormContext } from 'react-hook-form';
import { Field } from 'src/components/hook-form';
import { RHFNumeric } from 'src/components/hook-form/rhf-numeric';
import { getHitungBulananErrorMessage, useHitungBulanan } from 'src/sections/bupot-21-26/hitung';
import {
FG_FASILITAS_PPH_21,
FG_PERHITUNGAN,
FG_PERHITUNGAN_TEXT,
KODE_OBJEK_PAJAK,
METODE_POTONG,
PERHITUNGAN_BUPOT21,
PTKP,
PTKP_TEXT,
PTKP_TITLE,
} from '../../constant';
import { useMemo } from 'react';
import { RHFNumeric } from 'src/components/hook-form/rhf-numeric';
import { useFormContext } from 'react-hook-form';
import { LoadingButton } from '@mui/lab';
import { CalculateRounded } from '@mui/icons-material';
const fgPerhitunganOptions = Object.values(FG_PERHITUNGAN).map((value) => ({
value,
......@@ -21,9 +25,8 @@ const fgPerhitunganOptions = Object.values(FG_PERHITUNGAN).map((value) => ({
}));
function JumlahPerhitunganForm() {
const { watch } = useFormContext();
const { watch, getValues, setValue } = useFormContext();
const fgFasilitas = watch('fgFasilitas');
const ptkpOptions = useMemo(
() =>
Object.entries(PTKP)
......@@ -31,6 +34,41 @@ function JumlahPerhitunganForm() {
.filter((option) => !option.value.includes(PTKP_TITLE.HB)),
[]
);
const { mutate, isPending } = useHitungBulanan({
onSuccess: (data) => {
console.log('✅ Berhasil hitung PPh21:', data);
setValue(
'pph21',
[FG_FASILITAS_PPH_21.SKB_PPH_PASAL_21].includes(watch('fgFasilitas')) ? 0 : data.pphBulan
);
setValue('dpp', 100);
setValue('tarif', data.tarif);
setValue('tunjanganPPh', data.pph21ditanggungperusahaan ?? data.tunjanganPPh);
},
onError: (error) => {
console.error('❌ Error:', getHitungBulananErrorMessage(error));
},
});
const handleHitung = () => {
const { phBruto: penghasilanBruto, ptkp, fgPerhitungan, tglPemotongan, kdObjPjk } = getValues();
const tglBupot = dayjs(tglPemotongan).format('DDMMYYYY');
const data = {
status: ptkp.value,
kodeObjekPajak: kdObjPjk.value,
tglBupot,
penghasilanKotor: penghasilanBruto,
fgPerhitungan,
metode: fgPerhitungan !== '0' ? METODE_POTONG.GROSS_UP : METODE_POTONG.GROSS, // Simplify conditional assignment
};
mutate(data as any);
};
return (
<Grid container rowSpacing={2} columnSpacing={2} sx={{ my: 3 }}>
<Grid size={{ md: 3 }}>
......@@ -51,32 +89,15 @@ function JumlahPerhitunganForm() {
</Grid>
<Grid size={{ md: 6 }}>
<RHFNumeric name="tunjanganPPh" label="Tunjangan PPh 21 (Rp)" />
<RHFNumeric name="tunjanganPPh" label="Tunjangan PPh 21 (Rp)" readOnly />
</Grid>
<Grid size={{ md: 5 }}>
<RHFNumeric
name="tarif"
label="Tarif (%)"
allowDecimalValue
maxValue={100}
readOnly={
![
KODE_OBJEK_PAJAK.FINAL_38,
KODE_OBJEK_PAJAK.FINAL_99,
KODE_OBJEK_PAJAK.TIDAK_FINAL_99,
].includes(watch('kodeObjekPajak')) &&
![FG_FASILITAS_PPH_21.FASILITAS_LAINNYA].includes(watch('fgFasilitas'))
}
/>
<RHFNumeric name="tarif" label="Tarif (%)" allowDecimalValue maxValue={100} readOnly />
</Grid>
<Grid size={{ md: 5 }}>
<RHFNumeric
name="pph21"
label="PPh Pasal 21"
readOnly={![KODE_OBJEK_PAJAK.FINAL_38].includes(watch('kodeObjekPajak'))}
/>
<RHFNumeric name="pph21" label="PPh Pasal 21" readOnly />
</Grid>
<Grid size={{ md: 2 }} alignSelf="center">
......@@ -85,8 +106,8 @@ function JumlahPerhitunganForm() {
fullWidth
size="large"
color="primary"
// onClick={handleHitung}
// loading={hitung.isLoading}
onClick={handleHitung}
loading={isPending}
startIcon={<CalculateRounded />}
>
Hitung
......
......@@ -54,7 +54,7 @@ const PerhitunganPPhPasal21 = ({ kodeObjectPajak }: PPHDipotongProps) => {
<Field.Text
name="noDokLainnya"
label="Nomor Dokumen Lainnya"
disabled={['9', ''].includes(fgFasilitas)}
disabled={['9', ''].includes(fgFasilitas.value)}
sx={{ '& .MuiInputBase-root.Mui-disabled': { backgroundColor: '#f6f6f6' } }}
/>
</Grid>
......
......@@ -15,6 +15,12 @@ export const FG_PERHITUNGAN = {
MIXED: '2',
};
export const METODE_POTONG = {
GROSS: 'gross',
GROSS_UP: 'gross-up',
MIXED: 'mixed',
};
export const FG_PERHITUNGAN_TEXT = {
[FG_PERHITUNGAN.GROSS]: 'Gross',
[FG_PERHITUNGAN.GROSS_UP]: 'Gross Up',
......
......@@ -10,7 +10,7 @@ import type {
import bulananApi from '../utils/api';
export const transformFgStatusToFgSignStatus = (fgStatus: any) => {
console.log('🚀 ~ transformFgStatusToFgSignStatus ~ fgStatus:', fgStatus);
// console.log('🚀 ~ transformFgStatusToFgSignStatus ~ fgStatus:', fgStatus);
const splittedFgStatus = fgStatus?.split('-') || [];
if (splittedFgStatus.includes('SIGN') > 0) {
......@@ -65,36 +65,14 @@ export const transformSortModelToSortApiPayload = (transformedModel: any) => ({
sortingMethod: transformedModel.length > 0 ? transformedModel[0].sort : 'desc',
});
const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
const normalisePropsGetBulanan = (params: TGetListDataTableDn) => ({
...params,
nomorSP2D: params.dokumen_referensi?.[0]?.nomorSP2D || '',
metodePembayaranBendahara: params.dokumen_referensi?.[0]?.metodePembayaranBendahara || '',
dokReferensi: params.dokumen_referensi?.[0]?.dokReferensi || '',
nomorDokumen: params.dokumen_referensi?.[0]?.nomorDokumen || '',
id: params.id,
npwpPemotong: params.npwpPemotong,
idBupot: params.idBupot,
internal_id: params.internal_id,
// fgStatus: FG_STATUS[params.fgStatus],
fgStatus: params.fgStatus,
fgSignStatus: transformFgStatusToFgSignStatus(params.fgStatus),
fgPdf: getFgStatusPdf(params.link, transformFgStatusToFgSignStatus(params.fgStatus)),
fgLapor: params.fgLapor,
revNo: params.revNo,
thnPajak: params.tahunPajak,
msPajak: params.masaPajak,
kdObjPjk: params.kodeObjekPajak,
noBupot: params.noBupot,
masaPajak: params.msPajak,
pasalPPh: params.kdJnsPjk,
idDipotong: params.userId,
glAccount: params.glAccount,
namaDipotong: params.nama,
jmlBruto: params.dpp,
pphDipotong: params.pphDipotong,
created: params.created_by,
fgKirimEmail: params.fgkirimemail,
created_at: params.created_at,
updated: params.updated_by,
updated_at: params.updated_at,
});
const normalisPropsParmasGetDn = (params: any) => {
......@@ -119,7 +97,7 @@ const useGetBulanan = ({ params, ...props }: any) => {
return {
...response,
// data: response.data.map((data) => normalisePropsGetDn(data)),
data: response.data.map((data) => normalisePropsGetBulanan(data)),
};
},
initialData: {
......
import { useMutation } from '@tanstack/react-query';
import dayjs from 'dayjs';
import dnApi from '../utils/api';
import bulananApi from '../utils/api';
import type { TPostDnRequest } from '../types/types';
const transformParams = ({ isPengganti = false, ...dnData }: any): TPostDnRequest => {
const extractKapFromKodeObjekPajak = (kodeObjekPajak: string) =>
`4111${kodeObjekPajak.split('-')[0]}`;
const transformParams = ({ isPengganti = false, ...Data }: any): TPostDnRequest => {
const {
id,
idBupot,
......@@ -17,85 +20,69 @@ const transformParams = ({ isPengganti = false, ...dnData }: any): TPostDnReques
noDokLainnya,
kdObjPjk,
kdJnsPjk,
statusPph,
jmlBruto,
tarif,
pphDipotong,
kap,
kjs,
revNo: initialRevNo,
tglPemotongan,
namaDok,
nomorDok,
tglDok,
metodePembayaranBendahara,
nomorSP2D,
idTku,
email,
glAccount,
keterangan1,
keterangan2,
keterangan3,
keterangan4,
keterangan5,
} = dnData;
fgGrossUp,
komponen,
alamatDipotong,
foreignEmployee,
passportNo,
countryCode,
statusPtkp,
posisiJabatan,
} = Data;
const dokReferensi = [
{
dokReferensi: namaDok || '',
nomorDokumen: nomorDok || '',
tanggal_Dokumen: tglDok ? dayjs(tglDok).format('DDMMYYYY') : '',
metodePembayaranBendahara: metodePembayaranBendahara || '',
nomorSP2D: nomorSP2D || '',
},
];
console.log('🚀 ~ transformParams ~ Data:', Data);
const revNo = isPengganti ? parseInt(initialRevNo || 0, 10) + 1 : parseInt(initialRevNo || 0, 10);
const npwpLog = localStorage.getItem('npwp_log') ?? '';
const [status, jmlPtkp] = statusPtkp.split('/');
return {
id: !isPengganti ? (id ?? null) : null,
idBupot: idBupot ?? '',
noBupot: noBupot ?? '',
npwpPemotong: npwpLog,
idBupot: isPengganti ? (idBupot ?? '') : undefined,
noBupot: isPengganti ? (noBupot ?? '') : undefined,
npwpPemotong: idTku,
idTku: idTku ?? '',
masaPajak: msPajak ? dayjs(msPajak).format('MM') : '',
tahunPajak: thnPajak ? Number(dayjs(thnPajak).format('YYYY')) : 0,
tahunPajak: thnPajak ? dayjs(thnPajak).format('YYYY') : '',
npwp: idDipotong ?? '',
nik: nitku ?? (idDipotong ? `${idDipotong}000000` : ''),
nama: namaDipotong ?? '',
revNo,
fgNpwpNik: 'true', // static
fgJnsBupot: 'BPU', // static
dataDetilBpu: {
sertifikatInsentifDipotong: fgFasilitas ?? '9',
nomorSertifikatInsentif: noDokLainnya ?? '',
fgNpwpNik: true,
fgJnsBupot: 'A0',
alamat: alamatDipotong ?? '',
dataDetilA0: {
foreignEmployee: foreignEmployee ?? false,
passportNo: passportNo ?? '',
countryCode: countryCode ?? null,
statusPtkp: status ?? '',
jmlPtkp: jmlPtkp ?? '0',
posisiJabatan: posisiJabatan ?? '',
kodeObjekPajak: kdObjPjk ?? '',
pasalPPh: kdJnsPjk ?? '',
statusPPh: statusPph ?? '',
dpp: jmlBruto ?? '',
tarif: tarif ?? '',
pphDipotong: pphDipotong ?? '',
kap: kap ?? '',
kjs: kjs ?? '',
dokReferensi,
penghasilanKotor: jmlBruto ?? 0,
tarif: tarif ?? 0,
pphDipotong: pphDipotong ?? 0,
kap: extractKapFromKodeObjekPajak(kdObjPjk ?? ''),
kjs: '100',
sertifikatInsentifDipotong: fgFasilitas ?? '9',
nomorSertifikatInsentif: noDokLainnya ?? '',
},
tglPemotongan: tglPemotongan ? dayjs(tglPemotongan).format('DDMMYYYY') : '',
email: email ?? '',
glAccount: glAccount ?? '',
keterangan1: keterangan1 ?? '',
keterangan2: keterangan2 ?? '',
keterangan3: keterangan3 ?? '',
keterangan4: keterangan4 ?? '',
keterangan5: keterangan5 ?? '',
fgGrossUp: fgGrossUp ?? 1,
komponen: komponen ?? [],
};
};
const useSaveBulanan = (props?: any) =>
useMutation({
mutationKey: ['Save-Dn'],
mutationFn: (params: any) => dnApi.saveDn(transformParams(params)),
mutationFn: (params: any) => bulananApi.saveBulanan(transformParams(params)),
...props,
});
......
......@@ -8,66 +8,147 @@ export type TBaseResponseAPI<T> = {
total?: number;
};
type TResponseData = {};
export interface BupotRecord {
// --- Kunci numerik dinamis ("1" sampai "53") ---
// [key: `${number}`]: number | undefined;
type TBaseResponseMetaPage = {
pageNum: number | null;
rowPerPage: number | null;
totalRow: number;
};
export type TGetListDataTableDn = {
id: number;
npwpPemotong: string;
idTku: string;
masaPajak: string;
tahunPajak: string;
fgNpwpNik: string;
npwp: string;
nik: string;
nama: string;
sertifikatInsentifDipotong: string;
nomorSertifikatInsentif: string;
kodeObjekPajak: string;
pasalPPh: string;
statusPPh: string;
dpp: string;
idBupot: string | null;
noBupot: string | null;
thnPajak: string;
msPajak: string;
namaPemotong: string | null;
fgIdDipotong: string; // "true" / "false" (string)
idDipotong: string;
namaDipotong: string;
tglPemotongan: string;
kdJnsPjk: string;
namaTtd: string | null;
nikNpwpTtd: string | null;
created_at: string;
updated_at: string;
errorMsg: string | null;
created: string;
updated: string;
email: string | null;
npwp16Pemotong: string;
nitkuPemotong: string;
npwp16Dipotong: string;
fgKirimEmail: number;
statusEmail: string | null;
messageid: string | null;
passphrasePenandatangan: string | null;
dcPenandatangan: string | null;
serialNumberPenandatangan: string | null;
userId: string;
foreignEmployee: string; // "true" / "false" (string)
passportNo: string;
countryCode: string | null;
statusPtkp: string;
jmlPtkp: number;
posisiJabatan: string;
kdObjPjk: string;
nmObjPjk: string | null;
pasalPPh: string | null;
bruto: string;
tarif: string;
pphDipotong: string;
fgStatus: string;
fgFasilitas: string;
noDokLainnya: string;
kap: string;
kjs: string;
tglpemotongan: string;
userId: string;
created_at: string;
updated_at: string;
created_by: string;
updated_by: string;
fgStatus: string;
internal_id: string;
dokumen_referensi: {
dokReferensi: string;
nomorDokumen: string;
tanggal_Dokumen: string;
metodePembayaranBendahara: string;
nomorSP2D: string;
}[];
fgLapor: number;
revNo: number;
noBupot: string | null;
idBupot: string | null;
npwpNikPenandatangan: string;
namaPenandatangan: string;
tglPembatalan: string | null;
fgGrossUp: number;
link: string | null;
errorMsg: string | null;
email: string | null;
glAccount: string;
fgkirimemail: string;
tunjanganPPh: string;
pph21ditanggungperusahaan: string;
pph21ditanggungkaryawan: string;
alamat: string;
keterangan1: string | null;
keterangan2: string | null;
keterangan3: string | null;
keterangan4: string | null;
keterangan5: string | null;
}
type TBaseResponseMetaPage = {
pageNum: number | null;
rowPerPage: number | null;
totalRow: number;
};
export type TGetListDataTableDn = {
alamat: string;
bruto: string;
countryCode: string | null;
created?: string;
created_at: string;
dokReferensi: string;
email: string | null;
errorMsg: string | null;
fgFasilitas: string;
fgGrossUp: number;
fgIdDipotong: string;
fgKirimEmail: string;
fgLapor?: string;
fgPdf: string;
fgSignStatus: string | null;
fgStatus: string;
fgkirimemail: string;
foreignEmployee: string;
glAccount: string | null;
glName: string | null;
id: number;
idBupot: string | null;
idDipotong: string;
internal_id: string;
jmlBruto?: string;
jmlPtkp: number;
kap: string;
kdJnsPjk: string;
kdObjPjk: string;
keterangan1: string | null;
keterangan2: string | null;
keterangan3: string | null;
keterangan4: string | null;
keterangan5: string | null;
fgLapor: string;
kjs: string;
link: string | null;
masaPajak: string;
metodePembayaranBendahara: string;
msPajak: string;
namaDipotong?: string;
namaNegara: string | null;
namaPenandatangan: string | null;
nitkuPemotong: string;
noBupot: string | null;
noDokLainnya: string;
nomorDokumen: string;
nomorSP2D: string;
npwp16Dipotong: string;
npwp16Pemotong: string;
npwpNikPenandatangan: string | null;
npwpPemotong?: string;
passportNo: string;
posisiJabatan: string;
pph21ditanggungkaryawan: string;
pph21ditanggungperusahaan: string;
pphDipotong: string;
revNo: number;
statusPtkp: string;
tarif: string;
tglpemotongan: string;
thnPajak: string;
tunjanganPPh: string;
updated?: string;
updated_at: string;
userId: string;
};
export type TGetListDataTableDnResult = TGetListDataTableDn[];
......@@ -101,44 +182,41 @@ export type ActionItem = {
};
export type TPostDnRequest = {
id: string | null;
idBupot: string;
noBupot: string;
id?: number | null;
npwpPemotong: string;
idTku: string;
masaPajak: string;
tahunPajak: number;
tahunPajak: string;
fgNpwpNik: boolean;
npwp: string;
nik: string;
nama: string;
revNo: number;
fgNpwpNik: string; // static "true"
fgJnsBupot: string; // static "BPU"
dataDetilBpu: {
sertifikatInsentifDipotong: string;
nomorSertifikatInsentif: string;
fgJnsBupot: string;
alamat: string;
dataDetilA0: {
foreignEmployee: boolean;
passportNo: string;
countryCode: string | null;
statusPtkp: string;
jmlPtkp: string;
posisiJabatan: string;
kodeObjekPajak: string;
pasalPPh: string;
statusPPh: string;
dpp: string;
tarif: string;
pphDipotong: string;
penghasilanKotor: number;
tarif: number;
pphDipotong: number;
kap: string;
kjs: string;
dokReferensi: {
dokReferensi: string;
nomorDokumen: string;
tanggal_Dokumen: string;
metodePembayaranBendahara: string;
nomorSP2D: string;
}[];
sertifikatInsentifDipotong: string;
nomorSertifikatInsentif: string;
};
tglPemotongan: string;
email: string;
glAccount: string;
keterangan1: string;
keterangan2: string;
keterangan3: string;
keterangan4: string;
keterangan5: string;
noBupot?: string;
idBupot?: string;
revNo: number;
fgGrossUp: number;
komponen: Array<{
kode: string;
value: string;
}>;
};
import type {
BupotRecord,
TBaseResponseAPI,
TGetListDataKOPDnResult,
TGetListDataTableDnResult,
......@@ -44,7 +45,7 @@ bulananApi.saveBulanan = async (config: TPostDnRequest) => {
const {
data: { message, data },
status: statusCode,
} = await bulananClient.post<TBaseResponseAPI<TPostDnRequest>>('/IF_TXR_028/a0', {
} = await bulananClient.post<TBaseResponseAPI<BupotRecord>>('/IF_TXR_028/a0', {
...config,
});
if (statusCode !== 200) {
......
......@@ -9,9 +9,17 @@ import { DataGridPremium } from '@mui/x-data-grid-premium';
import { useMemo, useState } from 'react';
import TableHeaderLabel from 'src/shared/components/TableHeaderLabel';
import useGetBulanan from '../hooks/useGetBulanan';
import { isEmpty } from 'lodash';
import { Typography } from '@mui/material';
// import CustomToolbarDn from '../components/customToolbarDn';
// import CustomToolbar, { CustomFilterButton } from '../components/customToolbarDn2';
const numberIDR = (number: string) =>
new Intl.NumberFormat('id-ID', {
currency: 'IDR',
// style: 'currency'
}).format(Number(number));
export type IColumnGrid = GridColDef & {
field:
| 'fgStatus'
......@@ -20,16 +28,17 @@ export type IColumnGrid = GridColDef & {
| 'tahunPajak'
| 'kdObjPjk'
| 'pasalPPh'
| 'npwp'
| 'nama'
| 'dpp'
| 'passportNo'
| 'countryCode'
| 'npwp16Dipotong'
| 'namaDipotong'
| 'bruto'
| 'pph21ditanggungperusahaan'
| 'pphDipotong'
| 'idTku'
| 'dokReferensi'
| 'nomorDokumen'
| 'created_by'
| 'nitkuPemotong'
| 'created'
| 'created_at'
| 'updated_by'
| 'updated'
| 'updated_at'
| 'internal_id'
| 'keterangan1'
......@@ -49,7 +58,7 @@ export function BulananListView() {
});
const [filterModel, setFilterModel] = useState<GridFilterModel>({ items: [] });
const [sortModel, setSortModel] = useState<GridSortModel>([]);
const [rowSelectionModel, setRowSelectionModel] = useState<any>([]);
// const [rowSelectionModel, setRowSelectionModel] = useState<any>([]);
// const [rowSelectionModel, setRowSelectionModel] =
// useState<GridRowSelectionModel>(new Set<GridRowId>());
......@@ -105,7 +114,7 @@ export function BulananListView() {
const totalRows = data?.total || 0;
const rows = useMemo(() => data?.data || [], [data?.data]);
console.log(data, '123');
console.log({ data: data.data[0] });
// const handleChange = (event: React.SyntheticEvent, newValue: number) => {
// setTabs1(newValue);
......@@ -134,6 +143,8 @@ export function BulananListView() {
{
field: 'fgStatus',
headerName: 'Status',
headerAlign: 'center',
align: 'left',
width: 300,
type: 'singleSelect',
valueOptions: statusOptions.map((opt) => opt.value), // filter dropdown pakai value
......@@ -142,27 +153,164 @@ export function BulananListView() {
return option ? option.label : (params.value as string);
},
},
{ field: 'noBupot', headerName: 'Nomor Bukti Pemotongan', width: 200 },
{ field: 'masaPajak', headerName: 'Masa Pajak', width: 150 },
{ field: 'tahunPajak', headerName: 'Tahun Pajak', width: 150 },
{ field: 'kdObjPjk', headerName: 'Kode Objek Pajak', width: 150 },
{ field: 'npwp', headerName: 'Identitas', width: 150 },
{ field: 'nama', headerName: 'Nama', width: 150 },
{ field: 'dpp', headerName: 'Jumlah Penghasilan Bruto (Rp)', width: 150 },
{ field: 'pphDipotong', headerName: 'Jumlah PPh Terutang (Rp)', width: 200 },
{ field: 'idTku', headerName: 'NITKU Pemotong', width: 150 },
{ field: 'dokReferensi', headerName: 'Nama dokumen', width: 150 },
{ field: 'nomorDokumen', headerName: 'Nomor dokumen', width: 150 },
{ field: 'created_by', headerName: 'Created', width: 150 },
{ field: 'created_at', headerName: 'Created At', width: 200 },
{ field: 'updated_by', headerName: 'Updated', width: 150 },
{
field: 'noBupot',
headerName: 'Nomor Bukti Pemotongan',
headerAlign: 'center',
align: 'left',
width: 200,
},
{
field: 'masaPajak',
headerName: 'Masa - Tahun Pajak',
headerAlign: 'center',
align: 'center',
minWidth: 150,
renderCell: (params) => `${params.row.masaPajak} - ${params.row.thnPajak}`,
},
{
field: 'kdObjPjk',
headerName: 'Kode Objek Pajak',
headerAlign: 'center',
align: 'center',
width: 150,
minWidth: 150,
},
{
field: 'pasalPPh',
headerName: 'Pasal PPh',
headerAlign: 'center',
align: 'center',
minWidth: 150,
},
{
field: 'passportNo',
headerName: 'Nomor Paspor',
headerAlign: 'center',
minWidth: 200,
align: 'left',
},
{
field: 'countryCode',
headerName: 'Negara',
headerAlign: 'center',
align: 'left',
minWidth: 200,
renderCell: (params) =>
isEmpty(params.value) ? '' : `${params.value} - ${params.row.namaNegara}`,
},
{
field: 'internal_id',
headerName: 'Referensi',
headerAlign: 'center',
align: 'left',
minWidth: 200,
},
{
field: 'npwp16Dipotong',
headerName: 'NPWP',
headerAlign: 'center',
align: 'left',
minWidth: 200,
renderCell: (params) => <Typography className="text-[14px] ">{params.value}</Typography>,
},
{
field: 'namaDipotong',
headerName: 'Nama',
headerAlign: 'center',
align: 'left',
minWidth: 200,
},
{
field: 'bruto',
headerName: 'Jumlah Bruto (Rp)',
headerAlign: 'center',
align: 'right',
minWidth: 200,
valueFormatter: (params) => {
if (params == null) {
return '0';
}
return numberIDR(params);
},
},
{
field: 'pph21ditanggungperusahaan',
headerName: 'Tunjangan PPh (Rp)',
headerAlign: 'center',
align: 'right',
minWidth: 200,
type: 'number',
valueFormatter: (params) => {
if (params == null) {
return '0';
}
return numberIDR(params);
},
},
{
field: 'pphDipotong',
headerName: 'Jumlah PPh Terutang (Rp)',
headerAlign: 'center',
align: 'right',
type: 'number',
valueFormatter: (params) => {
if (params == null) {
return '0';
}
return numberIDR(params);
},
},
{
field: 'nitkuPemotong',
headerName: 'NITKU Pemotong',
headerAlign: 'center',
align: 'left',
minWidth: 200,
},
{
field: 'created',
headerName: 'Created',
headerAlign: 'center',
minWidth: 150,
},
{ field: 'created_at', headerName: 'Created At', width: 150 },
{ field: 'updated', headerName: 'Updated', width: 150 },
{ field: 'updated_at', headerName: 'Update At', width: 150 },
{ field: 'internal_id', headerName: 'Referensi', width: 150 },
{ field: 'keterangan1', headerName: 'Keterangan 1', width: 150 },
{ field: 'keterangan2', headerName: 'Keterangan 2', width: 200 },
{ field: 'keterangan3', headerName: 'Keterangan 3', width: 150 },
{ field: 'keterangan4', headerName: 'Keterangan 4', width: 150 },
{ field: 'keterangan5', headerName: 'Keterangan 5', width: 150 },
{
field: 'keterangan1',
headerName: 'Keterangan 1',
minWidth: 200,
flex: 1,
},
{
field: 'keterangan2',
headerName: 'Keterangan 2',
minWidth: 200,
flex: 1,
},
{
field: 'keterangan3',
headerName: 'Keterangan 3',
minWidth: 200,
flex: 1,
},
{
field: 'keterangan4',
headerName: 'Keterangan 4',
minWidth: 200,
flex: 1,
},
{
field: 'keterangan5',
headerName: 'Keterangan 5',
minWidth: 200,
flex: 1,
},
];
return (
......
......@@ -5,7 +5,7 @@ import {
} from '@tanstack/react-query';
import type { AxiosError } from 'axios';
import { fetcherJava, endpoints } from 'src/lib/axios-ctas-box';
import { fetcherHitung, endpoints } from 'src/lib/axios-ctas-box';
import type { transformParamsBupotBulananProps } from './type';
import { checkPerhitunganBupot21 } from './helper';
......@@ -105,7 +105,7 @@ const { bulanan, harian, pasal17, pensiun, pesangon, tahunan, tahunanA2 } = endp
const hitungBulananGross = async (
params: transformParamsBupotBulananProps
): Promise<ApiResponseBulanan> => {
const response = await fetcherJava<ApiResponse<ApiResponseBulanan>>([
const response = await fetcherHitung<ApiResponse<ApiResponseBulanan>>([
bulanan,
{
method: 'POST',
......@@ -119,7 +119,7 @@ const hitungBulananGross = async (
const hitungBulananGrossUp = async (
params: transformParamsBupotBulananProps
): Promise<ApiResponseBulanan> => {
const response = await fetcherJava<ApiResponse<ApiResponseBulanan>>([
const response = await fetcherHitung<ApiResponse<ApiResponseBulanan>>([
bulanan,
{
method: 'POST',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment