In Assette, it is common for firms to create custom Data Blocks that source data from system-defined blocks, such as the Account Master Data Block. In this article, we’ll refer to any such custom Data Block that queries the Account Master Data Block simply as “CustomBlock”. The CustomBlock Data Block is intended represent a Data Block which retrieves information for a single account based on user input.
This article explains why this happens, how to handle it properly, and how the behavior changes depending on whether you’re using one or multiple filtering parameters.
Understanding the Account Master Data Block #
The Account Master Data Block is a System Data Block provided by Assette. It includes complex logic to support a wide variety of filtering use cases across products, strategies, countries, and more. It is used behind the scenes by many components, and its behavior is intentionally flexible.
However, because of this flexibility, its filtering logic can behave differently based on the parameters passed to it — especially these three:
- Code
- AccountCode
- AccountCodeList
How Code, AccountCode, and AccountCodeList Interact #
When you pass parameters to AccountMaster, it goes through internal pre-processing logic that determines how the filtering will be applied. Here’s how these parameters influence the final behavior:
Code #
This is the main identifier used by AccountMaster Data Block to retrieve specific accounts.
If Code
contains multiple values (e.g., "12345,67890"
or "12345|67890"
), the logic assumes it is a list and moves the value to AccountCodeList
. Code
is then cleared to avoid ambiguity. As seen in the Account Master Data Block:
if Code != "" and "," in Code:
AccountCodeList = str(Code).replace(",", "|")
Code = ""
elif Code != "" and "|" in Code:
AccountCodeList = Code
Code = ""
AccountCode #
- This is typically the parameter passed from a user interface or calling block when the user selects a single account.
- Inside AccountMaster, if AccountCode is populated, it is automatically copied to the
Code
parameter ifCode
is not already set.
if (params.get("AccountCode") or "") != "":
Code = params.get("AccountCode")
if (params.get("Code") or "") == "":
params["Code"] = Code
AccountCodeList #
- This parameter is used when multiple accounts are being requested.
- If
Code
is treated as a list, it ends up here. This means your intended single-account filter could be silently broadened, causing AccountMaster to return multiple accounts.
The Role of Custom Data Blocks #
Many firms use a custom block, referred to here as “CustomBlock”, which is responsible for returning details about a single account. This block typically reads from AccountMaster and extracts specific fields like AccountID, Name, StrategyName, ProductCode, or RepAccountCode. This version assumes that the read() call will return exactly one record, which is often not the case due to the behavior described above.
A typical simplified definition of CustomBlock might look like this:
errors = []
data = {}
account_detail_keyset = {"AccountID", "Name", "StrategyName", "ProductCode", "RepAccountCode"}
try:
account_master = read("AccountMaster", {"Code": str(params["AccountCode"])})["data"][0]
for key in account_detail_keyset:
if key in account_master:
data[key] = account_master[key]
except:
errors.append("Error reading AccountMaster block.")
response["data"] = data
response["errors"] = errors
Problem Scenario: Only One Parameter Is Used #
When only one filtering parameter is passed — such as AccountCode — the results can be unpredictable if the value is misinterpreted by AccountMaster.
{
"AccountCode": "bblcvmodel"
}
If “bblcvmodel” is interpreted as a list (e.g., if it accidentally includes a comma or pipe), it is moved to AccountCodeList, and Code is cleared. As a result, the AccountMaster block performs a broad search, possibly returning multiple accounts. In your custom CustomBlock, the line:
account_master = read("AccountMaster", {"Code": str(params["AccountCode"])})["data"][0]
will return the first record, which may not be the correct account.
Solution: Filter the Result Locally in CustomBlock #
To ensure correctness, especially when only one parameter is being used, you should:
- Allow
AccountMaster
to return multiple results. - Apply local filtering in your CustomBlock block to find the specific match.
Updated Logic #
account_master_data = read("AccountMaster", {"Code": str(params["AccountCode"])})["data"]
account_master = next(
(item for item in account_master_data if item.get("Code") == str(params["AccountCode"])),
None
)
if account_master:
for key in bg_account_detail_keyset:
if key in account_master:
data[key] = account_master[key]
This ensures that even if AccountMaster returns multiple records, you only use the one that matches the provided AccountCode.
When Two or More Parameters Are Used #
If you provide multiple parameters — such as both ProductCode and StrategyName — the filtering behavior in AccountMaster becomes more targeted.
filters =
"ProductCode": "EQUITY",
"Strategy": "Core"
}
account_master_data = read("AccountMaster", filters)["data"]
In this case, AccountMaster
will try to return only those accounts that match both conditions. However, even this may not guarantee a single result, depending on your data.
You should still apply local filtering inside CustomBlock if you’re looking for a specific account:
account_master = next(
(item for item in account_master_data
if item.get("ProductCode") == "EQUITY" and item.get("StrategyName") == "Core"),
None
)
Dynamic Parameter Handling #
You can dynamically construct your filter dictionary based on the parameters the user provides:
filters = {}
if params.get("ProductCode"):
filters["ProductCode"] = params["ProductCode"]
if params.get("StrategyName"):
filters["Strategy"] = params["StrategyName"]
if params.get("AccountCode"):
filters["Code"] = params["AccountCode"]
account_master_data = read("AccountMaster", filters)["data"]
This keeps your logic flexible and scalable without assuming which parameters will always be available.
Best Practices #
The following table summarized the various scenarios discussed above and provided the recommended approach in each case.
Scenario | Recommended Approach |
---|---|
Only AccountCode used | Pass Code to AccountMaster, filter locally in CustomBlock |
Only non-unique param used (e.g., StrategyName ) | Pass it as filter, expect multiple, filter locally |
Multiple parameters used | Pass all filters, still validate locally in case of multiple results |
Want strict match on single account | Always apply next(... for item in data if item["Code"] == ...) |
Relying solely on ["data"][0] | Not recommended — may return wrong account |