Fixed the date ranges for the Reference options
This commit is contained in:
121
dist/chromium/content.js
vendored
121
dist/chromium/content.js
vendored
File diff suppressed because one or more lines are too long
121
dist/firefox/content.js
vendored
121
dist/firefox/content.js
vendored
File diff suppressed because one or more lines are too long
@@ -89,8 +89,7 @@ function createContentContracts() {
|
|||||||
"Next Quarter",
|
"Next Quarter",
|
||||||
"Previous Quarter",
|
"Previous Quarter",
|
||||||
"Current Fiscal Year",
|
"Current Fiscal Year",
|
||||||
"4 Rolling Quarters (CQ + 3)",
|
"4 Rolling Quarters (CQ + 3)"
|
||||||
"RENEWALS (Current + Past Due)"
|
|
||||||
];
|
];
|
||||||
const ALLOWED_PATHS = [
|
const ALLOWED_PATHS = [
|
||||||
"/hcmUI/faces/FuseWelcome",
|
"/hcmUI/faces/FuseWelcome",
|
||||||
|
|||||||
@@ -212,75 +212,6 @@ function createCrmSrForm({ MODAL_ID, allowsCrmActivities, isMissing, getOpportun
|
|||||||
active = view;
|
active = view;
|
||||||
const current = () => active === view && allowsCrmActivities();
|
const current = () => active === view && allowsCrmActivities();
|
||||||
function updateSave() { save.disabled = loadingFields.size > 0 || availableFields.size < 4 || Boolean(view.saving); }
|
function updateSave() { save.disabled = loadingFields.size > 0 || availableFields.size < 4 || Boolean(view.saving); }
|
||||||
function confirmPayload(payload, requestHeaders = {}) {
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const overlay = node("section", "opportunities-extension-action-form-modal opportunities-extension-sr-confirmation");
|
|
||||||
overlay.setAttribute("role", "dialog");
|
|
||||||
overlay.setAttribute("aria-modal", "true");
|
|
||||||
overlay.setAttribute("aria-labelledby", "opportunities-extension-sr-confirm-title");
|
|
||||||
const panel = node("div", "opportunities-extension-action-form-dialog opportunities-extension-sr-confirm-dialog");
|
|
||||||
const heading = node("header", "opportunities-extension-account-plans-header");
|
|
||||||
const title = node("h2", "", "Confirm Service Request");
|
|
||||||
title.id = "opportunities-extension-sr-confirm-title";
|
|
||||||
heading.append(title);
|
|
||||||
const body = node("div", "opportunities-extension-sr-confirm-body");
|
|
||||||
const label = node("p", "", "requestPostServiceRequest");
|
|
||||||
label.id = "opportunities-extension-sr-confirm-payload-label";
|
|
||||||
const preview = node("pre", "opportunities-extension-sr-confirm-payload", JSON.stringify(payload, null, 2));
|
|
||||||
preview.tabIndex = 0;
|
|
||||||
preview.setAttribute("aria-labelledby", label.id);
|
|
||||||
body.append(label, preview);
|
|
||||||
const headerPreviews = [];
|
|
||||||
for (const name of ["requestSrTokenCatalog", "requestPostServiceRequest"]) {
|
|
||||||
if (!requestHeaders[name]) continue;
|
|
||||||
const headerLabel = node("p", "", `${name} - Headers`);
|
|
||||||
headerLabel.id = `opportunities-extension-sr-confirm-${name}-headers`;
|
|
||||||
const headerPreview = node("pre", "opportunities-extension-sr-confirm-payload", JSON.stringify(requestHeaders[name], null, 2));
|
|
||||||
headerPreview.tabIndex = 0;
|
|
||||||
headerPreview.setAttribute("aria-labelledby", headerLabel.id);
|
|
||||||
headerPreviews.push(headerPreview);
|
|
||||||
body.append(headerLabel, headerPreview);
|
|
||||||
if (name === "requestSrTokenCatalog" && !requestHeaders[name]["x-appbuilder-client-id"]) {
|
|
||||||
body.append(node("p", "opportunities-extension-action-form-status", "x-appbuilder-client-id: not yet observed in SeaaS requests."));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const footer = node("footer", "opportunities-extension-action-form-footer");
|
|
||||||
const cancel = node("button", "", "Cancel");
|
|
||||||
const confirm = node("button", "", "Confirm Save");
|
|
||||||
cancel.type = confirm.type = "button";
|
|
||||||
footer.append(cancel, confirm);
|
|
||||||
panel.append(heading, body, footer);
|
|
||||||
overlay.append(panel);
|
|
||||||
dialog.inert = true;
|
|
||||||
dialog.setAttribute("aria-hidden", "true");
|
|
||||||
modal.setAttribute("aria-modal", "false");
|
|
||||||
modal.append(overlay);
|
|
||||||
let settled = false;
|
|
||||||
function finish(confirmed) {
|
|
||||||
if (settled) return;
|
|
||||||
settled = true;
|
|
||||||
overlay.remove();
|
|
||||||
dialog.inert = false;
|
|
||||||
dialog.setAttribute("aria-hidden", "false");
|
|
||||||
modal.setAttribute("aria-modal", "true");
|
|
||||||
resolve(confirmed);
|
|
||||||
}
|
|
||||||
cancel.addEventListener("click", () => finish(false));
|
|
||||||
confirm.addEventListener("click", () => finish(true));
|
|
||||||
overlay.addEventListener("keydown", event => {
|
|
||||||
event.stopPropagation();
|
|
||||||
if (event.key === "Escape") { event.preventDefault(); finish(false); }
|
|
||||||
if (event.key === "Tab") {
|
|
||||||
const controls = [preview, ...headerPreviews, cancel, confirm];
|
|
||||||
const index = controls.indexOf(document.activeElement);
|
|
||||||
if (index < 0 || (event.shiftKey ? index === 0 : index === controls.length - 1)) {
|
|
||||||
event.preventDefault(); controls[event.shiftKey ? controls.length - 1 : 0].focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
cancel.focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
form.addEventListener("submit", async event => {
|
form.addEventListener("submit", async event => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (!current() || view.saving || save.disabled) return;
|
if (!current() || view.saving || save.disabled) return;
|
||||||
@@ -331,7 +262,7 @@ function createCrmSrForm({ MODAL_ID, allowsCrmActivities, isMissing, getOpportun
|
|||||||
status.hidden = true;
|
status.hidden = true;
|
||||||
try {
|
try {
|
||||||
const prepared = await requestSave({ phase: "prepare", payload });
|
const prepared = await requestSave({ phase: "prepare", payload });
|
||||||
if (!current() || host.hidden || !await confirmPayload(prepared.payload, prepared.headers)) return;
|
if (!current() || host.hidden) return;
|
||||||
save.textContent = "Saving...";
|
save.textContent = "Saving...";
|
||||||
await requestSave({ phase: "confirm", payload: prepared.payload });
|
await requestSave({ phase: "confirm", payload: prepared.payload });
|
||||||
view.saving = false;
|
view.saving = false;
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
function createOpportunitiesFiscalPeriods() {
|
function createOpportunitiesFiscalPeriods() {
|
||||||
|
// Oracle fiscal years start on June 1; each range ends at the next period's start.
|
||||||
|
const FISCAL_START_MONTH = 5;
|
||||||
|
|
||||||
function getFiscalPeriodRange(period, currentDate) {
|
function getFiscalPeriodRange(period, currentDate) {
|
||||||
const currentYear = currentDate.getFullYear();
|
const currentYear = currentDate.getFullYear();
|
||||||
const currentMonth = currentDate.getMonth();
|
const currentMonth = currentDate.getMonth();
|
||||||
const fiscalStartYear = currentMonth >= 5 ? currentYear : currentYear - 1;
|
const fiscalStartYear = currentMonth >= FISCAL_START_MONTH ? currentYear : currentYear - 1;
|
||||||
const currentQuarterIndex = Math.floor(((currentMonth - 5 + 12) % 12) / 3);
|
const currentQuarterIndex = Math.floor(((currentMonth - FISCAL_START_MONTH + 12) % 12) / 3);
|
||||||
|
|
||||||
if (period === "Current Fiscal Year") {
|
if (period === "Current Fiscal Year") {
|
||||||
const isBeforeFiscalYearStart = currentMonth < 5;
|
return createDateRange(fiscalStartYear, FISCAL_START_MONTH, fiscalStartYear + 1, FISCAL_START_MONTH);
|
||||||
const startYear = isBeforeFiscalYearStart ? currentYear - 1 : currentYear;
|
|
||||||
const endYear = isBeforeFiscalYearStart ? currentYear : currentYear + 1;
|
|
||||||
return createDateRange(startYear, 5, endYear, 4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let quarterOffset;
|
let quarterOffset;
|
||||||
@@ -28,19 +28,14 @@ function createOpportunitiesFiscalPeriods() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const start = new Date(fiscalStartYear, 5 + (quarterOffset * 3), 1);
|
const startMonth = FISCAL_START_MONTH + (quarterOffset * 3);
|
||||||
const end = new Date(start.getFullYear(), start.getMonth() + (numberOfQuarters * 3), 0);
|
return createDateRange(fiscalStartYear, startMonth, fiscalStartYear, startMonth + (numberOfQuarters * 3));
|
||||||
|
|
||||||
return {
|
|
||||||
startCloseDate: formatOracleDate(start),
|
|
||||||
endCloseDate: formatOracleDate(end)
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDateRange(startYear, startMonth, endYear, endMonth) {
|
function createDateRange(startYear, startMonth, endYear, endMonth) {
|
||||||
return {
|
return {
|
||||||
startCloseDate: formatOracleDate(new Date(startYear, startMonth, 1)),
|
startCloseDate: formatOracleDate(new Date(startYear, startMonth, 1)),
|
||||||
endCloseDate: formatOracleDate(new Date(endYear, endMonth + 1, 0))
|
endCloseDate: formatOracleDate(new Date(endYear, endMonth, 1))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -310,17 +310,6 @@
|
|||||||
.opportunities-extension-action-form-field select { height: 38px; }
|
.opportunities-extension-action-form-field select { height: 38px; }
|
||||||
.opportunities-extension-action-form-field select[hidden] { display: none; }
|
.opportunities-extension-action-form-field select[hidden] { display: none; }
|
||||||
.opportunities-extension-sr-form-dialog { width: min(900px, 100%); }
|
.opportunities-extension-sr-form-dialog { width: min(900px, 100%); }
|
||||||
.opportunities-extension-sr-confirmation { z-index: 6; }
|
|
||||||
.opportunities-extension-sr-confirm-dialog { width: min(800px, 100%); display: flex; flex-direction: column; overflow: hidden; }
|
|
||||||
.opportunities-extension-sr-confirm-dialog > header,
|
|
||||||
.opportunities-extension-sr-confirm-dialog > footer { flex-shrink: 0; }
|
|
||||||
.opportunities-extension-sr-confirm-dialog > footer { padding: 16px 20px; }
|
|
||||||
.opportunities-extension-sr-confirm-body { min-height: 0; overflow: auto; padding: 0 20px; }
|
|
||||||
.opportunities-extension-sr-confirm-body > p { font-size: 13px; font-weight: 600; }
|
|
||||||
.opportunities-extension-sr-confirm-payload { margin: 0; padding: 16px; border: 1px solid #b9b4ae; border-radius: 4px; background: #f6f5f4; color: #312d2a; font: 13px/1.6 Consolas, monospace; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
||||||
.opportunities-extension-sr-confirmation :focus-visible { outline: 2px solid #00758f; outline-offset: 2px; }
|
|
||||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-sr-confirm-payload { background: #211f1d; color: #eeeae6; border-color: #756f68; }
|
|
||||||
.opportunities-extension-modal[data-theme="dark"] .opportunities-extension-sr-confirmation :focus-visible { outline-color: #80d6e8; }
|
|
||||||
.opportunities-extension-sr-form-dialog > .opportunities-extension-account-plans-header { padding: 20px 24px; }
|
.opportunities-extension-sr-form-dialog > .opportunities-extension-account-plans-header { padding: 20px 24px; }
|
||||||
.opportunities-extension-sr-form-dialog > .opportunities-extension-action-form { gap: 20px 24px; padding: 24px; align-items: start; }
|
.opportunities-extension-sr-form-dialog > .opportunities-extension-action-form { gap: 20px 24px; padding: 24px; align-items: start; }
|
||||||
.opportunities-extension-sr-form-dialog .opportunities-extension-action-form-field { gap: 8px; }
|
.opportunities-extension-sr-form-dialog .opportunities-extension-action-form-field { gap: 8px; }
|
||||||
|
|||||||
@@ -102,19 +102,21 @@ function createFusionOpportunities({
|
|||||||
op: "$and",
|
op: "$and",
|
||||||
criteria: [
|
criteria: [
|
||||||
{
|
{
|
||||||
op: "$or",
|
op: "$and",
|
||||||
criteria: Array.from(getOpportunityTypeValues(), (value) => ({
|
criteria: [{
|
||||||
op: "$eq",
|
op: "$or",
|
||||||
attribute: "RecordSet",
|
criteria: Array.from(getOpportunityTypeValues(), (value) => ({
|
||||||
value
|
op: "$eq",
|
||||||
}))
|
attribute: "RecordSet",
|
||||||
|
value
|
||||||
|
}))
|
||||||
|
}]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
op: "$wi",
|
op: "$wi",
|
||||||
attribute: "EffectiveDate",
|
attribute: "EffectiveDate",
|
||||||
value1: dateRange.startCloseDate,
|
value1: new Date(dateRange.startCloseDate).toISOString().replace(/Z$/, "+0000"),
|
||||||
value2: dateRange.endCloseDate,
|
value2: new Date(dateRange.endCloseDate).toISOString().replace(/Z$/, "+0000")
|
||||||
dynamicDate: false
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -154,7 +156,7 @@ function createFusionOpportunities({
|
|||||||
"CustomerAccount.Region_c",
|
"CustomerAccount.Region_c",
|
||||||
"CustomerAccount.PrimaryAddress.CountryCode"
|
"CustomerAccount.PrimaryAddress.CountryCode"
|
||||||
],
|
],
|
||||||
sort: [],
|
sort: null,
|
||||||
language: "en",
|
language: "en",
|
||||||
skipInValidFields: true,
|
skipInValidFields: true,
|
||||||
skipHiddenFromUIFields: true,
|
skipHiddenFromUIFields: true,
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ test("Redwood cascade is byte-identical to the characterized original", () => {
|
|||||||
test("fiscal module can run alone without DOM, browser APIs or network", () => {
|
test("fiscal module can run alone without DOM, browser APIs or network", () => {
|
||||||
const factory = new vm.Script(`${read("src/features/opportunities/fiscal-periods.js")}\ncreateOpportunitiesFiscalPeriods;`).runInNewContext();
|
const factory = new vm.Script(`${read("src/features/opportunities/fiscal-periods.js")}\ncreateOpportunitiesFiscalPeriods;`).runInNewContext();
|
||||||
assert.deepEqual(json(factory().getFiscalPeriodRange("Current Fiscal Year", new Date(2027, 0, 15))), {
|
assert.deepEqual(json(factory().getFiscalPeriodRange("Current Fiscal Year", new Date(2027, 0, 15))), {
|
||||||
startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2027-05-31T00:00:00.000Z"
|
startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2027-06-01T00:00:00.000Z"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const FILTER_KEY = "opportunities-extension-filter-preferences";
|
|||||||
const TYPE_KEY = "opportunities-extension-opportunity-type-preferences";
|
const TYPE_KEY = "opportunities-extension-opportunity-type-preferences";
|
||||||
const CACHE_KEY = "opportunitiesExtension.opptyProductsCache.v1";
|
const CACHE_KEY = "opportunitiesExtension.opptyProductsCache.v1";
|
||||||
const DAY = 86400000;
|
const DAY = 86400000;
|
||||||
const range = { startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2026-08-31T00:00:00.000Z" };
|
const range = { startCloseDate: "2026-06-01T00:00:00.000Z", endCloseDate: "2026-09-01T00:00:00.000Z" };
|
||||||
const drain = () => new Promise(resolve => setImmediate(resolve));
|
const drain = () => new Promise(resolve => setImmediate(resolve));
|
||||||
const productRequest = (requestId, optyNumbers = ["SYNTH-1"]) => ({
|
const productRequest = (requestId, optyNumbers = ["SYNTH-1"]) => ({
|
||||||
type: PREFIX + "requestOpptyProducts", requestId, optyNumbers, accessToken: "synthetic-products-token"
|
type: PREFIX + "requestOpptyProducts", requestId, optyNumbers, accessToken: "synthetic-products-token"
|
||||||
@@ -63,21 +63,47 @@ test("distribution contracts: manifests, execution boundaries and deterministic
|
|||||||
test("fiscal dates preserve quarter/year boundaries, leap year and unsupported renewals", () => {
|
test("fiscal dates preserve quarter/year boundaries, leap year and unsupported renewals", () => {
|
||||||
const h = loadContent();
|
const h = loadContent();
|
||||||
const cases = [
|
const cases = [
|
||||||
["Current Quarter", [2026, 5, 1], "2026-06-01", "2026-08-31"],
|
["Current Quarter", [2026, 0, 1], "2025-12-01", "2026-03-01"],
|
||||||
["Current Quarter", [2026, 7, 31], "2026-06-01", "2026-08-31"],
|
["Current Quarter", [2026, 1, 28], "2025-12-01", "2026-03-01"],
|
||||||
["Current Quarter", [2026, 8, 1], "2026-09-01", "2026-11-30"],
|
["Current Quarter", [2026, 2, 1], "2026-03-01", "2026-06-01"],
|
||||||
["Current Quarter", [2028, 1, 29], "2027-12-01", "2028-02-29"],
|
["Current Quarter", [2026, 4, 31], "2026-03-01", "2026-06-01"],
|
||||||
["Next Quarter", [2026, 10, 30], "2026-12-01", "2027-02-28"],
|
["Current Quarter", [2026, 5, 1], "2026-06-01", "2026-09-01"],
|
||||||
["Previous Quarter", [2026, 7, 24], "2026-03-01", "2026-05-31"],
|
["Current Quarter", [2026, 7, 31], "2026-06-01", "2026-09-01"],
|
||||||
["Current Fiscal Year", [2026, 4, 31], "2025-06-01", "2026-05-31"],
|
["Current Quarter", [2026, 8, 1], "2026-09-01", "2026-12-01"],
|
||||||
["Current Fiscal Year", [2026, 5, 1], "2026-06-01", "2027-05-31"],
|
["Current Quarter", [2026, 9, 19], "2026-09-01", "2026-12-01"],
|
||||||
["Current Fiscal Year", [2027, 0, 1], "2026-06-01", "2027-05-31"],
|
["Current Quarter", [2026, 10, 30], "2026-09-01", "2026-12-01"],
|
||||||
["4 Rolling Quarters (CQ + 3)", [2026, 8, 16], "2026-09-01", "2027-08-31"]
|
["Current Quarter", [2026, 11, 1], "2026-12-01", "2027-03-01"],
|
||||||
|
["Current Quarter", [2026, 11, 31], "2026-12-01", "2027-03-01"],
|
||||||
|
["Current Quarter", [2028, 1, 29], "2027-12-01", "2028-03-01"],
|
||||||
|
["Next Quarter", [2026, 0, 1], "2026-03-01", "2026-06-01"],
|
||||||
|
["Next Quarter", [2026, 4, 31], "2026-06-01", "2026-09-01"],
|
||||||
|
["Next Quarter", [2026, 5, 1], "2026-09-01", "2026-12-01"],
|
||||||
|
["Next Quarter", [2026, 10, 30], "2026-12-01", "2027-03-01"],
|
||||||
|
["Next Quarter", [2026, 11, 1], "2027-03-01", "2027-06-01"],
|
||||||
|
["Previous Quarter", [2026, 0, 1], "2025-09-01", "2025-12-01"],
|
||||||
|
["Previous Quarter", [2026, 2, 1], "2025-12-01", "2026-03-01"],
|
||||||
|
["Previous Quarter", [2026, 5, 1], "2026-03-01", "2026-06-01"],
|
||||||
|
["Previous Quarter", [2026, 8, 1], "2026-06-01", "2026-09-01"],
|
||||||
|
["Previous Quarter", [2026, 11, 1], "2026-09-01", "2026-12-01"],
|
||||||
|
["Current Fiscal Year", [2026, 0, 1], "2025-06-01", "2026-06-01"],
|
||||||
|
["Current Fiscal Year", [2026, 4, 31], "2025-06-01", "2026-06-01"],
|
||||||
|
["Current Fiscal Year", [2026, 5, 1], "2026-06-01", "2027-06-01"],
|
||||||
|
["Current Fiscal Year", [2027, 0, 1], "2026-06-01", "2027-06-01"],
|
||||||
|
["4 Rolling Quarters (CQ + 3)", [2026, 0, 1], "2025-12-01", "2026-12-01"],
|
||||||
|
["4 Rolling Quarters (CQ + 3)", [2026, 2, 1], "2026-03-01", "2027-03-01"],
|
||||||
|
["4 Rolling Quarters (CQ + 3)", [2026, 5, 1], "2026-06-01", "2027-06-01"],
|
||||||
|
["4 Rolling Quarters (CQ + 3)", [2026, 8, 16], "2026-09-01", "2027-09-01"],
|
||||||
|
["4 Rolling Quarters (CQ + 3)", [2026, 11, 1], "2026-12-01", "2027-12-01"]
|
||||||
];
|
];
|
||||||
for (const [period, date, start, end] of cases) {
|
for (const [period, date, start, end] of cases) {
|
||||||
assert.deepEqual(plain(h.getFiscalPeriodRange(period, new Date(...date))), {
|
assert.deepEqual(plain(h.getFiscalPeriodRange(period, new Date(...date))), {
|
||||||
startCloseDate: start + "T00:00:00.000Z", endCloseDate: end + "T00:00:00.000Z"
|
startCloseDate: start + "T00:00:00.000Z", endCloseDate: end + "T00:00:00.000Z"
|
||||||
});
|
});
|
||||||
|
const dateRange = h.getFiscalPeriodRange(period, new Date(...date));
|
||||||
|
const criterion = h.createOpportunitiesQueryPayload(dateRange, 0).q.criteria[1];
|
||||||
|
assert.equal(criterion.value1, start + "T00:00:00.000+0000");
|
||||||
|
assert.equal(criterion.value2, end + "T00:00:00.000+0000");
|
||||||
|
assert.equal(dateRange.endCloseDate, end + "T00:00:00.000Z", "payload must not mutate the shared date range");
|
||||||
}
|
}
|
||||||
assert.equal(h.getFiscalPeriodRange("RENEWALS (Current + Past Due)", new Date()), null);
|
assert.equal(h.getFiscalPeriodRange("RENEWALS (Current + Past Due)", new Date()), null);
|
||||||
});
|
});
|
||||||
@@ -90,11 +116,11 @@ test("opportunity query preserves fields, record-set OR, date AND, and paginatio
|
|||||||
assert.deepEqual(plain(h.createOpportunitiesQueryPayload(range, 15)), {
|
assert.deepEqual(plain(h.createOpportunitiesQueryPayload(range, 15)), {
|
||||||
aggregationResults: false, applications: ["ORACLE-ISS-APP"], onlyData: true, entity: "Opportunity",
|
aggregationResults: false, applications: ["ORACLE-ISS-APP"], onlyData: true, entity: "Opportunity",
|
||||||
q: { op: "$and", criteria: [
|
q: { op: "$and", criteria: [
|
||||||
{ op: "$or", criteria: types.map(value => ({ op: "$eq", attribute: "RecordSet", value })) },
|
{ op: "$and", criteria: [{ op: "$or", criteria: types.map(value => ({ op: "$eq", attribute: "RecordSet", value })) }] },
|
||||||
{ op: "$wi", attribute: "EffectiveDate", value1: range.startCloseDate, value2: range.endCloseDate, dynamicDate: false }
|
{ op: "$wi", attribute: "EffectiveDate", value1: "2026-06-01T00:00:00.000+0000", value2: "2026-09-01T00:00:00.000+0000" }
|
||||||
] }, keywords: null, keywordsFields: fields,
|
] }, keywords: null, keywordsFields: fields,
|
||||||
fields: [...fields, "CustomerAccount.PartyUniqueName", "CustomerAccount.PartyId", "CustomerAccount.PartyNumber", "CustomerAccount.Region_c", "CustomerAccount.PrimaryAddress.CountryCode"],
|
fields: [...fields, "CustomerAccount.PartyUniqueName", "CustomerAccount.PartyId", "CustomerAccount.PartyNumber", "CustomerAccount.Region_c", "CustomerAccount.PrimaryAddress.CountryCode"],
|
||||||
sort: [], language: "en", skipInValidFields: true, skipHiddenFromUIFields: true,
|
sort: null, language: "en", skipInValidFields: true, skipHiddenFromUIFields: true,
|
||||||
copiedFrom: "queries/d5dbcd01-9ff3-40be-9b5b-64744cbf7162", limit: 15, offset: 15
|
copiedFrom: "queries/d5dbcd01-9ff3-40be-9b5b-64744cbf7162", limit: 15, offset: 15
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -163,6 +189,15 @@ test("filter storage round-trip, WON_PENDING membership, search, sorting and ref
|
|||||||
assert.equal(reopened.filters().ownerSearch, "");
|
assert.equal(reopened.filters().ownerSearch, "");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("removed renewals reference falls back to Current Quarter without losing other filters", () => {
|
||||||
|
const storage = new Map([[FILTER_KEY, JSON.stringify({ period: "RENEWALS (Current + Past Due)", tableSearch: "cloud" })]]);
|
||||||
|
const h = loadContent({ storage });
|
||||||
|
const select = { value: "Current Quarter" };
|
||||||
|
h.restoreFilterPreferences(select);
|
||||||
|
assert.equal(select.value, "Current Quarter");
|
||||||
|
assert.equal(h.filters().tableSearch, "cloud");
|
||||||
|
});
|
||||||
|
|
||||||
test("malformed preferences fall back, while theme/type/cache keys retain their schema", () => {
|
test("malformed preferences fall back, while theme/type/cache keys retain their schema", () => {
|
||||||
const storage = new Map([[FILTER_KEY, "not JSON"], ["opportunities-extension-theme", "dark"],
|
const storage = new Map([[FILTER_KEY, "not JSON"], ["opportunities-extension-theme", "dark"],
|
||||||
[TYPE_KEY, JSON.stringify({ opportunityTypeValues: ["ORA_MYOPTIES", "UNKNOWN"], opptyProductsCacheHours: 24 })]]);
|
[TYPE_KEY, JSON.stringify({ opportunityTypeValues: ["ORA_MYOPTIES", "UNKNOWN"], opptyProductsCacheHours: 24 })]]);
|
||||||
|
|||||||
@@ -174,70 +174,28 @@ test("SR Save validates form, maps dynamic values, locks duplicate clicks and pr
|
|||||||
assert.equal(posts[0].message.phase, "prepare");
|
assert.equal(posts[0].message.phase, "prepare");
|
||||||
const preparedPayload = { ...payload, ReportedByPartyId: 303, SvcQuestionsCollection_c: [],
|
const preparedPayload = { ...payload, ReportedByPartyId: 303, SvcQuestionsCollection_c: [],
|
||||||
SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
|
SourceCd: "SVC_OPPORTUNITY_SCREEN", SeverityCd: "ORA_SVC_SEV2", ChannelTypeCd: "ORA_SVC_WEB" };
|
||||||
const preparedHeaders = {
|
posts[0].resolve({ ok: true, payload: preparedPayload });
|
||||||
requestSrTokenCatalog: { Authorization: "Session", "Content-Type": "application/vnd.oracle.adf.action+json", Origin: "https://example.invalid" },
|
|
||||||
requestPostServiceRequest: { Authorization: "Session", "Content-Type": "application/vnd.oracle.adf.action+json", Origin: "https://example.invalid", "vb-proxy-header-x-oracle-ectoken": "(generated after confirmation)" }
|
|
||||||
};
|
|
||||||
posts[0].resolve({ ok: true, payload: preparedPayload, headers: preparedHeaders });
|
|
||||||
await flush();
|
await flush();
|
||||||
const confirmation = () => h.nodes().find(node => node.className?.includes("opportunities-extension-sr-confirmation"));
|
assert.equal(h.nodes().some(node => node.className?.includes("opportunities-extension-sr-confirmation")), false);
|
||||||
const confirm = () => h.nodes().find(node => node.textContent === "Confirm Save");
|
assert.equal(h.nodes().some(node => node.textContent === "Confirm Save"), false);
|
||||||
assert.ok(confirmation());
|
assert.equal(posts.length, 2, "Save proceeds directly from preparation to creation");
|
||||||
const preview = h.nodes().find(node => node.tag === "pre");
|
assert.equal(posts[1].message.phase, "confirm");
|
||||||
assert.equal(preview.textContent, JSON.stringify(preparedPayload, null, 2));
|
assert.deepEqual(posts[1].message.payload, preparedPayload);
|
||||||
const previews = h.nodes().filter(node => node.tag === "pre");
|
assert.equal(save().textContent, "Saving...");
|
||||||
assert.equal(previews.length, 3);
|
|
||||||
assert.equal(previews[1].textContent, JSON.stringify(preparedHeaders.requestSrTokenCatalog, null, 2));
|
|
||||||
assert.equal(previews[2].textContent, JSON.stringify(preparedHeaders.requestPostServiceRequest, null, 2));
|
|
||||||
assert.ok(h.nodes().some(node => node.textContent === "x-appbuilder-client-id: not yet observed in SeaaS requests."));
|
|
||||||
assert.equal(h.form().parent.inert, true);
|
|
||||||
assert.equal(h.document.activeElement.textContent, "Cancel");
|
|
||||||
await submit();
|
await submit();
|
||||||
assert.equal(posts.length, 1, "awaiting confirmation must never send a creation request");
|
assert.equal(posts.length, 2, "duplicate clicks cannot create another SR");
|
||||||
confirm().focus();
|
posts[1].resolve({ ok: false, error: "Synthetic failure" });
|
||||||
confirmation().listeners.keydown({ key: "Tab", preventDefault() {}, stopPropagation() {} });
|
|
||||||
assert.equal(h.document.activeElement, preview);
|
|
||||||
h.nodes().filter(node => node.tag === "button" && node.textContent === "Cancel").at(-1).click();
|
|
||||||
await saving;
|
await saving;
|
||||||
assert.equal(posts.length, 1, "Cancel never creates SR");
|
assert.equal(notices.at(-1)[1], true);
|
||||||
assert.equal(confirmation(), undefined);
|
|
||||||
assert.equal(h.form().parent.inert, false);
|
|
||||||
assert.equal(save().disabled, false);
|
assert.equal(save().disabled, false);
|
||||||
assert.equal(h.nodes().find(node => node.name === "description").value, "Dynamic description");
|
assert.equal(h.nodes().find(node => node.name === "description").value, "Dynamic description");
|
||||||
assert.equal(h.document.activeElement, save());
|
|
||||||
assert.equal(notices.length, 0);
|
|
||||||
|
|
||||||
const escaped = submit();
|
|
||||||
posts[1].resolve({ ok: true, payload: preparedPayload });
|
|
||||||
await flush();
|
|
||||||
confirmation().listeners.keydown({ key: "Escape", preventDefault() {}, stopPropagation() {} });
|
|
||||||
await escaped;
|
|
||||||
assert.equal(posts.length, 2);
|
|
||||||
assert.ok(h.form(), "Escape closes only the confirmation");
|
|
||||||
|
|
||||||
const failed = submit();
|
|
||||||
posts[2].resolve({ ok: true, payload: preparedPayload });
|
|
||||||
await flush();
|
|
||||||
const confirmButton = confirm();
|
|
||||||
confirmButton.click();
|
|
||||||
confirmButton.click();
|
|
||||||
await flush();
|
|
||||||
assert.equal(posts.length, 4, "double confirm sends only one creation request");
|
|
||||||
assert.equal(posts[3].message.phase, "confirm");
|
|
||||||
assert.deepEqual(posts[3].message.payload, preparedPayload);
|
|
||||||
assert.equal(posts[3].message.payload.AccountPartyId, 100000000000101);
|
|
||||||
assert.equal(save().textContent, "Saving...");
|
|
||||||
posts[3].resolve({ ok: false, error: "Synthetic failure" });
|
|
||||||
await failed;
|
|
||||||
assert.equal(notices.at(-1)[1], true);
|
|
||||||
delete h.app.opportunitiesTableState.opportunitiesTableState.items[1].CustomerAccount.CountryCode;
|
delete h.app.opportunitiesTableState.opportunitiesTableState.items[1].CustomerAccount.CountryCode;
|
||||||
const retry = submit();
|
const retry = submit();
|
||||||
assert.equal(posts[4].message.payload.CountryText_c, "BR", "uses PrimaryAddress.CountryCode even without CustomerAccount.CountryCode");
|
assert.equal(posts[2].message.payload.CountryText_c, "BR", "uses PrimaryAddress.CountryCode even without CustomerAccount.CountryCode");
|
||||||
posts[4].resolve({ ok: true, payload: preparedPayload });
|
posts[2].resolve({ ok: true, payload: preparedPayload });
|
||||||
await flush();
|
await flush();
|
||||||
confirm().click();
|
assert.equal(posts.length, 4);
|
||||||
await flush();
|
posts[3].resolve({ ok: true, result: { SrId: 505, SrNumber: "SR505" } });
|
||||||
posts[5].resolve({ ok: true, result: { SrId: 505, SrNumber: "SR505" } });
|
|
||||||
await retry;
|
await retry;
|
||||||
assert.equal(h.form(), undefined);
|
assert.equal(h.form(), undefined);
|
||||||
assert.equal(notices.at(-1)[0], "Service Request saved successfully.");
|
assert.equal(notices.at(-1)[0], "Service Request saved successfully.");
|
||||||
|
|||||||
Reference in New Issue
Block a user