Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Sign in
Toggle navigation
C
ctas-box
Project overview
Project overview
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Container Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Fachri
ctas-box
Commits
d89056f3
Commit
d89056f3
authored
Oct 23, 2025
by
Fachri
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
bpu non residen
parent
9ffe9ff5
Changes
41
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
41 changed files
with
2610 additions
and
1514 deletions
+2610
-1514
src/main.tsx
src/main.tsx
+4
-0
src/pages/dashboard/index.tsx
src/pages/dashboard/index.tsx
+0
-6
src/pages/unifikasi/unifikasiNr.tsx
src/pages/unifikasi/unifikasiNr.tsx
+2
-2
src/routes/paths.ts
src/routes/paths.ts
+0
-25
src/routes/sections/dashboard.tsx
src/routes/sections/dashboard.tsx
+1
-0
src/sections/bupot-unifikasi/bupot-dn/components/rekamDn/Identitas.tsx
...bupot-unifikasi/bupot-dn/components/rekamDn/Identitas.tsx
+1
-173
src/sections/bupot-unifikasi/bupot-dn/components/rekamDn/PphDipotong.tsx
...pot-unifikasi/bupot-dn/components/rekamDn/PphDipotong.tsx
+5
-269
src/sections/bupot-unifikasi/bupot-dn/hooks/useAdvancedFilterDn.tsx
...ns/bupot-unifikasi/bupot-dn/hooks/useAdvancedFilterDn.tsx
+0
-192
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
+0
-18
src/sections/bupot-unifikasi/bupot-dn/view/dnRekamView.tsx
src/sections/bupot-unifikasi/bupot-dn/view/dnRekamView.tsx
+44
-30
src/sections/bupot-unifikasi/bupot-nr/components/CustomColumnsButton.tsx
...pot-unifikasi/bupot-nr/components/CustomColumnsButton.tsx
+2
-1
src/sections/bupot-unifikasi/bupot-nr/components/CustomFilterButton.tsx
...upot-unifikasi/bupot-nr/components/CustomFilterButton.tsx
+1
-507
src/sections/bupot-unifikasi/bupot-nr/components/CustomToolbar.tsx
...ons/bupot-unifikasi/bupot-nr/components/CustomToolbar.tsx
+3
-2
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCancelNr.tsx
...ot-unifikasi/bupot-nr/components/dialog/ModalCancelNr.tsx
+228
-0
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCetakPdfNr.tsx
...-unifikasi/bupot-nr/components/dialog/ModalCetakPdfNr.tsx
+3
-14
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalDeleteNr.tsx
...ot-unifikasi/bupot-nr/components/dialog/ModalDeleteNr.tsx
+5
-7
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalUploadNr.tsx
...ot-unifikasi/bupot-nr/components/dialog/ModalUploadNr.tsx
+9
-9
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/DokumenReferensi.tsx
...nifikasi/bupot-nr/components/rekamNr/DokumenReferensi.tsx
+59
-0
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/Identitas.tsx
...bupot-unifikasi/bupot-nr/components/rekamNr/Identitas.tsx
+231
-0
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/PanduanDnRekam.tsx
...-unifikasi/bupot-nr/components/rekamNr/PanduanDnRekam.tsx
+159
-0
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/PphDipotong.tsx
...pot-unifikasi/bupot-nr/components/rekamNr/PphDipotong.tsx
+674
-0
src/sections/bupot-unifikasi/bupot-nr/constant/queryKey.tsx
src/sections/bupot-unifikasi/bupot-nr/constant/queryKey.tsx
+8
-8
src/sections/bupot-unifikasi/bupot-nr/hooks/useAdvancedFilterNr.tsx
...ns/bupot-unifikasi/bupot-nr/hooks/useAdvancedFilterNr.tsx
+170
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelDn.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelDn.tsx
+0
-12
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelNr.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelNr.tsx
+12
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useCetakPdfNr.tsx
...sections/bupot-unifikasi/bupot-nr/hooks/useCetakPdfNr.tsx
+11
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useDeleteNr.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useDeleteNr.tsx
+12
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetKodeObjekPajakNr.tsx
...bupot-unifikasi/bupot-nr/hooks/useGetKodeObjekPajakNr.tsx
+12
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetNegara.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetNegara.tsx
+14
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetNr.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetNr.tsx
+72
-64
src/sections/bupot-unifikasi/bupot-nr/hooks/usePphDipotong.tsx
...ections/bupot-unifikasi/bupot-nr/hooks/usePphDipotong.tsx
+4
-3
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveDn.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveDn.tsx
+0
-102
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveNr.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveNr.tsx
+133
-0
src/sections/bupot-unifikasi/bupot-nr/hooks/useUpload.tsx
src/sections/bupot-unifikasi/bupot-nr/hooks/useUpload.tsx
+3
-3
src/sections/bupot-unifikasi/bupot-nr/types/types.ts
src/sections/bupot-unifikasi/bupot-nr/types/types.ts
+167
-0
src/sections/bupot-unifikasi/bupot-nr/utils/api.tsx
src/sections/bupot-unifikasi/bupot-nr/utils/api.tsx
+49
-35
src/sections/bupot-unifikasi/bupot-nr/utils/normalizePayloadCetakPdf.ts
...upot-unifikasi/bupot-nr/utils/normalizePayloadCetakPdf.ts
+4
-4
src/sections/bupot-unifikasi/bupot-nr/view/index.ts
src/sections/bupot-unifikasi/bupot-nr/view/index.ts
+0
-0
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
+58
-28
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
+385
-0
src/sections/bupot-unifikasi/bupot-nr/workers/normalizeNr.worker.js
...ns/bupot-unifikasi/bupot-nr/workers/normalizeNr.worker.js
+65
-0
No files found.
src/main.tsx
View file @
d89056f3
...
@@ -6,6 +6,10 @@ import { LicenseInfo } from '@mui/x-license';
...
@@ -6,6 +6,10 @@ import { LicenseInfo } from '@mui/x-license';
import
App
from
'
./app
'
;
import
App
from
'
./app
'
;
import
{
routesSection
}
from
'
./routes/sections
'
;
import
{
routesSection
}
from
'
./routes/sections
'
;
import
{
ErrorBoundary
}
from
'
./routes/components
'
;
import
{
ErrorBoundary
}
from
'
./routes/components
'
;
import
dayjs
from
'
dayjs
'
;
import
customParseFormat
from
'
dayjs/plugin/customParseFormat
'
;
dayjs
.
extend
(
customParseFormat
);
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
LicenseInfo
.
setLicenseKey
(
LicenseInfo
.
setLicenseKey
(
...
...
src/pages/dashboard/index.tsx
View file @
d89056f3
import
{
CONFIG
}
from
'
src/global-config
'
;
import
{
CONFIG
}
from
'
src/global-config
'
;
import
{
DashboardView
}
from
'
src/sections/dashboard/view
'
;
import
{
DashboardView
}
from
'
src/sections/dashboard/view
'
;
// import { OverviewAppView } from 'src/sections/overview/app/view';
// ----------------------------------------------------------------------
const
metadata
=
{
title
:
`Dashboard -
${
CONFIG
.
appName
}
`
};
const
metadata
=
{
title
:
`Dashboard -
${
CONFIG
.
appName
}
`
};
export
default
function
OverviewAppPage
()
{
export
default
function
OverviewAppPage
()
{
return
(
return
(
<>
<>
<
title
>
{
metadata
.
title
}
</
title
>
<
title
>
{
metadata
.
title
}
</
title
>
{
/* <OverviewAppView /> */
}
{
/* aaa */
}
<
DashboardView
/>
<
DashboardView
/>
</>
</>
);
);
...
...
src/pages/unifikasi/unifikasiNr.tsx
View file @
d89056f3
import
{
CONFIG
}
from
'
src/global-config
'
;
import
{
CONFIG
}
from
'
src/global-config
'
;
import
{
NrListView
}
from
'
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view
'
;
//
import { NrListView } from 'src/sections/bupot-unifikasi/bupot-nr/view';
import
{
NrListView
}
from
'
src/sections/bupot-unifikasi/bupot-nr/view
'
;
const
metadata
=
{
title
:
`E-Bupot Unifikasi-
${
CONFIG
.
appName
}
`
};
const
metadata
=
{
title
:
`E-Bupot Unifikasi-
${
CONFIG
.
appName
}
`
};
...
...
src/routes/paths.ts
View file @
d89056f3
...
@@ -143,30 +143,5 @@ export const paths = {
...
@@ -143,30 +143,5 @@ export const paths = {
edit
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/user/
${
id
}
/edit`
,
edit
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/user/
${
id
}
/edit`
,
demo
:
{
edit
:
`
${
ROOTS
.
DASHBOARD
}
/user/
${
MOCK_ID
}
/edit`
},
demo
:
{
edit
:
`
${
ROOTS
.
DASHBOARD
}
/user/
${
MOCK_ID
}
/edit`
},
},
},
product
:
{
root
:
`
${
ROOTS
.
DASHBOARD
}
/product`
,
new
:
`
${
ROOTS
.
DASHBOARD
}
/product/new`
,
details
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/product/
${
id
}
`
,
edit
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/product/
${
id
}
/edit`
,
demo
:
{
details
:
`
${
ROOTS
.
DASHBOARD
}
/product/
${
MOCK_ID
}
`
,
edit
:
`
${
ROOTS
.
DASHBOARD
}
/product/
${
MOCK_ID
}
/edit`
,
},
},
invoice
:
{
root
:
`
${
ROOTS
.
DASHBOARD
}
/invoice`
,
new
:
`
${
ROOTS
.
DASHBOARD
}
/invoice/new`
,
details
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/invoice/
${
id
}
`
,
edit
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/invoice/
${
id
}
/edit`
,
demo
:
{
details
:
`
${
ROOTS
.
DASHBOARD
}
/invoice/
${
MOCK_ID
}
`
,
edit
:
`
${
ROOTS
.
DASHBOARD
}
/invoice/
${
MOCK_ID
}
/edit`
,
},
},
order
:
{
root
:
`
${
ROOTS
.
DASHBOARD
}
/order`
,
details
:
(
id
:
string
)
=>
`
${
ROOTS
.
DASHBOARD
}
/order/
${
id
}
`
,
demo
:
{
details
:
`
${
ROOTS
.
DASHBOARD
}
/order/
${
MOCK_ID
}
`
},
},
},
},
};
};
src/routes/sections/dashboard.tsx
View file @
d89056f3
...
@@ -122,6 +122,7 @@ export const dashboardRoutes: RouteObject[] = [
...
@@ -122,6 +122,7 @@ export const dashboardRoutes: RouteObject[] = [
{
path
:
'
dn/:id/:type
'
,
element
:
<
OverviewUnifikasiRekamDnPage
/>
},
{
path
:
'
dn/:id/:type
'
,
element
:
<
OverviewUnifikasiRekamDnPage
/>
},
{
path
:
'
nr
'
,
element
:
<
OverviewUnifikasiNrPage
/>
},
{
path
:
'
nr
'
,
element
:
<
OverviewUnifikasiNrPage
/>
},
{
path
:
'
nr/new
'
,
element
:
<
OverviewUnifikasiRekamNrPage
/>
},
{
path
:
'
nr/new
'
,
element
:
<
OverviewUnifikasiRekamNrPage
/>
},
{
path
:
'
nr/:id/:type
'
,
element
:
<
OverviewUnifikasiRekamNrPage
/>
},
{
path
:
'
ssp
'
,
element
:
<
OverviewUnifikasiSspPage
/>
},
{
path
:
'
ssp
'
,
element
:
<
OverviewUnifikasiSspPage
/>
},
{
path
:
'
ssp/new
'
,
element
:
<
OverviewUnifikasiRekamSspPage
/>
},
{
path
:
'
ssp/new
'
,
element
:
<
OverviewUnifikasiRekamSspPage
/>
},
{
path
:
'
digunggung
'
,
element
:
<
OverviewUnifikasiDigunggungPage
/>
},
{
path
:
'
digunggung
'
,
element
:
<
OverviewUnifikasiDigunggungPage
/>
},
...
...
src/sections/bupot-unifikasi/bupot-dn/components/rekamDn/Identitas.tsx
View file @
d89056f3
// import Box from '@mui/material/Box';
// import Button from '@mui/material/Button';
// import Grid from '@mui/material/Grid';
// import dayjs from 'dayjs';
// import React, { useEffect, useState } from 'react';
// import { useFormContext } from 'react-hook-form';
// import { Field } from 'src/components/hook-form';
// type IdentitasProps = {
// isPengganti: boolean;
// existingDn?: any; // data penuh dari API
// };
// const Identitas = ({ isPengganti, existingDn }: IdentitasProps) => {
// const { setValue, watch, getValues } = useFormContext();
// const tanggalPemotongan = watch('tglPemotongan');
// const maxKeterangan = 5;
// const [jumlahKeterangan, setJumlahKeterangan] = useState<number>(0);
// // 🧩 Auto isi tahun & masa pajak berdasarkan tanggalPemotongan
// useEffect(() => {
// if (tanggalPemotongan) {
// const date = dayjs(tanggalPemotongan);
// setValue('thnPajak', date.format('YYYY'));
// setValue('msPajak', date.format('MM'));
// } else {
// setValue('thnPajak', '');
// setValue('msPajak', '');
// }
// }, [tanggalPemotongan, setValue]);
// useEffect(() => {
// // ambil nilai form saat ini (setelah reset di parent)
// const currentValues = getValues();
// const arr = [
// currentValues.keterangan1,
// currentValues.keterangan2,
// currentValues.keterangan3,
// currentValues.keterangan4,
// currentValues.keterangan5,
// ];
// const count = arr.filter((k) => !!k && k.trim() !== '').length;
// console.log('🧠 Detected keterangan count:', count, arr);
// // kalau ada field terisi, render sebanyak itu
// if (count > 0) {
// setJumlahKeterangan(count);
// }
// }, [existingDn, getValues]);
// // ➕ Tambah field
// const handleTambah = () => {
// if (jumlahKeterangan < maxKeterangan) {
// setJumlahKeterangan((prev) => prev + 1);
// }
// };
// // ➖ Hapus field terakhir
// const handleHapus = () => {
// if (jumlahKeterangan > 0) {
// setValue(`keterangan${jumlahKeterangan}`, '');
// setJumlahKeterangan((prev) => prev - 1);
// }
// };
// console.log(existingDn);
// return (
// <>
// <Grid container rowSpacing={2} alignItems="center" columnSpacing={2} sx={{ mb: 4 }}>
// {/* 📅 Tanggal & Masa Pajak */}
// <Grid size={{ md: 6 }}>
// <Field.DatePicker
// name="tglPemotongan"
// label="Tanggal Pemotongan"
// format="DD/MM/YYYY"
// maxDate={dayjs()}
// />
// </Grid>
// <Grid size={{ md: 3 }}>
// <Field.DatePicker name="thnPajak" label="Tahun Pajak" view="year" format="YYYY" />
// </Grid>
// <Grid size={{ md: 3 }}>
// <Field.DatePicker name="msPajak" label="Masa Pajak" view="month" format="MM" />
// </Grid>
// {/* 🧾 NPWP dan NITKU */}
// <Grid size={{ md: 6 }}>
// <Field.Text
// name="idDipotong"
// label="NPWP"
// onChange={(e) => {
// const value = e.target.value.replace(/\D/g, '').slice(0, 16);
// setValue('idDipotong', value, { shouldValidate: true, shouldDirty: true });
// setValue('nitku', value.length === 16 ? value + '000000' : value, {
// shouldValidate: true,
// shouldDirty: true,
// });
// }}
// />
// </Grid>
// <Grid size={{ md: 6 }}>
// <Field.Text
// name="nitku"
// label="NITKU"
// onChange={(e) => {
// const value = e.target.value.replace(/\D/g, '').slice(0, 22);
// setValue('nitku', value, { shouldValidate: true, shouldDirty: true });
// }}
// />
// </Grid>
// {/* 👤 Nama dan Email */}
// <Grid size={{ md: 6 }}>
// <Field.Text name="namaDipotong" label="Nama" />
// </Grid>
// <Grid size={{ md: 6 }}>
// <Field.Text name="email" label="Email (optional)" />
// </Grid>
// </Grid>
// {/* ✏️ Tombol Tambah / Hapus Keterangan */}
// <Box sx={{ display: 'flex', gap: 2, mb: 3 }}>
// <Box
// sx={{
// borderRadius: '18px',
// border: jumlahKeterangan >= maxKeterangan ? '1px solid #eee' : '1px solid #2e7d3280',
// color: jumlahKeterangan >= maxKeterangan ? '#eee' : '#2e7d3280',
// p: '0px 10px',
// }}
// >
// <Button disabled={jumlahKeterangan >= maxKeterangan} onClick={handleTambah}>
// Tambah Keterangan
// </Button>
// </Box>
// <Box
// sx={{
// borderRadius: '18px',
// border: jumlahKeterangan === 0 ? '1px solid #eee' : '1px solid #f44336',
// color: jumlahKeterangan === 0 ? '#eee' : '#f44336',
// p: '0px 10px',
// }}
// >
// <Button disabled={jumlahKeterangan === 0} onClick={handleHapus}>
// Hapus Keterangan
// </Button>
// </Box>
// </Box>
// {/* 🗒️ Input Keterangan Tambahan */}
// <Box sx={{ mb: 3 }}>
// {Array.from({ length: jumlahKeterangan }).map((_, i) => (
// <Grid size={{ md: 12 }} key={i}>
// <Field.Text
// sx={{ mb: 2 }}
// name={`keterangan${i + 1}`}
// label={`Keterangan Tambahan ${i + 1}`}
// />
// </Grid>
// ))}
// </Box>
// </>
// );
// };
// export default Identitas;
import
Box
from
'
@mui/material/Box
'
;
import
Box
from
'
@mui/material/Box
'
;
import
Button
from
'
@mui/material/Button
'
;
import
Button
from
'
@mui/material/Button
'
;
import
Grid
from
'
@mui/material/Grid
'
;
import
Grid
from
'
@mui/material/Grid
'
;
...
@@ -360,7 +188,7 @@ const Identitas = ({ isPengganti, existingDn }: IdentitasProps) => {
...
@@ -360,7 +188,7 @@ const Identitas = ({ isPengganti, existingDn }: IdentitasProps) => {
{
/* 🗒️ Input Keterangan Tambahan */
}
{
/* 🗒️ Input Keterangan Tambahan */
}
<
Box
sx=
{
{
mb
:
3
}
}
>
<
Box
sx=
{
{
mb
:
3
}
}
>
{
Array
.
from
({
length
:
jumlahKeterangan
}).
map
((
_
,
i
)
=>
(
{
Array
.
from
({
length
:
jumlahKeterangan
}).
map
((
_
,
i
)
=>
(
<
Grid
size=
{
{
md
:
12
}
}
key=
{
i
}
>
<
Grid
size=
{
{
md
:
12
}
}
key=
{
`keterangan${i + 1}`
}
>
<
Field
.
Text
<
Field
.
Text
sx=
{
{
mb
:
2
}
}
sx=
{
{
mb
:
2
}
}
name=
{
`keterangan${i + 1}`
}
name=
{
`keterangan${i + 1}`
}
...
...
src/sections/bupot-unifikasi/bupot-dn/components/rekamDn/PphDipotong.tsx
View file @
d89056f3
This diff is collapsed.
Click to expand it.
src/sections/bupot-unifikasi/bupot-dn/hooks/useAdvancedFilterDn.tsx
View file @
d89056f3
// type FilterItem = {
// field: string;
// operator: string;
// value?: string | number | Array<string | number> | null;
// join?: 'AND' | 'OR'; // optional: join connector BEFORE this item (first item usually undefined)
// };
// type BaseParams = Record<string, any>;
// export function useAdvancedFilter() {
// const numericFields = new Set(['masaPajak', 'tahunPajak', 'dpp', 'pphDipotong']);
// const dateFields = new Set(['created_at', 'updated_at']);
// const fieldMap: Record<string, string> = {
// noBupot: 'nomorBupot',
// };
// const dbField = (field: string) => fieldMap[field] ?? field;
// const escape = (v: string) => String(v).replace(/'/g, "''");
// const toDbDate = (value: string | Date) => {
// if (value instanceof Date) {
// const y = value.getFullYear();
// const m = String(value.getMonth() + 1).padStart(2, '0');
// const d = String(value.getDate()).padStart(2, '0');
// return `${y}${m}${d}`;
// }
// const digits = String(value).replace(/[^0-9]/g, '');
// if (digits.length >= 8) return digits.slice(0, 8);
// return digits;
// };
// const normalizeOp = (op: string) => op?.toString().trim();
// function buildAdvancedFilter(filters?: FilterItem[] | null) {
// if (!filters || filters.length === 0) return '';
// const exprs: string[] = []; // each item's expression
// const joins: ('AND' | 'OR')[] = []; // join before each expr (for item 0, push nothing/AND by default)
// for (let i = 0; i < filters.length; i++) {
// const f = filters[i];
// if (!f || !f.field) continue;
// const op = normalizeOp(f.operator ?? '');
// const fieldName = dbField(f.field);
// // build expression for this item
// let expr: string | null = null;
// // DATE handling
// if (dateFields.has(fieldName)) {
// const rawVal = f.value;
// if (!rawVal && !/is empty|is not empty/i.test(op)) {
// continue;
// }
// if (/^is$/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" >= '${ymd} 00:00:00' AND \"${fieldName}\" <= '${ymd} 23:59:59'`;
// } else if (/is on or after/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" >= '${ymd}'`;
// } else if (/is on or before/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" <= '${ymd}'`;
// }
// }
// // EMPTY checks (user requested LOWER("col") IS NULL semantics)
// if (/is empty/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") IS NULL`;
// } else if (/is not empty/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") IS NOT NULL`;
// }
// // IS ANY OF handling
// if (!expr && /is any of/i.test(op)) {
// // collect values array
// let values: Array<string | number> = [];
// if (Array.isArray(f.value)) values = f.value as any;
// else if (typeof f.value === 'string')
// values = (f.value as string)
// .split(',')
// .map((s) => s.trim())
// .filter(Boolean);
// else if (f.value != null) values = [f.value as any];
// if ((values || []).length === 0) {
// expr = null;
// } else {
// // special-case fgStatus: need LIKE %val% OR LIKE %val2%
// if (fieldName === 'fgStatus' || fieldName === 'fg_status') {
// const ors = (values as any[]).map((v) => {
// const s = escape(String(v).toLowerCase());
// return `LOWER(\"${fieldName}\") LIKE LOWER('%${s}%')`;
// });
// expr = `(${ors.join(' OR ')})`;
// } else {
// // default: OR of equality (case-insensitive)
// const ors = (values as any[]).map((v) => {
// const s = escape(String(v).toLowerCase());
// return `LOWER(\"${fieldName}\") = '${s}'`;
// });
// expr = `(${ors.join(' OR ')})`;
// }
// }
// }
// // FGSTATUS special single-value is / is not / contains semantics
// if (!expr && (fieldName === 'fgStatus' || fieldName === 'fg_status')) {
// const valRaw = f.value == null ? '' : String(f.value);
// if (valRaw === '' && !/is any of|is empty|is not empty/i.test(op)) {
// expr = null;
// } else {
// const valEscaped = escape(valRaw.toLowerCase());
// if (/^is$/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") LIKE LOWER('%${valEscaped}%')`;
// } else if (/is not/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") NOT LIKE LOWER('%${valEscaped}%')`;
// }
// }
// }
// // GENERIC text/numeric handling when expr still not set
// if (!expr) {
// const valRaw = f.value == null ? '' : String(f.value);
// if (valRaw === '') {
// expr = null;
// } else {
// const valEscaped = escape(valRaw.toLowerCase());
// // numeric fields: operators (=, >=, <=)
// if (numericFields.has(fieldName) && /^(=|>=|<=)$/.test(op)) {
// expr = `\"${fieldName}\" ${op} '${valEscaped}'`;
// } else if (/^contains$/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") LIKE LOWER('%${valEscaped}%')`;
// } else if (/^equals$/i.test(op)) {
// // equals should produce IN (wrap single value as IN)
// // attempt to parse CSV if provided
// let values: string[] = [];
// if (Array.isArray(f.value))
// values = (f.value as any[]).map((v) => escape(String(v).toLowerCase()));
// else values = [escape(String(f.value).toLowerCase())];
// expr = `LOWER(\"${fieldName}\") IN (${values.map((v) => `'${v}'`).join(',')})`;
// } else if (/^(>=|<=|=)$/.test(op) && !numericFields.has(fieldName)) {
// expr = `LOWER(\"${fieldName}\") ${op} '${valEscaped}'`;
// } else if (/^(is)$/i.test(op)) {
// // fallback: treat as equals
// expr = `LOWER(\"${fieldName}\") = '${valEscaped}'`;
// } else {
// // fallback equality
// expr = `LOWER(\"${fieldName}\") = '${valEscaped}'`;
// }
// }
// }
// if (expr) {
// exprs.push(expr);
// // record join for this item (use provided join or default AND except for first item)
// const joinBefore = (f.join as 'AND' | 'OR') ?? (exprs.length > 1 ? 'AND' : 'AND');
// joins.push(joinBefore);
// }
// }
// // now combine exprs with joins; joins[i] is join BEFORE exprs[i]
// if (exprs.length === 0) return '';
// let out = exprs[0];
// for (let i = 1; i < exprs.length; i++) {
// const j = joins[i] ?? 'AND';
// out = `(${out}) ${j} (${exprs[i]})`;
// }
// return out;
// }
// function buildRequestParams(base: BaseParams = {}, advanced: string) {
// const out: BaseParams = { ...(base ?? {}) };
// if ('noBupot' in out) {
// out.nomorBupot = out.noBupot;
// delete out.noBupot;
// }
// out.advanced = advanced || '';
// return out;
// }
// return { buildAdvancedFilter, buildRequestParams } as const;
// }
// export default useAdvancedFilter;
type
FilterItem
=
{
type
FilterItem
=
{
field
:
string
;
field
:
string
;
operator
:
string
;
operator
:
string
;
...
...
src/sections/bupot-unifikasi/bupot-dn/hooks/useGetDn.tsx
View file @
d89056f3
...
@@ -92,24 +92,6 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
...
@@ -92,24 +92,6 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
updated_at
:
formatDateToDDMMYYYY
(
params
.
updated_at
),
updated_at
:
formatDateToDDMMYYYY
(
params
.
updated_at
),
});
});
// ---------- normalizer for params request ----------
// const normalisPropsParmasGetDn = (params: any) => {
// const sorting = !isEmpty(params.sortModel)
// ? transformSortModelToSortApiPayload(params.sortModel)
// : {};
// return {
// ...params,
// page: (typeof params.page === 'number' ? params.page : 0) + 1,
// limit: params.pageSize,
// masaPajak: params.msPajak || null,
// tahunPajak: params.thnPajak || null,
// npwp: params.idDipotong || null,
// advanced: isEmpty(params.advanced) ? undefined : params.advanced,
// ...sorting,
// };
// };
const
normalizeParams
=
(
params
:
any
)
=>
{
const
normalizeParams
=
(
params
:
any
)
=>
{
const
{
const
{
page
=
0
,
page
=
0
,
...
...
src/sections/bupot-unifikasi/bupot-dn/view/dnRekamView.tsx
View file @
d89056f3
...
@@ -24,36 +24,50 @@ import useUpload from '../hooks/useUpload';
...
@@ -24,36 +24,50 @@ import useUpload from '../hooks/useUpload';
import
{
useGetDnById
}
from
'
../hooks/useGetDn
'
;
import
{
useGetDnById
}
from
'
../hooks/useGetDn
'
;
import
ModalUploadDn
from
'
../components/dialog/ModalUploadDn
'
;
import
ModalUploadDn
from
'
../components/dialog/ModalUploadDn
'
;
const
bpuSchema
=
z
.
object
({
const
bpuSchema
=
z
tglPemotongan
:
z
.
string
().
nonempty
(
'
Tanggal Pemotongan harus diisi
'
),
.
object
({
thnPajak
:
z
.
string
().
nonempty
(
'
Tahun Pajak harus diisi
'
),
tglPemotongan
:
z
.
string
().
nonempty
(
'
Tanggal Pemotongan harus diisi
'
),
msPajak
:
z
.
string
().
nonempty
(
'
Masa Pajak harus diisi
'
),
thnPajak
:
z
.
string
().
nonempty
(
'
Tahun Pajak harus diisi
'
),
idDipotong
:
z
msPajak
:
z
.
string
().
nonempty
(
'
Masa Pajak harus diisi
'
),
.
string
()
idDipotong
:
z
.
nonempty
(
'
NPWP harus diisi
'
)
.
string
()
.
regex
(
/^
\d{16}
$/
,
'
NPWP harus 16 digit
'
),
.
nonempty
(
'
NPWP harus diisi
'
)
nitku
:
z
.
regex
(
/^
\d{16}
$/
,
'
NPWP harus 16 digit
'
),
.
string
()
nitku
:
z
.
nonempty
(
'
NITKU harus diisi
'
)
.
string
()
.
regex
(
/^
\d{22}
$/
,
'
NITKU harus 22 digit
'
),
.
nonempty
(
'
NITKU harus diisi
'
)
namaDipotong
:
z
.
string
().
nonempty
(
'
Nama harus diisi
'
),
.
regex
(
/^
\d{22}
$/
,
'
NITKU harus 22 digit
'
),
email
:
z
.
string
().
email
({
message
:
'
Email tidak valid
'
}).
optional
().
or
(
z
.
literal
(
''
)),
namaDipotong
:
z
.
string
().
nonempty
(
'
Nama harus diisi
'
),
keterangan1
:
z
.
string
().
optional
(),
email
:
z
.
string
().
email
({
message
:
'
Email tidak valid
'
}).
optional
().
or
(
z
.
literal
(
''
)),
keterangan2
:
z
.
string
().
optional
(),
keterangan1
:
z
.
string
().
optional
(),
keterangan3
:
z
.
string
().
optional
(),
keterangan2
:
z
.
string
().
optional
(),
keterangan4
:
z
.
string
().
optional
(),
keterangan3
:
z
.
string
().
optional
(),
keterangan5
:
z
.
string
().
optional
(),
keterangan4
:
z
.
string
().
optional
(),
kdObjPjk
:
z
.
string
().
nonempty
(
'
Kode Objek Pajak harus diisi
'
),
keterangan5
:
z
.
string
().
optional
(),
fgFasilitas
:
z
.
string
().
nonempty
(
'
Fasilitas harus diisi
'
),
kdObjPjk
:
z
.
string
().
nonempty
(
'
Kode Objek Pajak harus diisi
'
),
noDokLainnya
:
z
.
string
().
nonempty
(
'
No Dokumen Lainnya harus diisi
'
),
fgFasilitas
:
z
.
string
().
nonempty
(
'
Fasilitas harus diisi
'
),
jmlBruto
:
z
.
string
().
nonempty
(
'
Jumlah Penghasilan Bruto harus diisi
'
),
noDokLainnya
:
z
.
string
().
optional
(),
tarif
:
z
.
union
([
z
.
string
().
nonempty
(
'
Tarif harus diisi
'
),
z
.
number
()]),
jmlBruto
:
z
.
string
().
nonempty
(
'
Jumlah Penghasilan Bruto harus diisi
'
),
pphDipotong
:
z
.
string
().
nonempty
(
'
PPh Yang Dipotong/Dipungut harus diisi
'
),
tarif
:
z
.
union
([
z
.
string
().
nonempty
(
'
Tarif harus diisi
'
),
z
.
number
()]),
namaDok
:
z
.
string
().
nonempty
(
'
Nama Dokumen harus diisi
'
),
pphDipotong
:
z
.
string
().
nonempty
(
'
PPh Yang Dipotong/Dipungut harus diisi
'
),
nomorDok
:
z
.
string
().
nonempty
(
'
Nomor Dokumen harus diisi
'
),
namaDok
:
z
.
string
().
nonempty
(
'
Nama Dokumen harus diisi
'
),
tglDok
:
z
.
string
().
nonempty
(
'
Tanggal Dokumen harus diisi
'
),
nomorDok
:
z
.
string
().
nonempty
(
'
Nomor Dokumen harus diisi
'
),
idTku
:
z
.
string
().
nonempty
(
'
Cabang harus diisi
'
),
tglDok
:
z
.
string
().
nonempty
(
'
Tanggal Dokumen harus diisi
'
),
});
idTku
:
z
.
string
().
nonempty
(
'
Cabang harus diisi
'
),
})
.
superRefine
((
data
,
ctx
)
=>
{
// Field dianggap DISABLED kalau fgFasilitas kosong ('') atau '9'
const
isDisabled
=
[
''
,
'
9
'
].
includes
(
data
.
fgFasilitas
);
// Jika tidak disabled, berarti aktif → wajib isi
if
(
!
isDisabled
&&
(
!
data
.
noDokLainnya
||
data
.
noDokLainnya
.
trim
()
===
''
))
{
ctx
.
addIssue
({
path
:
[
'
noDokLainnya
'
],
code
:
'
custom
'
,
message
:
'
No Dokumen Lainnya harus diisi
'
,
});
}
});
const
DnRekamView
=
()
=>
{
const
DnRekamView
=
()
=>
{
const
{
id
,
type
}
=
useParams
<
{
id
?:
string
;
type
?:
'
ubah
'
|
'
pengganti
'
|
'
new
'
}
>
();
const
{
id
,
type
}
=
useParams
<
{
id
?:
string
;
type
?:
'
ubah
'
|
'
pengganti
'
|
'
new
'
}
>
();
...
...
src/sections/bupot-unifikasi/bupot-nr/components/CustomColumnsButton.tsx
View file @
d89056f3
import
React
from
'
react
'
;
import
React
from
'
react
'
;
import
{
GridPreferencePanelsValue
,
useGridApiContext
}
from
'
@mui/x-data-grid-premium
'
;
import
type
{
GridPreferencePanelsValue
}
from
'
@mui/x-data-grid-premium
'
;
import
{
useGridApiContext
}
from
'
@mui/x-data-grid-premium
'
;
import
{
IconButton
,
Tooltip
}
from
'
@mui/material
'
;
import
{
IconButton
,
Tooltip
}
from
'
@mui/material
'
;
import
ViewColumnIcon
from
'
@mui/icons-material/ViewColumn
'
;
import
ViewColumnIcon
from
'
@mui/icons-material/ViewColumn
'
;
...
...
src/sections/bupot-unifikasi/bupot-nr/components/CustomFilterButton.tsx
View file @
d89056f3
This diff is collapsed.
Click to expand it.
src/sections/bupot-unifikasi/bupot-nr/components/CustomToolbar.tsx
View file @
d89056f3
import
*
as
React
from
'
react
'
;
import
*
as
React
from
'
react
'
;
import
{
GridToolbarContainer
,
GridToolbarProps
}
from
'
@mui/x-data-grid-premium
'
;
import
type
{
GridToolbarProps
}
from
'
@mui/x-data-grid-premium
'
;
import
{
GridToolbarContainer
}
from
'
@mui/x-data-grid-premium
'
;
import
{
Stack
,
Divider
,
IconButton
,
Tooltip
}
from
'
@mui/material
'
;
import
{
Stack
,
Divider
,
IconButton
,
Tooltip
}
from
'
@mui/material
'
;
import
{
ActionItem
}
from
'
../types/types
'
;
import
type
{
ActionItem
}
from
'
../types/types
'
;
import
{
CustomFilterButton
}
from
'
./CustomFilterButton
'
;
import
{
CustomFilterButton
}
from
'
./CustomFilterButton
'
;
import
CustomColumnsButton
from
'
./CustomColumnsButton
'
;
import
CustomColumnsButton
from
'
./CustomColumnsButton
'
;
...
...
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCancel
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCancel
Nr
.tsx
View file @
d89056f3
// import React, { useMemo, useState } from 'react';
// import { Stack, Button, Typography } from '@mui/material';
// import { useQueryClient } from '@tanstack/react-query';
// import { enqueueSnackbar } from 'notistack';
// import { DatePicker } from '@mui/x-date-pickers/DatePicker';
// import dayjs, { Dayjs } from 'dayjs';
// import minMax from 'dayjs/plugin/minMax';
// import DialogUmum from 'src/shared/components/dialog/DialogUmum';
// import DialogProgressBar from 'src/shared/components/dialog/DialogProgressBar';
// import useDialogProgressBar from 'src/shared/hooks/useDialogProgressBar';
// import useCancelDn from '../../hooks/useCancelDn';
// import { GridRowSelectionModel } from '@mui/x-data-grid-premium';
// dayjs.extend(minMax);
// // Helper format tanggal ke format API (DDMMYYYY)
// const formatDateDDMMYYYY = (d: Date) => {
// const dd = String(d.getDate()).padStart(2, '0');
// const mm = String(d.getMonth() + 1).padStart(2, '0');
// const yyyy = d.getFullYear();
// return `${dd}${mm}${yyyy}`;
// };
// interface ModalCancelDnProps {
// dataSelected?: any[]; // ✅ array of full row data (dari dataSelectedRef.current)
// setSelectionModel?: React.Dispatch<React.SetStateAction<GridRowSelectionModel | undefined>>;
// tableApiRef?: React.MutableRefObject<any>;
// isOpenDialogCancel: boolean;
// setIsOpenDialogCancel: (v: boolean) => void;
// successMessage?: string;
// }
// const ModalCancelDn: React.FC<ModalCancelDnProps> = ({
// dataSelected = [],
// setSelectionModel,
// tableApiRef,
// isOpenDialogCancel,
// setIsOpenDialogCancel,
// successMessage = 'Data berhasil dibatalkan',
// }) => {
// const queryClient = useQueryClient();
// const [tglPembatalan, setTglPembatalan] = useState<Dayjs | null>(null);
// const [isOpenDialogProgressBar, setIsOpenDialogProgressBar] = useState(false);
// const {
// numberOfData,
// numberOfDataFail,
// numberOfDataProcessed,
// numberOfDataSuccess,
// processSuccess,
// processFail,
// resetToDefault,
// status,
// } = useDialogProgressBar();
// const { mutateAsync } = useCancelDn({
// onSuccess: () => processSuccess(),
// onError: () => processFail(),
// });
// // ✅ Ambil tanggal pemotongan paling awal (minDate untuk DatePicker)
// const minPembatalanDate = useMemo(() => {
// if (!dataSelected.length) return null;
// const dates = dataSelected
// .map((d) => {
// const tgl = d.tglPemotongan || d.tglpemotongan;
// return tgl ? dayjs(tgl, ['YYYY-MM-DD', 'DD/MM/YYYY']) : null;
// })
// .filter((d): d is Dayjs => !!d && d.isValid());
// return dates.length > 0 ? dayjs.min(dates) : null;
// }, [dataSelected]);
// const handleCloseModal = () => {
// setIsOpenDialogCancel(false);
// resetToDefault();
// };
// const clearSelection = () => {
// tableApiRef?.current?.setRowSelectionModel?.([]);
// setSelectionModel?.(undefined);
// };
// const handleSubmit = async () => {
// if (!tglPembatalan) {
// enqueueSnackbar('Tanggal pembatalan harus diisi', { variant: 'warning' });
// return;
// }
// const formattedDate = formatDateDDMMYYYY(tglPembatalan.toDate());
// const ids = dataSelected.map((item) => String(item.id ?? item.internal_id));
// try {
// setIsOpenDialogProgressBar(true);
// const results = await Promise.allSettled(
// ids.map((id) => mutateAsync({ id, tglPembatalan: formattedDate }))
// );
// const rejected = results.filter((r) => r.status === 'rejected');
// const success = results.filter((r) => r.status === 'fulfilled');
// if (rejected.length > 0) {
// const errorMessages = rejected
// .map((r) => (r.status === 'rejected' ? r.reason?.message : ''))
// .filter(Boolean)
// .join('\n');
// enqueueSnackbar(
// <span style={{ whiteSpace: 'pre-line' }}>
// {errorMessages || `${rejected.length} dari ${ids.length} data gagal dibatalkan.`}
// </span>,
// { variant: 'error' }
// );
// processFail();
// } else {
// enqueueSnackbar(successMessage, { variant: 'success' });
// processSuccess();
// }
// // ✅ Langkah penting:
// // tunggu sampai React Query benar-benar refetch data baru
// await queryClient.invalidateQueries({ queryKey: ['unifikasi', 'dn'] });
// // ✅ lalu clear selection di DataGrid
// tableApiRef?.current?.setRowSelectionModel?.([]);
// setSelectionModel?.(undefined);
// handleCloseModal();
// } catch (error: any) {
// enqueueSnackbar(error?.message || 'Gagal membatalkan data', { variant: 'error' });
// } finally {
// setIsOpenDialogProgressBar(false);
// }
// };
// return (
// <>
// {/* ✅ Dialog reusable */}
// <DialogUmum
// isOpen={isOpenDialogCancel}
// onClose={handleCloseModal}
// title="Batal Bukti Pemotongan/Pemungutan PPh Unifikasi"
// >
// <Stack spacing={2}>
// <Typography>
// Silakan isi tanggal pembatalan. Tanggal tidak boleh sebelum tanggal pemotongan.
// </Typography>
// <DatePicker
// label="Tanggal Pembatalan"
// format="DD/MM/YYYY"
// value={tglPembatalan}
// maxDate={dayjs()} // tanggal maksimal = hari ini
// minDate={minPembatalanDate || undefined} // 🔹 minDate sesuai tglPemotongan
// onChange={(newValue) => setTglPembatalan(newValue)}
// slotProps={{
// textField: {
// size: 'medium',
// fullWidth: true,
// helperText:
// minPembatalanDate && `Tanggal minimal: ${minPembatalanDate.format('DD/MM/YYYY')}`,
// InputLabelProps: { shrink: true },
// sx: {
// '& .MuiOutlinedInput-root': {
// borderRadius: 1.5,
// backgroundColor: '#fff',
// '&:hover fieldset': {
// borderColor: '#123375 !important',
// },
// '&.Mui-focused fieldset': {
// borderColor: '#123375 !important',
// borderWidth: '1px',
// },
// },
// },
// },
// }}
// />
// <Stack direction="row" justifyContent="flex-end" spacing={1} mt={1}>
// <Button variant="outlined" onClick={handleCloseModal}>
// Batal
// </Button>
// <Button
// variant="contained"
// color="error"
// onClick={handleSubmit}
// disabled={!tglPembatalan}
// >
// Batalkan
// </Button>
// </Stack>
// </Stack>
// </DialogUmum>
// {/* ✅ Dialog progress bar */}
// <DialogProgressBar
// isOpen={isOpenDialogProgressBar}
// handleClose={() => {
// handleCloseModal();
// setIsOpenDialogProgressBar(false);
// }}
// numberOfData={numberOfData}
// numberOfDataProcessed={numberOfDataProcessed}
// numberOfDataFail={numberOfDataFail}
// numberOfDataSuccess={numberOfDataSuccess}
// status={status}
// />
// </>
// );
// };
// export default ModalCancelDn;
import
React
,
{
useEffect
,
useMemo
,
useState
}
from
'
react
'
;
import
React
,
{
useEffect
,
useMemo
,
useState
}
from
'
react
'
;
import
{
Stack
,
Button
,
Typography
}
from
'
@mui/material
'
;
import
{
Stack
,
Button
,
Typography
}
from
'
@mui/material
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
DatePicker
}
from
'
@mui/x-date-pickers/DatePicker
'
;
import
{
DatePicker
}
from
'
@mui/x-date-pickers/DatePicker
'
;
import
dayjs
,
{
Dayjs
}
from
'
dayjs
'
;
import
type
{
Dayjs
}
from
'
dayjs
'
;
import
dayjs
from
'
dayjs
'
;
import
minMax
from
'
dayjs/plugin/minMax
'
;
import
minMax
from
'
dayjs/plugin/minMax
'
;
import
DialogUmum
from
'
src/shared/components/dialog/DialogUmum
'
;
import
DialogUmum
from
'
src/shared/components/dialog/DialogUmum
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
useCancelDn
from
'
../../hooks/useCancel
Dn
'
;
import
useCancelDn
from
'
../../hooks/useCancel
Nr
'
;
import
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
import
type
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
dayjs
.
extend
(
minMax
);
dayjs
.
extend
(
minMax
);
...
@@ -247,7 +31,7 @@ interface ModalCancelDnProps {
...
@@ -247,7 +31,7 @@ interface ModalCancelDnProps {
successMessage
?:
string
;
successMessage
?:
string
;
}
}
const
ModalCancel
Dn
:
React
.
FC
<
ModalCancelDnProps
>
=
({
const
ModalCancel
Nr
:
React
.
FC
<
ModalCancelDnProps
>
=
({
dataSelected
=
[],
dataSelected
=
[],
setSelectionModel
,
setSelectionModel
,
tableApiRef
,
tableApiRef
,
...
@@ -441,4 +225,4 @@ const ModalCancelDn: React.FC<ModalCancelDnProps> = ({
...
@@ -441,4 +225,4 @@ const ModalCancelDn: React.FC<ModalCancelDnProps> = ({
);
);
};
};
export
default
ModalCancel
Dn
;
export
default
ModalCancel
Nr
;
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCetakPdf
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalCetakPdf
Nr
.tsx
View file @
d89056f3
...
@@ -4,7 +4,7 @@ import DialogUmum from 'src/shared/components/dialog/DialogUmum';
...
@@ -4,7 +4,7 @@ import DialogUmum from 'src/shared/components/dialog/DialogUmum';
import
DialogContent
from
'
@mui/material/DialogContent
'
;
import
DialogContent
from
'
@mui/material/DialogContent
'
;
import
CircularProgress
from
'
@mui/material/CircularProgress
'
;
import
CircularProgress
from
'
@mui/material/CircularProgress
'
;
import
Box
from
'
@mui/material/Box
'
;
import
Box
from
'
@mui/material/Box
'
;
import
useCetakPdfDn
from
'
../../hooks/useCetakPdf
Dn
'
;
import
useCetakPdfDn
from
'
../../hooks/useCetakPdf
Nr
'
;
import
normalizePayloadCetakPdf
from
'
../../utils/normalizePayloadCetakPdf
'
;
import
normalizePayloadCetakPdf
from
'
../../utils/normalizePayloadCetakPdf
'
;
interface
ModalCetakPdfDnProps
{
interface
ModalCetakPdfDnProps
{
...
@@ -13,18 +13,7 @@ interface ModalCetakPdfDnProps {
...
@@ -13,18 +13,7 @@ interface ModalCetakPdfDnProps {
onClose
:
()
=>
void
;
onClose
:
()
=>
void
;
}
}
const
formatTanggalIndo
=
(
isoDate
:
string
|
undefined
|
null
):
string
=>
{
const
ModalCetakPdfNr
:
React
.
FC
<
ModalCetakPdfDnProps
>
=
({
payload
,
isOpen
,
onClose
})
=>
{
if
(
!
isoDate
)
return
''
;
const
date
=
new
Date
(
isoDate
);
const
formatter
=
new
Intl
.
DateTimeFormat
(
'
id-ID
'
,
{
day
:
'
2-digit
'
,
month
:
'
long
'
,
year
:
'
numeric
'
,
});
return
formatter
.
format
(
date
);
};
const
ModalCetakPdfDn
:
React
.
FC
<
ModalCetakPdfDnProps
>
=
({
payload
,
isOpen
,
onClose
})
=>
{
const
[
pdfUrl
,
setPdfUrl
]
=
useState
<
string
|
null
>
(
null
);
const
[
pdfUrl
,
setPdfUrl
]
=
useState
<
string
|
null
>
(
null
);
const
[
loading
,
setLoading
]
=
useState
<
boolean
>
(
false
);
const
[
loading
,
setLoading
]
=
useState
<
boolean
>
(
false
);
...
@@ -104,4 +93,4 @@ const ModalCetakPdfDn: React.FC<ModalCetakPdfDnProps> = ({ payload, isOpen, onCl
...
@@ -104,4 +93,4 @@ const ModalCetakPdfDn: React.FC<ModalCetakPdfDnProps> = ({ payload, isOpen, onCl
);
);
};
};
export
default
ModalCetakPdf
Dn
;
export
default
ModalCetakPdf
Nr
;
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalDelete
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalDelete
Nr
.tsx
View file @
d89056f3
import
React
,
{
useEffect
,
useState
}
from
'
react
'
;
import
React
,
{
useEffect
,
useState
}
from
'
react
'
;
import
{
use
Mutation
,
use
QueryClient
}
from
'
@tanstack/react-query
'
;
import
{
useQueryClient
}
from
'
@tanstack/react-query
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
dnApi
from
'
../../utils/api
'
;
import
queryKey
from
'
../../constant/queryKey
'
;
import
DialogConfirm
from
'
src/shared/components/dialog/DialogConfirm
'
;
import
DialogConfirm
from
'
src/shared/components/dialog/DialogConfirm
'
;
import
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
import
type
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
import
useDeleteDn
from
'
../../hooks/useDelete
Dn
'
;
import
useDeleteDn
from
'
../../hooks/useDelete
Nr
'
;
interface
ModalDeleteDnProps
{
interface
ModalDeleteDnProps
{
dataSelected
?:
GridRowSelectionModel
;
dataSelected
?:
GridRowSelectionModel
;
...
@@ -46,7 +44,7 @@ const normalizeSelection = (sel?: any): (string | number)[] => {
...
@@ -46,7 +44,7 @@ const normalizeSelection = (sel?: any): (string | number)[] => {
return
[];
return
[];
};
};
const
ModalDelete
Dn
:
React
.
FC
<
ModalDeleteDnProps
>
=
({
const
ModalDelete
Nr
:
React
.
FC
<
ModalDeleteDnProps
>
=
({
dataSelected
,
dataSelected
,
setSelectionModel
,
setSelectionModel
,
tableApiRef
,
tableApiRef
,
...
@@ -145,4 +143,4 @@ const ModalDeleteDn: React.FC<ModalDeleteDnProps> = ({
...
@@ -145,4 +143,4 @@ const ModalDeleteDn: React.FC<ModalDeleteDnProps> = ({
);
);
};
};
export
default
ModalDelete
Dn
;
export
default
ModalDelete
Nr
;
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalUpload
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/components/dialog/ModalUpload
Nr
.tsx
View file @
d89056f3
...
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query';
...
@@ -3,7 +3,7 @@ import { useQueryClient } from '@tanstack/react-query';
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
DialogProgressBar
from
'
src/shared/components/dialog/DialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
useDialogProgressBar
from
'
src/shared/hooks/useDialogProgressBar
'
;
import
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
import
type
{
GridRowSelectionModel
}
from
'
@mui/x-data-grid-premium
'
;
import
useUpload
from
'
../../hooks/useUpload
'
;
import
useUpload
from
'
../../hooks/useUpload
'
;
import
DialogUmum
from
'
src/shared/components/dialog/DialogUmum
'
;
import
DialogUmum
from
'
src/shared/components/dialog/DialogUmum
'
;
import
Stack
from
'
@mui/material/Stack
'
;
import
Stack
from
'
@mui/material/Stack
'
;
...
@@ -11,12 +11,12 @@ import Grid from '@mui/material/Grid';
...
@@ -11,12 +11,12 @@ import Grid from '@mui/material/Grid';
import
{
Field
}
from
'
src/components/hook-form
'
;
import
{
Field
}
from
'
src/components/hook-form
'
;
import
MenuItem
from
'
@mui/material/MenuItem
'
;
import
MenuItem
from
'
@mui/material/MenuItem
'
;
import
{
useSelector
}
from
'
react-redux
'
;
import
{
useSelector
}
from
'
react-redux
'
;
import
{
RootState
}
from
'
src/store
'
;
import
type
{
RootState
}
from
'
src/store
'
;
import
Agreement
from
'
src/shared/components/agreement/Agreement
'
;
import
Agreement
from
'
src/shared/components/agreement/Agreement
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
{
FormProvider
,
useForm
}
from
'
react-hook-form
'
;
import
{
LoadingButton
}
from
'
@mui/lab
'
;
import
{
LoadingButton
}
from
'
@mui/lab
'
;
interface
ModalUpload
Dn
Props
{
interface
ModalUpload
Nr
Props
{
dataSelected
?:
GridRowSelectionModel
;
dataSelected
?:
GridRowSelectionModel
;
setSelectionModel
?:
React
.
Dispatch
<
React
.
SetStateAction
<
GridRowSelectionModel
|
undefined
>>
;
setSelectionModel
?:
React
.
Dispatch
<
React
.
SetStateAction
<
GridRowSelectionModel
|
undefined
>>
;
tableApiRef
?:
React
.
MutableRefObject
<
any
>
;
tableApiRef
?:
React
.
MutableRefObject
<
any
>
;
...
@@ -55,7 +55,7 @@ const normalizeSelection = (sel?: any): (string | number)[] => {
...
@@ -55,7 +55,7 @@ const normalizeSelection = (sel?: any): (string | number)[] => {
return
[];
return
[];
};
};
const
ModalUpload
Dn
:
React
.
FC
<
ModalUploadDn
Props
>
=
({
const
ModalUpload
Nr
:
React
.
FC
<
ModalUploadNr
Props
>
=
({
dataSelected
,
dataSelected
,
setSelectionModel
,
setSelectionModel
,
tableApiRef
,
tableApiRef
,
...
@@ -66,7 +66,7 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
...
@@ -66,7 +66,7 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
})
=>
{
})
=>
{
const
queryClient
=
useQueryClient
();
const
queryClient
=
useQueryClient
();
const
upload
Dn
=
useUpload
();
const
upload
Nr
=
useUpload
();
// custom hooks for progress state
// custom hooks for progress state
const
{
const
{
numberOfData
,
numberOfData
,
...
@@ -124,7 +124,7 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
...
@@ -124,7 +124,7 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
enqueueSnackbar
(
error
?.
message
||
'
Gagal upload data
'
,
{
variant
:
'
error
'
});
enqueueSnackbar
(
error
?.
message
||
'
Gagal upload data
'
,
{
variant
:
'
error
'
});
}
finally
{
}
finally
{
// sesuaikan queryKey jika perlu; tetap panggil invalidasi
// sesuaikan queryKey jika perlu; tetap panggil invalidasi
queryClient
.
invalidateQueries
({
queryKey
:
[
'
unifikasi
'
,
'
dn
'
]
});
queryClient
.
invalidateQueries
({
queryKey
:
[
'
unifikasi
'
,
'
nr
'
]
});
}
}
};
};
...
@@ -158,17 +158,17 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
...
@@ -158,17 +158,17 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
<
LoadingButton
<
LoadingButton
type=
"button"
type=
"button"
disabled=
{
!
isCheckedAgreement
}
disabled=
{
!
isCheckedAgreement
}
// onClick={onSubmit}
onClick=
{
async
()
=>
{
onClick=
{
async
()
=>
{
if
(
onConfirmUpload
)
{
if
(
onConfirmUpload
)
{
await
onConfirmUpload
();
await
onConfirmUpload
();
setIsOpenDialogUpload
(
false
);
setIsOpenDialogUpload
(
false
);
return
;
return
;
}
}
await
onSubmit
();
await
onSubmit
();
}
}
}
}
loading=
{
upload
Dn
.
isPending
}
loading=
{
upload
Nr
.
isPending
}
variant=
"contained"
variant=
"contained"
sx=
{
{
background
:
'
#143B88
'
}
}
sx=
{
{
background
:
'
#143B88
'
}
}
>
>
...
@@ -195,4 +195,4 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
...
@@ -195,4 +195,4 @@ const ModalUploadDn: React.FC<ModalUploadDnProps> = ({
);
);
};
};
export
default
ModalUpload
Dn
;
export
default
ModalUpload
Nr
;
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/DokumenReferensi.tsx
0 → 100644
View file @
d89056f3
import
Divider
from
'
@mui/material/Divider
'
;
import
Grid
from
'
@mui/material/Grid
'
;
import
MenuItem
from
'
@mui/material/MenuItem
'
;
import
{
useEffect
}
from
'
react
'
;
import
{
Field
}
from
'
src/components/hook-form
'
;
import
{
JENIS_DOKUMEN
}
from
'
../../constant
'
;
import
dayjs
from
'
dayjs
'
;
import
{
useSelector
}
from
'
react-redux
'
;
import
type
{
RootState
}
from
'
src/store
'
;
import
{
useFormContext
}
from
'
react-hook-form
'
;
const
DokumenReferensi
=
()
=>
{
const
{
watch
,
setValue
}
=
useFormContext
<
Record
<
string
,
any
>>
();
const
nitku
=
useSelector
((
state
:
RootState
)
=>
state
.
user
.
data
.
nitku_trial
);
const
nitkuValue
=
watch
(
'
idTku
'
);
useEffect
(()
=>
{
if
(
!
nitkuValue
&&
nitku
)
{
setValue
(
'
idTku
'
,
nitku
);
}
},
[
nitku
,
nitkuValue
,
setValue
]);
return
(
<
Grid
sx=
{
{
mb
:
3
}
}
container
rowSpacing=
{
2
}
columnSpacing=
{
2
}
>
<
Grid
sx=
{
{
mt
:
3
}
}
size=
{
{
md
:
12
}
}
>
<
Divider
sx=
{
{
fontWeight
:
'
bold
'
}
}
textAlign=
"left"
>
Daftar Dokumen
</
Divider
>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Select
name=
"namaDok"
label=
"Nama Dokumen"
>
{
JENIS_DOKUMEN
.
map
((
item
,
index
)
=>
(
<
MenuItem
key=
{
index
}
value=
{
item
.
value
}
>
{
item
.
label
}
</
MenuItem
>
))
}
</
Field
.
Select
>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"nomorDok"
label=
"Nomor Dokumen"
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
DatePicker
name=
"tglDok"
label=
"Tanggal Dokumen"
maxDate=
{
dayjs
()
}
minDate=
{
dayjs
(
'
2025-01-01
'
)
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Select
name=
"idTku"
label=
"NITKU Pemotong"
>
<
MenuItem
value=
{
nitku
}
>
{
nitku
}
</
MenuItem
>
</
Field
.
Select
>
</
Grid
>
</
Grid
>
);
};
export
default
DokumenReferensi
;
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/Identitas.tsx
0 → 100644
View file @
d89056f3
import
Box
from
'
@mui/material/Box
'
;
import
Button
from
'
@mui/material/Button
'
;
import
Grid
from
'
@mui/material/Grid
'
;
import
dayjs
from
'
dayjs
'
;
import
{
useEffect
,
useState
}
from
'
react
'
;
import
{
useFormContext
}
from
'
react-hook-form
'
;
import
{
Field
}
from
'
src/components/hook-form
'
;
import
type
{
TCountryResult
}
from
'
../../types/types
'
;
import
MenuItem
from
'
@mui/material/MenuItem
'
;
type
IdentitasProps
=
{
isPengganti
:
boolean
;
existingNr
?:
any
;
// Data penuh dari API (opsional, untuk edit/pengganti)
country
:
TCountryResult
;
};
const
Identitas
=
({
isPengganti
,
existingNr
,
country
}:
IdentitasProps
)
=>
{
const
{
setValue
,
watch
,
getValues
}
=
useFormContext
();
const
tanggalPemotongan
=
watch
(
'
tglPemotongan
'
);
const
maxKeterangan
=
5
;
const
[
jumlahKeterangan
,
setJumlahKeterangan
]
=
useState
<
number
>
(
0
);
// 🧩 Auto isi Tahun & Masa Pajak berdasarkan tanggalPemotongan
useEffect
(()
=>
{
if
(
tanggalPemotongan
)
{
const
date
=
dayjs
(
tanggalPemotongan
);
setValue
(
'
thnPajak
'
,
date
.
format
(
'
YYYY
'
));
setValue
(
'
masaPajak
'
,
date
.
format
(
'
MM
'
));
}
else
{
setValue
(
'
thnPajak
'
,
''
);
setValue
(
'
masaPajak
'
,
''
);
}
},
[
tanggalPemotongan
,
setValue
]);
// 🧠 Saat data API sudah masuk (edit/pengganti)
// Gunakan getValues() agar langsung membaca nilai dari form (bukan nunggu watch)
useEffect
(()
=>
{
if
(
existingNr
)
{
const
currentValues
=
getValues
();
const
arr
=
[
currentValues
.
keterangan1
,
currentValues
.
keterangan2
,
currentValues
.
keterangan3
,
currentValues
.
keterangan4
,
currentValues
.
keterangan5
,
];
const
count
=
arr
.
filter
((
k
)
=>
!!
k
&&
k
.
trim
()
!==
''
).
length
;
console
.
log
(
'
🧠 Detected existing keterangan:
'
,
arr
,
'
count:
'
,
count
);
if
(
count
>
0
)
{
setJumlahKeterangan
(
count
);
}
}
},
[
existingNr
,
getValues
]);
// 🧩 Pantau perubahan manual user (Tambah/Hapus)
useEffect
(()
=>
{
const
subscription
=
watch
((
values
)
=>
{
const
arr
=
[
values
.
keterangan1
,
values
.
keterangan2
,
values
.
keterangan3
,
values
.
keterangan4
,
values
.
keterangan5
,
];
const
count
=
arr
.
filter
((
k
)
=>
!!
k
&&
k
.
trim
()
!==
''
).
length
;
setJumlahKeterangan
(
count
);
});
return
()
=>
subscription
.
unsubscribe
();
},
[
watch
]);
// ➕ Tambah field baru
const
handleTambah
=
()
=>
{
if
(
jumlahKeterangan
<
maxKeterangan
)
{
setJumlahKeterangan
((
prev
)
=>
prev
+
1
);
}
};
// ➖ Hapus field terakhir
const
handleHapus
=
()
=>
{
if
(
jumlahKeterangan
>
0
)
{
setValue
(
`keterangan
${
jumlahKeterangan
}
`
,
''
);
setJumlahKeterangan
((
prev
)
=>
prev
-
1
);
}
};
return
(
<>
{
/* 📋 Identitas Dasar */
}
<
Grid
container
rowSpacing=
{
2
}
alignItems=
"center"
columnSpacing=
{
2
}
sx=
{
{
mb
:
4
}
}
>
{
/* 📅 Tanggal & Masa Pajak */
}
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
DatePicker
name=
"tglPemotongan"
label=
"Tanggal Pemotongan"
format=
"DD/MM/YYYY"
maxDate=
{
dayjs
()
}
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
3
}
}
>
<
Field
.
DatePicker
name=
"thnPajak"
label=
"Tahun Pajak"
view=
"year"
format=
"YYYY"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
3
}
}
>
<
Field
.
DatePicker
name=
"masaPajak"
label=
"Masa Pajak"
view=
"month"
format=
"MM"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"idDipotong"
label=
"Tax ID Number (TIN)"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"namaDipotong"
label=
"Nama"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
12
}
}
>
<
Field
.
Text
name=
"alamatDipotong"
label=
"Alamat"
multiline
minRows=
{
2
}
disabled=
{
isPengganti
}
sx=
{
{
'
& .MuiInputBase-inputMultiline
'
:
{
lineHeight
:
1.6
,
},
'
& .MuiOutlinedInput-root
'
:
{
borderRadius
:
'
8px
'
,
},
'
& .MuiOutlinedInput-notchedOutline
'
:
{
borderColor
:
'
#bdbdbd
'
,
},
'
&.Mui-focused .MuiOutlinedInput-notchedOutline
'
:
{
borderColor
:
'
#1976d2
'
,
borderWidth
:
'
2px
'
,
},
}
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
12
}
}
>
<
Field
.
Select
name=
"negaraDipotong"
label=
"Negara"
disabled=
{
isPengganti
}
>
{
country
.
map
((
item
)
=>
(
<
MenuItem
key=
{
item
.
kode
}
value=
{
item
.
kode
}
>
{
`${item.nama}`
}
</
MenuItem
>
))
}
</
Field
.
Select
>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"tmptLahirDipotong"
label=
"Tempat Lahir"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
DatePicker
name=
"tglLahirDipotong"
label=
"Tanggal Lahir"
format=
"DD/MM/YYYY"
maxDate=
{
dayjs
()
}
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"nomorPaspor"
label=
"No. Paspor"
disabled=
{
isPengganti
}
/>
</
Grid
>
<
Grid
size=
{
{
md
:
6
}
}
>
<
Field
.
Text
name=
"nomorKitasKitap"
label=
"No.KITAS/KITAP"
disabled=
{
isPengganti
}
/>
</
Grid
>
</
Grid
>
{
/* ✏️ Tombol Tambah / Hapus Keterangan */
}
<
Box
sx=
{
{
display
:
'
flex
'
,
gap
:
2
,
mb
:
3
}
}
>
<
Box
sx=
{
{
borderRadius
:
'
18px
'
,
border
:
jumlahKeterangan
>=
maxKeterangan
?
'
1px solid #eee
'
:
'
1px solid #2e7d3280
'
,
color
:
jumlahKeterangan
>=
maxKeterangan
?
'
#eee
'
:
'
#2e7d3280
'
,
p
:
'
0px 10px
'
,
}
}
>
<
Button
disabled=
{
jumlahKeterangan
>=
maxKeterangan
}
onClick=
{
handleTambah
}
>
Tambah Keterangan
</
Button
>
</
Box
>
<
Box
sx=
{
{
borderRadius
:
'
18px
'
,
border
:
jumlahKeterangan
===
0
?
'
1px solid #eee
'
:
'
1px solid #f44336
'
,
color
:
jumlahKeterangan
===
0
?
'
#eee
'
:
'
#f44336
'
,
p
:
'
0px 10px
'
,
}
}
>
<
Button
disabled=
{
jumlahKeterangan
===
0
}
onClick=
{
handleHapus
}
>
Hapus Keterangan
</
Button
>
</
Box
>
</
Box
>
{
/* 🗒️ Input Keterangan Tambahan */
}
<
Box
sx=
{
{
mb
:
3
}
}
>
{
Array
.
from
({
length
:
jumlahKeterangan
}).
map
((
_
,
i
)
=>
(
<
Grid
size=
{
{
md
:
12
}
}
key=
{
`keterangan${i + 1}`
}
>
<
Field
.
Text
sx=
{
{
mb
:
2
}
}
name=
{
`keterangan${i + 1}`
}
label=
{
`Keterangan Tambahan ${i + 1}`
}
/>
</
Grid
>
))
}
</
Box
>
</>
);
};
export
default
Identitas
;
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/PanduanDnRekam.tsx
0 → 100644
View file @
d89056f3
import
type
{
FC
}
from
'
react
'
;
import
{
Fragment
,
memo
}
from
'
react
'
;
import
{
Box
,
Button
,
Card
,
CardContent
,
CardHeader
,
IconButton
,
Typography
}
from
'
@mui/material
'
;
import
{
ChevronRightRounded
,
CloseRounded
}
from
'
@mui/icons-material
'
;
import
{
m
}
from
'
framer-motion
'
;
import
{
PANDUAN_REKAM_DN
}
from
'
../../constant
'
;
interface
PanduanDnRekamProps
{
handleOpen
:
()
=>
void
;
isOpen
:
boolean
;
}
const
PanduanDnRekam
:
FC
<
PanduanDnRekamProps
>
=
({
handleOpen
,
isOpen
})
=>
(
<
Box
position=
"sticky"
>
{
/* Tombol toggle */
}
{
!
isOpen
&&
(
<
Box
height=
"100%"
display=
"flex"
justifyContent=
"center"
alignItems=
"center"
>
<
Button
variant=
"contained"
sx=
{
{
height
:
'
fit-content
'
,
right
:
0
,
borderRadius
:
0
,
minWidth
:
35
,
pt
:
3
,
pb
:
3
,
fontWeight
:
'
bold
'
,
fontSize
:
16
,
backgroundColor
:
'
#143B88
'
,
}
}
size=
"small"
onClick=
{
handleOpen
}
>
<
span
style=
{
{
writingMode
:
'
vertical-rl
'
,
transform
:
'
rotate(180deg)
'
,
display
:
'
flex
'
,
alignItems
:
'
center
'
,
}
}
>
Panduan Penggunaan
<
ChevronRightRounded
sx=
{
{
fontSize
:
30
}
}
/>
</
span
>
</
Button
>
</
Box
>
)
}
{
/* Konten panduan */
}
{
isOpen
&&
(
<
m
.
div
initial=
{
{
x
:
20
,
opacity
:
0
}
}
animate=
{
{
x
:
0
,
opacity
:
1
,
transition
:
{
delay
:
0.2
}
}
}
>
<
Card
>
<
CardHeader
avatar=
{
<
img
src=
"/assets/icon_panduan_penggunaan_1.svg"
alt=
"Panduan"
loading=
"lazy"
/>
}
sx=
{
{
backgroundColor
:
'
#123375
'
,
color
:
'
#FFFFFF
'
,
p
:
2
,
'
& .MuiCardHeader-title
'
:
{
fontSize
:
18
},
}
}
action=
{
<
IconButton
aria
-
label=
"close"
onClick=
{
handleOpen
}
sx=
{
{
color
:
'
white
'
}
}
>
<
CloseRounded
/>
</
IconButton
>
}
title=
"Panduan Penggunaan"
/>
<
CardContent
sx=
{
{
maxHeight
:
300
,
overflow
:
'
auto
'
,
'
&::-webkit-scrollbar
'
:
{
width
:
6
},
'
&::-webkit-scrollbar-track
'
:
{
backgroundColor
:
'
#f0f0f0
'
,
borderRadius
:
8
,
},
'
&::-webkit-scrollbar-thumb
'
:
{
backgroundColor
:
'
#123375
'
,
borderRadius
:
8
,
},
'
&::-webkit-scrollbar-thumb:hover
'
:
{
backgroundColor
:
'
#0d2858
'
,
},
scrollbarWidth
:
'
thin
'
,
scrollbarColor
:
'
#123375 #f0f0f0
'
,
}
}
>
{
/* Deskripsi Form */
}
<
Typography
variant=
"body2"
sx=
{
{
mb
:
2
,
whiteSpace
:
'
pre-line
'
}
}
>
<
strong
>
Deskripsi Form:
</
strong
>
<
br
/>
{
PANDUAN_REKAM_DN
.
description
.
intro
}
</
Typography
>
<
Typography
variant=
"body2"
>
{
PANDUAN_REKAM_DN
.
description
.
textList
}
</
Typography
>
<
Box
component=
"ol"
sx=
{
{
pl
:
3
,
mb
:
2
}
}
>
{
PANDUAN_REKAM_DN
.
description
.
list
.
map
((
item
,
idx
)
=>
(
<
Typography
key=
{
`desc-${idx}`
}
variant=
"body2"
component=
"li"
>
{
item
}
</
Typography
>
))
}
</
Box
>
<
Typography
variant=
"body2"
sx=
{
{
mb
:
2
}
}
>
{
PANDUAN_REKAM_DN
.
description
.
closing
}
</
Typography
>
{
/* Bagian-bagian */
}
{
PANDUAN_REKAM_DN
.
sections
.
map
((
section
,
i
)
=>
(
<
Box
key=
{
`section-${i}`
}
sx=
{
{
mb
:
2
}
}
>
<
Typography
variant=
"body2"
sx=
{
{
fontWeight
:
'
bold
'
,
fontSize
:
'
0.95rem
'
,
mb
:
0.5
}
}
>
{
section
.
title
}
</
Typography
>
<
Box
component=
"ul"
sx=
{
{
pl
:
2
,
listStyle
:
'
disc
'
}
}
>
{
section
.
items
.
map
((
item
,
idx
)
=>
(
<
Fragment
key=
{
`item-${i}-${idx}`
}
>
<
Box
component=
"li"
sx=
{
{
mb
:
0.5
}
}
>
<
Typography
variant=
"body2"
component=
"span"
>
{
item
.
text
}
</
Typography
>
{
item
.
subItems
?.
length
>
0
&&
(
<
Box
component=
"ol"
sx=
{
{
pl
:
3
,
listStyle
:
'
decimal
'
}
}
>
{
item
.
subItems
.
map
((
sub
,
subIdx
)
=>
(
<
Typography
key=
{
`sub-${i}-${idx}-${subIdx}`
}
variant=
"body2"
component=
"li"
>
{
sub
}
</
Typography
>
))
}
</
Box
>
)
}
</
Box
>
</
Fragment
>
))
}
</
Box
>
</
Box
>
))
}
</
CardContent
>
</
Card
>
</
m
.
div
>
)
}
</
Box
>
);
export
default
memo
(
PanduanDnRekam
);
src/sections/bupot-unifikasi/bupot-nr/components/rekamNr/PphDipotong.tsx
0 → 100644
View file @
d89056f3
This diff is collapsed.
Click to expand it.
src/sections/bupot-unifikasi/bupot-nr/constant/queryKey.tsx
View file @
d89056f3
...
@@ -2,14 +2,14 @@ const appRootKey = 'unifikasi';
...
@@ -2,14 +2,14 @@ const appRootKey = 'unifikasi';
const
queryKey
=
{
const
queryKey
=
{
getKodeObjekPajak
:
(
params
:
any
)
=>
[
appRootKey
,
'
kode-objek-pajak
'
,
params
],
getKodeObjekPajak
:
(
params
:
any
)
=>
[
appRootKey
,
'
kode-objek-pajak
'
,
params
],
dn
:
{
nr
:
{
all
:
(
params
:
any
)
=>
[
appRootKey
,
'
dn
'
,
params
],
all
:
(
params
:
any
)
=>
[
appRootKey
,
'
nr
'
,
params
],
detail
:
(
params
:
any
)
=>
[
appRootKey
,
'
dn
'
,
'
detail
'
,
params
],
detail
:
(
params
:
any
)
=>
[
appRootKey
,
'
nr
'
,
'
detail
'
,
params
],
draft
:
[
appRootKey
,
'
dn
'
,
'
draft
'
],
draft
:
[
appRootKey
,
'
nr
'
,
'
draft
'
],
delete
:
[
appRootKey
,
'
dn
'
,
'
delete
'
],
delete
:
[
appRootKey
,
'
nr
'
,
'
delete
'
],
upload
:
[
appRootKey
,
'
dn
'
,
'
upload
'
],
upload
:
[
appRootKey
,
'
nr
'
,
'
upload
'
],
cancel
:
[
appRootKey
,
'
dn
'
,
'
cancel
'
],
cancel
:
[
appRootKey
,
'
nr
'
,
'
cancel
'
],
cetakPdf
:
(
params
:
any
)
=>
[
appRootKey
,
'
dn
-cetak-pdf
'
,
params
],
cetakPdf
:
(
params
:
any
)
=>
[
appRootKey
,
'
nr
-cetak-pdf
'
,
params
],
},
},
};
};
...
...
src/sections/bupot-unifikasi/bupot-nr/hooks/useAdvancedFilter
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/hooks/useAdvancedFilter
Nr
.tsx
View file @
d89056f3
// type FilterItem = {
// field: string;
// operator: string;
// value?: string | number | Array<string | number> | null;
// join?: 'AND' | 'OR'; // optional: join connector BEFORE this item (first item usually undefined)
// };
// type BaseParams = Record<string, any>;
// export function useAdvancedFilter() {
// const numericFields = new Set(['masaPajak', 'tahunPajak', 'dpp', 'pphDipotong']);
// const dateFields = new Set(['created_at', 'updated_at']);
// const fieldMap: Record<string, string> = {
// noBupot: 'nomorBupot',
// };
// const dbField = (field: string) => fieldMap[field] ?? field;
// const escape = (v: string) => String(v).replace(/'/g, "''");
// const toDbDate = (value: string | Date) => {
// if (value instanceof Date) {
// const y = value.getFullYear();
// const m = String(value.getMonth() + 1).padStart(2, '0');
// const d = String(value.getDate()).padStart(2, '0');
// return `${y}${m}${d}`;
// }
// const digits = String(value).replace(/[^0-9]/g, '');
// if (digits.length >= 8) return digits.slice(0, 8);
// return digits;
// };
// const normalizeOp = (op: string) => op?.toString().trim();
// function buildAdvancedFilter(filters?: FilterItem[] | null) {
// if (!filters || filters.length === 0) return '';
// const exprs: string[] = []; // each item's expression
// const joins: ('AND' | 'OR')[] = []; // join before each expr (for item 0, push nothing/AND by default)
// for (let i = 0; i < filters.length; i++) {
// const f = filters[i];
// if (!f || !f.field) continue;
// const op = normalizeOp(f.operator ?? '');
// const fieldName = dbField(f.field);
// // build expression for this item
// let expr: string | null = null;
// // DATE handling
// if (dateFields.has(fieldName)) {
// const rawVal = f.value;
// if (!rawVal && !/is empty|is not empty/i.test(op)) {
// continue;
// }
// if (/^is$/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" >= '${ymd} 00:00:00' AND \"${fieldName}\" <= '${ymd} 23:59:59'`;
// } else if (/is on or after/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" >= '${ymd}'`;
// } else if (/is on or before/i.test(op)) {
// const ymd = toDbDate(rawVal as string | Date);
// if (!ymd) continue;
// expr = `\"${fieldName}\" <= '${ymd}'`;
// }
// }
// // EMPTY checks (user requested LOWER("col") IS NULL semantics)
// if (/is empty/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") IS NULL`;
// } else if (/is not empty/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") IS NOT NULL`;
// }
// // IS ANY OF handling
// if (!expr && /is any of/i.test(op)) {
// // collect values array
// let values: Array<string | number> = [];
// if (Array.isArray(f.value)) values = f.value as any;
// else if (typeof f.value === 'string')
// values = (f.value as string)
// .split(',')
// .map((s) => s.trim())
// .filter(Boolean);
// else if (f.value != null) values = [f.value as any];
// if ((values || []).length === 0) {
// expr = null;
// } else {
// // special-case fgStatus: need LIKE %val% OR LIKE %val2%
// if (fieldName === 'fgStatus' || fieldName === 'fg_status') {
// const ors = (values as any[]).map((v) => {
// const s = escape(String(v).toLowerCase());
// return `LOWER(\"${fieldName}\") LIKE LOWER('%${s}%')`;
// });
// expr = `(${ors.join(' OR ')})`;
// } else {
// // default: OR of equality (case-insensitive)
// const ors = (values as any[]).map((v) => {
// const s = escape(String(v).toLowerCase());
// return `LOWER(\"${fieldName}\") = '${s}'`;
// });
// expr = `(${ors.join(' OR ')})`;
// }
// }
// }
// // FGSTATUS special single-value is / is not / contains semantics
// if (!expr && (fieldName === 'fgStatus' || fieldName === 'fg_status')) {
// const valRaw = f.value == null ? '' : String(f.value);
// if (valRaw === '' && !/is any of|is empty|is not empty/i.test(op)) {
// expr = null;
// } else {
// const valEscaped = escape(valRaw.toLowerCase());
// if (/^is$/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") LIKE LOWER('%${valEscaped}%')`;
// } else if (/is not/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") NOT LIKE LOWER('%${valEscaped}%')`;
// }
// }
// }
// // GENERIC text/numeric handling when expr still not set
// if (!expr) {
// const valRaw = f.value == null ? '' : String(f.value);
// if (valRaw === '') {
// expr = null;
// } else {
// const valEscaped = escape(valRaw.toLowerCase());
// // numeric fields: operators (=, >=, <=)
// if (numericFields.has(fieldName) && /^(=|>=|<=)$/.test(op)) {
// expr = `\"${fieldName}\" ${op} '${valEscaped}'`;
// } else if (/^contains$/i.test(op)) {
// expr = `LOWER(\"${fieldName}\") LIKE LOWER('%${valEscaped}%')`;
// } else if (/^equals$/i.test(op)) {
// // equals should produce IN (wrap single value as IN)
// // attempt to parse CSV if provided
// let values: string[] = [];
// if (Array.isArray(f.value))
// values = (f.value as any[]).map((v) => escape(String(v).toLowerCase()));
// else values = [escape(String(f.value).toLowerCase())];
// expr = `LOWER(\"${fieldName}\") IN (${values.map((v) => `'${v}'`).join(',')})`;
// } else if (/^(>=|<=|=)$/.test(op) && !numericFields.has(fieldName)) {
// expr = `LOWER(\"${fieldName}\") ${op} '${valEscaped}'`;
// } else if (/^(is)$/i.test(op)) {
// // fallback: treat as equals
// expr = `LOWER(\"${fieldName}\") = '${valEscaped}'`;
// } else {
// // fallback equality
// expr = `LOWER(\"${fieldName}\") = '${valEscaped}'`;
// }
// }
// }
// if (expr) {
// exprs.push(expr);
// // record join for this item (use provided join or default AND except for first item)
// const joinBefore = (f.join as 'AND' | 'OR') ?? (exprs.length > 1 ? 'AND' : 'AND');
// joins.push(joinBefore);
// }
// }
// // now combine exprs with joins; joins[i] is join BEFORE exprs[i]
// if (exprs.length === 0) return '';
// let out = exprs[0];
// for (let i = 1; i < exprs.length; i++) {
// const j = joins[i] ?? 'AND';
// out = `(${out}) ${j} (${exprs[i]})`;
// }
// return out;
// }
// function buildRequestParams(base: BaseParams = {}, advanced: string) {
// const out: BaseParams = { ...(base ?? {}) };
// if ('noBupot' in out) {
// out.nomorBupot = out.noBupot;
// delete out.noBupot;
// }
// out.advanced = advanced || '';
// return out;
// }
// return { buildAdvancedFilter, buildRequestParams } as const;
// }
// export default useAdvancedFilter;
type
FilterItem
=
{
type
FilterItem
=
{
field
:
string
;
field
:
string
;
operator
:
string
;
operator
:
string
;
...
...
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelDn.tsx
deleted
100644 → 0
View file @
9ffe9ff5
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
{
TCancelDnRequest
,
TCancelDnResponse
}
from
'
../types/types
'
;
import
dnApi
from
'
../utils/api
'
;
const
useCancelDn
=
(
props
?:
any
)
=>
useMutation
<
TCancelDnResponse
,
Error
,
TCancelDnRequest
>
({
mutationKey
:
[
'
cancel-dn
'
],
mutationFn
:
(
payload
)
=>
dnApi
.
cancel
(
payload
),
...
props
,
});
export
default
useCancelDn
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useCancelNr.tsx
0 → 100644
View file @
d89056f3
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
type
{
TCancelNrRequest
,
TCancelNrResponse
}
from
'
../types/types
'
;
import
nrApi
from
'
../utils/api
'
;
const
useCancelNr
=
(
props
?:
any
)
=>
useMutation
<
TCancelNrResponse
,
Error
,
TCancelNrRequest
>
({
mutationKey
:
[
'
cancel-nr
'
],
mutationFn
:
(
payload
)
=>
nrApi
.
cancel
(
payload
),
...
props
,
});
export
default
useCancelNr
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useCetakPdf
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/hooks/useCetakPdf
Nr
.tsx
View file @
d89056f3
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
dn
Api
from
'
../utils/api
'
;
import
nr
Api
from
'
../utils/api
'
;
const
useCetakPdf
Dn
=
(
options
?:
any
)
=>
const
useCetakPdf
Nr
=
(
options
?:
any
)
=>
useMutation
({
useMutation
({
mutationKey
:
[
'
unifikasi
'
,
'
dn
'
,
'
cetak-pdf
'
],
mutationKey
:
[
'
unifikasi
'
,
'
nr
'
,
'
cetak-pdf
'
],
mutationFn
:
async
(
params
:
any
)
=>
dn
Api
.
cetakPdfDetail
(
params
),
mutationFn
:
async
(
params
:
any
)
=>
nr
Api
.
cetakPdfDetail
(
params
),
...
options
,
...
options
,
});
});
export
default
useCetakPdf
Dn
;
export
default
useCetakPdf
Nr
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useDelete
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/hooks/useDelete
Nr
.tsx
View file @
d89056f3
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
{
TDeleteDnRequest
,
TBaseResponseAPI
}
from
'
../types/types
'
;
import
type
{
TBaseResponseAPI
,
TDeleteNrRequest
}
from
'
../types/types
'
;
import
dn
Api
from
'
../utils/api
'
;
import
nr
Api
from
'
../utils/api
'
;
const
useDeleteDn
=
(
props
?:
any
)
=>
const
useDeleteDn
=
(
props
?:
any
)
=>
useMutation
<
TBaseResponseAPI
<
null
>
,
Error
,
TDelete
Dn
Request
>
({
useMutation
<
TBaseResponseAPI
<
null
>
,
Error
,
TDelete
Nr
Request
>
({
mutationKey
:
[
'
delete-
dn
'
],
mutationKey
:
[
'
delete-
nr
'
],
mutationFn
:
(
payload
)
=>
dnApi
.
deleteDn
(
payload
),
mutationFn
:
(
payload
)
=>
nrApi
.
deleteNr
(
payload
),
...
props
,
...
props
,
});
});
...
...
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetKodeObjekPajak.tsx
→
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetKodeObjekPajak
Nr
.tsx
View file @
d89056f3
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
{
TBaseResponseAPI
,
TGetListDataKOPDn
Result
}
from
'
../types/types
'
;
import
type
{
TBaseResponseAPI
,
TGetListDataKOPNr
Result
}
from
'
../types/types
'
;
import
queryKey
from
'
../constant/queryKey
'
;
import
queryKey
from
'
../constant/queryKey
'
;
import
dn
Api
from
'
../utils/api
'
;
import
nr
Api
from
'
../utils/api
'
;
const
useGetKodeObjekPajak
=
(
params
?:
Record
<
string
,
any
>
)
=>
const
useGetKodeObjekPajak
Nr
=
(
params
?:
Record
<
string
,
any
>
)
=>
useQuery
<
TBaseResponseAPI
<
TGetListDataKOP
Dn
Result
>>
({
useQuery
<
TBaseResponseAPI
<
TGetListDataKOP
Nr
Result
>>
({
queryKey
:
queryKey
.
getKodeObjekPajak
(
params
),
queryKey
:
queryKey
.
getKodeObjekPajak
(
params
),
queryFn
:
()
=>
dnApi
.
getKodeObjekPajakDn
(
params
),
queryFn
:
()
=>
nrApi
.
getKodeObjekPajakNr
(
params
),
});
});
export
default
useGetKodeObjekPajak
;
export
default
useGetKodeObjekPajak
Nr
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useGetNegara.tsx
0 → 100644
View file @
d89056f3
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
type
{
TCountryResult
}
from
'
../types/types
'
;
import
nrApi
from
'
../utils/api
'
;
export
const
useGetNegara
=
(
params
?:
Record
<
string
,
any
>
)
=>
useQuery
<
TCountryResult
>
({
queryKey
:
[
'
negara-nr
'
],
queryFn
:
async
()
=>
{
const
res
=
await
nrApi
.
getCountryNr
(
params
);
return
res
.
data
;
// ✅ langsung array negara
},
});
export
default
useGetNegara
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useGet
Dn
.tsx
→
src/sections/bupot-unifikasi/bupot-nr/hooks/useGet
Nr
.tsx
View file @
d89056f3
import
{
isEmpty
}
from
'
lodash
'
;
import
{
isEmpty
}
from
'
lodash
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
{
useQuery
}
from
'
@tanstack/react-query
'
;
import
dnApi
from
'
../utils/api
'
;
import
type
{
import
{
TGetListDataTableDn
,
TGetListDataTableDnResult
}
from
'
../types/types
'
;
// TGetListDataTableDnResult,
TGetListDataTableNr
,
TGetListDataTableNrResult
,
}
from
'
../types/types
'
;
import
{
FG_PDF_STATUS
,
FG_SIGN_STATUS
}
from
'
../constant
'
;
import
{
FG_PDF_STATUS
,
FG_SIGN_STATUS
}
from
'
../constant
'
;
import
queryKey
from
'
../constant/queryKey
'
;
import
queryKey
from
'
../constant/queryKey
'
;
import
nrApi
from
'
../utils/api
'
;
import
dayjs
from
'
dayjs
'
;
export
type
TGetDnApiWrapped
=
{
export
type
TGetDnApiWrapped
=
{
data
:
TGetListDataTable
Dn
Result
[];
data
:
TGetListDataTable
Nr
Result
[];
total
:
number
;
total
:
number
;
pageSize
:
number
;
pageSize
:
number
;
page
:
number
;
// 1-based
page
:
number
;
// 1-based
...
@@ -61,7 +66,7 @@ export const formatDateToDDMMYYYY = (dateString: string | null | undefined) => {
...
@@ -61,7 +66,7 @@ export const formatDateToDDMMYYYY = (dateString: string | null | undefined) => {
return
`
${
day
}
/
${
month
}
/
${
year
}
`
;
return
`
${
day
}
/
${
month
}
/
${
year
}
`
;
};
};
const
normalisePropsGet
Dn
=
(
params
:
TGetListDataTableDn
)
=>
({
const
normalisePropsGet
Nr
=
(
params
:
TGetListDataTableNr
)
=>
({
...
params
,
...
params
,
nomorSP2D
:
params
.
dokumen_referensi
?.[
0
]?.
nomorSP2D
||
''
,
nomorSP2D
:
params
.
dokumen_referensi
?.[
0
]?.
nomorSP2D
||
''
,
metodePembayaranBendahara
:
params
.
dokumen_referensi
?.[
0
]?.
metodePembayaranBendahara
||
''
,
metodePembayaranBendahara
:
params
.
dokumen_referensi
?.[
0
]?.
metodePembayaranBendahara
||
''
,
...
@@ -74,7 +79,7 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
...
@@ -74,7 +79,7 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
fgStatus
:
params
.
fgStatus
,
fgStatus
:
params
.
fgStatus
,
fgSignStatus
:
transformFgStatusToFgSignStatus
(
params
.
fgStatus
),
fgSignStatus
:
transformFgStatusToFgSignStatus
(
params
.
fgStatus
),
fgPdf
:
getFgStatusPdf
(
params
.
link
,
transformFgStatusToFgSignStatus
(
params
.
fgStatus
)),
fgPdf
:
getFgStatusPdf
(
params
.
link
,
transformFgStatusToFgSignStatus
(
params
.
fgStatus
)),
fgLapor
:
params
.
fgLapor
,
//
fgLapor: params.fgLapor,
revNo
:
params
.
revNo
,
revNo
:
params
.
revNo
,
thnPajak
:
params
.
tahunPajak
,
thnPajak
:
params
.
tahunPajak
,
msPajak
:
params
.
masaPajak
,
msPajak
:
params
.
masaPajak
,
...
@@ -92,23 +97,55 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
...
@@ -92,23 +97,55 @@ const normalisePropsGetDn = (params: TGetListDataTableDn) => ({
updated_at
:
formatDateToDDMMYYYY
(
params
.
updated_at
),
updated_at
:
formatDateToDDMMYYYY
(
params
.
updated_at
),
});
});
// ---------- normalizer for params request ----------
export
const
normalizeExistingNr
=
(
res
:
any
)
=>
({
const
normalisPropsParmasGetDn
=
(
params
:
any
)
=>
{
// 🧾 Data Pajak Utama
const
sorting
=
!
isEmpty
(
params
.
sortModel
)
tglPemotongan
:
res
.
tglpemotongan
??
''
,
?
transformSortModelToSortApiPayload
(
params
.
sortModel
)
thnPajak
:
res
.
tahunPajak
??
''
,
:
{};
msPajak
:
res
.
masaPajak
??
''
,
return
{
// 👤 Identitas Dipotong
...
params
,
idDipotong
:
res
.
npwpPemotong
??
''
,
page
:
(
typeof
params
.
page
===
'
number
'
?
params
.
page
:
0
)
+
1
,
namaDipotong
:
res
.
namaDipotong
??
''
,
limit
:
params
.
pageSize
,
alamatDipotong
:
res
.
alamatDipotong
??
''
,
masaPajak
:
params
.
msPajak
||
null
,
negaraDipotong
:
res
.
negaraDipotong
??
''
,
tahunPajak
:
params
.
thnPajak
||
null
,
tmptLahirDipotong
:
res
.
tmptLahirDipotong
??
''
,
npwp
:
params
.
idDipotong
||
null
,
tglLahirDipotong
:
advanced
:
isEmpty
(
params
.
advanced
)
?
undefined
:
params
.
advanced
,
res
.
tglLahirDipotong
&&
res
.
tglLahirDipotong
.
length
===
8
...
sorting
,
?
dayjs
(
res
.
tglLahirDipotong
,
'
DDMMYYYY
'
).
format
(
'
YYYY-MM-DD
'
)
};
:
''
,
};
nomorPaspor
:
res
.
nomorPaspor
??
''
,
nomorKitasKitap
:
res
.
nomorKitasKitap
??
''
,
// 🧠 Informasi Tambahan
email
:
res
.
email
??
''
,
keterangan1
:
res
.
keterangan1
??
''
,
keterangan2
:
res
.
keterangan2
??
''
,
keterangan3
:
res
.
keterangan3
??
''
,
keterangan4
:
res
.
keterangan4
??
''
,
keterangan5
:
res
.
keterangan5
??
''
,
// 💰 Pajak dan Penghasilan
kodeObjekPajak
:
res
.
kodeObjekPajak
??
''
,
fgFasilitas
:
res
.
sertifikatInsentifDipotong
??
''
,
noDokLainnya
:
res
.
nomorSertifikatInsentif
??
''
,
penghasilanBruto
:
res
.
penghasilanBruto
??
''
,
normaPenghasilanNeto
:
res
.
normaPenghasilanNeto
??
''
,
tarif
:
String
(
res
.
tarif
??
''
),
pphDipotong
:
String
(
res
.
pphDipotong
??
''
),
// 📄 Dokumen Referensi
namaDok
:
res
.
dokumen_referensi
?.[
0
]?.
dokReferensi
??
''
,
nomorDok
:
res
.
dokumen_referensi
?.[
0
]?.
nomorDokumen
??
''
,
tglDok
:
res
.
dokumen_referensi
?.[
0
]?.
tanggal_Dokumen
??
''
,
// 🏢 Cabang / Unit
idTku
:
res
.
idTku
??
''
,
// 🆔 Metadata tambahan
idBupot
:
res
.
idBupot
??
''
,
noBupot
:
res
.
noBupot
??
''
,
revNo
:
res
.
revNo
??
0
,
});
const
normalizeParams
=
(
params
:
any
)
=>
{
const
normalizeParams
=
(
params
:
any
)
=>
{
const
{
const
{
...
@@ -155,7 +192,7 @@ const normalizeParams = (params: any) => {
...
@@ -155,7 +192,7 @@ const normalizeParams = (params: any) => {
};
};
};
};
export
const
useGet
Dn
=
({
params
}:
{
params
:
any
})
=>
{
export
const
useGet
Nr
=
({
params
}:
{
params
:
any
})
=>
{
const
{
page
,
limit
,
advanced
,
sortingMode
,
sortingMethod
}
=
params
;
const
{
page
,
limit
,
advanced
,
sortingMode
,
sortingMethod
}
=
params
;
const
normalized
=
normalizeParams
(
params
);
const
normalized
=
normalizeParams
(
params
);
...
@@ -163,19 +200,19 @@ export const useGetDn = ({ params }: { params: any }) => {
...
@@ -163,19 +200,19 @@ export const useGetDn = ({ params }: { params: any }) => {
queryKey
:
[
'
dn
'
,
page
,
limit
,
advanced
,
sortingMode
,
sortingMethod
],
queryKey
:
[
'
dn
'
,
page
,
limit
,
advanced
,
sortingMode
,
sortingMethod
],
queryFn
:
async
()
=>
{
queryFn
:
async
()
=>
{
const
res
:
any
=
await
dnApi
.
getDn
({
params
:
normalized
});
const
res
:
any
=
await
nrApi
.
getNr
({
params
:
normalized
});
const
rawData
:
any
[]
=
Array
.
isArray
(
res
?.
data
)
?
res
.
data
:
res
?.
data
?
[
res
.
data
]
:
[];
const
rawData
:
any
[]
=
Array
.
isArray
(
res
?.
data
)
?
res
.
data
:
res
?.
data
?
[
res
.
data
]
:
[];
const
total
=
Number
(
res
?.
total
??
res
?.
totalRow
??
0
);
const
total
=
Number
(
res
?.
total
??
res
?.
totalRow
??
0
);
let
dataArray
:
TGetListDataTable
Dn
Result
[]
=
[];
let
dataArray
:
TGetListDataTable
Nr
Result
[]
=
[];
const
normalizeWithWorker
=
()
=>
const
normalizeWithWorker
=
()
=>
new
Promise
<
TGetListDataTable
Dn
Result
[]
>
((
resolve
,
reject
)
=>
{
new
Promise
<
TGetListDataTable
Nr
Result
[]
>
((
resolve
,
reject
)
=>
{
try
{
try
{
const
worker
=
new
Worker
(
const
worker
=
new
Worker
(
new
URL
(
'
../workers/normalize
Dn
.worker.js
'
,
import
.
meta
.
url
),
new
URL
(
'
../workers/normalize
Nr
.worker.js
'
,
import
.
meta
.
url
),
{
type
:
'
module
'
}
{
type
:
'
module
'
}
);
);
...
@@ -186,7 +223,7 @@ export const useGetDn = ({ params }: { params: any }) => {
...
@@ -186,7 +223,7 @@ export const useGetDn = ({ params }: { params: any }) => {
reject
(
new
Error
(
error
));
reject
(
new
Error
(
error
));
}
else
{
}
else
{
worker
.
terminate
();
worker
.
terminate
();
resolve
(
data
as
TGetListDataTable
Dn
Result
[]);
resolve
(
data
as
TGetListDataTable
Nr
Result
[]);
}
}
};
};
...
@@ -206,11 +243,11 @@ export const useGetDn = ({ params }: { params: any }) => {
...
@@ -206,11 +243,11 @@ export const useGetDn = ({ params }: { params: any }) => {
dataArray
=
await
normalizeWithWorker
();
dataArray
=
await
normalizeWithWorker
();
}
else
{
}
else
{
console
.
warn
(
'
⚠️ Worker not supported, using sync normalization
'
);
console
.
warn
(
'
⚠️ Worker not supported, using sync normalization
'
);
dataArray
=
rawData
.
map
(
normalisePropsGet
Dn
)
as
unknown
as
TGetListDataTableDn
Result
[];
dataArray
=
rawData
.
map
(
normalisePropsGet
Nr
)
as
unknown
as
TGetListDataTableNr
Result
[];
}
}
}
catch
(
err
)
{
}
catch
(
err
)
{
console
.
error
(
'
❌ Worker failed, fallback to sync normalize:
'
,
err
);
console
.
error
(
'
❌ Worker failed, fallback to sync normalize:
'
,
err
);
dataArray
=
rawData
.
map
(
normalisePropsGet
Dn
)
as
unknown
as
TGetListDataTableDn
Result
[];
dataArray
=
rawData
.
map
(
normalisePropsGet
Nr
)
as
unknown
as
TGetListDataTableNr
Result
[];
}
}
return
{
return
{
...
@@ -230,43 +267,14 @@ export const useGetDn = ({ params }: { params: any }) => {
...
@@ -230,43 +267,14 @@ export const useGetDn = ({ params }: { params: any }) => {
});
});
};
};
export
const
useGet
Dn
ById
=
(
id
:
string
,
options
=
{})
=>
export
const
useGet
Nr
ById
=
(
id
:
string
,
options
=
{})
=>
useQuery
({
useQuery
({
queryKey
:
queryKey
.
dn
.
detail
(
id
),
queryKey
:
queryKey
.
nr
.
detail
(
id
),
queryFn
:
async
()
=>
{
queryFn
:
async
()
=>
{
cons
ole
.
log
(
'
🔍 Fetching getDnById with ID:
'
,
id
);
cons
t
res
=
await
nrApi
.
getNrById
(
id
);
cons
t
res
=
await
dnApi
.
getDnById
(
id
);
cons
ole
.
log
(
res
);
if
(
!
res
)
throw
new
Error
(
'
Data tidak ditemukan
'
);
if
(
!
res
)
throw
new
Error
(
'
Data tidak ditemukan
'
);
const
normalized
=
normalizeExistingNr
(
res
);
const
normalized
=
{
id
:
res
.
id
??
''
,
tglPemotongan
:
res
.
tglpemotongan
??
''
,
thnPajak
:
res
.
tahunPajak
??
''
,
msPajak
:
res
.
masaPajak
??
''
,
idDipotong
:
res
.
npwpPemotong
??
''
,
nitku
:
res
.
idTku
??
''
,
namaDipotong
:
res
.
nama
??
''
,
email
:
res
.
email
??
''
,
keterangan1
:
res
.
keterangan1
??
''
,
keterangan2
:
res
.
keterangan2
??
''
,
keterangan3
:
res
.
keterangan3
??
''
,
keterangan4
:
res
.
keterangan4
??
''
,
keterangan5
:
res
.
keterangan5
??
''
,
kdObjPjk
:
res
.
kodeObjekPajak
??
''
,
fgFasilitas
:
res
.
sertifikatInsentifDipotong
??
''
,
noDokLainnya
:
res
.
nomorSertifikatInsentif
??
''
,
jmlBruto
:
res
.
dpp
??
''
,
tarif
:
String
(
res
.
tarif
??
''
),
pphDipotong
:
String
(
res
.
pphDipotong
??
''
),
namaDok
:
res
.
dokumen_referensi
?.[
0
]?.
dokReferensi
??
''
,
nomorDok
:
res
.
dokumen_referensi
?.[
0
]?.
nomorDokumen
??
''
,
tglDok
:
res
.
dokumen_referensi
?.[
0
]?.
tanggal_Dokumen
??
''
,
idTku
:
res
.
idTku
??
''
,
revNo
:
res
.
revNo
??
0
,
noBupot
:
res
.
noBupot
??
''
,
idBupot
:
res
.
idBupot
??
''
,
};
console
.
log
(
'
✅ Normalized data:
'
,
normalized
);
console
.
log
(
'
✅ Normalized data:
'
,
normalized
);
return
normalized
;
return
normalized
;
},
},
...
@@ -275,4 +283,4 @@ export const useGetDnById = (id: string, options = {}) =>
...
@@ -275,4 +283,4 @@ export const useGetDnById = (id: string, options = {}) =>
...
options
,
...
options
,
});
});
export
default
useGet
Dn
;
export
default
useGet
Nr
;
src/sections/bupot-unifikasi/bupot-nr/hooks/usePphDipotong.tsx
View file @
d89056f3
/* eslint-disable @typescript-eslint/no-shadow */
import
{
useEffect
}
from
'
react
'
;
import
{
useEffect
}
from
'
react
'
;
import
{
useFormContext
,
useWatch
}
from
'
react-hook-form
'
;
import
{
useFormContext
,
useWatch
}
from
'
react-hook-form
'
;
import
{
TGetListDataKOPDn
}
from
'
../types/types
'
;
import
type
{
TGetListDataKOPNr
}
from
'
../types/types
'
;
const
usePphDipotong
=
(
kodeObjekPajakSelected
?:
TGetListDataKOP
Dn
)
=>
{
const
usePphDipotong
=
(
kodeObjekPajakSelected
?:
TGetListDataKOP
Nr
)
=>
{
const
{
watch
,
setValue
,
control
}
=
useFormContext
();
const
{
watch
,
setValue
,
control
}
=
useFormContext
();
// ambil value dari form
// ambil value dari form
...
@@ -35,7 +36,6 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
...
@@ -35,7 +36,6 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
name
:
[
'
thnPajak
'
,
'
fgFasilitas
'
,
'
fgIdDipotong
'
,
'
jmlBruto
'
,
'
tarif
'
],
name
:
[
'
thnPajak
'
,
'
fgFasilitas
'
,
'
fgIdDipotong
'
,
'
jmlBruto
'
,
'
tarif
'
],
});
});
// eslint-disable-next-line @typescript-eslint/no-shadow
const
calculateAndSetPphDipotong
=
(
const
calculateAndSetPphDipotong
=
(
thnPajak
:
number
,
thnPajak
:
number
,
fgFasilitas
:
string
,
fgFasilitas
:
string
,
...
@@ -67,6 +67,7 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
...
@@ -67,6 +67,7 @@ const usePphDipotong = (kodeObjekPajakSelected?: TGetListDataKOPDn) => {
Number
(
handlerSetPphDipotong
[
4
])
Number
(
handlerSetPphDipotong
[
4
])
);
);
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
handlerSetPphDipotong
]);
},
[
handlerSetPphDipotong
]);
return
{
return
{
...
...
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveDn.tsx
deleted
100644 → 0
View file @
9ffe9ff5
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
dayjs
from
'
dayjs
'
;
import
dnApi
from
'
../utils/api
'
;
import
{
TPostDnRequest
}
from
'
../types/types
'
;
const
transformParams
=
({
isPengganti
=
false
,
...
dnData
}:
any
):
TPostDnRequest
=>
{
const
{
id
,
idBupot
,
noBupot
,
msPajak
,
thnPajak
,
idDipotong
,
nitku
,
namaDipotong
,
fgFasilitas
,
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
;
const
dokReferensi
=
[
{
dokReferensi
:
namaDok
||
''
,
nomorDokumen
:
nomorDok
||
''
,
tanggal_Dokumen
:
tglDok
?
dayjs
(
tglDok
).
format
(
'
DDMMYYYY
'
)
:
''
,
metodePembayaranBendahara
:
metodePembayaranBendahara
||
''
,
nomorSP2D
:
nomorSP2D
||
''
,
},
];
const
revNo
=
isPengganti
?
parseInt
(
initialRevNo
||
0
,
10
)
+
1
:
parseInt
(
initialRevNo
||
0
,
10
);
const
npwpLog
=
localStorage
.
getItem
(
'
npwp_log
'
)
??
''
;
return
{
id
:
!
isPengganti
?
(
id
??
null
)
:
null
,
idBupot
:
idBupot
??
null
,
noBupot
:
noBupot
??
null
,
npwpPemotong
:
npwpLog
,
idTku
:
idTku
??
''
,
masaPajak
:
msPajak
?
dayjs
(
msPajak
).
format
(
'
MM
'
)
:
''
,
tahunPajak
:
thnPajak
?
Number
(
dayjs
(
thnPajak
).
format
(
'
YYYY
'
))
:
0
,
npwp
:
idDipotong
??
''
,
nik
:
nitku
??
(
idDipotong
?
`
${
idDipotong
}
000000`
:
''
),
nama
:
namaDipotong
??
''
,
revNo
,
fgNpwpNik
:
'
true
'
,
// static
fgJnsBupot
:
'
BPU
'
,
// static
dataDetilBpu
:
{
sertifikatInsentifDipotong
:
fgFasilitas
??
'
9
'
,
nomorSertifikatInsentif
:
noDokLainnya
??
''
,
kodeObjekPajak
:
kdObjPjk
??
''
,
pasalPPh
:
kdJnsPjk
??
''
,
statusPPh
:
statusPph
??
''
,
dpp
:
jmlBruto
??
''
,
tarif
:
tarif
??
''
,
pphDipotong
:
pphDipotong
??
''
,
kap
:
kap
??
''
,
kjs
:
kjs
??
''
,
dokReferensi
,
},
tglPemotongan
:
tglPemotongan
?
dayjs
(
tglPemotongan
).
format
(
'
DDMMYYYY
'
)
:
''
,
email
:
email
??
''
,
glAccount
:
glAccount
??
''
,
keterangan1
:
keterangan1
??
''
,
keterangan2
:
keterangan2
??
''
,
keterangan3
:
keterangan3
??
''
,
keterangan4
:
keterangan4
??
''
,
keterangan5
:
keterangan5
??
''
,
};
};
const
useSaveDn
=
(
props
?:
any
)
=>
useMutation
({
mutationKey
:
[
'
Save-Dn
'
],
mutationFn
:
(
params
:
any
)
=>
dnApi
.
saveDn
(
transformParams
(
params
)),
...
props
,
});
export
default
useSaveDn
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useSaveNr.tsx
0 → 100644
View file @
d89056f3
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
dayjs
from
'
dayjs
'
;
import
type
{
TPostNrRequest
}
from
'
../types/types
'
;
import
nrApi
from
'
../utils/api
'
;
const
transformParams
=
({
isPengganti
=
false
,
...
nrData
}:
any
):
TPostNrRequest
=>
{
const
{
id
,
idBupot
,
noBupot
,
npwpPemotong
,
idTku
,
masaPajak
,
tahunPajak
,
tinDipotong
,
namaDipotong
,
alamatDipotong
,
negaraDipotong
,
tglLahirDipotong
,
tmptLahirDipotong
,
nomorPaspor
,
nomorKitasKitap
,
sertifikatInsentifDipotong
,
nomorSertifikatInsentif
,
kodeObjekPajak
,
pasalPph
,
statusPph
,
penghasilanBruto
,
normaPenghasilanNeto
,
tarif
,
pphDipotong
,
kap
,
kjs
,
metodePembayaranBendahara
,
nomorSP2D
,
tglPemotongan
,
userId
,
kanal
,
revNo
:
initialRevNo
,
glAccount
,
keterangan1
,
keterangan2
,
keterangan3
,
keterangan4
,
keterangan5
,
}
=
nrData
;
// Increment revNo kalau pengganti
const
revNo
=
isPengganti
?
parseInt
(
initialRevNo
?.
toString
()
||
'
0
'
,
10
)
+
1
:
parseInt
(
initialRevNo
?.
toString
()
||
'
0
'
,
10
);
// Ambil NPWP dari localStorage kalau mau fallback
const
npwpLog
=
localStorage
.
getItem
(
'
npwp_log
'
)
??
''
;
return
{
id
:
!
isPengganti
?
(
id
??
null
)
:
null
,
idBupot
:
idBupot
??
null
,
noBupot
:
noBupot
??
null
,
// Header-level Identitas
npwpPemotong
:
npwpPemotong
??
npwpLog
,
idTku
:
idTku
??
''
,
masaPajak
:
masaPajak
?
dayjs
(
masaPajak
).
format
(
'
MM
'
)
:
''
,
tahunPajak
:
tahunPajak
?
Number
(
dayjs
(
tahunPajak
).
format
(
'
YYYY
'
))
:
new
Date
().
getFullYear
(),
// Data Wajib Pajak Dipotong
tinDipotong
:
tinDipotong
??
''
,
namaDipotong
:
namaDipotong
??
''
,
alamatDipotong
:
alamatDipotong
??
''
,
negaraDipotong
:
negaraDipotong
??
''
,
tglLahirDipotong
:
tglLahirDipotong
?
dayjs
(
tglLahirDipotong
).
format
(
'
DDMMYYYY
'
)
:
''
,
tmptLahirDipotong
:
tmptLahirDipotong
??
''
,
nomorPaspor
:
nomorPaspor
??
''
,
nomorKitasKitap
:
nomorKitasKitap
??
''
,
keterangan1
:
keterangan1
??
''
,
keterangan2
:
keterangan2
??
''
,
keterangan3
:
keterangan3
??
''
,
keterangan4
:
keterangan4
??
''
,
keterangan5
:
keterangan5
??
''
,
// Fasilitas
sertifikatInsentifDipotong
:
sertifikatInsentifDipotong
??
'
9
'
,
nomorSertifikatInsentif
:
nomorSertifikatInsentif
??
''
,
// Objek Pajak
kodeObjekPajak
:
kodeObjekPajak
??
''
,
pasalPph
:
pasalPph
??
''
,
statusPph
:
statusPph
??
''
,
penghasilanBruto
:
Number
(
penghasilanBruto
??
0
),
normaPenghasilanNeto
:
Number
(
normaPenghasilanNeto
??
0
),
tarif
:
Number
(
tarif
??
0
),
pphDipotong
:
Number
(
pphDipotong
??
0
),
kap
:
Number
(
kap
??
0
),
kjs
:
Number
(
kjs
??
0
),
dokReferensi
:
(()
=>
{
const
{
namaDok
,
nomorDok
,
tglDok
}
=
nrData
;
// pastikan tidak undefined dan tanggal valid
if
(
!
namaDok
||
!
nomorDok
||
!
tglDok
)
return
[];
const
parsedDate
=
dayjs
(
tglDok
);
const
tanggalFormatted
=
parsedDate
.
isValid
()
?
parsedDate
.
format
(
'
DDMMYYYY
'
)
:
''
;
if
(
!
tanggalFormatted
)
return
[];
return
[
{
dokReferensi
:
namaDok
,
nomorDokumen
:
nomorDok
,
tanggal_Dokumen
:
tanggalFormatted
,
},
];
})(),
metodePembayaranBendahara
:
metodePembayaranBendahara
??
''
,
nomorSP2D
:
nomorSP2D
??
''
,
tglPemotongan
:
tglPemotongan
?
dayjs
(
tglPemotongan
).
format
(
'
DDMMYYYY
'
)
:
''
,
userId
:
userId
??
''
,
kanal
:
kanal
??
''
,
revNo
,
glAccount
:
glAccount
??
''
,
};
};
const
useSaveNr
=
(
props
?:
any
)
=>
useMutation
({
mutationKey
:
[
'
Save-Nr
'
],
mutationFn
:
(
params
:
any
)
=>
nrApi
.
saveNr
(
transformParams
(
params
)),
...
props
,
});
export
default
useSaveNr
;
src/sections/bupot-unifikasi/bupot-nr/hooks/useUpload.tsx
View file @
d89056f3
// hooks/useUpload.ts
// hooks/useUpload.ts
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
{
useMutation
}
from
'
@tanstack/react-query
'
;
import
dn
Api
from
'
../utils/api
'
;
import
nr
Api
from
'
../utils/api
'
;
const
useUpload
=
(
props
?:
any
)
=>
const
useUpload
=
(
props
?:
any
)
=>
useMutation
({
useMutation
({
mutationKey
:
[
'
upload-
dn
'
],
mutationKey
:
[
'
upload-
nr
'
],
mutationFn
:
(
payload
:
{
id
:
string
|
number
})
=>
dn
Api
.
upload
(
payload
),
mutationFn
:
(
payload
:
{
id
:
string
|
number
})
=>
nr
Api
.
upload
(
payload
),
...
props
,
...
props
,
});
});
...
...
src/sections/bupot-unifikasi/bupot-nr/types/types.ts
0 → 100644
View file @
d89056f3
export
type
TBaseResponseAPI
<
T
>
=
{
status
:
string
;
message
:
string
;
data
:
T
;
time
:
string
;
code
:
number
;
metaPage
:
TBaseResponseMetaPage
;
total
?:
number
;
};
type
TBaseResponseMetaPage
=
{
pageNum
:
number
|
null
;
rowPerPage
:
number
|
null
;
totalRow
:
number
;
};
export
type
TGetListDataTableNr
=
{
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
;
tarif
:
string
;
pphDipotong
:
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
;
}[];
revNo
:
number
;
noBupot
:
string
;
idBupot
:
string
;
npwpNikPenandatangan
:
string
;
namaPenandatangan
:
string
;
link
:
string
|
null
;
errorMsg
:
string
|
null
;
email
:
string
|
null
;
glAccount
:
string
;
fgkirimemail
:
string
;
glName
:
string
|
null
;
keterangan1
:
string
|
null
;
keterangan2
:
string
|
null
;
keterangan3
:
string
|
null
;
keterangan4
:
string
|
null
;
keterangan5
:
string
|
null
;
};
export
type
TGetListDataTableNrResult
=
TGetListDataTableNr
[];
export
type
TGetListDataKOPNr
=
{
kode
:
string
;
nama
:
string
;
pasal
:
string
;
statuspph
:
string
;
normanetto
:
string
;
tarif
:
string
;
kap
:
string
;
kjs
:
string
;
noCertificate
:
number
;
certofDomicile
:
number
;
otherCert
:
number
;
};
export
type
TGetListDataKOPNrResult
=
TGetListDataKOPNr
[];
export
type
ActionItem
=
{
title
:
string
;
icon
:
React
.
ReactNode
;
func
?:
()
=>
void
;
disabled
?:
boolean
;
};
export
type
TDokReferensi
=
{
dokReferensi
:
string
;
nomorDokumen
:
string
;
tanggal_Dokumen
:
string
;
// format: DDMMYYYY
};
export
type
TPostNrRequest
=
{
id
:
string
|
null
;
idBupot
:
string
;
noBupot
:
string
;
npwpPemotong
:
string
;
idTku
:
string
;
masaPajak
:
string
;
tahunPajak
:
number
;
tinDipotong
:
string
;
namaDipotong
:
string
;
alamatDipotong
:
string
;
negaraDipotong
:
string
;
tglLahirDipotong
:
string
;
tmptLahirDipotong
:
string
;
nomorPaspor
:
string
;
nomorKitasKitap
:
string
;
sertifikatInsentifDipotong
:
string
;
nomorSertifikatInsentif
:
string
;
kodeObjekPajak
:
string
;
pasalPph
:
string
;
statusPph
:
string
;
penghasilanBruto
:
number
;
normaPenghasilanNeto
:
number
;
tarif
:
number
;
pphDipotong
:
number
;
kap
:
number
;
kjs
:
number
;
dokReferensi
:
TDokReferensi
[];
metodePembayaranBendahara
:
string
;
nomorSP2D
:
string
;
tglPemotongan
:
string
;
userId
:
string
;
kanal
:
string
;
revNo
:
number
;
glAccount
:
string
;
keterangan1
:
string
|
null
;
keterangan2
:
string
|
null
;
keterangan3
:
string
|
null
;
keterangan4
:
string
|
null
;
keterangan5
:
string
|
null
;
};
export
type
TCountry
=
{
kode
:
string
;
nama
:
string
;
};
export
type
TCountryResult
=
TCountry
[];
export
type
TPostUpload
=
{
id
:
string
;
};
export
type
TDeleteNrRequest
=
{
id
:
string
;
};
export
type
TCancelNrRequest
=
{
id
:
string
|
number
;
tglPembatalan
:
string
;
// format: DDMMYYYY
};
export
type
TCancelNrResponse
=
TBaseResponseAPI
<
{
id
:
string
|
number
;
statusBatal
?:
string
;
message
?:
string
;
}
>
;
src/sections/bupot-unifikasi/bupot-nr/utils/api.tsx
View file @
d89056f3
import
axios
from
'
axios
'
;
import
axios
from
'
axios
'
;
import
{
import
type
{
TBaseResponseAPI
,
TBaseResponseAPI
,
TCancel
Dn
Request
,
TCancel
Nr
Request
,
TCancel
Dn
Response
,
TCancel
Nr
Response
,
T
DeleteDnReques
t
,
T
CountryResul
t
,
T
GetListDataKOPDnResul
t
,
T
DeleteNrReques
t
,
TGetListData
TableDn
Result
,
TGetListData
KOPNr
Result
,
T
PostDnReques
t
,
T
GetListDataTableNrResul
t
,
TPost
Upload
,
TPost
NrRequest
,
}
from
'
../types/types
'
;
}
from
'
../types/types
'
;
import
unifikasiClient
from
'
./unifikasiClient
'
;
import
unifikasiClient
from
'
./unifikasiClient
'
;
const
dn
Api
=
()
=>
{};
const
nr
Api
=
()
=>
{};
const
axiosCetakPdf
=
axios
.
create
({
const
axiosCetakPdf
=
axios
.
create
({
baseURL
:
import
.
meta
.
env
.
VITE_APP_BASE_API_URL_CETAK
,
baseURL
:
import
.
meta
.
env
.
VITE_APP_BASE_API_URL_CETAK
,
...
@@ -22,11 +23,11 @@ const axiosCetakPdf = axios.create({
...
@@ -22,11 +23,11 @@ const axiosCetakPdf = axios.create({
});
});
// API untuk get list table
// API untuk get list table
dnApi
.
getDn
=
async
(
config
:
any
)
=>
{
nrApi
.
getNr
=
async
(
config
:
any
)
=>
{
const
{
const
{
data
:
{
status
,
message
,
metaPage
,
data
},
data
:
{
message
,
metaPage
,
data
},
status
:
statusCode
,
status
:
statusCode
,
}
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TGetListDataTable
DnResult
>
>
('IF_TXR_028/bpu
',
{
}
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TGetListDataTable
NrResult
>
>
('IF_TXR_029/
',
{
...
config
,
...
config
,
}
);
}
);
...
@@ -37,9 +38,24 @@ dnApi.getDn = async (config: any) => {
...
@@ -37,9 +38,24 @@ dnApi.getDn = async (config: any) => {
return
{
total
:
metaPage
?
Number
(
metaPage
.
totalRow
)
:
0
,
data
}
;
return
{
total
:
metaPage
?
Number
(
metaPage
.
totalRow
)
:
0
,
data
}
;
};
};
dnApi.getKodeObjekPajakDn = async (params?: Record
<
string
,
any
>
) =
>
{
nrApi.getKodeObjekPajakNr = async (params?: Record
<
string
,
any
>
) =
>
{
const
response
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TGetListDataKOPDnResult
>
>
(
const
response
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TGetListDataKOPNrResult
>
>
(
'/sandbox/mst_kop_bpu',
'/sandbox/mst_kop_bpnr',
{
params
}
);
const body = response.data;
if (response.status !== 200 || body.status !== 'success')
{
throw
new
Error
(
body
.
message
);
}
return body;
}
;
nrApi.getCountryNr = async (params?: Record
<
string
,
any
>
) =
>
{
const
response
=
await
unifikasiClient
.
get
<
TBaseResponseAPI
<
TCountryResult
>
>
(
'/sandbox/mst_negara',
{
params
}
{
params
}
);
);
...
@@ -52,11 +68,10 @@ dnApi.getKodeObjekPajakDn = async (params?: Record<string, any>) => {
...
@@ -52,11 +68,10 @@ dnApi.getKodeObjekPajakDn = async (params?: Record<string, any>) => {
return body;
return body;
}
;
}
;
dnApi.saveDn = async (config: TPostDn
Request) =
>
{
nrApi.saveNr = async (config: TPostNr
Request) =
>
{
const
{
const
{
data
:
{
status
,
message
,
data
,
code
},
data
:
{
message
,
data
,
code
},
status
:
statusCode
,
}
=
await
unifikasiClient
.
post
<
TBaseResponseAPI
<
TPostNrRequest
>>
(
'
/IF_TXR_029/
'
,
{
}
=
await
unifikasiClient
.
post
<
TBaseResponseAPI
<
TPostDnRequest
>>
(
'
/IF_TXR_028/bpu
'
,
{
...
config
,
...
config
,
});
});
if
(
code
===
0
)
{
if
(
code
===
0
)
{
...
@@ -66,8 +81,8 @@ dnApi.saveDn = async (config: TPostDnRequest) => {
...
@@ -66,8 +81,8 @@ dnApi.saveDn = async (config: TPostDnRequest) => {
return
data
;
return
data
;
}
;
}
;
dnApi.getDn
ById = async (id: string) =
>
{
nrApi.getNr
ById = async (id: string) =
>
{
const
res
=
await
unifikasiClient
.
get
(
'
/IF_TXR_02
8/bpu
'
,
{
params
:
{
id
}
});
const
res
=
await
unifikasiClient
.
get
(
'
/IF_TXR_02
9/
'
,
{
params
:
{
id
}
});
const
{
const
{
data
:
{
status
,
message
,
data
},
data
:
{
status
,
message
,
data
},
...
@@ -75,8 +90,8 @@ dnApi.getDnById = async (id: string) => {
...
@@ -75,8 +90,8 @@ dnApi.getDnById = async (id: string) => {
}
=
res
;
}
=
res
;
if
(
statusCode
!==
200
||
status
?.
toLowerCase
()
!==
'
success
'
)
{
if
(
statusCode
!==
200
||
status
?.
toLowerCase
()
!==
'
success
'
)
{
console
.
error
(
'
get
DnBy
Id failed:
'
,
{
statusCode
,
status
,
message
});
console
.
error
(
'
get
Nr
Id failed:
'
,
{
statusCode
,
status
,
message
});
throw
new
Error
(
message
||
'
Gagal mengambil data
DN
'
);
throw
new
Error
(
message
||
'
Gagal mengambil data
NR
'
);
}
}
const
dnData
=
Array
.
isArray
(
data
)
?
data
[
0
]
:
data
;
const
dnData
=
Array
.
isArray
(
data
)
?
data
[
0
]
:
data
;
...
@@ -84,40 +99,39 @@ dnApi.getDnById = async (id: string) => {
...
@@ -84,40 +99,39 @@ dnApi.getDnById = async (id: string) => {
return
dnData
;
return
dnData
;
}
;
}
;
dn
Api.upload = async (
{
id
}
:
{
id
:
string
|
number
}
) =
>
{
nr
Api.upload = async (
{
id
}
:
{
id
:
string
|
number
}
) =
>
{
const
{
const
{
data
:
{
status
,
message
,
data
,
code
},
data
:
{
status
,
message
,
data
,
code
},
status
:
statusCode
,
status
:
statusCode
,
}
=
await
unifikasiClient
.
post
(
'
/IF_TXR_02
8/bpu
/upload
'
,
{
id
});
}
=
await
unifikasiClient
.
post
(
'
/IF_TXR_02
9
/upload
'
,
{
id
});
return
{
status
,
message
,
data
,
code
,
statusCode
};
return
{
status
,
message
,
data
,
code
,
statusCode
};
}
;
}
;
dnApi.deleteDn = async (payload: TDeleteDn
Request, config?: Record
<
string
,
any
>
): Promise
<
any
>
=
>
{
nrApi.deleteNr = async (payload: TDeleteNr
Request, config?: Record
<
string
,
any
>
): Promise
<
any
>
=
>
{
const
{
const
{
data
:
{
status
,
message
,
data
},
data
:
{
status
,
message
,
data
},
status
:
statusCode
,
status
:
statusCode
,
}
=
await
unifikasiClient
.
post
<
TBaseResponseAPI
<
any
>>
(
'
/IF_TXR_02
8/bpu
/delete
'
,
payload
,
{
}
=
await
unifikasiClient
.
post
<
TBaseResponseAPI
<
any
>>
(
'
/IF_TXR_02
9
/delete
'
,
payload
,
{
...
config
,
...
config
,
});
});
if
(
statusCode
!==
200
||
status
?.
toLowerCase
()
===
'
error
'
)
{
if
(
statusCode
!==
200
||
status
?.
toLowerCase
()
===
'
error
'
)
{
throw
new
Error
(
message
||
'
Gagal menghapus data
DN
'
);
throw
new
Error
(
message
||
'
Gagal menghapus data
NR
'
);
}
}
return
data
;
return
data
;
}
;
}
;
dnApi.cancel = async (
{
id
,
tglPembatalan
}
: TCancelDnRequest): Promise
<
TCancelDn
Response
>
=
>
{
nrApi.cancel = async (
{
id
,
tglPembatalan
}
: TCancelNrRequest): Promise
<
TCancelNr
Response
>
=
>
{
const
{
const
{
data
:
{
status
,
message
,
data
,
code
,
time
,
metaPage
,
total
},
data
:
{
status
,
message
,
data
,
code
,
time
,
metaPage
,
total
},
status
:
statusCode
,
}
=
await
unifikasiClient
.
post
(
'
/IF_TXR_029/batal
'
,
{
}
=
await
unifikasiClient
.
post
(
'
/IF_TXR_028/bpu/batal
'
,
{
id
,
id
,
tglPembatalan
,
tglPembatalan
,
});
});
console
.
log
(
'
Cancel
DN
response:
'
,
{
code
,
message
,
status
});
console
.
log
(
'
Cancel
NR
response:
'
,
{
code
,
message
,
status
});
if
(
code
===
0
)
{
if
(
code
===
0
)
{
throw
new
Error
(
message
||
'
Gagal membatalkan data
'
);
throw
new
Error
(
message
||
'
Gagal membatalkan data
'
);
}
}
...
@@ -133,8 +147,8 @@ dnApi.cancel = async ({ id, tglPembatalan }: TCancelDnRequest): Promise<TCancelD
...
@@ -133,8 +147,8 @@ dnApi.cancel = async ({ id, tglPembatalan }: TCancelDnRequest): Promise<TCancelD
};
};
}
;
}
;
dn
Api.cetakPdfDetail = async (payload: Record
<
string
,
any
>
) =
>
{
nr
Api.cetakPdfDetail = async (payload: Record
<
string
,
any
>
) =
>
{
const
response
=
await
axiosCetakPdf
.
post
(
'
/report/ctas/bp
u
'
,
payload
);
const
response
=
await
axiosCetakPdf
.
post
(
'
/report/ctas/bp
nr
'
,
payload
);
const
body
=
response
.
data
;
const
body
=
response
.
data
;
...
@@ -153,4 +167,4 @@ dnApi.cetakPdfDetail = async (payload: Record<string, any>) => {
...
@@ -153,4 +167,4 @@ dnApi.cetakPdfDetail = async (payload: Record<string, any>) => {
return
body
;
return
body
;
}
;
}
;
export default
dn
Api;
export default
nr
Api;
src/sections/bupot-unifikasi/bupot-nr/utils/normalizePayloadCetakPdf.ts
View file @
d89056f3
...
@@ -43,11 +43,11 @@ export const normalizePayloadCetakPdf = (payload: Record<string, any>) => {
...
@@ -43,11 +43,11 @@ export const normalizePayloadCetakPdf = (payload: Record<string, any>) => {
adjusted
.
metodePembayaranBendahara
=
adjusted
.
metodePembayaranBendahara
||
'
-
'
;
adjusted
.
metodePembayaranBendahara
=
adjusted
.
metodePembayaranBendahara
||
'
-
'
;
adjusted
.
nomorSP2D
=
adjusted
.
nomorSP2D
||
'
-
'
;
adjusted
.
nomorSP2D
=
adjusted
.
nomorSP2D
||
'
-
'
;
adjusted
.
npwpDipotong
=
adjusted
.
npwp
||
''
;
adjusted
.
npwpDipotong
=
adjusted
.
npwp
||
''
;
adjusted
.
namaDipotong
=
adjusted
.
nama
||
''
;
adjusted
.
namaDipotong
=
adjusted
.
nama
Dipotong
||
''
;
adjusted
.
nitkuDipotong
=
adjusted
.
nik
||
''
;
adjusted
.
nitkuDipotong
=
adjusted
.
nik
||
''
;
adjusted
.
namaPemotong
=
adjusted
.
nama
||
''
;
adjusted
.
namaPemotong
=
adjusted
.
nama
Dipotong
||
''
;
adjusted
.
nitkuPemotong
=
adjusted
.
nik
||
''
;
adjusted
.
nitkuPemotong
=
adjusted
.
idTku
||
''
;
adjusted
.
penghasilanBruto
=
adjusted
.
dpp
||
''
;
adjusted
.
penghasilanBruto
=
adjusted
.
penghasilanBruto
||
''
;
adjusted
.
tanggal_Dokumen
=
adjusted
.
dokumen_referensi
[
0
].
tanggal_Dokumen
;
adjusted
.
tanggal_Dokumen
=
adjusted
.
dokumen_referensi
[
0
].
tanggal_Dokumen
;
adjusted
.
status
=
'
Proforma
'
;
adjusted
.
status
=
'
Proforma
'
;
adjusted
.
msPajak
=
adjusted
.
masaPajak
;
adjusted
.
msPajak
=
adjusted
.
masaPajak
;
...
...
src/sections/bupot-unifikasi/bupot-nr/view/index.ts
x
→
src/sections/bupot-unifikasi/bupot-nr/view/index.ts
View file @
d89056f3
File moved
src/sections/bupot-unifikasi/bupot-nr/view/nr-list-view.tsx
View file @
d89056f3
...
@@ -29,18 +29,20 @@ import {
...
@@ -29,18 +29,20 @@ import {
// import { CustomToolbar } from '../components/CustomToolbar';
// import { CustomToolbar } from '../components/CustomToolbar';
import
{
formatRupiah
}
from
'
src/shared/FormatRupiah/FormatRupiah
'
;
import
{
formatRupiah
}
from
'
src/shared/FormatRupiah/FormatRupiah
'
;
import
{
FG_STATUS_DN
}
from
'
../constant
'
;
import
{
FG_STATUS_DN
}
from
'
../constant
'
;
import
ModalDelete
Dn
from
'
../components/dialog/ModalDeleteDn
'
;
import
ModalDelete
Nr
from
'
../components/dialog/ModalDeleteNr
'
;
import
ModalUpload
Dn
from
'
../components/dialog/ModalUploadDn
'
;
import
ModalUpload
Nr
from
'
../components/dialog/ModalUploadNr
'
;
import
ModalCancel
Dn
from
'
../components/dialog/ModalCancelDn
'
;
import
ModalCancel
Nr
from
'
../components/dialog/ModalCancelNr
'
;
import
ModalCetakPdf
Dn
from
'
../components/dialog/ModalCetakPdfDn
'
;
import
ModalCetakPdf
Nr
from
'
../components/dialog/ModalCetakPdfNr
'
;
import
useGetDn
from
'
../hooks/useGetDn
'
;
import
{
useGetNr
}
from
'
../hooks/useGetNr
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
enqueueSnackbar
}
from
'
notistack
'
;
import
{
usePaginationStore
}
from
'
../store/paginationStore
'
;
import
{
usePaginationStore
}
from
'
../store/paginationStore
'
;
import
StatusChip
from
'
../components/StatusChip
'
;
import
StatusChip
from
'
../components/StatusChip
'
;
import
{
useDebounce
,
useThrottle
}
from
'
src/shared/hooks/useDebounceThrottle
'
;
import
{
useDebounce
,
useThrottle
}
from
'
src/shared/hooks/useDebounceThrottle
'
;
import
useGetKodeObjekPajak
from
'
../hooks/useGetKodeObjekPajak
'
;
import
useGetKodeObjekPajak
from
'
../hooks/useGetKodeObjekPajak
Nr
'
;
import
useAdvancedFilter
from
'
../hooks/useAdvancedFilter
Dn
'
;
import
useAdvancedFilter
from
'
../hooks/useAdvancedFilter
Nr
'
;
import
{
CustomToolbar
}
from
'
../components/CustomToolbar
'
;
import
{
CustomToolbar
}
from
'
../components/CustomToolbar
'
;
import
{
useSelector
}
from
'
react-redux
'
;
import
type
{
RootState
}
from
'
src/store
'
;
export
type
IColumnGrid
=
GridColDef
&
{
export
type
IColumnGrid
=
GridColDef
&
{
field
:
field
:
...
@@ -48,13 +50,14 @@ export type IColumnGrid = GridColDef & {
...
@@ -48,13 +50,14 @@ export type IColumnGrid = GridColDef & {
|
'
noBupot
'
|
'
noBupot
'
|
'
masaPajak
'
|
'
masaPajak
'
|
'
tahunPajak
'
|
'
tahunPajak
'
|
'
k
dObjPj
k
'
|
'
k
odeObjekPaja
k
'
|
'
pasalPPh
'
|
'
pasalPPh
'
|
'
npwp
'
|
'
npwp
Pemotong
'
|
'
nama
'
|
'
nama
Dipotong
'
|
'
dpp
'
|
'
penghasilanBruto
'
|
'
pphDipotong
'
|
'
pphDipotong
'
|
'
idTku
'
|
'
idTku
'
|
'
negaraDipotong
'
|
'
dokReferensi
'
|
'
dokReferensi
'
|
'
nomorDokumen
'
|
'
nomorDokumen
'
|
'
created_by
'
|
'
created_by
'
...
@@ -75,13 +78,14 @@ type TKodeObjekPajak = {
...
@@ -75,13 +78,14 @@ type TKodeObjekPajak = {
nama
:
string
;
nama
:
string
;
pasal
:
string
;
pasal
:
string
;
statuspph
:
string
;
statuspph
:
string
;
normanetto
:
string
;
};
};
export
function
NrListView
()
{
export
function
NrListView
()
{
const
apiRef
=
useGridApiRef
();
const
apiRef
=
useGridApiRef
();
const
navigate
=
useNavigate
();
const
navigate
=
useNavigate
();
const
tableKey
=
'
dn
'
;
const
tableKey
=
'
nr
'
;
const
page
=
usePaginationStore
((
s
)
=>
s
.
tables
[
tableKey
]?.
page
??
0
);
const
page
=
usePaginationStore
((
s
)
=>
s
.
tables
[
tableKey
]?.
page
??
0
);
const
pageSize
=
usePaginationStore
((
s
)
=>
s
.
tables
[
tableKey
]?.
pageSize
??
10
);
const
pageSize
=
usePaginationStore
((
s
)
=>
s
.
tables
[
tableKey
]?.
pageSize
??
10
);
...
@@ -104,7 +108,8 @@ export function NrListView() {
...
@@ -104,7 +108,8 @@ export function NrListView() {
const
[
selectionVersion
,
setSelectionVersion
]
=
useState
(
0
);
const
[
selectionVersion
,
setSelectionVersion
]
=
useState
(
0
);
const
[
kodeObjekPajaks
,
setKodeObjekPajaks
]
=
useState
<
TKodeObjekPajak
[]
>
([]);
const
[
kodeObjekPajaks
,
setKodeObjekPajaks
]
=
useState
<
TKodeObjekPajak
[]
>
([]);
const
{
data
:
kodeObjekPajak
,
isLoading
:
isLoadingKop
}
=
useGetKodeObjekPajak
();
const
{
data
:
kodeObjekPajak
}
=
useGetKodeObjekPajak
();
const
signer
=
useSelector
((
state
:
RootState
)
=>
state
.
user
.
data
.
signer
);
const
{
buildAdvancedFilter
,
buildRequestParams
}
=
useAdvancedFilter
();
const
{
buildAdvancedFilter
,
buildRequestParams
}
=
useAdvancedFilter
();
...
@@ -127,7 +132,7 @@ export function NrListView() {
...
@@ -127,7 +132,7 @@ export function NrListView() {
return
buildRequestParams
(
baseParams
,
advanced
);
return
buildRequestParams
(
baseParams
,
advanced
);
},
[
page
,
pageSize
,
sortModel
,
filterModel
.
items
,
buildAdvancedFilter
,
buildRequestParams
]);
},
[
page
,
pageSize
,
sortModel
,
filterModel
.
items
,
buildAdvancedFilter
,
buildRequestParams
]);
const
{
data
,
isFetching
,
refetch
}
=
useGet
Dn
({
const
{
data
,
isFetching
,
refetch
}
=
useGet
Nr
({
params
,
params
,
});
});
const
idStatusMapRef
=
useRef
<
Map
<
string
|
number
,
string
>>
(
new
Map
());
const
idStatusMapRef
=
useRef
<
Map
<
string
|
number
,
string
>>
(
new
Map
());
...
@@ -174,6 +179,7 @@ export function NrListView() {
...
@@ -174,6 +179,7 @@ export function NrListView() {
// ---------- status options and columns (kept identical to your original) ----------
// ---------- status options and columns (kept identical to your original) ----------
type
Status
=
'
draft
'
|
'
normal
'
|
'
cancelled
'
|
'
amended
'
;
type
Status
=
'
draft
'
|
'
normal
'
|
'
cancelled
'
|
'
amended
'
;
type
StatusOption
=
{
value
:
Status
;
label
:
string
};
type
StatusOption
=
{
value
:
Status
;
label
:
string
};
// eslint-disable-next-line react-hooks/exhaustive-deps
const
statusOptions
:
StatusOption
[]
=
[
const
statusOptions
:
StatusOption
[]
=
[
{
value
:
'
draft
'
,
label
:
'
Draft
'
},
{
value
:
'
draft
'
,
label
:
'
Draft
'
},
{
value
:
'
normal
'
,
label
:
'
Normal
'
},
{
value
:
'
normal
'
,
label
:
'
Normal
'
},
...
@@ -198,14 +204,24 @@ export function NrListView() {
...
@@ -198,14 +204,24 @@ export function NrListView() {
{
field
:
'
noBupot
'
,
headerName
:
'
Nomor Bukti Pemotongan
'
,
width
:
200
},
{
field
:
'
noBupot
'
,
headerName
:
'
Nomor Bukti Pemotongan
'
,
width
:
200
},
{
field
:
'
masaPajak
'
,
headerName
:
'
Masa Pajak
'
,
width
:
150
},
{
field
:
'
masaPajak
'
,
headerName
:
'
Masa Pajak
'
,
width
:
150
},
{
field
:
'
tahunPajak
'
,
headerName
:
'
Tahun Pajak
'
,
width
:
150
},
{
field
:
'
tahunPajak
'
,
headerName
:
'
Tahun Pajak
'
,
width
:
150
},
{
field
:
'
k
dObjPj
k
'
,
headerName
:
'
Kode Objek Pajak
'
,
width
:
150
},
{
field
:
'
k
odeObjekPaja
k
'
,
headerName
:
'
Kode Objek Pajak
'
,
width
:
150
},
{
field
:
'
npwp
'
,
headerName
:
'
Identitas
'
,
width
:
150
},
{
field
:
'
npwp
Pemotong
'
,
headerName
:
'
Identitas
'
,
width
:
150
},
{
field
:
'
nama
'
,
headerName
:
'
Nama
'
,
width
:
150
},
{
field
:
'
nama
Dipotong
'
,
headerName
:
'
Nama
'
,
width
:
150
},
{
{
field
:
'
dpp
'
,
field
:
'
negaraDipotong
'
,
headerName
:
'
Negara
'
,
width
:
180
,
renderCell
:
({
row
})
=>
{
const
kode
=
row
.
negaraDipotong
||
'
-
'
;
const
nama
=
row
.
namaNegara
||
''
;
return
`
${
kode
}${
nama
?
'
-
'
+
nama
:
''
}
`
;
},
},
{
field
:
'
penghasilanBruto
'
,
headerName
:
'
Jumlah Penghasilan Bruto (Rp)
'
,
headerName
:
'
Jumlah Penghasilan Bruto (Rp)
'
,
width
:
150
,
width
:
150
,
renderCell
:
({
row
})
=>
formatRupiah
(
row
.
dpp
),
renderCell
:
({
row
})
=>
formatRupiah
(
row
.
penghasilanBruto
),
},
},
{
{
field
:
'
pphDipotong
'
,
field
:
'
pphDipotong
'
,
...
@@ -217,7 +233,7 @@ export function NrListView() {
...
@@ -217,7 +233,7 @@ export function NrListView() {
{
field
:
'
dokReferensi
'
,
headerName
:
'
Nama dokumen
'
,
width
:
150
},
{
field
:
'
dokReferensi
'
,
headerName
:
'
Nama dokumen
'
,
width
:
150
},
{
field
:
'
nomorDokumen
'
,
headerName
:
'
Nomor dokumen
'
,
width
:
150
},
{
field
:
'
nomorDokumen
'
,
headerName
:
'
Nomor dokumen
'
,
width
:
150
},
{
field
:
'
created_by
'
,
headerName
:
'
Created
'
,
width
:
150
},
{
field
:
'
created_by
'
,
headerName
:
'
Created
'
,
width
:
150
},
{
field
:
'
created_at
'
,
headerName
:
'
Created At
'
,
width
:
20
0
},
{
field
:
'
created_at
'
,
headerName
:
'
Created At
'
,
width
:
15
0
},
{
field
:
'
updated_by
'
,
headerName
:
'
Updated
'
,
width
:
150
},
{
field
:
'
updated_by
'
,
headerName
:
'
Updated
'
,
width
:
150
},
{
field
:
'
updated_at
'
,
headerName
:
'
Update At
'
,
width
:
150
},
{
field
:
'
updated_at
'
,
headerName
:
'
Update At
'
,
width
:
150
},
{
field
:
'
internal_id
'
,
headerName
:
'
Referensi
'
,
width
:
150
},
{
field
:
'
internal_id
'
,
headerName
:
'
Referensi
'
,
width
:
150
},
...
@@ -243,7 +259,7 @@ export function NrListView() {
...
@@ -243,7 +259,7 @@ export function NrListView() {
(
type
=
'
ubah
'
)
=>
{
(
type
=
'
ubah
'
)
=>
{
const
selectedRow
=
dataSelectedRef
.
current
[
0
];
const
selectedRow
=
dataSelectedRef
.
current
[
0
];
if
(
!
selectedRow
)
return
;
if
(
!
selectedRow
)
return
;
navigate
(
`/unifikasi/
dn
/
${
selectedRow
.
id
}
/
${
type
}
`
);
navigate
(
`/unifikasi/
nr
/
${
selectedRow
.
id
}
/
${
type
}
`
);
},
},
[
navigate
]
[
navigate
]
);
);
...
@@ -305,6 +321,7 @@ export function NrListView() {
...
@@ -305,6 +321,7 @@ export function NrListView() {
canReplacement
:
count
===
1
&&
dataSelected
[
0
].
fgStatus
===
FG_STATUS_DN
.
NORMAL_DONE
,
canReplacement
:
count
===
1
&&
dataSelected
[
0
].
fgStatus
===
FG_STATUS_DN
.
NORMAL_DONE
,
canCancel
:
hasSelection
&&
allNormal
,
canCancel
:
hasSelection
&&
allNormal
,
};
};
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
selectionVersion
]);
},
[
selectionVersion
]);
useEffect
(()
=>
{
useEffect
(()
=>
{
...
@@ -321,9 +338,13 @@ export function NrListView() {
...
@@ -321,9 +338,13 @@ export function NrListView() {
return
;
return
;
}
}
console
.
log
(
'
🧾 selectedRow:
'
,
selectedRow
);
console
.
log
(
'
🧩 Keys:
'
,
Object
.
keys
(
selectedRow
));
const
kode
=
selectedRow
.
kodeObjekPajak
||
selectedRow
.
kdObjPjk
;
const
kode
=
selectedRow
.
kodeObjekPajak
||
selectedRow
.
kdObjPjk
;
const
detailKop
=
kodeObjekPajaks
.
find
((
item
)
=>
item
.
kode
===
kode
);
const
detailKop
=
kodeObjekPajaks
.
find
((
item
)
=>
item
.
kode
===
kode
);
console
.
log
(
detailKop
);
const
mergedRow
=
{
const
mergedRow
=
{
...
selectedRow
,
...
selectedRow
,
...(
detailKop
...(
detailKop
...
@@ -331,8 +352,11 @@ export function NrListView() {
...
@@ -331,8 +352,11 @@ export function NrListView() {
namaObjekPajak
:
detailKop
.
nama
,
namaObjekPajak
:
detailKop
.
nama
,
pasalPPh
:
detailKop
.
pasal
,
pasalPPh
:
detailKop
.
pasal
,
statusPPh
:
detailKop
.
statuspph
,
statusPPh
:
detailKop
.
statuspph
,
normaPenghasilanNeto
:
detailKop
.
normanetto
,
}
}
:
{}),
:
{}),
tinDipotong
:
selectedRow
.
npwpPemotong
??
''
,
namaPenandatangan
:
signer
,
};
};
setPreviewPayload
(
mergedRow
);
setPreviewPayload
(
mergedRow
);
...
@@ -391,6 +415,7 @@ export function NrListView() {
...
@@ -391,6 +415,7 @@ export function NrListView() {
},
},
],
],
],
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[
validatedActions
,
refetch
,
handleEditData
]
[
validatedActions
,
refetch
,
handleEditData
]
);
);
...
@@ -416,20 +441,25 @@ export function NrListView() {
...
@@ -416,20 +441,25 @@ export function NrListView() {
const
api
=
apiRef
.
current
;
const
api
=
apiRef
.
current
;
if
(
!
api
)
return
;
if
(
!
api
)
return
;
const
id
=
window
.
setTimeout
(()
=>
{
const
id
=
window
.
setTimeout
(()
=>
{
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const
selected
=
getSelectedRowByKey
(
'
all
'
);
const
selected
=
getSelectedRowByKey
(
'
all
'
);
},
100
);
},
100
);
// eslint-disable-next-line consistent-return
// eslint-disable-next-line consistent-return
return
()
=>
clearTimeout
(
id
);
return
()
=>
clearTimeout
(
id
);
// eslint-disable-next-line react-hooks/exhaustive-deps
},
[
apiRef
,
selectionVersion
]);
},
[
apiRef
,
selectionVersion
]);
return
(
return
(
<>
<>
<
DashboardContent
>
<
DashboardContent
>
<
CustomBreadcrumbs
<
CustomBreadcrumbs
heading=
"Bupot Unifikasi"
heading=
"Bupot Unifikasi Non Residen"
links=
{
[{
name
:
'
Dashboard
'
,
href
:
paths
.
dashboard
.
root
},
{
name
:
'
e-Bupot Unifikasi
'
}]
}
links=
{
[
{
name
:
'
Dashboard
'
,
href
:
paths
.
dashboard
.
root
},
{
name
:
'
e-Bupot Unifikasi Non Residen
'
},
]
}
action=
{
action=
{
<
Button
component=
{
RouterLink
}
href=
{
paths
.
unifikasi
.
dn
New
}
variant=
"contained"
>
<
Button
component=
{
RouterLink
}
href=
{
paths
.
unifikasi
.
nr
New
}
variant=
"contained"
>
Rekam Data
Rekam Data
</
Button
>
</
Button
>
}
}
...
@@ -487,7 +517,7 @@ export function NrListView() {
...
@@ -487,7 +517,7 @@ export function NrListView() {
</
DashboardContent
>
</
DashboardContent
>
{
isDeleteModalOpen
&&
(
{
isDeleteModalOpen
&&
(
<
ModalDelete
Dn
<
ModalDelete
Nr
dataSelected=
{
rowSelectionModel
}
dataSelected=
{
rowSelectionModel
}
setSelectionModel=
{
setRowSelectionModel
}
setSelectionModel=
{
setRowSelectionModel
}
tableApiRef=
{
apiRef
}
tableApiRef=
{
apiRef
}
...
@@ -498,7 +528,7 @@ export function NrListView() {
...
@@ -498,7 +528,7 @@ export function NrListView() {
)
}
)
}
{
isUploadModalOpen
&&
(
{
isUploadModalOpen
&&
(
<
ModalUpload
Dn
<
ModalUpload
Nr
dataSelected=
{
rowSelectionModel
}
dataSelected=
{
rowSelectionModel
}
setSelectionModel=
{
setRowSelectionModel
}
setSelectionModel=
{
setRowSelectionModel
}
tableApiRef=
{
apiRef
}
tableApiRef=
{
apiRef
}
...
@@ -509,7 +539,7 @@ export function NrListView() {
...
@@ -509,7 +539,7 @@ export function NrListView() {
)
}
)
}
{
isCancelModalOpen
&&
(
{
isCancelModalOpen
&&
(
<
ModalCancel
Dn
<
ModalCancel
Nr
dataSelected=
{
dataSelectedRef
.
current
}
dataSelected=
{
dataSelectedRef
.
current
}
setSelectionModel=
{
setRowSelectionModel
}
setSelectionModel=
{
setRowSelectionModel
}
tableApiRef=
{
apiRef
}
tableApiRef=
{
apiRef
}
...
@@ -520,7 +550,7 @@ export function NrListView() {
...
@@ -520,7 +550,7 @@ export function NrListView() {
)
}
)
}
{
isPreviewOpen
&&
(
{
isPreviewOpen
&&
(
<
ModalCetakPdf
Dn
<
ModalCetakPdf
Nr
payload=
{
previewPayload
}
payload=
{
previewPayload
}
isOpen=
{
isPreviewOpen
}
isOpen=
{
isPreviewOpen
}
onClose=
{
()
=>
{
onClose=
{
()
=>
{
...
...
src/sections/bupot-unifikasi/bupot-nr/view/nrRekamView.tsx
View file @
d89056f3
This diff is collapsed.
Click to expand it.
src/sections/bupot-unifikasi/bupot-nr/workers/normalizeNr.worker.js
0 → 100644
View file @
d89056f3
// src/workers/normalizeDn.worker.js
// NOTE: keep this file plain JS - no TS imports - copy needed transform functions here.
function
formatDateToDDMMYYYY
(
dateString
)
{
if
(
!
dateString
)
return
''
;
const
d
=
new
Date
(
dateString
);
const
day
=
String
(
d
.
getDate
()).
padStart
(
2
,
'
0
'
);
const
month
=
String
(
d
.
getMonth
()
+
1
).
padStart
(
2
,
'
0
'
);
const
year
=
d
.
getFullYear
();
return
`
${
day
}
/
${
month
}
/
${
year
}
`
;
}
// minimal transform helpers used in normalize
function
transformFgStatusToFgSignStatus
(
fgStatus
)
{
const
splitted
=
(
fgStatus
||
''
).
split
(
'
-
'
)
||
[];
if
(
splitted
.
includes
(
'
SIGN
'
)
>
0
)
return
'
FAILED
'
;
if
(
splitted
.
includes
(
'
SIGNING IN PROGRESS
'
))
return
'
IN_PROGRESS
'
;
switch
(
splitted
[
1
])
{
case
'
document signed successfully
'
:
case
'
Done
'
:
return
'
SIGNED
'
;
default
:
return
null
;
}
}
function
getFgStatusPdf
(
link
,
fgSignStatus
)
{
if
(
!
link
||
fgSignStatus
===
'
IN_PROGRESS
'
)
return
'
TIDAK_TERSEDIA
'
;
if
(
!
link
.
includes
(
'
https://coretaxdjp.pajak.go.id/
'
))
return
'
BELUM_TERBENTUK
'
;
return
'
TERBENTUK
'
;
}
function
normalisePropsGetDn
(
params
)
{
if
(
!
params
)
return
params
;
return
{
...
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
,
internal_id
:
params
.
internal_id
,
fgStatus
:
params
.
fgStatus
,
fgSignStatus
:
transformFgStatusToFgSignStatus
(
params
.
fgStatus
),
fgPdf
:
getFgStatusPdf
(
params
.
link
,
transformFgStatusToFgSignStatus
(
params
.
fgStatus
)),
created_at
:
formatDateToDDMMYYYY
(
params
.
created_at
),
updated_at
:
formatDateToDDMMYYYY
(
params
.
updated_at
),
};
}
// eslint-disable-next-line func-names
onmessage
=
function
(
e
)
{
const
{
data
}
=
e
;
// data should be array of items
if
(
!
Array
.
isArray
(
data
))
{
postMessage
({
error
:
'
expected array
'
});
return
;
}
try
{
const
out
=
data
.
map
(
normalisePropsGetDn
);
postMessage
({
data
:
out
});
}
catch
(
err
)
{
postMessage
({
error
:
(
err
&&
err
.
message
)
||
String
(
err
)
});
}
};
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment