Improved the background request loading order

This commit is contained in:
2026-10-29 17:35:20 -03:00
parent 9b5e751179
commit 00bcfdc42e
6 changed files with 59 additions and 9 deletions

View File

@@ -3684,6 +3684,7 @@ function createFusionOpportunities({
getOpportunityTypeValues,
readResponseBodyForDebug,
resetDebugRequests = () => {},
loadServiceRequests = async () => {},
environment
}) {
const { fetch } = environment;
@@ -3712,7 +3713,13 @@ function createFusionOpportunities({
const payload = createOpportunitiesQueryPayload(dateRange, offset, savedSearchNumber);
const requestBody = JSON.stringify(payload);
// One history per query, preserving all pages of the same result set.
if (page === 1) resetDebugRequests();
if (page === 1) {
resetDebugRequests();
// SR queries do not depend on opportunity or product IDs. Keep their
// diagnostics in this query's history and reuse the loaded SR state later.
try { await loadServiceRequests(token, requestVersion); } catch { /* SR failure must not block opportunities. */ }
if (!isCurrentPeriodRequest(requestVersion)) return null;
}
const requestEntry = logRequestStart({
name: "requestOpportunities",
method: "POST",
@@ -3953,7 +3960,7 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
}
// Source: src/features/opportunities/crm-service-requests.js
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment }) {
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment, onStateChange = () => {} }) {
let status = "idle";
let byOpportunity = new Map();
let pending = null;
@@ -3975,6 +3982,7 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
async function run(token, version, currentGeneration, cacheOptions) {
const isActive = () => currentGeneration === generation;
status = "loading";
onStateChange();
try {
const cached = cacheOptions ? await activityCache.read("sr", "all", cacheOptions.cachedAt) : null;
if (!isActive()) return false;
@@ -4000,9 +4008,11 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
loadedItems = items;
status = "ready";
if (!cached) reportProgress("complete", items.length, items.length);
// Refresh SR badges, filters and totals independently of product/action jobs.
onStateChange();
return true;
} catch {
if (isActive()) { status = "error"; reportProgress("error"); }
if (isActive()) { status = "error"; reportProgress("error"); onStateChange(); }
return false;
}
}
@@ -11714,6 +11724,7 @@ function createContentApplication(overrides = {}) {
setOpportunitiesTableState: (...args) => opportunitiesTable.setOpportunitiesTableState(...args)
});
const fusionOpportunities = createFusionOpportunities({
loadServiceRequests: (token, version) => opportunityCrmServiceRequests.load(token, version),
resetDebugRequests: () => debugState.reset(),
OPPORTUNITIES_PAGE_LIMIT: contracts.OPPORTUNITIES_PAGE_LIMIT,
OPPORTUNITIES_QUERY_URL: contracts.OPPORTUNITIES_QUERY_URL,
@@ -11793,6 +11804,9 @@ function createContentApplication(overrides = {}) {
requestPillars: () => requestSrFormLookup(messageTypes.requestSrLookupPrimaryPillar)
});
const opportunityCrmServiceRequests = createOpportunityCrmServiceRequests({
onStateChange: () => {
if (environment.document?.getElementById?.(contracts.OPPORTUNITIES_TABLE_ID)) opportunitiesTable.renderOpportunitiesTable();
},
formatDate: value => opportunitiesFormatters.formatOracleResponseDate(value),
createSrButton: number => crmSrForm.createButton(number),
closeSrForm: () => { internalSrForm.close(false); crmSrForm.close(false); },

View File

@@ -3684,6 +3684,7 @@ function createFusionOpportunities({
getOpportunityTypeValues,
readResponseBodyForDebug,
resetDebugRequests = () => {},
loadServiceRequests = async () => {},
environment
}) {
const { fetch } = environment;
@@ -3712,7 +3713,13 @@ function createFusionOpportunities({
const payload = createOpportunitiesQueryPayload(dateRange, offset, savedSearchNumber);
const requestBody = JSON.stringify(payload);
// One history per query, preserving all pages of the same result set.
if (page === 1) resetDebugRequests();
if (page === 1) {
resetDebugRequests();
// SR queries do not depend on opportunity or product IDs. Keep their
// diagnostics in this query's history and reuse the loaded SR state later.
try { await loadServiceRequests(token, requestVersion); } catch { /* SR failure must not block opportunities. */ }
if (!isCurrentPeriodRequest(requestVersion)) return null;
}
const requestEntry = logRequestStart({
name: "requestOpportunities",
method: "POST",
@@ -3953,7 +3960,7 @@ function createFusionCrmServiceRequests({ isCurrentPeriodRequest,
}
// Source: src/features/opportunities/crm-service-requests.js
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment }) {
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment, onStateChange = () => {} }) {
let status = "idle";
let byOpportunity = new Map();
let pending = null;
@@ -3975,6 +3982,7 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
async function run(token, version, currentGeneration, cacheOptions) {
const isActive = () => currentGeneration === generation;
status = "loading";
onStateChange();
try {
const cached = cacheOptions ? await activityCache.read("sr", "all", cacheOptions.cachedAt) : null;
if (!isActive()) return false;
@@ -4000,9 +4008,11 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
loadedItems = items;
status = "ready";
if (!cached) reportProgress("complete", items.length, items.length);
// Refresh SR badges, filters and totals independently of product/action jobs.
onStateChange();
return true;
} catch {
if (isActive()) { status = "error"; reportProgress("error"); }
if (isActive()) { status = "error"; reportProgress("error"); onStateChange(); }
return false;
}
}
@@ -11714,6 +11724,7 @@ function createContentApplication(overrides = {}) {
setOpportunitiesTableState: (...args) => opportunitiesTable.setOpportunitiesTableState(...args)
});
const fusionOpportunities = createFusionOpportunities({
loadServiceRequests: (token, version) => opportunityCrmServiceRequests.load(token, version),
resetDebugRequests: () => debugState.reset(),
OPPORTUNITIES_PAGE_LIMIT: contracts.OPPORTUNITIES_PAGE_LIMIT,
OPPORTUNITIES_QUERY_URL: contracts.OPPORTUNITIES_QUERY_URL,
@@ -11793,6 +11804,9 @@ function createContentApplication(overrides = {}) {
requestPillars: () => requestSrFormLookup(messageTypes.requestSrLookupPrimaryPillar)
});
const opportunityCrmServiceRequests = createOpportunityCrmServiceRequests({
onStateChange: () => {
if (environment.document?.getElementById?.(contracts.OPPORTUNITIES_TABLE_ID)) opportunitiesTable.renderOpportunitiesTable();
},
formatDate: value => opportunitiesFormatters.formatOracleResponseDate(value),
createSrButton: number => crmSrForm.createButton(number),
closeSrForm: () => { internalSrForm.close(false); crmSrForm.close(false); },

View File

@@ -273,6 +273,7 @@ function createContentApplication(overrides = {}) {
setOpportunitiesTableState: (...args) => opportunitiesTable.setOpportunitiesTableState(...args)
});
const fusionOpportunities = createFusionOpportunities({
loadServiceRequests: (token, version) => opportunityCrmServiceRequests.load(token, version),
resetDebugRequests: () => debugState.reset(),
OPPORTUNITIES_PAGE_LIMIT: contracts.OPPORTUNITIES_PAGE_LIMIT,
OPPORTUNITIES_QUERY_URL: contracts.OPPORTUNITIES_QUERY_URL,
@@ -352,6 +353,9 @@ function createContentApplication(overrides = {}) {
requestPillars: () => requestSrFormLookup(messageTypes.requestSrLookupPrimaryPillar)
});
const opportunityCrmServiceRequests = createOpportunityCrmServiceRequests({
onStateChange: () => {
if (environment.document?.getElementById?.(contracts.OPPORTUNITIES_TABLE_ID)) opportunitiesTable.renderOpportunitiesTable();
},
formatDate: value => opportunitiesFormatters.formatOracleResponseDate(value),
createSrButton: number => crmSrForm.createButton(number),
closeSrForm: () => { internalSrForm.close(false); crmSrForm.close(false); },

View File

@@ -1,4 +1,4 @@
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment }) {
function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurrentPeriodRequest, bindPopover, closePopover, createSrButton, closeSrForm, allowsCrmActivities, reportProgress, activityCache, setBadgeContent, formatDate, environment, onStateChange = () => {} }) {
let status = "idle";
let byOpportunity = new Map();
let pending = null;
@@ -20,6 +20,7 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
async function run(token, version, currentGeneration, cacheOptions) {
const isActive = () => currentGeneration === generation;
status = "loading";
onStateChange();
try {
const cached = cacheOptions ? await activityCache.read("sr", "all", cacheOptions.cachedAt) : null;
if (!isActive()) return false;
@@ -45,9 +46,11 @@ function createOpportunityCrmServiceRequests({ requestCrmServiceRequest, isCurre
loadedItems = items;
status = "ready";
if (!cached) reportProgress("complete", items.length, items.length);
// Refresh SR badges, filters and totals independently of product/action jobs.
onStateChange();
return true;
} catch {
if (isActive()) { status = "error"; reportProgress("error"); }
if (isActive()) { status = "error"; reportProgress("error"); onStateChange(); }
return false;
}
}

View File

@@ -9,6 +9,7 @@ function createFusionOpportunities({
getOpportunityTypeValues,
readResponseBodyForDebug,
resetDebugRequests = () => {},
loadServiceRequests = async () => {},
environment
}) {
const { fetch } = environment;
@@ -37,7 +38,13 @@ function createFusionOpportunities({
const payload = createOpportunitiesQueryPayload(dateRange, offset, savedSearchNumber);
const requestBody = JSON.stringify(payload);
// One history per query, preserving all pages of the same result set.
if (page === 1) resetDebugRequests();
if (page === 1) {
resetDebugRequests();
// SR queries do not depend on opportunity or product IDs. Keep their
// diagnostics in this query's history and reuse the loaded SR state later.
try { await loadServiceRequests(token, requestVersion); } catch { /* SR failure must not block opportunities. */ }
if (!isCurrentPeriodRequest(requestVersion)) return null;
}
const requestEntry = logRequestStart({
name: "requestOpportunities",
method: "POST",

View File

@@ -0,0 +1,8 @@
const {test}=require('node:test');const assert=require('node:assert/strict');const fs=require('node:fs');const vm=require('node:vm');
const factory=vm.runInNewContext(fs.readFileSync('src/services/fusion/opportunities.js','utf8')+'\ncreateFusionOpportunities');
for(const mode of ['success','failure','stale'])test(`SR precedes opportunities: ${mode}`,async()=>{
const order=[];let active=true;let page=0;
const api=factory({OPPORTUNITIES_PAGE_LIMIT:1,OPPORTUNITIES_QUERY_URL:'https://example.invalid',isCurrentPeriodRequest:()=>active,getOpportunityTypeValues:()=>new Set(['ALL']),logRequestStart:()=>({}),logRequestSuccess(){},logRequestFailure(){},readResponseBodyForDebug:async()=>'',resetDebugRequests:()=>order.push('reset'),loadServiceRequests:async()=>{order.push('sr');if(mode==='stale')active=false;if(mode==='failure')throw Error('SR failed');},environment:{fetch:async()=>{order.push('opportunities');page++;return {ok:true,json:async()=>({items:[{OptyNumber:String(page)}],hasMore:page===1,count:1,offset:page-1})};}}});
await api.requestOpportunitiesPage('token','CQ',{startCloseDate:'2026-09-01',endCloseDate:'2026-12-01'},0,[],1,1,'saved-search');
assert.deepEqual(order,mode==='stale'?['reset','sr']:['reset','sr','opportunities','opportunities']);
});