diff --git a/coretabs.user.js b/coretabs.user.js index 4aecdbe..bd9b6b0 100644 --- a/coretabs.user.js +++ b/coretabs.user.js @@ -10,57 +10,25 @@ // @run-at document-idle // ==/UserScript== -(function () { - "use strict"; +(function() { + 'use strict'; - // Prevent running inside iframes and prevent duplicate injection into the top window. - // This ensures only one instance of the sidebar is created even if the userscript is executed - // for multiple frames on the same page. - try { - if (window.top !== window.self) { - // We're in an iframe - do nothing. - return; - } - } catch (e) { - // In rare cross-origin cases accessing window.top may throw; if so, abort to be safe. - return; - } + // --- 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_PREFIX = 'Pengembalian'; + // ---------------------------- - // Guard against double-injection in the top window (e.g., userscript re-run) - if (window.__coretabs_injected) { - return; - } - Object.defineProperty(window, "__coretabs_injected", { - value: true, - configurable: false, - writable: false, - enumerable: false, - }); + // --- State Management --- + let allMyCases = [], allCaseDocuments = [], allCaseUsers = [], refundReviewData = []; + let filteredRefundData = []; + let selectedCaseId = null; + let loadedDocsForCaseId = null; + let loadedUsersForCaseId = null; - // --- 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_PREFIX = "Pengembalian"; - // ---------------------------- - - // --- State Management --- - let allMyCases = [], - allCaseDocuments = [], - allCaseUsers = [], - caseHistoryData = [], - caseSubProcessData = {}, - refundReviewData = [], - filteredRefundData = [], - selectedCaseId = null, - loadedDocsForCaseId = null, - loadedUsersForCaseId = null, - loadedHistoryForCaseId = null; - - function addStyles() { - GM_addStyle(` - #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:2147483647;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-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background-color:transparent;z-index:2147483646;display:none} + function addStyles() { + GM_addStyle(` + #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; } @@ -88,16 +56,13 @@ .currency-wrapper { display: flex; justify-content: space-between; } .currency-num { font-variant-numeric: tabular-nums; } `); - } + } - function createSidebar() { - // ensure we don't create duplicate DOM elements even if something else tries to run - if (document.getElementById("ct-sidebar")) return; - - const sidebarContainer = document.createElement("div"); - sidebarContainer.innerHTML = ` + function createSidebar() { + const sidebarContainer = document.createElement('div'); + sidebarContainer.innerHTML = `
- +
@@ -112,20 +77,12 @@ -

Loading my cases...

Please select a case to view its documents.

Please select a case to view its users.

-
-
-
- -
-

Please select a case to view its history.

-
@@ -137,1115 +94,299 @@
`; - // Append to document.body (we are running only in top window so this is safe) - document.body.appendChild(sidebarContainer); + document.body.appendChild(sidebarContainer); + } - // Create overlay for click-outside functionality - const overlay = document.createElement("div"); - overlay.className = "ct-overlay"; - document.body.appendChild(overlay); - } - - // --- RENDER FUNCTIONS --- - 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 = - '

No cases match the selected filter.

'); - 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 += `${currentGroup || "Uncategorized"}`; - } - 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 && - e.CaseTypeName.startsWith(REFUND_CASE_PREFIX) && - (d = ``), - (t.innerHTML = ` - ${e.CaseNumber || "N/A"} - ${e.MainTaxpayerName || "N/A"} - ${e.CaseTypeName || "N/A"} - ${e.CaseStatus || "N/A"} + // --- RENDER FUNCTIONS --- + 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='

No cases match the selected filter.

');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+=`${currentGroup||"Uncategorized"}`}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&&e.CaseTypeName.startsWith(REFUND_CASE_PREFIX)&&(d=``),t.innerHTML=` + ${e.CaseNumber||"N/A"} + ${e.MainTaxpayerName||"N/A"} + ${e.CaseTypeName||"N/A"} + ${e.CaseStatus||"N/A"} ${a} Open ${d} - `), - tbody.appendChild(t)); - }), - table.appendChild(tbody), - (responseArea.innerHTML = ""), - responseArea.appendChild(table), - tbody.addEventListener("click", handleGroupToggle), - tbody.addEventListener("click", handleCaseSelection)); - } - 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 = - '

No documents found or match the selected filter.

'); - 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 += `${currentGroup || "Uncategorized"}`; - } - const t = e.DocumentDate - ? new Date(e.DocumentDate).toLocaleDateString("id-ID") - : "N/A"; - tbody.innerHTML += ` + `,tbody.appendChild(t)}),table.appendChild(tbody),responseArea.innerHTML="",responseArea.appendChild(table),tbody.addEventListener("click",handleGroupToggle),tbody.addEventListener("click",handleCaseSelection)} + 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='

No documents found or match the selected filter.

');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+=`${currentGroup||"Uncategorized"}`}const t=e.DocumentDate?new Date(e.DocumentDate).toLocaleDateString("id-ID"):"N/A";tbody.innerHTML+=` - ${e.LetterNumber || "N/A"} - ${e.FileName || "N/A"} - ${e.DocumentStatus || "N/A"} + ${e.LetterNumber||"N/A"} + ${e.FileName||"N/A"} + ${e.DocumentStatus||"N/A"} ${t} - `; - }), - 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 = - '

No users found or match the selected filter.

'); - 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 += ` + `}),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='

No users found or match the selected filter.

');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+=` - ${e.FullName || "N/A"} - ${e.Nip || "N/A"} - ${e.Jabatan || "N/A"} - ${e.OfficeName || "N/A"} - ${e.CaseRoleType || "N/A"} - `; - }), - table.appendChild(tbody), - (responseArea.innerHTML = ""), - responseArea.appendChild(table)); - } - function renderCaseHistoryTable() { - const responseArea = document.querySelector( - "#tab-history .results-container", - ); - if (!caseHistoryData || caseHistoryData.length === 0) { - responseArea.innerHTML = - '

No history data available for this case.

'; - return; - } - - const filterValue = document.getElementById("history-type-filter").value; - const filteredHistory = caseHistoryData.filter( - (item) => filterValue === "all" || item.CaseType === filterValue, - ); - - if (filteredHistory.length === 0) { - responseArea.innerHTML = - '

No history items match the selected filter.

'; - return; - } - - const table = createTable([ - "Routing Date", - "Performed By", - "Workflow Step", - ]), - tbody = document.createElement("tbody"); - - // Group items by CaseType - const groupedHistory = {}; - filteredHistory.forEach((item) => { - const caseType = item.CaseType || "Unknown"; - if (!groupedHistory[caseType]) { - groupedHistory[caseType] = []; - } - groupedHistory[caseType].push(item); - }); - - // Create table rows with grouping - Object.keys(groupedHistory).forEach((caseType) => { - const groupItems = groupedHistory[caseType]; - - // Create group header row - const sanitizedCaseType = caseType.replace(/[^a-zA-Z0-9]/g, "_"); - tbody.innerHTML += `${caseType} (${groupItems.length} items)`; - - // Add current role type at the top of each group - // Find the most recent item (with latest RoutingDate) - const mostRecentItem = groupItems.reduce((latest, current) => { - const latestDate = new Date(latest.RoutingDate); - const currentDate = new Date(current.RoutingDate); - return currentDate > latestDate ? current : latest; - }); - const currentCaseRoleTypeCode = mostRecentItem.ToWorkflowStepIdentifier - ? caseSubProcessData[mostRecentItem.ToWorkflowStepIdentifier] - : null; - - // Always add the current role row, even if CaseRoleTypeCode is not available - const currentRoleRow = document.createElement("tr"); - currentRoleRow.className = `group-member history-group-${sanitizedCaseType}`; - - // Empty Routing Date - const emptyDateCell = document.createElement("td"); - emptyDateCell.textContent = ""; - currentRoleRow.appendChild(emptyDateCell); - - // CaseRoleTypeCode in Performed By column - const roleCell = document.createElement("td"); - if (currentCaseRoleTypeCode) { - roleCell.innerHTML = `Current Role:
${currentCaseRoleTypeCode}`; - } else { - roleCell.innerHTML = `Current Role:
Not available`; - } - currentRoleRow.appendChild(roleCell); - - // Last To Step in Workflow Step column - const workflowStepCell = document.createElement("td"); - workflowStepCell.textContent = mostRecentItem.ToWorkflowStep || "-"; - currentRoleRow.appendChild(workflowStepCell); - - tbody.appendChild(currentRoleRow); - - // Create member rows for this group - groupItems.forEach((item) => { - const row = document.createElement("tr"); - row.className = `group-member history-group-${sanitizedCaseType}`; - - // Format RoutingDate - const routingDate = new Date(item.RoutingDate); - const dateCell = document.createElement("td"); - dateCell.textContent = routingDate.toLocaleString("id-ID"); - row.appendChild(dateCell); - - // PerformedByUser - const performedByCell = document.createElement("td"); - performedByCell.textContent = item.PerformedByUser || "-"; - row.appendChild(performedByCell); - - // FromWorkflowStep (renamed to Workflow Step) - const workflowStepCell = document.createElement("td"); - workflowStepCell.textContent = item.FromWorkflowStep || "-"; - row.appendChild(workflowStepCell); - - tbody.appendChild(row); - }); - }); - - responseArea.innerHTML = ""; - table.appendChild(tbody); - responseArea.appendChild(table); - tbody.addEventListener("click", handleGroupToggle); - } - 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 = - '

No refund review data matches the filter.

'); - 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 = ` + ${e.FullName||"N/A"} + ${e.Nip||"N/A"} + ${e.Jabatan||"N/A"} + ${e.OfficeName||"N/A"} + ${e.CaseRoleType||"N/A"} + `}),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='

No refund review data matches the filter.

');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=` -
${e.Name || "Unknown Name"}
-
${e.Tin || "Unknown TIN"}
-
`)); - } - 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 - ? '' - : ''; - ((o.insertCell().textContent = e.DocumentNumber || "N/A"), - (o.insertCell().textContent = a)); - const r = o.insertCell(); - r.innerHTML = `
Rp${(e.SellingPrice || 0).toLocaleString("id-ID")}
`; - const d = o.insertCell(); - d.innerHTML = `
Rp${(e.VatPaid || 0).toLocaleString("id-ID")}
`; - const n = o.insertCell(); - ((n.innerHTML = `
Rp${(e.StlgPaid || 0).toLocaleString("id-ID")}
`), - (o.insertCell().textContent = e.TransactionCode || "N/A")); - const c = o.insertCell(); - ((c.className = "reported-cell"), (c.innerHTML = s)); - }), - tbody.addEventListener("click", handleGroupToggle)); - } +
${e.Name||"Unknown Name"}
+
${e.Tin||"Unknown TIN"}
+ `}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?'':'';o.insertCell().textContent=e.DocumentNumber||"N/A",o.insertCell().textContent=a;const r=o.insertCell();r.innerHTML=`
Rp${(e.SellingPrice||0).toLocaleString("id-ID")}
`;const d=o.insertCell();d.innerHTML=`
Rp${(e.VatPaid||0).toLocaleString("id-ID")}
`;const n=o.insertCell();n.innerHTML=`
Rp${(e.StlgPaid||0).toLocaleString("id-ID")}
`,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 = - '

Loading documents...

'; - 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 = - '

Loading users...

'; - 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 fetchCaseHistory(caseId) { - const responseArea = document.querySelector( - "#tab-history .results-container", - ); - responseArea.innerHTML = - '

Loading history...

'; - try { - if (!caseId) throw new Error("No Case ID provided."); - const apiUrl = - "https://coretax.intranet.pajak.go.id/casemanagement/api/caseroutinghistory/list", - payload = { - AggregateIdentifier: caseId, - First: 0, - Rows: 1000, - SortField: "RoutingDate", - 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(); - caseHistoryData = data?.Payload?.Data || []; - loadedHistoryForCaseId = caseId; + // --- 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='

Loading documents...

';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='

Loading users...

';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} + + // NEW: This function now includes the fallback logic. + async function fetchC02FormDetail(caseId, subProcessId) { + const payload = { caseAggregateIdentifier: caseId, CaseSubProcessIdentifier: subProcessId }; + const fetchOptions = { method: "POST", headers: getHeaders(caseId), body: JSON.stringify(payload) }; - // Fetch CaseRoleTypeCode for each ToWorkflowStepIdentifier - await fetchCaseSubProcessData(caseId); - - populateFilter("history-type-filter", caseHistoryData, "CaseType"); - renderCaseHistoryTable(); - } catch (error) { - handleError(error, responseArea); - } - } - async function fetchCaseSubProcessData(caseId) { - try { - const apiUrl = - "https://coretax.intranet.pajak.go.id/casemanagement/api/casesubprocess/list", - payload = { - First: 0, - Rows: 10000, - Filters: [], - AggregateIdentifier: caseId, - }, - 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( - `Case SubProcess API Error: ${errorData.Message || response.statusText}`, - ); - } - - const data = await response.json(); - const subProcessData = data?.Payload?.Data || []; - - // Create a lookup map for WorkflowStepIdentifier to CaseRoleTypeCode - caseSubProcessData = {}; - subProcessData.forEach((item) => { - if (item.WorkflowStepIdentifier && item.CaseRoleTypeCode) { - caseSubProcessData[item.WorkflowStepIdentifier] = - item.CaseRoleTypeCode; - } - }); - } catch (error) { - console.error("Error fetching case subprocess data:", error); - caseSubProcessData = {}; - } - } - - 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) + // 1. Primary API Attempt + const primaryApiUrl = "https://coretax.intranet.pajak.go.id/casecomponentspayment/api/c02form014/c02form009detail/current"; 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 response = await fetch(primaryApiUrl, fetchOptions); + if (response.ok) { + const data = await response.json(); + const reference = data?.Payload?.[0]?.Reference; + if (reference) return reference; // Success on primary API + } + } catch (error) { + console.warn("Primary API for C02Form failed, trying fallback.", error); } - 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)); + + // 2. Fallback API Attempt + const responseArea = document.querySelector('#tab-refund .results-container'); + responseArea.innerHTML = `

Step 2/3: Fetching reference number (trying fallback API)...

`; + const fallbackApiUrl = "https://coretax.intranet.pajak.go.id/casecomponentspayment/api/c02form014/view"; + const fallbackResponse = await fetch(fallbackApiUrl, fetchOptions); + if (!fallbackResponse.ok) { + const errorData = await fallbackResponse.json(); + throw new Error(`C02Form Detail API Error (Fallback): ${errorData.Message || fallbackResponse.statusText}`); + } + const fallbackData = await fallbackResponse.json(); + const fallbackReference = fallbackData?.Payload?.Details?.[0]?.Reference; + if (fallbackReference) return fallbackReference; // Success on fallback API + + // 3. If both fail + throw new Error("Could not find a 'Reference' number in either C02Form Detail response."); } - } - 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 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)}} - // NEW: This function now includes the fallback logic. - async function fetchC02FormDetail(caseId, subProcessId) { - const payload = { - caseAggregateIdentifier: caseId, - CaseSubProcessIdentifier: subProcessId, - }; - const fetchOptions = { - method: "POST", - headers: getHeaders(caseId), - body: JSON.stringify(payload), - }; - - // 1. Primary API Attempt - const primaryApiUrl = - "https://coretax.intranet.pajak.go.id/casecomponentspayment/api/c02form014/c02form009detail/current"; - try { - const response = await fetch(primaryApiUrl, fetchOptions); - if (response.ok) { - const data = await response.json(); - const reference = data?.Payload?.[0]?.Reference; - if (reference) return reference; // Success on primary API - } - } catch (error) { - console.warn("Primary API for C02Form failed, trying fallback.", error); - } - - // 2. Fallback API Attempt - const responseArea = document.querySelector( - "#tab-refund .results-container", - ); - responseArea.innerHTML = `

Step 2/3: Fetching reference number (trying fallback API)...

`; - const fallbackApiUrl = - "https://coretax.intranet.pajak.go.id/casecomponentspayment/api/c02form014/view"; - const fallbackResponse = await fetch(fallbackApiUrl, fetchOptions); - if (!fallbackResponse.ok) { - const errorData = await fallbackResponse.json(); - throw new Error( - `C02Form Detail API Error (Fallback): ${errorData.Message || fallbackResponse.statusText}`, - ); - } - const fallbackData = await fallbackResponse.json(); - const fallbackReference = fallbackData?.Payload?.Details?.[0]?.Reference; - if (fallbackReference) return fallbackReference; // Success on fallback API - - // 3. If both fail - throw new Error( - "Could not find a 'Reference' number in either C02Form Detail response.", - ); - } - - 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 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 = ''), - values.sort().forEach((e) => { - select.innerHTML += ``; - })); - } - function populateBooleanFilter(selectId) { - const e = document.getElementById(selectId); - e && - (e.innerHTML = - ''); - } - 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"); - thead.innerHTML = `${headers.map((e) => `${e}`).join("")}`; - table.appendChild(thead); - return table; - } - function handleError(error, area) { - console.error("Userscript Error:", error); - let errorHtml; - (error.message.includes("Authorization Token not found") - ? (errorHtml = ` + // --- 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 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='',values.sort().forEach(e=>{select.innerHTML+=``})} + function populateBooleanFilter(selectId){const e=document.getElementById(selectId);e&&(e.innerHTML='')} + 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=`${headers.map(e=>`${e}`).join("")}`,table.appendChild(thead),table} + function handleError(error,area){console.error("Userscript Error:",error);let errorHtml;error.message.includes("Authorization Token not found")?errorHtml=`
Session Expired or Token Not Found

Please refresh the page to log in again.

-
`) - : (errorHtml = `

An error occurred:
${error.message}

`), - (area.innerHTML = errorHtml)); - const refreshBtn = area.querySelector("#auth-refresh-btn"); - refreshBtn && - refreshBtn.addEventListener("click", () => window.location.reload()); - } - function updateHeader(caseObject) { - const titleEl = document.getElementById("ct-header-title"); - const subtitleEl = document.getElementById("ct-header-subtitle"); - const actionsContainer = document.getElementById("ct-header-actions"); - actionsContainer.innerHTML = ""; // Clear previous buttons +
`:errorHtml=`

An error occurred:
${error.message}

`,area.innerHTML=errorHtml;const refreshBtn=area.querySelector("#auth-refresh-btn");refreshBtn&&refreshBtn.addEventListener("click",()=>window.location.reload())} + function updateHeader(caseObject) { + const titleEl = document.getElementById('ct-header-title'); + const subtitleEl = document.getElementById('ct-header-subtitle'); + const actionsContainer = document.getElementById('ct-header-actions'); + actionsContainer.innerHTML = ''; // Clear previous buttons - if (caseObject) { - titleEl.textContent = caseObject.MainTaxpayerName || "N/A"; - subtitleEl.textContent = caseObject.CaseNumber || "N/A"; - const caseId = caseObject.AggregateIdentifier; - const hasValidId = - caseId && typeof caseId === "string" && caseId.trim() !== ""; + if (caseObject) { + titleEl.textContent = caseObject.MainTaxpayerName || 'N/A'; + subtitleEl.textContent = caseObject.CaseNumber || 'N/A'; + const caseId = caseObject.AggregateIdentifier; + const hasValidId = caseId && typeof caseId === 'string' && caseId.trim() !== ''; - const openBtn = document.createElement("a"); - openBtn.href = `https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${caseId}`; - openBtn.className = "action-btn open-case"; - openBtn.textContent = "Open"; - if (!hasValidId) openBtn.disabled = true; + const openBtn = document.createElement('a'); + openBtn.href = `https://coretax.intranet.pajak.go.id/case-management/id-ID/case-overview/${caseId}`; + openBtn.className = 'action-btn open-case'; + openBtn.textContent = 'Open'; + if (!hasValidId) openBtn.disabled = true; - const docsBtn = document.createElement("button"); - docsBtn.className = "action-btn view-docs"; - docsBtn.textContent = "View Docs"; - if (!hasValidId) docsBtn.disabled = true; - docsBtn.addEventListener("click", () => switchTab("tab-docs")); + const docsBtn = document.createElement('button'); + docsBtn.className = 'action-btn view-docs'; + docsBtn.textContent = 'View Docs'; + if (!hasValidId) docsBtn.disabled = true; + docsBtn.addEventListener('click', () => switchTab('tab-docs')); - const usersBtn = document.createElement("button"); - usersBtn.className = "action-btn view-users"; - usersBtn.textContent = "View Users"; - if (!hasValidId) usersBtn.disabled = true; - usersBtn.addEventListener("click", () => switchTab("tab-users")); + const usersBtn = document.createElement('button'); + usersBtn.className = 'action-btn view-users'; + usersBtn.textContent = 'View Users'; + if (!hasValidId) usersBtn.disabled = true; + usersBtn.addEventListener('click', () => switchTab('tab-users')); - actionsContainer.append(openBtn, docsBtn, usersBtn); + actionsContainer.append(openBtn, docsBtn, usersBtn); - if ( - caseObject.CaseTypeName && - caseObject.CaseTypeName.startsWith(REFUND_CASE_PREFIX) - ) { - const refundBtn = document.createElement("button"); - refundBtn.className = "action-btn review-refund-case"; - refundBtn.textContent = "Refund Review"; - actionsContainer.appendChild(refundBtn); - refundBtn.addEventListener("click", () => - startRefundReviewProcess(caseId, refundBtn), - ); - } - } else { - titleEl.textContent = "No Case Selected"; - subtitleEl.textContent = 'Please select a case from the "My Cases" tab'; - } - } - - // MODIFIED: This function has updated user-facing messages. - 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 = `

Step 1/3: Fetching Sub Process ID...

`; - if (!caseId) throw new Error("A case must be selected."); - const subProcessId = await fetchSubProcessId(caseId); - responseArea.innerHTML = `

Step 2/3: Fetching reference number (trying primary API)...

`; - const referenceNumber = await fetchC02FormDetail(caseId, subProcessId); - responseArea.innerHTML = `

Step 3/3: Fetching refund details...

`; - await fetchRefundReview(caseId, referenceNumber); - } catch (error) { - handleError(error, responseArea); - } finally { - button.textContent = originalText; - button.disabled = false; - } - } - - function handleCaseSelection(event) { - const selectedRow = event.target.closest("tr"); - if (!selectedRow || selectedRow.classList.contains("group-header")) return; - const caseId = selectedRow.dataset.id; - if (!caseId || caseId === selectedCaseId) return; - - const selectedCase = allMyCases.find( - (e) => e.AggregateIdentifier === caseId, - ); - updateHeader(selectedCase); - selectedCaseId = caseId; - loadedDocsForCaseId = null; - loadedUsersForCaseId = null; - refundReviewData = []; - filteredRefundData = []; - document.querySelector("#tab-refund .results-container").innerHTML = - `

Please use the 'Review Refund' action on a relevant case.

`; - 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"); - if (actionButton) { - if (actionButton.matches(".view-docs")) { - switchTab("tab-docs"); - } else if (actionButton.matches(".view-users")) { - switchTab("tab-users"); - } else if (actionButton.matches(".review-refund-case")) { - startRefundReviewProcess(caseId, actionButton); - } - } - } - function handleDocumentAction(event) { - 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 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(`tr.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), - "tab-history" === tabId && - selectedCaseId && - selectedCaseId !== loadedHistoryForCaseId && - fetchCaseHistory(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"; + if (caseObject.CaseTypeName && caseObject.CaseTypeName.startsWith(REFUND_CASE_PREFIX)) { + const refundBtn = document.createElement('button'); + refundBtn.className = 'action-btn review-refund-case'; + refundBtn.textContent = 'Refund Review'; + actionsContainer.appendChild(refundBtn); + refundBtn.addEventListener('click', () => startRefundReviewProcess(caseId, refundBtn)); + } } else { - row[map.excelHeader] = - item[map.apiField] === null ? "" : item[map.apiField]; + titleEl.textContent = 'No Case Selected'; + subtitleEl.textContent = 'Please select a case from the "My Cases" tab'; } - }); - 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 --- - function main() { - addStyles(); - createSidebar(); - const toggleBtn = document.getElementById("ct-sidebar-toggle"); - if (toggleBtn) { - toggleBtn.addEventListener("click", () => { - const sidebar = document.getElementById("ct-sidebar"); - const overlay = document.querySelector(".ct-overlay"); - if (sidebar) { - sidebar.classList.toggle("open"); - if (overlay) - overlay.style.display = sidebar.classList.contains("open") - ? "block" - : "none"; + } + + // MODIFIED: This function has updated user-facing messages. + 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 = `

Step 1/3: Fetching Sub Process ID...

`; + if (!caseId) throw new Error("A case must be selected."); + const subProcessId = await fetchSubProcessId(caseId); + responseArea.innerHTML = `

Step 2/3: Fetching reference number (trying primary API)...

`; + const referenceNumber = await fetchC02FormDetail(caseId, subProcessId); + responseArea.innerHTML = `

Step 3/3: Fetching refund details...

`; + await fetchRefundReview(caseId, referenceNumber); + } catch(error) { + handleError(error, responseArea); + } finally { + button.textContent = originalText; + button.disabled = false; } - }); } - // Add click outside handler - const overlay = document.querySelector(".ct-overlay"); - if (overlay) { - overlay.addEventListener("click", () => { - const sidebar = document.getElementById("ct-sidebar"); - if (sidebar && sidebar.classList.contains("open")) { - sidebar.classList.remove("open"); - overlay.style.display = "none"; + function handleCaseSelection(event) { + const selectedRow = event.target.closest("tr"); + if (!selectedRow || selectedRow.classList.contains("group-header")) return; + const caseId = selectedRow.dataset.id; + if (!caseId || caseId === selectedCaseId) return; + + const selectedCase = allMyCases.find(e => e.AggregateIdentifier === caseId); + updateHeader(selectedCase); + selectedCaseId = caseId; + loadedDocsForCaseId = null; + loadedUsersForCaseId = null; + refundReviewData = []; + filteredRefundData = []; + document.querySelector('#tab-refund .results-container').innerHTML = `

Please use the 'Review Refund' action on a relevant case.

`; + 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"); + if (actionButton) { + if (actionButton.matches(".view-docs")) { switchTab("tab-docs"); } + else if (actionButton.matches(".view-users")) { switchTab("tab-users"); } + else if (actionButton.matches(".review-refund-case")) { startRefundReviewProcess(caseId, actionButton); } } - }); + } + function handleDocumentAction(event) { + 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 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); } - const tabBar = document.getElementById("ct-tab-bar"); - if (tabBar) { - tabBar.addEventListener("click", (e) => { - if (e.target.matches(".ct-tab-button")) switchTab(e.target.dataset.tab); - }); + 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'; } - const casesFilterEl = document.getElementById("cases-status-filter"); - if (casesFilterEl) - casesFilterEl.addEventListener("change", renderMyCasesTable); + // --- MAIN INITIALIZATION --- + function main() { + addStyles(); + createSidebar(); + document.getElementById('ct-sidebar-toggle').addEventListener('click', () => { + document.getElementById('ct-sidebar').classList.toggle('open'); + }); + document.getElementById('ct-tab-bar').addEventListener('click', (e) => { + if (e.target.matches('.ct-tab-button')) switchTab(e.target.dataset.tab); + }); + 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); - const docsFilterEl = document.getElementById("docs-status-filter"); - if (docsFilterEl) - docsFilterEl.addEventListener("change", renderCaseDocumentsTable); + 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)); - const usersFilterEl = document.getElementById("users-role-filter"); - if (usersFilterEl) - usersFilterEl.addEventListener("change", renderCaseUsersTable); + fetchMyCases(); + } - const historyFilterEl = document.getElementById("history-type-filter"); - if (historyFilterEl) - historyFilterEl.addEventListener("change", renderCaseHistoryTable); - - const refundFilterEl = document.getElementById("refund-reported-filter"); - if (refundFilterEl) - refundFilterEl.addEventListener("change", renderRefundReviewTable); - - const refundDownloadBtn = document.getElementById("refund-download-btn"); - if (refundDownloadBtn) - refundDownloadBtn.addEventListener("click", downloadRefundExcel); - - const toggleCasesBtn = document.getElementById("toggle-cases-btn"); - if (toggleCasesBtn) - toggleCasesBtn.addEventListener("click", (e) => - toggleAllGroups("#tab-my-cases .results-container", e.target), - ); - - const toggleDocsBtn = document.getElementById("toggle-docs-btn"); - if (toggleDocsBtn) - toggleDocsBtn.addEventListener("click", (e) => - toggleAllGroups("#tab-docs .results-container", e.target), - ); - - const toggleRefundBtn = document.getElementById("toggle-refund-btn"); - if (toggleRefundBtn) - toggleRefundBtn.addEventListener("click", (e) => - toggleAllGroups("#tab-refund .results-container", e.target), - ); - - const toggleHistoryBtn = document.getElementById("toggle-history-btn"); - if (toggleHistoryBtn) - toggleHistoryBtn.addEventListener("click", (e) => - toggleAllGroups("#tab-history .results-container", e.target), - ); - - // initial load - fetchMyCases(); - } - - // Run main when DOM is ready (script runs at document-idle but ensure body exists) - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", main); - } else { main(); - } -})(); +})(); \ No newline at end of file