Updated the process for retrieving records from CRMServiceRequests
This commit is contained in:
@@ -296,8 +296,8 @@ function createContentApplication(overrides = {}) {
|
||||
});
|
||||
const internalSrSession = createInternalSrSession({
|
||||
environment,
|
||||
query: async srId => {
|
||||
const result = await platformContentMessages.sendRuntimeMessage({ type: messageTypes.requestInternalServiceRequest, srId });
|
||||
query: async (srId, srNumber) => {
|
||||
const result = await platformContentMessages.sendRuntimeMessage({ type: messageTypes.requestInternalServiceRequest, srId, srNumber });
|
||||
debugContentLog.appendBackgroundRequestLog(result?.debugRequests || []);
|
||||
return result;
|
||||
}
|
||||
@@ -305,7 +305,7 @@ function createContentApplication(overrides = {}) {
|
||||
const crmSrPopover = createCrmSrPopover({
|
||||
openInternalSrForm: (parent, trigger) => internalSrForm.open(parent, trigger),
|
||||
formatDate: value => opportunitiesFormatters.formatOracleResponseDate(value),
|
||||
requestDetails: srId => internalSrSession.request(srId),
|
||||
requestDetails: (srId, srNumber) => internalSrSession.request(srId, srNumber),
|
||||
MODAL_ID: contracts.MODAL_ID,
|
||||
createCopyButton: (...args) => opportunitiesLinks.createCopyOpportunityButton(...args),
|
||||
createCloseIcon: (...args) => contentIcons.createCloseIcon(...args), environment
|
||||
@@ -364,7 +364,43 @@ function createContentApplication(overrides = {}) {
|
||||
requestCrmServiceRequest: (...args) => fusionCrmServiceRequests.requestCrmServiceRequest(...args),
|
||||
isCurrentPeriodRequest: (...args) => opportunitiesController.isCurrentPeriodRequest(...args), environment
|
||||
});
|
||||
const opportunitiesPrint = createOpportunitiesPrint({ environment, getSnapshot: () => {
|
||||
const state = opportunitiesTableState.opportunitiesTableState;
|
||||
const items = opportunitiesFilters.getSortedOpportunities();
|
||||
const filter = opportunitiesFilterState;
|
||||
const selectedLabels = (options, selected, allLabel) => {
|
||||
const matching = options.filter(option => selected.has(option.key));
|
||||
if (options.length && matching.length === options.length) return allLabel;
|
||||
return matching.map(option => option.label).join(", ") || "None";
|
||||
};
|
||||
const root = environment.document.getElementById(contracts.MODAL_ID);
|
||||
const filters = [
|
||||
["Reference", root?.querySelector(".opportunities-extension-form select")?.value || "-"],
|
||||
["Customer", selectedLabels(opportunitiesFormatters.getCustomerOptions(), filter.selectedCustomerKeys, "All customers")],
|
||||
["Owner", selectedLabels(opportunitiesFormatters.getOwnerOptions(), filter.selectedOwnerKeys, "All owners")],
|
||||
["Search", filter.tableSearch || "All"],
|
||||
["Status", [...filter.selectedStatuses].join(", ")],
|
||||
["Stage", [...filter.selectedStages].join(", ")],
|
||||
["Opportunity type", [...filter.selectedProductTypes].join(", ")]
|
||||
];
|
||||
for (const group of root?.querySelectorAll(".opportunities-extension-activity-filter-controls") || []) {
|
||||
filters.push([group.getAttribute("aria-label").replace(/^Filter by /, ""),
|
||||
[...group.querySelectorAll('[aria-pressed="true"]')].map(button => button.getAttribute("aria-label")).join("; ")]);
|
||||
}
|
||||
const totals = [{ label: "Total Opportunities", value: `${items.length}/${state.items.length}`, detail: "Filtered / total" }];
|
||||
for (const stage of contracts.STAGE_OPTIONS) {
|
||||
const stageItems = items.filter(item => item.ForecastGroup_c === stage);
|
||||
const amount = stageItems.reduce((total, item) => {
|
||||
const value = Number(item.PrimaryRevenue?.RevnAmount);
|
||||
return total + (Number.isFinite(value) ? value : 0);
|
||||
}, 0);
|
||||
totals.push({ label: stage, value: opportunitiesDashboard.createStageAmountFormatter(stageItems)(amount), detail: `${stageItems.length} opportunities` });
|
||||
}
|
||||
return { status: state.status, filters, totals, columns,
|
||||
rows: items.map(item => columns.map(column => opportunitiesFormatters.displayOpportunityValue(column, item))) };
|
||||
} });
|
||||
const opportunityCalendar = createOpportunityCalendar({
|
||||
createPrintButton: () => opportunitiesPrint.createButton(),
|
||||
productTypeSelection: opportunitiesFilterState.selectedProductTypes,
|
||||
onProductTypeChange: () => opportunitiesTable.renderOpportunitiesTable(),
|
||||
createViewIcon: view => contentIcons.createViewIcon(view),
|
||||
|
||||
@@ -78,7 +78,18 @@
|
||||
.opportunities-extension-modal table[data-table-layout="standard"] th[data-align="money"] .opportunities-extension-ratecard-sort-icon { order: -1; margin-left: 0; margin-right: auto; }
|
||||
.opportunities-extension-modal table[data-table-layout="standard"] td[data-align="money"] > span[class*="skeleton"] { margin-left: auto; }
|
||||
/* Preserve room for Overview badges and the compact daily entry controls. */
|
||||
.opportunities-extension-modal table.opportunities-extension-table > tbody > tr > td:is([data-column="name"], [data-column="customer"]) { padding-bottom: 38px; vertical-align: top; }
|
||||
.opportunities-extension-modal table.opportunities-extension-table > tbody > tr > td:is([data-column="name"], [data-column="customer"]) { padding-bottom: 10px; vertical-align: top; }
|
||||
.opportunities-extension-modal table.opportunities-extension-table > tbody > tr > td[data-column] > .opportunities-extension-cell-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
min-height: 0;
|
||||
}
|
||||
.opportunities-extension-modal table.opportunities-extension-table > tbody > tr > td[data-column] > .opportunities-extension-cell-content > .opportunities-extension-cell-actions {
|
||||
position: static;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.opportunities-extension-modal .oe-time-grid table[data-table-layout="standard"] > :is(thead, tbody) > tr > :is(th, td) { padding-left: 5px; padding-right: 5px; }
|
||||
.opportunities-extension-modal .oe-time-grid table[data-table-layout="standard"] { table-layout: fixed; }
|
||||
.opportunities-extension-modal table[data-table-layout="standard"] > tbody > tr > th[scope="row"] { padding: 10px 12px; text-align: left; background: var(--oe-table-heading); border-bottom: 1px solid var(--oe-table-border); }
|
||||
|
||||
@@ -27,7 +27,7 @@ function createDebugContentView({
|
||||
if (entry.name === "requestOpptyProducts") return "products";
|
||||
if (["requestWlActions", "requestActionTeamMembers", "requestActionFindLookup", "requestMgmtAction"].includes(entry.name)) return "actions";
|
||||
if (entry.name === "requestInstallBase") return "installBase";
|
||||
if (["requestSRInfo", "requestLookupTypePlatform", "requestInternalSrServices", "requestPostInternalServiceRequest"].includes(entry.name)) return "serviceRequests";
|
||||
if (["requestSRSearchResources", "requestSRInfo", "requestLookupTypePlatform", "requestInternalSrServices", "requestPostInternalServiceRequest"].includes(entry.name)) return "serviceRequests";
|
||||
if (["requestCustomerServiceRequest", "resquestCrmServiceRequest", "requestInternalServiceRequest", "requestSrLookupPrimaryPillar", "requestSrLookupLanguage", "requestSrLookupTechPlatform", "requestSrLookupOpportunityBusiness", "requestResourceUser", "requestSrCatalog", "requestSrTokenCatalog", "requestPostServiceRequest"].includes(entry.name)) return "serviceRequests";
|
||||
if (/^request(?:AccountSummary|AccountPlans|ConsumptionService|ConsumptionPlanDetail|RatecardByPlan)/.test(entry.name)) return "consumption";
|
||||
return "other";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
function createOpportunityCalendar({ environment, loadLibrary, getState, getItems, getVersion, getRange,
|
||||
formatRevenue, formatDate, getOwnerName, createDetailLink, createCopyButton, createCloseIcon,
|
||||
createStageBadge, createStatusBadge, opportunityUrl, modalId, closeSrPopover, createViewIcon,
|
||||
productTypeSelection, onProductTypeChange }) {
|
||||
productTypeSelection, onProductTypeChange, createPrintButton }) {
|
||||
const { document } = environment;
|
||||
let elements = null;
|
||||
let calendar = null;
|
||||
@@ -223,6 +223,7 @@ function createOpportunityCalendar({ environment, loadLibrary, getState, getItem
|
||||
});
|
||||
filters.append(button);
|
||||
}
|
||||
if (createPrintButton) filters.append(createPrintButton());
|
||||
viewbar.append(filters);
|
||||
}
|
||||
workspace.append(viewbar, tableSurface, surface); body.append(workspace);
|
||||
|
||||
@@ -124,9 +124,13 @@ function createCrmSrPopover({ MODAL_ID, createCopyButton, createCloseIcon, reque
|
||||
children.setAttribute("role", "status");
|
||||
children.textContent = "Loading SR details...";
|
||||
section.append(children);
|
||||
detailLoads.push(async () => {
|
||||
const loadDetails = async () => {
|
||||
if (active !== entry || entry.panel !== panel) return;
|
||||
children.textContent = "Loading SR details...";
|
||||
children.setAttribute("aria-busy", "true");
|
||||
children.setAttribute("role", "status");
|
||||
try {
|
||||
const records = await requestDetails(item.SrId);
|
||||
const records = await requestDetails(item.SrId, item.SrNumber);
|
||||
if (active !== entry || entry.panel !== panel) return;
|
||||
children.textContent = "";
|
||||
if (!records.length) children.textContent = "No further details.";
|
||||
@@ -157,13 +161,20 @@ function createCrmSrPopover({ MODAL_ID, createCopyButton, createCloseIcon, reque
|
||||
if (active !== entry || entry.panel !== panel) return;
|
||||
children.setAttribute("role", "alert");
|
||||
children.textContent = error.message || "Unable to load SR details.";
|
||||
const retry = document.createElement("button");
|
||||
retry.type = "button";
|
||||
retry.textContent = "Retry";
|
||||
retry.setAttribute("aria-label", "Retry loading Internal Service Requests");
|
||||
retry.addEventListener("click", () => { retry.disabled = true; loadDetails(); });
|
||||
children.append(retry);
|
||||
} finally {
|
||||
if (active === entry && entry.panel === panel) {
|
||||
children.setAttribute("aria-busy", "false");
|
||||
entry.position();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
detailLoads.push(loadDetails);
|
||||
panel.append(section);
|
||||
});
|
||||
panel.addEventListener("pointerenter", () => clearTimeout(entry.timer));
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
function createInternalSrSession({ query, environment, sessionUrl, sessionLabel = "Internal SR" }) {
|
||||
const { document, setTimeout } = environment;
|
||||
const pending = new Map();
|
||||
let recovery = null;
|
||||
const wait = duration => new Promise(resolve => setTimeout(resolve, duration));
|
||||
const now = () => environment.performance?.now() ?? Date.now();
|
||||
function isUnauthorized(result) {
|
||||
const status = result?.httpStatus ?? result?.debugRequests?.at(-1)?.httpStatus;
|
||||
if (status != null) return Number(status) === 401;
|
||||
// Older background workers may return only the request error text.
|
||||
return /failed with status 401\.$/.test(result?.error || "");
|
||||
return status != null ? Number(status) === 401 : /failed with status 401\.$/.test(result?.error || "");
|
||||
}
|
||||
async function warmSession(id) {
|
||||
async function warmSession(id, srNumber) {
|
||||
const document = environment?.document;
|
||||
if (!document?.body) throw new Error(`${sessionLabel} session cannot be initialized on this page.`);
|
||||
const frame = document.createElement("iframe");
|
||||
// Restore the original session initialization route without opening a tab.
|
||||
frame.src = sessionUrl || "https://eeho.fa.us2.oraclecloud.com/fscmUI/redwood/service/ec/container/sr/edit?srNumber=SR0001421040&srId=302963804324543&view=showisr";
|
||||
frame.title = `${sessionLabel} session initialization`;
|
||||
frame.tabIndex = -1;
|
||||
@@ -18,43 +17,46 @@ function createInternalSrSession({ query, environment, sessionUrl, sessionLabel
|
||||
frame.setAttribute("sandbox", "allow-scripts allow-same-origin allow-forms");
|
||||
frame.style.cssText = "position:fixed;width:1px;height:1px;left:-10000px;top:0;border:0;opacity:0;pointer-events:none;";
|
||||
let blocked = false;
|
||||
let loaded = false;
|
||||
frame.addEventListener("error", () => { blocked = true; });
|
||||
frame.addEventListener("load", () => { loaded = true; });
|
||||
const failed = () => { blocked = true; };
|
||||
frame.addEventListener("error", failed);
|
||||
try {
|
||||
if (!document.body) throw new Error(`The current page cannot initialize the ${sessionLabel} session.`);
|
||||
document.body.append(frame);
|
||||
const deadline = now() + 60000;
|
||||
for (let attempt = 0; now() < deadline; attempt += 1) {
|
||||
// Check immediately, then briefly poll faster before backing off.
|
||||
if (attempt > 0) await wait(Math.min(attempt <= 4 ? 250 : 1000, deadline - now()));
|
||||
if (now() >= deadline) break;
|
||||
if (blocked) throw new Error(`The ${sessionLabel} session iframe could not be loaded. Sign in manually and try again.`);
|
||||
const result = await query(id);
|
||||
if (result?.ok) return { id, result };
|
||||
// Keep the iframe alive while its application completes authentication.
|
||||
// A load event is not proof of a ready session. Limit and space API checks.
|
||||
for (const delay of [2000, 4000, 8000, 12000, 16000]) {
|
||||
await new Promise(resolve => environment.setTimeout(resolve, delay));
|
||||
if (blocked) throw new Error(`${sessionLabel} session iframe could not be loaded. Retry after signing in.`);
|
||||
const result = await query(id, srNumber);
|
||||
if (result?.ok) return { id: String(id), result };
|
||||
if (!isUnauthorized(result)) throw new Error(result?.error || `Unable to load ${sessionLabel} data.`);
|
||||
}
|
||||
throw new Error(`${sessionLabel} session timed out after 60 seconds: the API still returned HTTP 401. ${loaded ? "The iframe emitted a load event, but authentication was not confirmed." : "The iframe did not emit a load event."} Sign in manually and try again.`);
|
||||
throw new Error(`${sessionLabel} session is still unavailable. Retry loading the details after signing in.`);
|
||||
} finally {
|
||||
// Stop remaining page resources without stopping the caller's page.
|
||||
try { frame.contentWindow?.stop(); } catch { /* Redirects can make the frame cross-origin. */ }
|
||||
frame.src = "about:blank";
|
||||
frame.removeEventListener("error", failed);
|
||||
try { frame.contentWindow?.stop(); } catch { /* SSO may redirect cross-origin. */ }
|
||||
frame.remove();
|
||||
}
|
||||
}
|
||||
async function request(id) {
|
||||
let result = await query(id);
|
||||
for (let attempt = 0; !result?.ok && isUnauthorized(result) && attempt < 2; attempt += 1) {
|
||||
if (!recovery) {
|
||||
recovery = warmSession(id);
|
||||
const pending = recovery;
|
||||
pending.finally(() => { if (recovery === pending) recovery = null; }).catch(() => {});
|
||||
}
|
||||
const recovered = await recovery;
|
||||
result = recovered.id === id ? recovered.result : await query(id);
|
||||
function request(id, srNumber) {
|
||||
const key = String(id);
|
||||
if (!pending.has(key)) {
|
||||
const operation = (async () => {
|
||||
let result = await query(id, srNumber);
|
||||
if (!result?.ok && isUnauthorized(result)) {
|
||||
if (!recovery) {
|
||||
const current = warmSession(id, srNumber);
|
||||
recovery = current;
|
||||
current.finally(() => { if (recovery === current) recovery = null; }).catch(() => {});
|
||||
}
|
||||
const recovered = await recovery;
|
||||
result = recovered.id === key ? recovered.result : await query(id, srNumber);
|
||||
}
|
||||
if (!result?.ok) throw new Error(result?.error || `Unable to load ${sessionLabel} data.`);
|
||||
return result.items;
|
||||
})().finally(() => pending.delete(key));
|
||||
pending.set(key, operation);
|
||||
}
|
||||
if (!result?.ok) throw new Error(result?.error || `Unable to load ${sessionLabel} data.`);
|
||||
return result.items;
|
||||
return pending.get(key);
|
||||
}
|
||||
return { request };
|
||||
}
|
||||
|
||||
100
src/features/opportunities/print.js
Normal file
100
src/features/opportunities/print.js
Normal file
@@ -0,0 +1,100 @@
|
||||
function createOpportunitiesPrint({ environment, getSnapshot }) {
|
||||
function createButton() {
|
||||
const doc = environment.document;
|
||||
const button = doc.createElement("button");
|
||||
button.type = "button";
|
||||
button.className = "opportunities-extension-icon-button";
|
||||
button.title = "Print opportunities";
|
||||
button.setAttribute("aria-label", "Print opportunities");
|
||||
const svg = doc.createElementNS("http://www.w3.org/2000/svg", "svg");
|
||||
svg.setAttribute("viewBox", "0 0 24 24");
|
||||
svg.setAttribute("fill", "none");
|
||||
svg.setAttribute("stroke", "currentColor");
|
||||
svg.setAttribute("stroke-width", "1.6");
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
const path = doc.createElementNS("http://www.w3.org/2000/svg", "path");
|
||||
path.setAttribute("d", "M6 9V3h12v6M6 17H3V9h18v8h-3M6 14h12v7H6zM17 11h1");
|
||||
svg.append(path); button.append(svg);
|
||||
button.addEventListener("click", print);
|
||||
return button;
|
||||
}
|
||||
function print() {
|
||||
const snapshot = getSnapshot();
|
||||
if (!["ready", "empty"].includes(snapshot.status)) {
|
||||
environment.window.alert("Wait for opportunities to finish loading before printing.");
|
||||
return;
|
||||
}
|
||||
const popup = environment.window.open("", "_blank", "width=1200,height=850");
|
||||
if (!popup) { environment.window.alert("Allow pop-ups to print opportunities."); return; }
|
||||
popup.opener = null;
|
||||
const doc = popup.document;
|
||||
doc.title = "Opportunities Extension — Report";
|
||||
doc.documentElement.lang = "en";
|
||||
const node = (tag, text, parent) => {
|
||||
const el = doc.createElement(tag);
|
||||
if (text != null) el.textContent = String(text);
|
||||
if (parent) parent.append(el);
|
||||
return el;
|
||||
};
|
||||
node("style", `
|
||||
@page { size: A4 landscape; margin: 15mm; }
|
||||
* { box-sizing: border-box; }
|
||||
body { margin: 32px; font: 11px/1.4 Arial, sans-serif; color: #222; background: white; }
|
||||
h1 { margin: 0 0 4px; font-size: 21px; } h2 { font-size: 13px; margin: 16px 0 6px; }
|
||||
.meta { color: #555; margin: 0 0 12px; }
|
||||
.filters { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px 16px; border: 1px solid #ccc; padding: 10px; }
|
||||
.filters div { overflow-wrap: anywhere; } .filters strong { margin-right: 5px; }
|
||||
.totals { display: grid; grid-template-columns: repeat(6, 1fr); gap: 8px; margin: 12px 0; }
|
||||
.totals section { border: 1px solid #bbb; border-top: 3px solid #426a75; padding: 8px; break-inside: avoid; }
|
||||
.totals strong { display: block; font-size: 16px; } .totals small { display: block; }
|
||||
table { width: 100%; table-layout: fixed; border-collapse: collapse; font-size: 9px; }
|
||||
th, td { padding: 6px 4px; border-bottom: 1px solid #ccc; text-align: left; vertical-align: top; overflow-wrap: anywhere; }
|
||||
th { background: #eee; font-weight: bold; } tbody tr:nth-child(even) { background: #f7f7f7; }
|
||||
thead { display: table-header-group; } tr { break-inside: avoid; }
|
||||
th:nth-child(1), th:nth-child(3) { width: 22%; }
|
||||
th:nth-child(2) { width: 7%; } th:nth-child(4) { width: 8%; }
|
||||
th:nth-child(5) { width: 9%; } th:nth-child(6) { width: 7%; }
|
||||
th:nth-child(7), th:nth-child(8) { width: 5%; } th:nth-child(9) { width: 7%; } th:nth-child(10) { width: 8%; }
|
||||
.money { text-align: right; font-variant-numeric: tabular-nums; }
|
||||
.toolbar { display: flex; gap: 8px; margin-bottom: 16px; }
|
||||
button { padding: 8px 14px; cursor: pointer; }
|
||||
@media print { body { margin: 0; } .toolbar { display: none; } th { print-color-adjust: exact; } }
|
||||
`, doc.head);
|
||||
const toolbar = node("div", null, doc.body); toolbar.className = "toolbar";
|
||||
node("button", "Print / Save PDF", toolbar).addEventListener("click", () => popup.print());
|
||||
node("button", "Close", toolbar).addEventListener("click", () => popup.close());
|
||||
node("h1", "Opportunities Extension", doc.body);
|
||||
node("p", `Generated ${new Date().toLocaleString("en-US")} • ${snapshot.rows.length} opportunities`, doc.body).className = "meta";
|
||||
const filters = node("div", null, doc.body); filters.className = "filters";
|
||||
for (const [label, value] of snapshot.filters) {
|
||||
const field = node("div", null, filters);
|
||||
node("strong", `${label}:`, field); node("span", value || "None", field);
|
||||
}
|
||||
const totals = node("div", null, doc.body); totals.className = "totals";
|
||||
for (const card of snapshot.totals) {
|
||||
const section = node("section", null, totals);
|
||||
node("span", card.label, section); node("strong", card.value, section);
|
||||
if (card.detail) node("small", card.detail, section);
|
||||
}
|
||||
node("h2", "Opportunities", doc.body);
|
||||
const table = node("table", null, doc.body);
|
||||
const header = node("tr", null, node("thead", null, table));
|
||||
for (const column of snapshot.columns) {
|
||||
const th = node("th", column.label, header); th.scope = "col";
|
||||
if (column.key === "revenue") th.className = "money";
|
||||
}
|
||||
const body = node("tbody", null, table);
|
||||
for (const values of snapshot.rows) {
|
||||
const row = node("tr", null, body);
|
||||
values.forEach((value, index) => {
|
||||
const td = node("td", value, row);
|
||||
if (snapshot.columns[index].key === "revenue") td.className = "money";
|
||||
});
|
||||
}
|
||||
if (!snapshot.rows.length) node("p", "No opportunities match the selected filters.", doc.body);
|
||||
popup.focus();
|
||||
// Print a frozen snapshot, without changing the extension's scroll or selected view.
|
||||
environment.setTimeout(() => { if (!popup.closed) popup.print(); }, 150);
|
||||
}
|
||||
return { createButton, print };
|
||||
}
|
||||
@@ -2,10 +2,27 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
|
||||
logRequestStart, logRequestSuccess, logRequestFailure, readResponseBodyForDebug, maskToken, environment }) {
|
||||
const { fetch } = environment;
|
||||
const url = "https://eeho.fa.us2.oraclecloud.com/crmRestApi/searchResources/latest/custom-actions/queries";
|
||||
function createPayload(offset) {
|
||||
async function requestSRSearchResources(token) {
|
||||
const searchUrl = `${url}?applications=461e5791-9f15-4cd1-b5cd-19e8480cbce4&entity=CRMServiceRequest&language=en&limit=-1&onlyData=true`;
|
||||
const headers = { Accept: "application/json", Authorization: `Bearer ${token}` };
|
||||
const entry = logRequestStart({ name: "requestSRSearchResources", method: "GET", url: searchUrl, headers });
|
||||
try {
|
||||
const response = await fetch(searchUrl, { method: "GET", credentials: "include", headers });
|
||||
logRequestSuccess(entry, response, await readResponseBodyForDebug(response));
|
||||
if (response.status !== 200) throw new Error(`requestSRSearchResources failed with status ${response.status}.`);
|
||||
const data = await response.json();
|
||||
const number = data?.items?.[0]?.savedSearchNumber;
|
||||
if (!Array.isArray(data?.items) || typeof number !== "string" || !number.trim()) {
|
||||
throw new Error("requestSRSearchResources did not return savedSearchNumber in its first item.");
|
||||
}
|
||||
return number.trim();
|
||||
} catch (error) { logRequestFailure(entry, error.message); throw error; }
|
||||
}
|
||||
function createPayload(offset, savedSearchNumber) {
|
||||
if (typeof savedSearchNumber !== "string" || !savedSearchNumber.trim()) throw new Error("Missing SR savedSearchNumber.");
|
||||
return {
|
||||
aggregationResults: false, applications: ["ORACLE-ISS-APP"], onlyData: true,
|
||||
entity: "CRMServiceRequest", copiedFrom: "queries/c521309c-1310-49a0-9723-da2eb1988205",
|
||||
entity: "CRMServiceRequest", copiedFrom: `queries/${savedSearchNumber.trim()}`,
|
||||
q: { op: "$and", criteria: [
|
||||
{ op: "$in", attribute: "StatusCd", values: ["SVC_PENDING_DELIVERY"] },
|
||||
{ op: "$eq", attribute: "RecordSet", value: "ORA_SVC_MY_SR_TEAM_SRS" }
|
||||
@@ -19,11 +36,13 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
|
||||
};
|
||||
}
|
||||
async function requestCrmServiceRequest(token, version, isActive = () => isCurrentPeriodRequest(version), onProgress = () => {}) {
|
||||
if (!isActive()) return null;
|
||||
const savedSearchNumber = await requestSRSearchResources(token);
|
||||
const items = [];
|
||||
let offset = 0;
|
||||
let attempt = 1;
|
||||
while (isActive()) {
|
||||
const payload = createPayload(offset);
|
||||
const payload = createPayload(offset, savedSearchNumber);
|
||||
const headers = {
|
||||
Accept: "application/json", "Content-Type": "application/json", Authorization: `Bearer ${token}`,
|
||||
Origin: "https://eeho.fa.us2.oraclecloud.com", Preference: "transient"
|
||||
@@ -60,5 +79,5 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return { createPayload, requestCrmServiceRequest };
|
||||
return { createPayload, requestCrmServiceRequest, requestSRSearchResources };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user