Fixed the date ranges for the Reference options

This commit is contained in:
2026-10-19 18:10:16 -03:00
parent cc025e488d
commit 71d550dfb1
10 changed files with 135 additions and 372 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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",

View File

@@ -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;

View File

@@ -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))
}; };
} }

View File

@@ -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; }

View File

@@ -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,

View File

@@ -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"
}); });
}); });

View File

@@ -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 })]]);

View File

@@ -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.");