Implement C02 form API fallback

- Adds a fallback API call to `.../c02form014/view` when the primary endpoint `.../c02form009detail/current` returns no result.
- Handles the different response structures for the reference number:
  - Primary: `response.Payload[0].Reference`
  - Fallback: `response.Payload.Details[0].Reference`
- This increases the reliability of fetching the reference number required for the refund review process.
This commit is contained in:
2025-08-26 02:40:59 +00:00
parent 3c88246e13
commit 5c6e1d4273

View File

@@ -1,7 +1,7 @@
// ==UserScript==
// @name CoreTabs
// @namespace https://git.diasbaskara.id/diasbaskara/userscripts/
// @version 0.3.1
// @version 0.4
// @description Manage your cases easily.
// @author Dias Baskara
// @match https://coretax.intranet.pajak.go.id/*
@@ -141,7 +141,42 @@
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}
// 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 = `<p style="padding:15px;color:#666;">Step 2/3: Fetching reference number (trying fallback API)...</p>`;
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 ---
@@ -151,11 +186,11 @@
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=`
<div style="padding:15px; color: #d9534f; text-align: center;">
<b>Session Expired or Token Not Found</b>
<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())}
<div style="padding:15px; color: #d9534f; text-align: center;">
<b>Session Expired or Token Not Found</b>
<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 updateHeader(caseObject) {
const titleEl = document.getElementById('ct-header-title');
const subtitleEl = document.getElementById('ct-header-subtitle');
@@ -167,9 +202,7 @@
subtitleEl.textContent = caseObject.CaseNumber || 'N/A';
const caseId = caseObject.AggregateIdentifier;
const hasValidId = caseId && typeof caseId === 'string' && caseId.trim() !== '';
const disabledAttribute = hasValidId ? '' : 'disabled';
// Create buttons programmatically to attach listeners safely
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';
@@ -202,6 +235,8 @@
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 = '...';
@@ -212,7 +247,7 @@
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>`;
responseArea.innerHTML = `<p style="padding:15px;color:#666;">Step 2/3: Fetching reference number (trying primary API)...</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);
@@ -228,7 +263,7 @@
const selectedRow = event.target.closest("tr");
if (!selectedRow || selectedRow.classList.contains("group-header")) return;
const caseId = selectedRow.dataset.id;
if (!caseId || caseId === selectedCaseId) return; // Don't re-process if the same row is clicked
if (!caseId || caseId === selectedCaseId) return;
const selectedCase = allMyCases.find(e => e.AggregateIdentifier === caseId);
updateHeader(selectedCase);