Untitled

 avatar
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