Friday, April 25, 2025

Enhancing Readability of Custom Report Fields Using SSRS Expressions (Switch case)

 Hi Everyone,

I have received a custom report where one of the fields displays non-readable strings based on different processing levels. To improve clarity, I need to convert these codes into meaningful descriptions. To achieve this, I'm utilizing the Switch function within the SSRS report field expressions.

=Switch(

    Fields!Name.Value = "Step1", "L1 Team Review",

    Fields!Name.Value = "Step2", "L2 Team Review",

    Fields!Name.Value = "Step3", "L3 Team Review"

)

In this expression, when the Name field has a specific value (e.g., "Step1"), it displays the corresponding descriptive text ("L1 Team Review"). If none of the specified conditions are met, the expression returns Nothing by default.​

Handling Unmatched Values:

To display the original value when it doesn't match any specified condition, you can add a default case using True as the final condition:​

=Switch(

    Fields!Name.Value = "Step1", "L1 Team Review",

    Fields!Name.Value = "Step2", "L2 Team Review",

    Fields!Name.Value = "Step3", "L3 Team Review",

    True, Fields!Name.Value

)

This approach ensures that if the Name field's value doesn't match any of the specified cases, the original value is displayed. This technique is commonly used in SSRS to provide a default case in Switch expressions. ​

Note on Single Value Replacement:

For replacing a single value, the Replace function can be used:​

=Replace(Fields!Name.Value, "Step1", "L1 Team Review")

However, this method is limited to replacing one value at a time. For multiple replacements, the Switch function is more efficient.​

Additional Considerations:

Handling Null or Empty Values: To prevent errors when the Name field is null or empty, incorporate a check using IsNothing:​

  =IIF(IsNothing(Fields!Name.Value), "No Data", 

      Switch(

          Fields!Name.Value = "Step1", "L1 Team Review",

          Fields!Name.Value = "Step2", "L2 Team Review",

          Fields!Name.Value = "Step3", "L3 Team Review",

          True, Fields!Name.Value

      )

  )

This ensures that if the Name field is null, the expression returns "No Data" instead of causing an error. 

Handling Dimension Data: Validating Defaults and Offsets in Code

 Hi everyone,

Recently, I encountered a requirement to create a journal entry against a vendor. However, there’s an important condition: if the vendor has a matching policy enabled, the system should restrict creating a ledger journal for that vendor.

To handle this, I implemented a validation that triggers whenever a Vendor ID is selected at the journal line level. This check ensures that if the vendor has a matching policy, the system will block the action and notify the user accordingly.

In this post, I’ll Walk you through:

  • How I implemented the vendor validation logic

  • How default and offset dimension validation works

The SegmentedEntryControl is used to input ledger accounts and financial dimension combinations. To enforce custom validation logic, follow these steps:​

  1. Create a CoC Extension for SegmentedEntryControl: Use the Chain of Command to extend the SegmentedEntryControl class.

  2. Override the validate () Method: Within your extension, override the validate () method to include your custom validation logic.

  3. Implement Vendor Matching Policy Check: In your overridden method, retrieve the selected Vendor ID and check if the vendor has a matching policy enabled. If so, prevent the creation of a ledger journal.

Here's a sample implementation:

[ExtensionOf(classStr(SegmentedEntryControl))]
 final class SegmentedEntryControl_Extension
{
    public boolean validate()
    {
        boolean isValid = true;
       
        DimensionControlSegment segment;
        VendTable  VendTable;
        LedgerJournalName LedgerJournalName;

        isValid = next validate();

       //Common table = formRefDataSource.cursor();
        str journalName = dimensionController.parmJournalName();;
       // LedgerJournalTrans ledgerJournalLine = table;
        Ynv_LedgerJournalName = LedgerJournalName::find(journalName);
        EnumName accountType = accountTypeEnumType;
        int number = dimensionController.parmAccountType();
        ModuleInventCustVend model = dimensionController.parmCustVend();

        if (currentControlInfo && number == 2)//&& ledgerJournalLine.AccountType == LedgerJournalACType::Vend)
        {
            // Print segment error messages
            for (int ind = 1; ind <= currentControlInfo.parmSegments().lastIndex();ind++)
            {
                segment = currentControlInfo.parmSegments().value(ind);                

                VendTable = VendTable::find(segment.parmValue());

                if ((VendTable.MatchingPolicy == PurchMatchingPolicyWithNotSetOption::ThreeWayMatch || VendTable.MatchingPolicy  == PurchMatchingPolicyWithNotSetOption::TwoWayMatch))
                {
                    throw Error (strFmt("Invoice journal does not create without purchase order of this %1 vendor",VendTable.AccountNum));
                  
                    isValid =false;
                }
                else if(VendTable.MatchingPolicy == PurchMatchingPolicyWithNotSetOption::NotSet)
                {
                    if((Vend Parameters::find().MatchingPolicy == PurchMatchingPolicyOption::ThreeWayMatch || VendParameters::find().MatchingPolicy == PurchMatchingPolicyOption::TwoWayMatch))
                    {
                        throw Error(strFmt("Invoice journal does not create without purchase order of this %1 vendor",_VendTable.AccountNum));
                      
                        isValid =false;
                    }
                }

            }
        }
        return isValid;
    }

}

The validate() method in the SegmentedEntryControl class can be overridden using Chain of Command (CoC) to apply custom validation logic to both the Ledger Dimension and Offset Dimension fields in Dynamics 365 Finance and Operations. This approach ensures that your validation logic is consistently applied across different segmented entry controls.