Dev

Okay, let's consolidate the final, refined plan into one set of code changes. This incorporates: Modifying Functions.js (getUaRiskProfileChoices) to provide adjusted keys (11, 21...) to the UI when planType is '18'. (Fixes UI Display) Creating an idempotent helper (_getFinalRiskProfileValue) in ProductModelHelpers.js to calculate the correct final state (base or adjusted). Refactoring autoSelect... functions in ProductModelHelpers.js to use the helper and set the correct final state based on business rules. Refining the listener (updateUaRiskProfile) in ProductModelHelpers.js to handle conversion on planType change and trigger recalculation for other dependency changes. Ensuring the listener configuration (ProductModelListeners.js) monitors the correct fields. File 1: Functions.js Action: Modify the internal getUaRiskProfileChoices function definition. // Inside Functions.js /** * NOTE: Find the ACTUAL internal function definition. It might have slightly different arguments * or internal logic for getting the base choices, adapt accordingly. */ functions.getUaRiskProfileChoices = function (invObjSameAcross, dummyOrProductArg, /* could be product or dummy */ productOrRiskArg /* ... other args maybe */) { // --- Determine the actual 'product' object --- // The wrapper function (line 5025) likely passes the real product as the 3rd or later argument. // Identify the correct argument holding the product model instance. var product = /* Logic to get the real product model instance from arguments */; // Example if 3rd arg is product: var product = productOrRiskArg; // Example if using context was needed: var product = functions.getProductFromUnifiedAccountContext(this); Needs 'this' passed correctly. // > if (!product) { // console.warn("Could not determine product in getUaRiskProfileChoices"); // Optional logging // Fallback to trying dummyOrProductArg maybe? Requires knowing the function signature. // For safety, return empty if product missing. return []; } var riskProfile = /* Logic to get riskProfile arg */; var low = /* Logic to get low arg */; var medium = /* Logic to get medium arg */; var high = /* Logic to get high arg */; // --- Original logic to determine BASE choices --- // This logic MUST remain unchanged and populate 'baseFilteredChoices' // with the standard {key: "10", value: "..."} etc. options. var baseFilteredChoices = []; // Initialize var accountProgram = product.get(ddField.Product.accountProgram.id); var holdProspectusExemptFund = product.get(ddField.Product.holdProspectusExemptFund.id); if (accountProgram) { if ((accountProgram === ddEnum.AccountProgram.Centrally_Managed) && (holdProspectusExemptFund === ddEnum.YesNo.YES)) { // PEF Only Logic (Original) baseFilteredChoices.push({ key: ddEnum.UARiskProfile.PROSPECTUS_EXEMPT_FUND, // Base key "90" value: global.Messages.get('choices.UARiskProfile.PROSPECTUS_EXEMPT_FUND') }); } else if ((accountProgram === ddEnum.AccountProgram.PM_Managed) || ((accountProgram === ddEnum.AccountProgram.Centrally_Managed) && (holdProspectusExemptFund === ddEnum.YesNo.NO))) { // Standard logic (Original) if ((invObjSameAcross === ddEnum.YesNo.YES) && !(product.isJoint())) { baseFilteredChoices = functions.allUaRiskProfiles(); // Uses BASE keys } else { baseFilteredChoices = functions.uaRiskProfileMatchingRiskProfile(riskProfile, low, medium, high); // Uses BASE keys } } } // --- End Original Logic --- // --- START NEW FHSA KEY ADJUSTMENT --- // Now, adjust the keys in the final array if planType is FHSA if (product && product.get(ddField.Product.planType.id) === '18') { var adjustedChoices = []; _.each(baseFilteredChoices, function(choice) { // Iterate the BASE choices determined above var baseKey = choice.key; var adjustedKey = baseKey; // Default if (baseKey && typeof baseKey === 'string') { var parsedKey = parseInt(baseKey, 10); if (!isNaN(parsedKey)) { // Apply +1 adjustment (handles 90->91 implicitly if PEF was the only choice) adjustedKey = (parsedKey + 1).toString(); } } adjustedChoices.push({ key: adjustedKey, // Use adjusted key (e.g., "11", "21", "91") value: choice.value // Keep original value identifier }); }); // Return the choices array with ADJUSTED keys specifically for FHSA return adjustedChoices; } else { // Not FHSA, return the standard BASE choices determined earlier return baseFilteredChoices; } // --- END NEW FHSA KEY ADJUSTMENT --- }; // --- E

Apr 8, 2025 - 04:33
 0
Dev

Okay, let's consolidate the final, refined plan into one set of code changes. This incorporates:

Modifying Functions.js (getUaRiskProfileChoices) to provide adjusted keys (11, 21...) to the UI when planType is '18'. (Fixes UI Display)
Creating an idempotent helper (_getFinalRiskProfileValue) in ProductModelHelpers.js to calculate the correct final state (base or adjusted).
Refactoring autoSelect... functions in ProductModelHelpers.js to use the helper and set the correct final state based on business rules.
Refining the listener (updateUaRiskProfile) in ProductModelHelpers.js to handle conversion on planType change and trigger recalculation for other dependency changes.
Ensuring the listener configuration (ProductModelListeners.js) monitors the correct fields.
File 1: Functions.js

Action: Modify the internal getUaRiskProfileChoices function definition.
// Inside Functions.js

/**
 * NOTE: Find the ACTUAL internal function definition. It might have slightly different arguments
 * or internal logic for getting the base choices, adapt accordingly.
 */
functions.getUaRiskProfileChoices = function (invObjSameAcross, dummyOrProductArg, /* could be product or dummy */ productOrRiskArg /* ... other args maybe */) {

    // --- Determine the actual 'product' object ---
    // The wrapper function (line 5025) likely passes the real product as the 3rd or later argument.
    // Identify the correct argument holding the product model instance.
    var product = /* Logic to get the real product model instance from arguments */;
    // Example if 3rd arg is product: var product = productOrRiskArg;
    // Example if using context was needed: var product = functions.getProductFromUnifiedAccountContext(this); Needs 'this' passed correctly.
    // <<< You MUST correctly identify the 'product' variable here >>>
    if (!product) {
       // console.warn("Could not determine product in getUaRiskProfileChoices"); // Optional logging
       // Fallback to trying dummyOrProductArg maybe? Requires knowing the function signature.
       // For safety, return empty if product missing.
       return [];
    }

    var riskProfile = /* Logic to get riskProfile arg */;
    var low = /* Logic to get low arg */;
    var medium = /* Logic to get medium arg */;
    var high = /* Logic to get high arg */;


    // --- Original logic to determine BASE choices ---
    // This logic MUST remain unchanged and populate 'baseFilteredChoices'
    // with the standard {key: "10", value: "..."} etc. options.
    var baseFilteredChoices = []; // Initialize
    var accountProgram = product.get(ddField.Product.accountProgram.id);
    var holdProspectusExemptFund = product.get(ddField.Product.holdProspectusExemptFund.id);

    if (accountProgram) {
        if ((accountProgram === ddEnum.AccountProgram.Centrally_Managed) && (holdProspectusExemptFund === ddEnum.YesNo.YES)) {
            // PEF Only Logic (Original)
            baseFilteredChoices.push({
                key: ddEnum.UARiskProfile.PROSPECTUS_EXEMPT_FUND, // Base key "90"
                value: global.Messages.get('choices.UARiskProfile.PROSPECTUS_EXEMPT_FUND')
            });
        } else if ((accountProgram === ddEnum.AccountProgram.PM_Managed) ||
                   ((accountProgram === ddEnum.AccountProgram.Centrally_Managed) && (holdProspectusExemptFund === ddEnum.YesNo.NO))) {
            // Standard logic (Original)
            if ((invObjSameAcross === ddEnum.YesNo.YES) && !(product.isJoint())) {
                baseFilteredChoices = functions.allUaRiskProfiles(); // Uses BASE keys
            } else {
                baseFilteredChoices = functions.uaRiskProfileMatchingRiskProfile(riskProfile, low, medium, high); // Uses BASE keys
            }
        }
    }
    // --- End Original Logic ---


    // --- START NEW FHSA KEY ADJUSTMENT ---
    // Now, adjust the keys in the final array if planType is FHSA
    if (product && product.get(ddField.Product.planType.id) === '18') {
        var adjustedChoices = [];
        _.each(baseFilteredChoices, function(choice) { // Iterate the BASE choices determined above
            var baseKey = choice.key;
            var adjustedKey = baseKey; // Default

            if (baseKey && typeof baseKey === 'string') {
                 var parsedKey = parseInt(baseKey, 10);
                 if (!isNaN(parsedKey)) {
                      // Apply +1 adjustment (handles 90->91 implicitly if PEF was the only choice)
                      adjustedKey = (parsedKey + 1).toString();
                 }
            }
            adjustedChoices.push({
                 key: adjustedKey,    // Use adjusted key (e.g., "11", "21", "91")
                 value: choice.value // Keep original value identifier
            });
        });
        // Return the choices array with ADJUSTED keys specifically for FHSA
        return adjustedChoices;
    } else {
        // Not FHSA, return the standard BASE choices determined earlier
        return baseFilteredChoices;
    }
    // --- END NEW FHSA KEY ADJUSTMENT ---
};

// --- Ensure all helper functions called above (allUaRiskProfiles, uaRiskProfileMatchingRiskProfile, etc.) ---
// --- remain UNCHANGED and work with BASE keys (10-90) ---
content_copy
download
Use code with caution.
JavaScript
File 2: ProductModelHelpers.js

Action: Add helper, refactor autoSelect... functions, replace updateUaRiskProfile.
// In ProductModelHelpers.js

// --- 1. Add/Refine the Idempotent Helper Function ---
/**
 * Determines the correct final uaRiskProfile value (base or adjusted)
 * based on a conceptual base value and the product's current planType.
 * Handles adjustment (X -> X+1) for FHSA and reversion (X+1 -> X) for non-FHSA.
 * Is idempotent (calling with adjusted value for FHSA won't change it).
 *
 * @param {string} baseOrCurrentValue - The conceptual base value OR the current model value.
 * @param {Backbone.Model} product - The product model instance.
 * @returns {string|undefined} The final correct value (string) or undefined if input is invalid/undefined.
 */
var _getFinalRiskProfileValue = function(baseOrCurrentValue, product) {
    var finalValue = baseOrCurrentValue;
    // Ensure product exists before trying to get planType
    if (!product) return baseOrCurrentValue;
    var isFHSA = (product.get(ddField.Product.planType.id) === '18');

    if (baseOrCurrentValue && typeof baseOrCurrentValue === 'string') {
        var parsedValue = parseInt(baseOrCurrentValue, 10);

        if (!isNaN(parsedValue)) {
            if (isFHSA) {
                // FHSA: Adjust BASE values up, leave ADJUSTED values alone
                if (parsedValue === 90) finalValue = "91";
                else if (parsedValue >= 10 && parsedValue <= 80) finalValue = (parsedValue + 1).toString();
                // else finalValue remains baseOrCurrentValue (if already 11-81 or 91)
            } else {
                // Non-FHSA: Revert ADJUSTED values down, leave BASE values alone
                if (parsedValue === 91) finalValue = "90";
                else if (baseOrCurrentValue.endsWith('1') && parsedValue > 10 && parsedValue <= 81) finalValue = (parsedValue - 1).toString();
                // else finalValue remains baseOrCurrentValue (if already 10-80 or 90)
            }
        }
        // If not parsable, finalValue remains baseOrCurrentValue
    }
    // Return undefined if input was undefined
    return finalValue;
};


// --- 2. Refactor autoSelect... Functions ---

// Refactor autoSelectUaRiskProfile
productModelHelpers.autoSelectUaRiskProfile = function(invObjSameAcross, accountProgram, holdProspectusExemptFund, riskProfile, product, low, medium, high) {
    // *** Use the EXACT field ID consistently ***
    var uaRiskProfileFieldId = "_regProduct[0].investmentObjectiveRisk.uaRiskProfile"; // Use actual ID confirmed from HTML
    var valueToSet = undefined; // Default to unsetting

    // Determine the conceptual BASE value based on logic
    var conceptualBaseValue = undefined;
    if (!accountProgram) {
        conceptualBaseValue = undefined;
    } else if (accountProgram === ddEnum.AccountProgram.Centrally_Managed) {
        if (!holdProspectusExemptFund) {
             conceptualBaseValue = undefined;
        } else if (holdProspectusExemptFund === ddEnum.YesNo.YES) {
             conceptualBaseValue = ddEnum.UARiskProfile.PROSPECTUS_EXEMPT_FUND; // "90"
        } else if (holdProspectusExemptFund === ddEnum.YesNo.NO) {
             // Delegate to get the BASE key if single option determined
             conceptualBaseValue = productModelHelpers._determineSingleOptionBaseKey(invObjSameAcross, riskProfile, product, low, medium, high);
        }
    } else if (accountProgram === ddEnum.AccountProgram.PM_Managed) {
         conceptualBaseValue = productModelHelpers._determineSingleOptionBaseKey(invObjSameAcross, riskProfile, product, low, medium, high);
    }
    // ... other conditions ...

    // Now determine the final value using the helper
    valueToSet = _getFinalRiskProfileValue(conceptualBaseValue, product);

    // --- Final Set/Unset ---
    var currentValue = product.get(uaRiskProfileFieldId);
    if (valueToSet !== undefined && currentValue !== valueToSet) {
        product.set(uaRiskProfileFieldId, valueToSet);
    } else if (valueToSet === undefined && currentValue !== undefined) {
        product.unset(uaRiskProfileFieldId);
    }
};

// New helper to just return BASE key from single option logic (or undefined)
// Replaces/Refactors the old autoSelectUaRiskProfileForSingleOption SET logic
productModelHelpers._determineSingleOptionBaseKey = function(invObjSameAcross, riskProfile, product, low, medium, high) {
    // Gets BASE choices (10-80)
    var uaRiskProfileOptions = functions.getUaRiskProfileChoices(invObjSameAcross, product, riskProfile, low, medium, high);
    var uaRiskProfileFieldId = "_regProduct[0].investmentObjectiveRisk.uaRiskProfile"; // Use actual ID
    var currentValue = product.get(uaRiskProfileFieldId); // Current value on model

    if (uaRiskProfileOptions.length === 1) {
        // Exactly one option applies programmatically
        return uaRiskProfileOptions[0].key; // Return the BASE key ("30")
    } else {
        // Multiple/Zero options - Validate existing value if one exists
        var existingUaRiskProfile = currentValue;
        if (existingUaRiskProfile) {
            // Derive BASE value from potentially adjusted existing value
            var baseExisting = existingUaRiskProfile;
            if (product.get(ddField.Product.planType.id) === '18') { // Check current plan type
                 var parsedExisting = parseInt(existingUaRiskProfile, 10);
                 if (!isNaN(parsedExisting)) {
                      if (parsedExisting === 91) baseExisting = "90";
                      else if (existingUaRiskProfile.endsWith('1') && parsedExisting > 10 && parsedExisting <= 81) baseExisting = (parsedExisting - 1).toString();
                 }
            }
            // Check if this base value is in the currently valid BASE options
            for (var i = 0; i < uaRiskProfileOptions.length; i++) {
                if (uaRiskProfileOptions[i].key === baseExisting) {
                    // If the base is valid, return the BASE key. The main function will adjust if needed.
                    return baseExisting;
                }
            }
        }
        // If no existing value OR existing value is no longer valid -> return undefined
        return undefined;
    }
};


// --- 3. Refine Listener Handler ---
productModelHelpers.updateUaRiskProfile = function(attributes) {
    var context = this;
    var product = context.product;
    if (!product) return;

    var uaRiskProfileFieldId = "_regProduct[0].investmentObjectiveRisk.uaRiskProfile"; // Use actual EXACT ID
    var planTypeFieldId = ddField.Product.planType.id;
    var planTypeChanged = attributes.hasOwnProperty(planTypeFieldId);
    var uaProfileChangedDirectly = attributes.hasOwnProperty(uaRiskProfileFieldId); // Did dropdown cause change?

    // --- 1. Handle PlanType Change (Convert Value) ---
    if (planTypeChanged && product.previous(planTypeFieldId) !== undefined) {
        var currentValue = product.get(uaRiskProfileFieldId);
        if (currentValue !== undefined) {
            // Calculate NEW correct value based on NEW planType using the helper
            var valueThatShouldBeSet = _getFinalRiskProfileValue(currentValue, product);
            if (valueThatShouldBeSet !== currentValue) {
                // Use silent ONLY if needed to prevent loops
                product.set(uaRiskProfileFieldId, valueThatShouldBeSet /*, {silent: true}*/);
            }
        } else {
             // If value was already undefined, maybe trigger autoSelect to set a default for the new planType?
             // Optional: Re-run autoSelect logic here if needed after clearing/transitioning
             // productModelHelpers.autoSelectUaRiskProfile(...)
        }
        // Do NOT return early - let other dependency logic run if needed
        // return; // REMOVED return
    }

    // --- 2. Handle Other Changes & Ensure Consistency ---
    // If triggered by uaRiskProfile dropdown OR other dependencies (Account Program etc.)
    // We need to ensure the final state is correct. Re-running autoSelect handles this.
    var dependenciesChanged = _.some(attributes, function(value, key) {
         // Check if any key OTHER than planType (which we just handled) changed
         return key !== planTypeFieldId;
    });

    if (dependenciesChanged) {
         // Rerun the main auto-select logic. It will call the helper and set the correct final state.
         var invObjSameAcross = context.client.get(ddField.Client.invObjSameAccross.id);
         var accountProgram = product.get(ddField.Product.accountProgram.id);
         var holdProspectusExemptFund = product.get(ddField.Product.holdProspectusExemptFund.id);
         var riskProfile = product.get(ddField.Product.investmentObjectiveRisk.id + '.' + ddField.InvestmentObjectiveRisk.riskProfile.id);
         var low = product.get(ddField.Product.investmentObjectiveRisk.id + '.' + ddField.InvestmentObjectiveRisk.riskToleranceLow.id);
         var medium = product.get(ddField.Product.investmentObjectiveRisk.id + '.' + ddField.InvestmentObjectiveRisk.riskToleranceMedium.id);
         var high = product.get(ddField.Product.investmentObjectiveRisk.id + '.' + ddField.InvestmentObjectiveRisk.riskToleranceHigh.id);

         productModelHelpers.autoSelectUaRiskProfile(invObjSameAcross, accountProgram, holdProspectusExemptFund, riskProfile, product, low, medium, high);
    }
};
content_copy
download
Use code with caution.
JavaScript
File 3: ProductModelListeners.js

Action: Ensure the listener configuration is correct.
// In ProductModelListeners.js (Conceptual)
{
    fieldIds: [
        // === Other original dependencies ===
        ddField.Product.accountProgram.id,
        ddField.Product.holdProspectusExemptFund.id,
        // Add other risk fields if they influence the available choices
        ddField.Product.investmentObjectiveRisk.id + "." + ddField.InvestmentObjectiveRisk.riskProfile.id,
        ddField.Product.investmentObjectiveRisk.id + "." + ddField.InvestmentObjectiveRisk.riskToleranceLow.id,
        ddField.Product.investmentObjectiveRisk.id + "." + ddField.InvestmentObjectiveRisk.riskToleranceMedium.id,
        ddField.Product.investmentObjectiveRisk.id + "." + ddField.InvestmentObjectiveRisk.riskToleranceHigh.id,
        // === Fields needed for FHSA logic ===
        ddField.Product.planType.id, // For conversion trigger
        "_regProduct[0].investmentObjectiveRisk.uaRiskProfile" // For direct changes & ensuring final state
        // Use the actual exact field ID string here ^
    ],
    listener: productModelHelpers.updateUaRiskProfile, // The refined handler
    // ... other properties like invokeOnLoad: true etc...
},
content_copy
download
Use code with caution.
JavaScript
This consolidated set of changes should achieve all goals: Correct UI display, correct model value for W360, and correct value conversion (not clearing) when planType changes.