Update 'coretabs.user.js'
Add Refund Review tab.
This commit is contained in:
409
coretabs.user.js
409
coretabs.user.js
@@ -1,11 +1,12 @@
|
||||
// ==UserScript==
|
||||
// @name CoreTabs
|
||||
// @namespace https://git.diasbaskara.id
|
||||
// @version 0.1
|
||||
// @namespace https://git.diasbaskara.id/diasbaskara/userscripts/
|
||||
// @version 0.2
|
||||
// @description Manage your cases easily.
|
||||
// @author Dias Baskara
|
||||
// @match https://coretax.intranet.pajak.go.id/*
|
||||
// @grant GM_addStyle
|
||||
// @require https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js
|
||||
// @run-at document-idle
|
||||
// ==/UserScript==
|
||||
|
||||
@@ -15,22 +16,44 @@
|
||||
// --- SCRIPT CONFIGURATION ---
|
||||
const AUTH_STORAGE_KEY = 'cats-angular-clientuser:https://coretax.intranet.pajak.go.id/identityprovider:cats-angular-client';
|
||||
const DEFAULT_CASES_FILTER = 'In Progress';
|
||||
const REFUND_CASE_TYPE_NAME = 'Pengembalian Melalui Pelaporan Surat Pemberitahuan (SPT)';
|
||||
// ----------------------------
|
||||
|
||||
// --- State Management ---
|
||||
let allMyCases = [], allCaseDocuments = [], allCaseUsers = [];
|
||||
let allMyCases = [], allCaseDocuments = [], allCaseUsers = [], refundReviewData = [];
|
||||
let filteredRefundData = [];
|
||||
let selectedCaseId = null;
|
||||
let loadedDocsForCaseId = null;
|
||||
let loadedUsersForCaseId = null;
|
||||
|
||||
function addStyles() {
|
||||
GM_addStyle(`
|
||||
#ct-sidebar{position:fixed;top:100px;right:-850px;width:850px;height:80vh;max-height:800px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:8px 0 0 8px;box-shadow:-3px 0 8px rgba(0,0,0,.15);z-index:9999;transition:right .4s ease-in-out;display:flex;flex-direction:column;font-family:sans-serif}#ct-sidebar.open{right:0}#ct-sidebar-toggle{position:absolute;top:50%;right:850px;transform:translateY(-50%);width:30px;height:80px;background-color:#0056b3;color:#fff;border:none;border-radius:8px 0 0 8px;cursor:pointer;writing-mode:vertical-rl;text-orientation:mixed;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;letter-spacing:1px}#ct-tab-bar{display:flex;background-color:#e9ecef;border-bottom:1px solid #ccc;flex-shrink:0}.ct-tab-button{padding:10px 15px;border:none;background-color:transparent;cursor:pointer;font-size:14px;border-bottom:3px solid transparent;transition:background-color .2s,border-color .2s}.ct-tab-button:hover{background-color:#dcdcdc}.ct-tab-button.active{border-bottom:3px solid #0056b3;font-weight:700;background-color:#fff}#ct-tab-content-area{padding:15px;flex-grow:1;overflow:hidden;display:flex;flex-direction:column}.ct-tab-panel{display:none;flex-grow:1;overflow:hidden;flex-direction:column}.ct-tab-panel.active{display:flex}.filter-container{margin-bottom:10px;flex-shrink:0}.filter-container label{font-weight:700;margin-right:5px}.filter-container select{padding:5px;border-radius:4px;border:1px solid #ccc}.results-container{flex-grow:1;overflow-y:auto;border:1px solid #ddd}.ct-results-table{width:100%;border-collapse:collapse;font-size:12px}.ct-results-table td,.ct-results-table th{border:1px solid #ddd;padding:8px;text-align:left}.ct-results-table th{background-color:#f2f2f2;font-weight:700;position:sticky;top:-1px}
|
||||
#ct-sidebar{position:fixed;top:100px;right:-950px;width:950px;height:80vh;max-height:800px;background-color:#f9f9f9;border:1px solid #ccc;border-radius:8px 0 0 8px;box-shadow:-3px 0 8px rgba(0,0,0,.15);z-index:9999;transition:right .4s ease-in-out;display:flex;flex-direction:column;font-family:sans-serif}#ct-sidebar.open{right:0}#ct-sidebar-toggle{position:absolute;top:50%;right:950px;transform:translateY(-50%);width:30px;height:80px;background-color:#0056b3;color:#fff;border:none;border-radius:8px 0 0 8px;cursor:pointer;writing-mode:vertical-rl;text-orientation:mixed;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;letter-spacing:1px}
|
||||
#ct-header-area { padding: 12px 15px; background-color: #343a40; color: white; flex-shrink: 0; display: flex; align-items: center; gap: 15px; border-bottom: 1px solid #495057; }
|
||||
#ct-header-icon { flex-shrink: 0; }
|
||||
#ct-header-icon svg { width: 32px; height: 32px; fill: #e9ecef; }
|
||||
#ct-header-text { flex-grow: 1; min-width: 0; }
|
||||
#ct-header-title { font-size: 16px; font-weight: bold; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
||||
#ct-header-subtitle { font-size: 12px; color: #adb5bd; }
|
||||
#ct-tab-bar{display:flex;background-color:#e9ecef;border-bottom:1px solid #ccc;flex-shrink:0}.ct-tab-button{padding:10px 15px;border:none;background-color:transparent;cursor:pointer;font-size:14px;border-bottom:3px solid transparent;transition:background-color .2s,border-color .2s}.ct-tab-button:hover{background-color:#dcdcdc}.ct-tab-button.active{border-bottom:3px solid #0056b3;font-weight:700;background-color:#fff}#ct-tab-content-area{padding:15px;flex-grow:1;overflow:hidden;display:flex;flex-direction:column}.ct-tab-panel{display:none;flex-grow:1;overflow:hidden;flex-direction:column}.ct-tab-panel.active{display:flex}
|
||||
.filter-container{margin-bottom:10px;flex-shrink:0;display:flex;gap:15px;align-items:center;}
|
||||
.filter-container label{font-weight:700;margin-right:5px}.filter-container select{padding:5px;border-radius:4px;border:1px solid #ccc}
|
||||
.results-container{flex-grow:1;overflow-y:auto;overflow-x:auto;border:1px solid #ddd}
|
||||
.ct-results-table{width:100%;border-collapse:collapse;font-size:12px}.ct-results-table td,.ct-results-table th{border:1px solid #ddd;padding:8px;text-align:left;vertical-align:middle}.ct-results-table th{background-color:#f2f2f2;font-weight:700;position:sticky;top:-1px}
|
||||
.ct-results-table tbody tr:not(.group-header) { cursor: pointer; }
|
||||
.ct-results-table tbody tr:not(.group-header):hover{background-color:#e9ecef}.ct-results-table tr.selected{background-color:#dbeafe!important;font-weight:700}.group-header td{background-color:#343a40;color:#fff;font-weight:700;padding:6px 8px}
|
||||
.actions-cell{text-align:center!important;white-space:nowrap}.action-btn{display:inline-block;padding:4px 8px;margin:0 2px;border-radius:4px;text-decoration:none;cursor:pointer;border:1px solid #ccc;font-size:11px}.action-btn:disabled{background-color:#e9ecef;color:#6c757d;cursor:not-allowed;border-color:#ddd}.action-btn.open-case{background-color:#6c757d;color:#fff}.action-btn.view-docs{background-color:#007bff;color:#fff;border-color:#007bff}.action-btn.view-users{background-color:#17a2b8;color:#fff;border-color:#17a2b8}
|
||||
.actions-cell{text-align:left!important;white-space:nowrap}.action-btn{display:inline-block;padding:4px 8px;margin:0 2px;border-radius:4px;text-decoration:none;cursor:pointer;border:1px solid #ccc;font-size:11px}.action-btn:disabled{background-color:#e9ecef;color:#6c757d;cursor:not-allowed;border-color:#ddd}.action-btn.open-case{background-color:#6c757d;color:#fff}.action-btn.view-docs{background-color:#007bff;color:#fff;border-color:#007bff}.action-btn.view-users{background-color:#17a2b8;color:#fff;border-color:#17a2b8}
|
||||
.action-btn.download-doc { background-color: #28a745; color: white; border-color: #28a745; }
|
||||
.action-btn.review-refund-case { background-color: #6f42c1; color: white; border-color: #6f42c1; }
|
||||
.refresh-btn{margin-top:10px;padding:8px 12px;font-weight:700;cursor:pointer;border:1px solid #007bff;background-color:#007bff;color:#fff;border-radius:4px;transition:background-color .2s}.refresh-btn:hover{background-color:#0056b3}
|
||||
.group-header .group-title { font-weight: bold; font-size: 14px; color: white; }
|
||||
.group-header .group-subtitle { font-size: 11px; color: #ccc; margin-top: 2px; font-weight: normal; }
|
||||
.reported-cell { text-align: center !important; font-size: 16px; }
|
||||
.toggle-icon { display: inline-block; width: 1em; }
|
||||
.group-header.expanded .toggle-icon::before { content: '▼'; }
|
||||
.group-header.collapsed .toggle-icon::before { content: '►'; }
|
||||
.currency-wrapper { display: flex; justify-content: space-between; }
|
||||
.currency-num { font-variant-numeric: tabular-nums; }
|
||||
`);
|
||||
}
|
||||
|
||||
@@ -39,15 +62,33 @@
|
||||
sidebarContainer.innerHTML = `
|
||||
<div id="ct-sidebar">
|
||||
<button id="ct-sidebar-toggle">API Fetcher</button>
|
||||
<div id="ct-header-area">
|
||||
<div id="ct-header-icon">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 6h-8l-2-2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V6h5.17l2 2H20v10z"/></svg>
|
||||
</div>
|
||||
<div id="ct-header-text">
|
||||
<div id="ct-header-title">No Case Selected</div>
|
||||
<div id="ct-header-subtitle">Please select a case from the "My Cases" tab</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ct-tab-bar">
|
||||
<button class="ct-tab-button active" data-tab="tab-my-cases">My Cases</button>
|
||||
<button class="ct-tab-button" data-tab="tab-docs">Case Documents</button>
|
||||
<button class="ct-tab-button" data-tab="tab-users">Case Users</button>
|
||||
<button class="ct-tab-button" data-tab="tab-refund">Refund Review</button>
|
||||
</div>
|
||||
<div id="ct-tab-content-area">
|
||||
<div id="tab-my-cases" class="ct-tab-panel active"><div class="filter-container"><label for="cases-status-filter">Filter by Status:</label><select id="cases-status-filter"></select></div><div class="results-container"><p style="padding:15px;color:#666;">Loading my cases...</p></div></div>
|
||||
<div id="tab-docs" class="ct-tab-panel"><div class="filter-container"><label for="docs-status-filter">Filter by Status:</label><select id="docs-status-filter"></select></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case or use 'View Docs'.</p></div></div>
|
||||
<div id="tab-users" class="ct-tab-panel"><div class="filter-container"><label for="users-role-filter">Filter by Role:</label><select id="users-role-filter"></select></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case or use 'View Users'.</p></div></div>
|
||||
<div id="tab-my-cases" class="ct-tab-panel active"><div class="filter-container"><div><label for="cases-status-filter">Filter by Status:</label><select id="cases-status-filter"></select></div><button id="toggle-cases-btn" class="action-btn" style="margin-left:auto">Collapse All</button></div><div class="results-container"><p style="padding:15px;color:#666;">Loading my cases...</p></div></div>
|
||||
<div id="tab-docs" class="ct-tab-panel"><div class="filter-container"><div><label for="docs-status-filter">Filter by Status:</label><select id="docs-status-filter"></select></div><button id="toggle-docs-btn" class="action-btn" style="margin-left:auto">Collapse All</button></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case or use 'View Docs'.</p></div></div>
|
||||
<div id="tab-users" class="ct-tab-panel"><div class="filter-container"><div><label for="users-role-filter">Filter by Role:</label><select id="users-role-filter"></select></div></div><div class="results-container"><p style="padding:15px;color:#666;">Please select a case or use 'View Users'.</p></div></div>
|
||||
<div id="tab-refund" class="ct-tab-panel">
|
||||
<div class="filter-container">
|
||||
<div><label for="refund-reported-filter">Filter by Reported:</label><select id="refund-reported-filter"></select></div>
|
||||
<button id="toggle-refund-btn" class="action-btn" style="margin-left:auto">Collapse All</button>
|
||||
<button id="refund-download-btn" class="action-btn download-doc">Download Excel</button>
|
||||
</div>
|
||||
<div class="results-container"><p style="padding:15px;color:#666;">Please use the 'Review Refund' action on a relevant case.</p></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -55,169 +96,56 @@
|
||||
}
|
||||
|
||||
// --- RENDER FUNCTIONS ---
|
||||
function renderMyCasesTable() {
|
||||
const responseArea = document.querySelector('#tab-my-cases .results-container');
|
||||
const filterValue = document.getElementById('cases-status-filter').value;
|
||||
const filteredCases = filterValue === 'all' ? allMyCases : allMyCases.filter(c => c.CaseStatus === filterValue);
|
||||
if (filteredCases.length === 0) {
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">No cases match the selected filter.</p>`;
|
||||
return;
|
||||
}
|
||||
filteredCases.sort((a, b) => {
|
||||
const typeCompare = (a.CaseTypeName || '').localeCompare(b.CaseTypeName || '');
|
||||
if (typeCompare !== 0) return typeCompare;
|
||||
return (b.CaseNumber || '').localeCompare(a.CaseNumber || '', undefined, { numeric: true });
|
||||
});
|
||||
const table = createTable(['Case Number', 'Taxpayer Name', 'Case Type', 'Status', 'Created Date', 'Actions']);
|
||||
const tbody = document.createElement('tbody');
|
||||
let currentGroup = '';
|
||||
filteredCases.forEach(caseItem => {
|
||||
if (caseItem.CaseTypeName !== currentGroup) {
|
||||
currentGroup = caseItem.CaseTypeName;
|
||||
tbody.innerHTML += `<tr class="group-header"><td colspan="6">${currentGroup || 'Uncategorized'}</td></tr>`;
|
||||
}
|
||||
const tr = document.createElement('tr');
|
||||
const caseId = caseItem.AggregateIdentifier;
|
||||
tr.dataset.id = caseId;
|
||||
if (caseId === selectedCaseId) tr.classList.add('selected');
|
||||
const createdDate = new Date(caseItem.CreatedDate).toLocaleDateString('id-ID');
|
||||
const hasValidId = caseId && typeof caseId === 'string' && caseId.trim() !== '';
|
||||
const disabledAttribute = hasValidId ? '' : 'disabled title="Action unavailable: Case ID is missing"';
|
||||
tr.innerHTML = `
|
||||
<td>${caseItem.CaseNumber || 'N/A'}</td>
|
||||
<td>${caseItem.MainTaxpayerName || 'N/A'}</td>
|
||||
<td>${caseItem.CaseTypeName || 'N/A'}</td>
|
||||
<td>${caseItem.CaseStatus || 'N/A'}</td>
|
||||
<td>${createdDate}</td>
|
||||
function renderMyCasesTable() {const responseArea=document.querySelector("#tab-my-cases .results-container"),filterValue=document.getElementById("cases-status-filter").value,filteredCases="all"===filterValue?allMyCases:allMyCases.filter(e=>e.CaseStatus===filterValue);if(document.getElementById("toggle-cases-btn").textContent="Collapse All",0===filteredCases.length)return void(responseArea.innerHTML='<p style="padding:15px;color:#666;">No cases match the selected filter.</p>');filteredCases.sort((e,t)=>{const o=(e.CaseTypeName||"").localeCompare(t.CaseTypeName||"");return 0!==o?o:(t.CaseNumber||"").localeCompare(e.CaseNumber||"",void 0,{numeric:!0})});const table=createTable(["Case Number","Taxpayer Name","Case Type","Status","Created Date","Actions"]),tbody=document.createElement("tbody");let currentGroup="";let groupIndex=0;filteredCases.forEach(e=>{if(e.CaseTypeName!==currentGroup){currentGroup=e.CaseTypeName;groupIndex++;tbody.innerHTML+=`<tr class="group-header expanded" data-group-id="my-cases-group-${groupIndex}"><td colspan="6"><span class="toggle-icon"></span>${currentGroup||"Uncategorized"}</td></tr>`}const t=document.createElement("tr");t.className=`group-member my-cases-group-${groupIndex}`;const o=e.AggregateIdentifier;t.dataset.id=o,o===selectedCaseId&&t.classList.add("selected");const a=new Date(e.CreatedDate).toLocaleDateString("id-ID"),s=o&&"string"==typeof o&&""!==o.trim(),r=s?"":'disabled title="Action unavailable: Case ID is missing"';let d="";e.CaseTypeName===REFUND_CASE_TYPE_NAME&&(d=`<button class="action-btn review-refund-case" data-id="${o}">Review Refund</button>`),t.innerHTML=`
|
||||
<td>${e.CaseNumber||"N/A"}</td>
|
||||
<td>${e.MainTaxpayerName||"N/A"}</td>
|
||||
<td>${e.CaseTypeName||"N/A"}</td>
|
||||
<td>${e.CaseStatus||"N/A"}</td>
|
||||
<td>${a}</td>
|
||||
<td class="actions-cell">
|
||||
<a href="https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${caseId}" class="action-btn open-case" ${disabledAttribute}>Open</a>
|
||||
<button class="action-btn view-docs" data-id="${caseId}" ${disabledAttribute}>View Docs</button>
|
||||
<button class="action-btn view-users" data-id="${caseId}" ${disabledAttribute}>View Users</button>
|
||||
</td>`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
table.appendChild(tbody);
|
||||
responseArea.innerHTML = '';
|
||||
responseArea.appendChild(table);
|
||||
tbody.addEventListener('click', handleCaseAction);
|
||||
}
|
||||
|
||||
function renderCaseDocumentsTable() {
|
||||
const responseArea = document.querySelector('#tab-docs .results-container');
|
||||
const filterValue = document.getElementById('docs-status-filter').value;
|
||||
const filteredDocs = filterValue === 'all' ? allCaseDocuments : allCaseDocuments.filter(d => d.DocumentStatus === filterValue);
|
||||
if (filteredDocs.length === 0) {
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">No documents found or match the selected filter.</p>`;
|
||||
return;
|
||||
}
|
||||
filteredDocs.sort((a, b) => (a.DocumentTypeCode || '').localeCompare(b.DocumentTypeCode));
|
||||
|
||||
// --- CHANGE 1 of 3: Updated the table headers ---
|
||||
const table = createTable(['Letter Number', 'File Name', 'Status', 'Date', 'Actions']);
|
||||
const tbody = document.createElement('tbody');
|
||||
let currentGroup = '';
|
||||
filteredDocs.forEach(doc => {
|
||||
if (doc.DocumentTypeCode !== currentGroup) {
|
||||
currentGroup = doc.DocumentTypeCode;
|
||||
// --- CHANGE 2 of 3: Updated the colspan for the group header ---
|
||||
tbody.innerHTML += `<tr class="group-header"><td colspan="5">${currentGroup || 'Uncategorized'}</td></tr>`;
|
||||
}
|
||||
const docDate = doc.DocumentDate ? new Date(doc.DocumentDate).toLocaleDateString('id-ID') : 'N/A';
|
||||
// --- CHANGE 3 of 3: Changed the column data from DocumentTypeCode to FileName ---
|
||||
tbody.innerHTML += `
|
||||
<tr>
|
||||
<td>${doc.LetterNumber || 'N/A'}</td>
|
||||
<td>${doc.FileName || 'N/A'}</td>
|
||||
<td>${doc.DocumentStatus || 'N/A'}</td>
|
||||
<td>${docDate}</td>
|
||||
<a href="https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${o}" class="action-btn open-case" ${r}>Open</a>
|
||||
<button class="action-btn view-docs" data-id="${o}" ${r}>View Docs</button>
|
||||
<button class="action-btn view-users" data-id="${o}" ${r}>View Users</button>
|
||||
${d}
|
||||
</td>`,tbody.appendChild(t)}),table.appendChild(tbody),responseArea.innerHTML="",responseArea.appendChild(table),tbody.addEventListener("click",handleGroupToggle),tbody.addEventListener("click",handleCaseAction)}
|
||||
function renderCaseDocumentsTable() {const responseArea=document.querySelector("#tab-docs .results-container"),filterValue=document.getElementById("docs-status-filter").value,filteredDocs="all"===filterValue?allCaseDocuments:allCaseDocuments.filter(e=>e.DocumentStatus===filterValue);if(document.getElementById("toggle-docs-btn").textContent="Collapse All",0===filteredDocs.length)return void(responseArea.innerHTML='<p style="padding:15px;color:#666;">No documents found or match the selected filter.</p>');filteredDocs.sort((e,t)=>(e.DocumentTypeCode||"").localeCompare(t.DocumentTypeCode||""));const table=createTable(["Letter Number","File Name","Status","Date","Actions"]),tbody=document.createElement("tbody");let currentGroup="";let groupIndex=0;filteredDocs.forEach(e=>{if(e.DocumentTypeCode!==currentGroup){currentGroup=e.DocumentTypeCode;groupIndex++;tbody.innerHTML+=`<tr class="group-header expanded" data-group-id="docs-group-${groupIndex}"><td colspan="5"><span class="toggle-icon"></span>${currentGroup||"Uncategorized"}</td></tr>`}const t=e.DocumentDate?new Date(e.DocumentDate).toLocaleDateString("id-ID"):"N/A";tbody.innerHTML+=`
|
||||
<tr class="group-member docs-group-${groupIndex}">
|
||||
<td>${e.LetterNumber||"N/A"}</td>
|
||||
<td>${e.FileName||"N/A"}</td>
|
||||
<td>${e.DocumentStatus||"N/A"}</td>
|
||||
<td>${t}</td>
|
||||
<td class="actions-cell">
|
||||
<button class="action-btn download-doc"
|
||||
data-doc-id="${doc.DocumentAggregateIdentifier}"
|
||||
data-filename="${doc.OriginalName}">
|
||||
Download
|
||||
</button>
|
||||
<button class="action-btn download-doc" data-doc-id="${e.DocumentAggregateIdentifier}" data-filename="${e.OriginalName}">Download</button>
|
||||
</td>
|
||||
</tr>`;
|
||||
});
|
||||
table.appendChild(tbody);
|
||||
responseArea.innerHTML = '';
|
||||
responseArea.appendChild(table);
|
||||
tbody.addEventListener('click', handleDocumentAction);
|
||||
}
|
||||
|
||||
function renderCaseUsersTable() {
|
||||
const responseArea = document.querySelector('#tab-users .results-container');
|
||||
const filterValue = document.getElementById('users-role-filter').value;
|
||||
const filteredUsers = filterValue === 'all' ? allCaseUsers : allCaseUsers.filter(u => u.CaseRoleType === filterValue);
|
||||
if (filteredUsers.length === 0) {
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">No users found or match the selected filter.</p>`;
|
||||
return;
|
||||
}
|
||||
filteredUsers.sort((a, b) => (a.FullName || '').localeCompare(b.FullName || ''));
|
||||
const table = createTable(['Full Name', 'NIP', 'Position', 'Office', 'Case Role']);
|
||||
const tbody = document.createElement('tbody');
|
||||
filteredUsers.forEach(user => {
|
||||
tbody.innerHTML += `
|
||||
</tr>`}),table.appendChild(tbody),responseArea.innerHTML="",responseArea.appendChild(table),tbody.addEventListener("click",handleGroupToggle),tbody.addEventListener("click",handleDocumentAction)}
|
||||
function renderCaseUsersTable(){const responseArea=document.querySelector("#tab-users .results-container"),filterValue=document.getElementById("users-role-filter").value,filteredUsers="all"===filterValue?allCaseUsers:allCaseUsers.filter(e=>e.CaseRoleType===filterValue);if(0===filteredUsers.length)return void(responseArea.innerHTML='<p style="padding:15px;color:#666;">No users found or match the selected filter.</p>');filteredUsers.sort((e,t)=>(e.FullName||"").localeCompare(t.FullName||""));const table=createTable(["Full Name","NIP","Position","Office","Case Role"]),tbody=document.createElement("tbody");filteredUsers.forEach(e=>{tbody.innerHTML+=`
|
||||
<tr>
|
||||
<td>${user.FullName || 'N/A'}</td>
|
||||
<td>${user.Nip || 'N/A'}</td>
|
||||
<td>${user.Jabatan || 'N/A'}</td>
|
||||
<td>${user.OfficeName || 'N/A'}</td>
|
||||
<td>${user.CaseRoleType || 'N/A'}</td>
|
||||
</tr>`;
|
||||
});
|
||||
table.appendChild(tbody);
|
||||
responseArea.innerHTML = '';
|
||||
responseArea.appendChild(table);
|
||||
}
|
||||
<td>${e.FullName||"N/A"}</td>
|
||||
<td>${e.Nip||"N/A"}</td>
|
||||
<td>${e.Jabatan||"N/A"}</td>
|
||||
<td>${e.OfficeName||"N/A"}</td>
|
||||
<td>${e.CaseRoleType||"N/A"}</td>
|
||||
</tr>`}),table.appendChild(tbody),responseArea.innerHTML="",responseArea.appendChild(table)}
|
||||
function renderRefundReviewTable() {const responseArea=document.querySelector("#tab-refund .results-container"),filterValue=document.getElementById("refund-reported-filter").value;document.getElementById("refund-download-btn").disabled=!refundReviewData||0===refundReviewData.length,document.getElementById("toggle-refund-btn").textContent="Collapse All";let dataToRender=refundReviewData;"all"!==filterValue&&(dataToRender=refundReviewData.filter(e=>e.ReportedBySeller===("true"===filterValue))),filteredRefundData=dataToRender;if(!dataToRender||0===dataToRender.length)return void(responseArea.innerHTML='<p style="padding:15px;color:#666;">No refund review data matches the filter.</p>');dataToRender.sort((e,t)=>{const o=(e.Tin||"")+(e.Name||""),a=(t.Tin||"")+(t.Name||"");return o.localeCompare(a)});const table=createTable(["Doc Number","Date","Selling Price","VAT Paid","STLG Paid","Trans Code","Reported"]),tbody=document.createElement("tbody");let currentGroupKey="";let groupIndex=0;responseArea.innerHTML="",table.appendChild(tbody),responseArea.appendChild(table),dataToRender.forEach(e=>{const t=(e.Tin||"")+(e.Name||"");if(t!==currentGroupKey){currentGroupKey=t,groupIndex++;const o=tbody.insertRow();o.className="group-header expanded",o.dataset.groupId=`refund-group-${groupIndex}`;const a=o.insertCell();a.colSpan=7,a.innerHTML=`
|
||||
<span class="toggle-icon"></span>
|
||||
<span>
|
||||
<div class="group-title">${e.Name||"Unknown Name"}</div>
|
||||
<div class="group-subtitle">${e.Tin||"Unknown TIN"}</div>
|
||||
</span>`}const o=tbody.insertRow();o.className=`group-member refund-group-${groupIndex}`;const a=e.DocumentDate?new Date(e.DocumentDate).toLocaleDateString("id-ID"):"N/A",s=e.ReportedBySeller?'<span style="color: #28a745;">✔</span>':'<span style="color: #dc3545;">❌</span>';o.insertCell().textContent=e.DocumentNumber||"N/A",o.insertCell().textContent=a;const r=o.insertCell();r.innerHTML=`<div class="currency-wrapper"><span>Rp</span><span class="currency-num">${(e.SellingPrice||0).toLocaleString("id-ID")}</span></div>`;const d=o.insertCell();d.innerHTML=`<div class="currency-wrapper"><span>Rp</span><span class="currency-num">${(e.VatPaid||0).toLocaleString("id-ID")}</span></div>`;const n=o.insertCell();n.innerHTML=`<div class="currency-wrapper"><span>Rp</span><span class="currency-num">${(e.StlgPaid||0).toLocaleString("id-ID")}</span></div>`,o.insertCell().textContent=e.TransactionCode||"N/A";const c=o.insertCell();c.className="reported-cell",c.innerHTML=s}),tbody.addEventListener("click",handleGroupToggle)}
|
||||
|
||||
// --- DATA FETCHING FUNCTIONS ---
|
||||
async function fetchMyCases(){const responseArea=document.querySelector("#tab-my-cases .results-container");try{const authToken=getAuthToken(),apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/caselist/mycases",fetchOptions={method:"POST",headers:getHeaders(),body:JSON.stringify({})},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();allMyCases=data?.Payload?.Data||[],populateFilter("cases-status-filter",allMyCases,"CaseStatus");const casesFilter=document.getElementById("cases-status-filter");Array.from(casesFilter.options).some(e=>e.value===DEFAULT_CASES_FILTER)&&(casesFilter.value=DEFAULT_CASES_FILTER),renderMyCasesTable()}catch(error){handleError(error,responseArea)}}
|
||||
async function fetchCaseDocuments(caseId){const responseArea=document.querySelector("#tab-docs .results-container");responseArea.innerHTML='<p style="padding:15px;color:#666;">Loading documents...</p>';try{if(!caseId)throw new Error("No Case ID provided.");const authToken=getAuthToken(),apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/casedocument/list",fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify({AggregateIdentifier:caseId})},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();allCaseDocuments=data?.Payload?.Data||[],loadedDocsForCaseId=caseId,populateFilter("docs-status-filter",allCaseDocuments,"DocumentStatus"),renderCaseDocumentsTable()}catch(error){handleError(error,responseArea)}}
|
||||
async function fetchCaseUsers(caseId){const responseArea=document.querySelector("#tab-users .results-container");responseArea.innerHTML='<p style="padding:15px;color:#666;">Loading users...</p>';try{if(!caseId)throw new Error("No Case ID provided.");const authToken=getAuthToken(),apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/caseuser/list",payload={AggregateIdentifier:caseId,First:0,Rows:200,SortField:"",SortOrder:1,Filters:[],LanguageId:"id-ID"},fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();allCaseUsers=data?.Payload?.Data||[],loadedUsersForCaseId=caseId,populateFilter("users-role-filter",allCaseUsers,"CaseRoleType"),renderCaseUsersTable()}catch(error){handleError(error,responseArea)}}
|
||||
async function downloadDocument(docId, filename, button) {
|
||||
const originalText = button.textContent;
|
||||
button.textContent = 'Downloading...';
|
||||
button.disabled = true;
|
||||
try {
|
||||
const authToken = getAuthToken();
|
||||
const apiUrl = 'https://coretax.intranet.pajak.go.id/documentmanagement/api/download';
|
||||
const payload = { DocumentAggregateIdentifier: docId, IsDocumentCases: false, IsNeedWatermark: null };
|
||||
const fetchOptions = {
|
||||
method: 'POST',
|
||||
headers: getHeaders(), // Using generic headers should be fine
|
||||
body: JSON.stringify(payload)
|
||||
};
|
||||
const response = await fetch(apiUrl, fetchOptions);
|
||||
if (!response.ok) {
|
||||
try {
|
||||
const errorData = await response.json();
|
||||
throw new Error(`API Error: ${errorData.Message || response.statusText}`);
|
||||
} catch (e) {
|
||||
throw new Error(`API request failed! Status: ${response.statusText}`);
|
||||
}
|
||||
}
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
a.download = filename || 'download.pdf';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
a.remove();
|
||||
} catch (error) {
|
||||
alert(`Download failed: ${error.message}`);
|
||||
} finally {
|
||||
button.textContent = originalText;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchCaseDocuments(caseId){const responseArea=document.querySelector("#tab-docs .results-container");responseArea.innerHTML='<p style="padding:15px;color:#666;">Loading documents...</p>';try{if(!caseId)throw new Error("No Case ID provided.");const apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/casedocument/list",fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify({AggregateIdentifier:caseId})},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();allCaseDocuments=data?.Payload?.Data||[],loadedDocsForCaseId=caseId,populateFilter("docs-status-filter",allCaseDocuments,"DocumentStatus"),renderCaseDocumentsTable()}catch(error){handleError(error,responseArea)}}
|
||||
async function fetchCaseUsers(caseId){const responseArea=document.querySelector("#tab-users .results-container");responseArea.innerHTML='<p style="padding:15px;color:#666;">Loading users...</p>';try{if(!caseId)throw new Error("No Case ID provided.");const apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/caseuser/list",payload={AggregateIdentifier:caseId,First:0,Rows:200,SortField:"",SortOrder:1,Filters:[],LanguageId:"id-ID"},fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();allCaseUsers=data?.Payload?.Data||[],loadedUsersForCaseId=caseId,populateFilter("users-role-filter",allCaseUsers,"CaseRoleType"),renderCaseUsersTable()}catch(error){handleError(error,responseArea)}}
|
||||
async function downloadDocument(docId, filename, button) {const originalText=button.textContent;button.textContent="Downloading...",button.disabled=!0;try{const apiUrl="https://coretax.intranet.pajak.go.id/documentmanagement/api/download",payload={DocumentAggregateIdentifier:docId,IsDocumentCases:!1,IsNeedWatermark:null},fetchOptions={method:"POST",headers:getHeaders(),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok)try{const errorData=await response.json();throw new Error(`API Error: ${errorData.Message||response.statusText}`)}catch(e){throw new Error(`API request failed! Status: ${response.statusText}`)}const blob=await response.blob(),url=window.URL.createObjectURL(blob),a=document.createElement("a");a.style.display="none",a.href=url,a.download=filename||"download.pdf",document.body.appendChild(a),a.click(),window.URL.revokeObjectURL(url),a.remove()}catch(error){alert(`Download failed: ${error.message}`)}finally{button.textContent=originalText,button.disabled=!1}}
|
||||
async function fetchSubProcessId(caseId) {const apiUrl="https://coretax.intranet.pajak.go.id/casemanagement/api/caserouting/view",payload={AggregateIdentifier:caseId,LanguageId:"id-ID"},fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`Sub Process ID API Error: ${errorData.Message||response.statusText}`)}const data=await response.json(),firstResult=data?.Payload?.[0];if(!firstResult||!firstResult.SubProcessIdentifier)throw new Error("Could not find a 'SubProcessIdentifier' in the caserouting/view response.");return firstResult.SubProcessIdentifier}
|
||||
async function fetchC02FormDetail(caseId, subProcessId) {const apiUrl="https://coretax.intranet.pajak.go.id/casecomponentspayment/api/c02form014/c02form009detail/current",payload={caseAggregateIdentifier:caseId,CaseSubProcessIdentifier:subProcessId},fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`C02Form Detail API Error: ${errorData.Message||response.statusText}`)}const data=await response.json(),firstResult=data?.Payload?.[0];if(!firstResult||!firstResult.Reference)throw new Error("Could not find a 'Reference' number in the C02Form Detail response.");return firstResult.Reference}
|
||||
async function fetchRefundReview(caseId, refNumber) {const responseArea=document.querySelector("#tab-refund .results-container");try{const apiUrl="https://coretax.intranet.pajak.go.id/casecomponentspayment/api/refundprocessreview/get-detailed-review",payload={CaseAggregateIdentifier:caseId,RevenueCode:"411211",TaxPaymentCode:"100",TaxReturnType:"VAT_VATR",ReferenceNumber:refNumber},fetchOptions={method:"POST",headers:getHeaders(caseId),body:JSON.stringify(payload)},response=await fetch(apiUrl,fetchOptions);if(!response.ok){const errorData=await response.json();throw new Error(`Refund Review API Error: ${errorData.Message||response.statusText}`)}const data=await response.json();refundReviewData=data?.Payload||[],populateBooleanFilter("refund-reported-filter"),renderRefundReviewTable()}catch(error){handleError(error,responseArea)}}
|
||||
|
||||
// --- HELPER FUNCTIONS ---
|
||||
function getHeaders(caseId = null) { const authToken = getAuthToken(), requestFromUrl = caseId ? `https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${caseId}` : window.location.href; return { 'accept': 'application/json, text/plain, */*', 'authorization': `Bearer ${authToken}`, 'content-type': 'application/json', 'languageid': 'id-ID', 'request_from': requestFromUrl, }; }
|
||||
function getHeaders(caseId=null){const authToken=getAuthToken(),requestFromUrl=caseId?`https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${caseId}`:window.location.href;return{accept:"application/json, text/plain, */*",authorization:`Bearer ${authToken}`,"content-type":"application/json",languageid:"id-ID",request_from:requestFromUrl}}
|
||||
function populateFilter(selectId,data,key){const select=document.getElementById(selectId);if(!select)return;const values=[...new Set(data.map(e=>e[key]).filter(Boolean))];select.innerHTML='<option value="all">Show All</option>',values.sort().forEach(e=>{select.innerHTML+=`<option value="${e}">${e}</option>`})}
|
||||
function populateBooleanFilter(selectId){const e=document.getElementById(selectId);e&&(e.innerHTML='<option value="all">Show All</option><option value="true">Yes</option><option value="false">No</option>')}
|
||||
function getAuthToken(){const userDataString=localStorage.getItem(AUTH_STORAGE_KEY),userData=userDataString?JSON.parse(userDataString):null,authToken=userData?.access_token;if(!authToken)throw new Error("Authorization Token not found.");return authToken}
|
||||
function createTable(headers){const table=document.createElement("table");table.className="ct-results-table";const thead=document.createElement("thead");return thead.innerHTML=`<tr>${headers.map(e=>`<th>${e}</th>`).join("")}</tr>`,table.appendChild(thead),table}
|
||||
function handleError(error,area){console.error("Userscript Error:",error);let errorHtml;error.message.includes("Authorization Token not found")?errorHtml=`
|
||||
@@ -226,48 +154,130 @@
|
||||
<p style="margin: 10px 0;">Please refresh the page to log in again.</p>
|
||||
<button id="auth-refresh-btn" class="refresh-btn">Refresh Page</button>
|
||||
</div>`:errorHtml=`<p style="color: #d9534f;padding:15px;"><b>An error occurred:</b><br>${error.message}</p>`,area.innerHTML=errorHtml;const refreshBtn=area.querySelector("#auth-refresh-btn");refreshBtn&&refreshBtn.addEventListener("click",()=>window.location.reload())}
|
||||
|
||||
function handleCaseAction(event) {
|
||||
const selectedRow = event.target.closest('tr');
|
||||
if (!selectedRow || selectedRow.classList.contains('group-header')) return;
|
||||
const caseId = selectedRow.dataset.id;
|
||||
if (!caseId) return;
|
||||
selectedCaseId = caseId;
|
||||
loadedDocsForCaseId = null;
|
||||
loadedUsersForCaseId = null;
|
||||
const allRows = selectedRow.closest('tbody').querySelectorAll('tr');
|
||||
allRows.forEach(row => row.classList.remove('selected'));
|
||||
selectedRow.classList.add('selected');
|
||||
const actionButton = event.target.closest('.action-btn');
|
||||
if (actionButton) {
|
||||
if (actionButton.matches('.view-docs')) switchTab('tab-docs');
|
||||
else if (actionButton.matches('.view-users')) switchTab('tab-users');
|
||||
function updateHeader(caseObject) {const titleEl=document.getElementById("ct-header-title"),subtitleEl=document.getElementById("ct-header-subtitle");caseObject?(titleEl.textContent=caseObject.MainTaxpayerName||"N/A",subtitleEl.textContent=caseObject.CaseNumber||"N/A"):(titleEl.textContent="No Case Selected",subtitleEl.textContent='Please select a case from the "My Cases" tab')}
|
||||
async function startRefundReviewProcess(caseId, button) {
|
||||
const originalText = button.textContent;
|
||||
button.textContent = '...';
|
||||
button.disabled = true;
|
||||
const responseArea = document.querySelector('#tab-refund .results-container');
|
||||
try {
|
||||
switchTab('tab-refund');
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">Step 1/3: Fetching Sub Process ID...</p>`;
|
||||
if (!caseId) throw new Error("A case must be selected.");
|
||||
const subProcessId = await fetchSubProcessId(caseId);
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">Step 2/3: Fetching reference number...</p>`;
|
||||
const referenceNumber = await fetchC02FormDetail(caseId, subProcessId);
|
||||
responseArea.innerHTML = `<p style="padding:15px;color:#666;">Step 3/3: Fetching refund details...</p>`;
|
||||
await fetchRefundReview(caseId, referenceNumber);
|
||||
} catch(error) {
|
||||
handleError(error, responseArea);
|
||||
} finally {
|
||||
button.textContent = originalText;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
function handleCaseAction(event){
|
||||
const selectedRow=event.target.closest("tr");
|
||||
if(!selectedRow||selectedRow.classList.contains("group-header"))return;
|
||||
const caseId=selectedRow.dataset.id;
|
||||
if(!caseId)return;
|
||||
const selectedCase=allMyCases.find(e=>e.AggregateIdentifier===caseId);
|
||||
updateHeader(selectedCase);
|
||||
// Only reset dependent data if the selection actually changes
|
||||
if (selectedCaseId !== caseId) {
|
||||
selectedCaseId=caseId;
|
||||
loadedDocsForCaseId=null;
|
||||
loadedUsersForCaseId=null;
|
||||
// --- FIX IS HERE ---
|
||||
refundReviewData = [];
|
||||
filteredRefundData = [];
|
||||
document.querySelector('#tab-refund .results-container').innerHTML = `<p style="padding:15px;color:#666;">Please use the 'Review Refund' action on a relevant case.</p>`;
|
||||
document.getElementById('refund-download-btn').disabled = true;
|
||||
}
|
||||
const allRows=selectedRow.closest("tbody").querySelectorAll("tr");
|
||||
allRows.forEach(e=>e.classList.remove("selected"));
|
||||
selectedRow.classList.add("selected");
|
||||
const actionButton=event.target.closest(".action-btn");
|
||||
actionButton&&(actionButton.matches(".view-docs")?switchTab("tab-docs"):actionButton.matches(".view-users")?switchTab("tab-users"):actionButton.matches(".review-refund-case")&&startRefundReviewProcess(caseId,actionButton));
|
||||
}
|
||||
function handleDocumentAction(event) {
|
||||
const downloadButton = event.target.closest('.download-doc');
|
||||
if (downloadButton) {
|
||||
const docId = downloadButton.dataset.docId;
|
||||
const filename = downloadButton.dataset.filename;
|
||||
downloadDocument(docId, filename, downloadButton);
|
||||
const target = event.target.closest('.action-btn');
|
||||
if (!target) return;
|
||||
if (target.matches('.download-doc')) {
|
||||
const docId = target.dataset.docId;
|
||||
const filename = target.dataset.filename;
|
||||
downloadDocument(docId, filename, target);
|
||||
}
|
||||
}
|
||||
|
||||
function switchTab(tabId) {
|
||||
document.querySelectorAll('.ct-tab-button').forEach(btn => btn.classList.remove('active'));
|
||||
document.querySelectorAll('.ct-tab-panel').forEach(panel => panel.classList.remove('active'));
|
||||
document.querySelector(`[data-tab="${tabId}"]`).classList.add('active');
|
||||
document.getElementById(tabId).classList.add('active');
|
||||
if (tabId === 'tab-docs') {
|
||||
if (selectedCaseId && selectedCaseId !== loadedDocsForCaseId) {
|
||||
fetchCaseDocuments(selectedCaseId);
|
||||
}
|
||||
} else if (tabId === 'tab-users') {
|
||||
if (selectedCaseId && selectedCaseId !== loadedUsersForCaseId) {
|
||||
fetchCaseUsers(selectedCaseId);
|
||||
function handleGroupToggle(event) {
|
||||
const headerRow = event.target.closest('.group-header');
|
||||
if (!headerRow) return;
|
||||
const groupId = headerRow.dataset.groupId;
|
||||
if (!groupId) return;
|
||||
headerRow.classList.toggle('collapsed');
|
||||
headerRow.classList.toggle('expanded');
|
||||
const isCollapsed = headerRow.classList.contains('collapsed');
|
||||
const tableBody = headerRow.parentElement;
|
||||
const memberRows = tableBody.querySelectorAll(`.group-member.${groupId}`);
|
||||
memberRows.forEach(row => { row.style.display = isCollapsed ? 'none' : 'table-row'; });
|
||||
}
|
||||
function switchTab(tabId){document.querySelectorAll(".ct-tab-button").forEach(e=>e.classList.remove("active")),document.querySelectorAll(".ct-tab-panel").forEach(e=>e.classList.remove("active")),document.querySelector(`[data-tab="${tabId}"]`).classList.add("active"),document.getElementById(tabId).classList.add("active"),"tab-docs"===tabId?selectedCaseId&&selectedCaseId!==loadedDocsForCaseId&&fetchCaseDocuments(selectedCaseId):"tab-users"===tabId&&selectedCaseId&&selectedCaseId!==loadedUsersForCaseId&&fetchCaseUsers(selectedCaseId)}
|
||||
function downloadRefundExcel() {
|
||||
if (!filteredRefundData || filteredRefundData.length === 0) {
|
||||
alert("No data to download. Please filter the data first.");
|
||||
return;
|
||||
}
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = String(now.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(now.getDate()).padStart(2, '0');
|
||||
const hours = String(now.getHours()).padStart(2, '0');
|
||||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||||
const datetimeSuffix = `${year}${month}${day}_${hours}${minutes}`;
|
||||
let filenamePrefix = 'Refund_Review_Data';
|
||||
if (selectedCaseId) {
|
||||
const selectedCase = allMyCases.find(c => c.AggregateIdentifier === selectedCaseId);
|
||||
if (selectedCase && selectedCase.CaseNumber) {
|
||||
const sanitizedCaseNumber = selectedCase.CaseNumber.replace(/[\\/:"*?<>|]/g, '_');
|
||||
filenamePrefix = `Refund_Review_Data_${sanitizedCaseNumber}`;
|
||||
}
|
||||
}
|
||||
const filename = `${filenamePrefix}_${datetimeSuffix}.xlsx`;
|
||||
const headerMapping = [
|
||||
{ apiField: "ReportedBySeller", excelHeader: "Telah dilaporkan oleh Penjual?" }, { apiField: "Tin", excelHeader: "NPWP Penjual" }, { apiField: "Name", excelHeader: "Nama Penjual" }, { apiField: "DocumentNumber", excelHeader: "Nomor Faktur Pajak/Dokumen yang Dipersamakan/Nota Retur/Nota Pembatalan" }, { apiField: "DocumentDate", excelHeader: "Tanggal Faktur Pajak/Dokumen yang Dipersamakan/Nota Retur/Nota Pembatalan" }, { apiField: "TransactionCode", excelHeader: "Kode Transaksi" }, { apiField: "SellingPrice", excelHeader: "Harga Jual/Dasar Pengenaan Pajak/Dasar Pengenaan Pajak Lainnya (Rp)" }, { apiField: "VatPaid", excelHeader: "PPN yang dikreditkan pada SPT yang Dilaporkan" }, { apiField: "StlgPaid", excelHeader: "PPnBM yang dikreditkan pada SPT yang Dilaporkan" }
|
||||
];
|
||||
const excelData = filteredRefundData.map(item => {
|
||||
const row = {};
|
||||
headerMapping.forEach(map => {
|
||||
if (map.apiField === 'ReportedBySeller') {
|
||||
row[map.excelHeader] = item[map.apiField] ? 'Yes' : 'No';
|
||||
} else {
|
||||
row[map.excelHeader] = item[map.apiField] === null ? '' : item[map.apiField];
|
||||
}
|
||||
});
|
||||
return row;
|
||||
});
|
||||
const worksheet = XLSX.utils.json_to_sheet(excelData);
|
||||
const workbook = XLSX.utils.book_new();
|
||||
XLSX.utils.book_append_sheet(workbook, worksheet, "Refund Review");
|
||||
XLSX.writeFile(workbook, filename);
|
||||
}
|
||||
|
||||
function toggleAllGroups(containerSelector, button) {
|
||||
const isCollapsing = button.textContent === 'Collapse All';
|
||||
const headers = document.querySelectorAll(`${containerSelector} .group-header`);
|
||||
const members = document.querySelectorAll(`${containerSelector} .group-member`);
|
||||
|
||||
headers.forEach(h => {
|
||||
h.classList.toggle('expanded', !isCollapsing);
|
||||
h.classList.toggle('collapsed', isCollapsing);
|
||||
});
|
||||
members.forEach(m => {
|
||||
m.style.display = isCollapsing ? 'none' : 'table-row';
|
||||
});
|
||||
|
||||
button.textContent = isCollapsing ? 'Expand All' : 'Collapse All';
|
||||
}
|
||||
|
||||
// --- MAIN INITIALIZATION ---
|
||||
@@ -283,6 +293,13 @@
|
||||
document.getElementById('cases-status-filter').addEventListener('change', renderMyCasesTable);
|
||||
document.getElementById('docs-status-filter').addEventListener('change', renderCaseDocumentsTable);
|
||||
document.getElementById('users-role-filter').addEventListener('change', renderCaseUsersTable);
|
||||
document.getElementById('refund-reported-filter').addEventListener('change', renderRefundReviewTable);
|
||||
document.getElementById('refund-download-btn').addEventListener('click', downloadRefundExcel);
|
||||
|
||||
document.getElementById('toggle-cases-btn').addEventListener('click', (e) => toggleAllGroups('#tab-my-cases .results-container', e.target));
|
||||
document.getElementById('toggle-docs-btn').addEventListener('click', (e) => toggleAllGroups('#tab-docs .results-container', e.target));
|
||||
document.getElementById('toggle-refund-btn').addEventListener('click', (e) => toggleAllGroups('#tab-refund .results-container', e.target));
|
||||
|
||||
fetchMyCases();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user