Untitled
unknown
plain_text
a month ago
20 kB
5
Indexable
var ACWAPP = ACWAPP || {};
ACWAPP.AbuseCaseRibbonClose = (function () {
const STATUS_CLOSED = 100000009; // Closed
const ENTITY_LOGICAL = "acwapp_abusecases";
/* =========================
CLOSE REASON VALUES
========================= */
const CLOSE_REASON = {
INACCURATE_PROPERTY: 100000000,
INFORMANT_CHANGED: 100000001,
OTHERS: 100000002
};
function stripBraces(id) {
return (id || "").replace(/[{}]/g, "");
}
function isFirstSaveDone(primaryControl) {
if (!primaryControl) return false;
const formType = primaryControl.ui.getFormType();
const recordId = primaryControl.data.entity.getId();
// 1 = Create form, no saved record yet
return formType !== 1 && !!recordId;
}
/* =========================
ENABLE RULES (UPDATED)
========================= */
// Close enabled
function canEnableClose(primaryControl) {
if (!primaryControl) return false;
// Hide Close Case button before first save
if (!isFirstSaveDone(primaryControl)) {
return false;
}
const statusAttr = primaryControl.getAttribute("acwapp_casestatus");
const roles = Xrm.Utility.getGlobalContext().userSettings.roles.getAll();
// ===============================
// ROLE CONTROL (UPDATED)
// ===============================
// Admin always allowed
if (roles.some(r => r.name === "System Administrator")) {
return true;
}
// Block restricted roles
if (roles.some(r =>
r.name === "EO User" ||
r.name === "HRMO(MOT) Officer"
)) {
return false;
}
// Allow AM or ABOVE (EO + MOT)
const isAllowedRole = roles.some(r =>
r.name === "EO AM" ||
r.name === "EO Senior Manager" ||
r.name === "HRMO(MOT) AM" ||
r.name === "HRMO(MOT) Senior Manager"
);
if (!isAllowedRole) {
return false;
}
const isComplete = statusAttr &&
statusAttr.getValue() === 100000008; // COMPLETE
const isHRMOSenior = roles.some(r => r.name === "HRMO(MOT) Senior Manager");
const isEOSenior = roles.some(r => r.name === "EO Senior Manager");
if (isComplete) {
if (isHRMOSenior && typeof ownerUser !== "undefined" &&
hasRole(ownerUser, Role.HRMO_OFFICER)) {
return true;
}
if (isEOSenior &&
typeof ownerUser !== "undefined" &&
hasRole(ownerUser, Role.EO_USER) &&
typeof hasMatchingEstate === "function" &&
hasMatchingEstate(primaryControl, currentUser)) {
return true;
}
return false;
}
const attr = primaryControl.getAttribute("acwapp_reasonsforclosecase");
if (!attr) return true;
const value = attr.getValue();
if (value === null || value === undefined) return true;
return (
value === CLOSE_REASON.INACCURATE_PROPERTY ||
value === CLOSE_REASON.OTHERS
);
}
// Close & Recreate
function canEnableCloseAndRecreate(primaryControl) {
if (!primaryControl) return false;
const statusAttr = primaryControl.getAttribute("acwapp_casestatus");
const reasonAttr = primaryControl.getAttribute("acwapp_reasonsforclosecase");
const roles = Xrm.Utility.getGlobalContext().userSettings.roles.getAll();
// ===============================
// ROLE CONTROL (UPDATED)
// ===============================
// Admin always allowed
if (roles.some(r => r.name === "System Administrator")) {
return true;
}
// Block ALL EO + MOT Officer
if (roles.some(r =>
r.name === "EO User" ||
r.name === "EO AM" ||
r.name === "EO Senior Manager" ||
r.name === "HRMO(MOT) Officer"
)) {
return false;
}
// Allow ONLY MOT AM or ABOVE
const isAllowedRole = roles.some(r =>
r.name === "HRMO(MOT) AM" ||
r.name === "HRMO(MOT) Senior Manager"
);
if (!isAllowedRole) {
return false;
}
if (!reasonAttr) return false;
const reasonValue = reasonAttr.getValue();
if (reasonValue === CLOSE_REASON.INACCURATE_PROPERTY) {
return false;
}
if (reasonValue === null || reasonValue === undefined) {
return true;
}
const isValidReason =
reasonValue === CLOSE_REASON.INFORMANT_CHANGED ||
reasonValue === CLOSE_REASON.OTHERS;
if (!isValidReason) {
return false;
}
const isComplete =
statusAttr && statusAttr.getValue() === 100000008; // COMPLETE
const isHRMOSenior =
roles.some(r => r.name === "HRMO(MOT) Senior Manager");
if (isComplete) {
if (isHRMOSenior &&
typeof ownerUser !== "undefined" &&
hasRole(ownerUser, Role.HRMO_OFFICER)) {
return true;
}
return false;
}
return true;
}
/* =========================
FIELD LOCKING
========================= */
function lockAllFieldsIfClosed(fc) {
const statusAttr = fc.getAttribute("acwapp_casestatus");
if (!statusAttr) return;
if (statusAttr.getValue() === STATUS_CLOSED) {
console.log("[Close] Locking all fields (status = CLOSED)");
// Lock everything
fc.ui.tabs.forEach(tab => {
tab.sections.forEach(section => {
section.controls.forEach(control => {
if (control && control.setDisabled) {
control.setDisabled(true);
}
});
});
});
// Unlock allowed fields
[
"acwapp_reasonsforclosecase",
"acwapp_closereason"
].forEach(fieldName => {
const ctrl = fc.getControl(fieldName);
if (ctrl) ctrl.setDisabled(false);
});
}
}
/* =========================
CLOSE CASE BUTTON
========================= */
function closeCase(primaryControl) {
console.log("[Command][Close] Clicked");
const fc = primaryControl;
const recordId = stripBraces(fc.data.entity.getId());
if (!recordId) return;
Xrm.WebApi.updateRecord(ENTITY_LOGICAL, recordId, {
acwapp_casestatus: STATUS_CLOSED,
acwapp_closetype: 100000000 // Normal Close
}).then(() => {
const statusAttr = fc.getAttribute("acwapp_casestatus");
const closeTypeAttr = fc.getAttribute("acwapp_closetype");
if (statusAttr) {
statusAttr.setValue(STATUS_CLOSED);
statusAttr.setSubmitMode("always");
statusAttr.fireOnChange();
}
if (closeTypeAttr) {
closeTypeAttr.setValue(100000000);
closeTypeAttr.setSubmitMode("always");
closeTypeAttr.fireOnChange();
}
console.log("[Close] Normal close initiated");
fc.ui.refreshRibbon();
}).catch(err => {
console.error("[Close] Update failed", err);
});
}
/* =========================
CLOSE & RECREATE
========================= */
async function closeAndRecreate(primaryControl) {
console.log("[Command][Close & Recreate] Clicked");
const fc = primaryControl;
const recordId = stripBraces(fc.data.entity.getId());
if (!recordId) return;
const statusAttr = fc.getAttribute("acwapp_casestatus");
const closeTypeAttr = fc.getAttribute("acwapp_closetype");
const recreateReasonAttr = fc.getAttribute("acwapp_reasonsforcloseandrecreatecase");
const recreateReasonCtrl = fc.getControl("acwapp_reasonsforcloseandrecreatecase");
const currentStatus = statusAttr?.getValue();
// =========================================
// FIRST CLICK → Move to CLOSED
// =========================================
if (currentStatus !== STATUS_CLOSED) {
await Xrm.WebApi.updateRecord(ENTITY_LOGICAL, recordId, {
acwapp_casestatus: STATUS_CLOSED,
acwapp_closetype: 100000001 // Close & Recreate
});
if (statusAttr) {
statusAttr.setValue(STATUS_CLOSED);
statusAttr.setSubmitMode("always");
statusAttr.fireOnChange();
}
if (closeTypeAttr) {
closeTypeAttr.setValue(100000001);
closeTypeAttr.setSubmitMode("always");
closeTypeAttr.fireOnChange();
}
console.log("[Close & Recreate] Status moved to CLOSED");
fc.ui.refreshRibbon();
return;
}
// =========================================
// SECOND CLICK → Validate Reason
// =========================================
const recreateReason = recreateReasonAttr?.getValue();
if (!recreateReason) {
recreateReasonCtrl?.setNotification(
"Please select a Reason for Close & Recreate.",
"req_recreate_reason"
);
return;
}
recreateReasonCtrl?.clearNotification("req_recreate_reason");
console.log("[Close & Recreate] Triggering recreateCase()");
await recreateCase(fc);
}
async function isCaseAlreadyRecreated(oldCaseId) {
const result = await Xrm.WebApi.retrieveMultipleRecords(
"acwapp_abusecases",
`?$select=acwapp_abusecasesid&$filter=_acwapp_closedcase_value eq ${oldCaseId}`
);
return result.entities.length > 0;
}
// RECREATE FUNCTION
async function recreateCase(fc) {
console.log("=== RECREATE CASE START ===");
Xrm.Utility.showProgressIndicator("Creating new case...");
try {
let oldCaseId = fc.data.entity.getId();
if (!oldCaseId) return;
oldCaseId = oldCaseId.replace(/[{}]/g, "");
// CHECK IF ALREADY RECREATED
const alreadyRecreated = await isCaseAlreadyRecreated(oldCaseId);
if (alreadyRecreated) {
Xrm.Utility.closeProgressIndicator();
fc.ui.setFormNotification(
"This case has already been recreated.",
"ERROR",
"recreate_blocked"
);
console.warn("Recreate blocked: case already recreated.");
return;
}
// continue normal recreate flow
const newCaseId = await cloneParentCase(oldCaseId);
await cloneChildRecords(
"acwapp_suspectedabuse",
oldCaseId,
newCaseId,
"acwapp_suspectedabuseid"
);
await cloneChildRecords(
"acwapp_familycomposition",
oldCaseId,
newCaseId,
"acwapp_familycompositionid"
);
Xrm.Utility.closeProgressIndicator();
fc.ui.setFormNotification(
"This case has been recreated.",
"INFO",
"recreate_success"
);
} catch (err) {
console.error("=== RECREATE CASE FAILED ===", err);
Xrm.Utility.closeProgressIndicator();
fc.ui.setFormNotification(
"Failed to recreate case.",
"ERROR",
"recreate_error"
);
}
}
async function getRootCaseId(caseId) {
let currentId = caseId;
while (true) {
const record = await Xrm.WebApi.retrieveRecord(
"acwapp_abusecases",
currentId,
"?$select=_acwapp_closedcase_value"
);
// If no closed case → this is the root
if (!record._acwapp_closedcase_value) {
return currentId;
}
currentId = record._acwapp_closedcase_value;
}
}
// Clone Parent Case
async function cloneParentCase(oldCaseId) {
// STEP 1: Find ROOT case (for naming only)
const rootCaseId = await getRootCaseId(oldCaseId);
// STEP 2: Get ROOT case name
const rootRecord = await Xrm.WebApi.retrieveRecord(
"acwapp_abusecases",
rootCaseId,
"?$select=acwapp_name"
);
const baseName = rootRecord.acwapp_name;
// Clean base name (remove up to 100 characters if any)
const cleanBase = baseName.replace(/[A-Z]{1,100}$/, "");
// STEP 3: Count ALL records that start with the base name
const existing = await Xrm.WebApi.retrieveMultipleRecords(
"acwapp_abusecases",
`?$select=acwapp_name&$filter=startswith(acwapp_name,'${cleanBase}')`
);
// Exclude the root itself
const count = existing.entities.length - 1;
// Excel-style suffix generator (A, B, ... Z, AA, AB, AC ...)
function numberToLetters(num) {
let letters = "";
while (num >= 0) {
letters = String.fromCharCode((num % 26) + 65) + letters;
num = Math.floor(num / 26) - 1;
}
return letters;
}
const suffix = numberToLetters(count);
const finalName = cleanBase + suffix;
// STEP 4: Clone full record
const oldRecord = await Xrm.WebApi.retrieveRecord(
"acwapp_abusecases",
oldCaseId
);
const payload = {};
for (const key in oldRecord) {
if (
key === "acwapp_abusecasesid" ||
key === "statecode" ||
key === "statuscode" ||
key === "createdon" ||
key === "modifiedon" ||
key === "createdby" ||
key === "modifiedby" ||
key === "ownerid" ||
key === "owningbusinessunit" ||
key === "owninguser" ||
key === "versionnumber" ||
key === "timezoneruleversionnumber" ||
key.includes("@odata") ||
key.includes("@")
) continue;
if (key.startsWith("_") && key.endsWith("_value")) {
const lookupValue = oldRecord[key];
const navProperty =
oldRecord[`${key}@Microsoft.Dynamics.CRM.associatednavigationproperty`];
const entityLogicalName =
oldRecord[`${key}@Microsoft.Dynamics.CRM.lookuplogicalname`];
if (lookupValue && navProperty && entityLogicalName) {
payload[`${navProperty}@odata.bind`] =
`/${entityLogicalName}s(${lookupValue})`;
}
continue;
}
if (typeof oldRecord[key] === "object") continue;
payload[key] = oldRecord[key];
}
// Reset fields
payload.acwapp_casestatus = 100000000; // CREATE
payload.acwapp_closetype = null;
payload.acwapp_reasonsforclosecase = null;
payload.acwapp_reasonsforcloseandrecreatecase = null;
// 🔥 IMPORTANT: Link to CURRENT record (one-child-per-parent)
payload["[email protected]"] =
`/acwapp_abusecaseses(${oldCaseId})`;
// STEP 5: Create record (Auto Number runs first)
const result = await Xrm.WebApi.createRecord(
"acwapp_abusecases",
payload
);
const newCaseId = result.id.replace(/[{}]/g, "");
// STEP 6: Overwrite Auto Number with correct suffix
await Xrm.WebApi.updateRecord("acwapp_abusecases", newCaseId, {
acwapp_name: finalName
});
return newCaseId;
}
// Clone Child Record
async function cloneChildRecords(entityLogicalName, oldCaseId, newCaseId, primaryIdField) {
const records = await Xrm.WebApi.retrieveMultipleRecords(
entityLogicalName,
`?$select=*&$filter=_acwapp_abusecase_value eq ${oldCaseId}`
);
console.log(`${entityLogicalName} found:`, records.entities.length);
for (const oldRecord of records.entities) {
const payload = {};
let caseNavigationProperty = null;
for (const key in oldRecord) {
if (
key === primaryIdField ||
key === "statecode" ||
key === "statuscode" ||
key === "createdon" ||
key === "modifiedon" ||
key === "createdby" ||
key === "modifiedby" ||
key === "ownerid" ||
key === "owningbusinessunit" ||
key === "owninguser" ||
key === "versionnumber" ||
key.includes("@odata") ||
key.includes("@")
) continue;
if (key.startsWith("_") && key.endsWith("_value")) {
const lookupValue = oldRecord[key];
const navProperty =
oldRecord[`${key}@Microsoft.Dynamics.CRM.associatednavigationproperty`];
const entityLogicalName =
oldRecord[`${key}@Microsoft.Dynamics.CRM.lookuplogicalname`];
if (!lookupValue || !navProperty || !entityLogicalName) continue;
if (key === "_acwapp_abusecase_value") {
caseNavigationProperty = navProperty;
continue;
}
payload[`${navProperty}@odata.bind`] =
`/${entityLogicalName}s(${lookupValue})`;
continue;
}
if (typeof oldRecord[key] === "object") continue;
payload[key] = oldRecord[key];
}
if (!caseNavigationProperty) {
throw new Error(`Navigation property not detected for ${entityLogicalName}`);
}
payload[`${caseNavigationProperty}@odata.bind`] =
`/acwapp_abusecaseses(${newCaseId})`;
await Xrm.WebApi.createRecord(entityLogicalName, payload);
}
}
return {
closeCase,
closeAndRecreate,
canEnableClose,
canEnableCloseAndRecreate
};
})();Editor is loading...
Leave a Comment