SfSkills Domains Docs GitHub

Home / data / financial-data-quality

financial-data-quality

Use when validating FinancialAccount record integrity in Financial Services Cloud, detecting duplicate financial records, or reconciling FSC data against core banking and custodial source systems.

Open the skill folder on GitHub


Financial Data Quality

Activate this skill when a practitioner needs to enforce, audit, or recover data quality on Financial Services Cloud financial records — specifically FinancialAccount, FinancialHolding, and household rollup aggregations. This skill covers FSC-specific validation constraints, the gap in standard Duplicate Rule coverage for FinancialAccount, and the reconciliation patterns required when FSC data must be kept consistent with upstream core banking or custodial systems.

This skill does not cover generic Salesforce data quality (see data/data-quality-and-governance), step-by-step Duplicate Rule configuration for standard objects, or bulk data migration tooling.


Before Starting

Gather this context before working on anything in this domain:


Core Concepts

FinancialAccount Validation — What Platform Enforces vs. What You Must Build

Salesforce platform validation rules work on FinancialAccount the same as any other object. You can define formula-based validation to enforce required field combinations, value ranges, and cross-field consistency. However, several behaviors are specific to FSC:

Duplicate Detection on FinancialAccount — Standard Gap and Custom Solutions

Standard Salesforce Duplicate Rules are defined for Account, Contact, and Lead. The FinancialAccount object (both Core FSC and managed-package FinServ__FinancialAccount__c) has no out-of-box Duplicate Rule support. Attempting to create a standard Duplicate Rule targeting FinancialAccount directly through Setup will fail — there is no native Matching Rule for the object.

Two supported approaches for FSC duplicate detection:

  1. Matching Rules on related Account/Contact: Create a custom Matching Rule on the Account or Contact object that uses a field from the related financial account (e.g., external account number stored on the contact). This is indirect and only works when the duplicate signal exists on the parent object. Three platform limits shape this option and matter for financial data:
    • Cardinality caps: A maximum of five active matching rules per object and 25 active matching rules org-wide. FSC and any managed-package matching rules count against this budget, so custom rules on Account/Contact compete for the same slots as standard ones — plan the set deliberately rather than adding one rule per edge case.
    • Fuzzy matching is Latin-only: Standard and custom matching rules using fuzzy matching methods support only Latin characters. Account holder or beneficiary names with diacritics or non-Latin scripts will not match reliably under fuzzy methods — use exact matching for those fields.
    • Shield Platform Encryption narrows matching: Custom matching rules work only against deterministically encrypted fields; probabilistic encryption is not supported. With Shield enabled the matching service also searches fewer candidates (100 instead of 200), so encrypted PII used as a match key can produce fewer or no detected duplicates.
    • Person Account skip: If a matching rule includes a Contact lookup field and the looked-up value is a Person Account, the rule is not applied. For Person Account households, match on account lookup fields instead.
  2. Apex trigger-based duplicate detection: For financial records specifically, implement a before insert Apex trigger on FinancialAccount (or FinServ__FinancialAccount__c) that queries for existing records with matching key attributes (e.g., ExternalAccountNumber__c, AccountNumber, FinancialAccountType + owner combination). Use Database.query() with selective filters. Block the insert and return a meaningful error message if a match is found.
  3. Programmatically invoking or bypassing standard rules from Apex: When the duplicate signal lives on the related Account or Contact rather than on FinancialAccount itself, you do not have to re-implement matching by hand. Datacloud.FindDuplicates (and Datacloud.FindDuplicatesByIds) run the object's active duplicate rules against a list of sObjects and return the matched records, so a FinancialAccount trigger can check for parent-record duplicates before promoting a load. Conversely, when a governed integration must save parent records that would otherwise trip a duplicate Alert, set Database.DMLOptions.DuplicateRuleHeader.allowSave = true to bypass the alert on that DML and save the record, and use runAsCurrentUser to control whether the current user's sharing rules are enforced while the duplicate rules run. allowSave applies only to the Alert action — it does not override a Block action, and a record flagged by a blocking duplicate rule cannot be saved regardless of this setting. (Integration paths skip the Alert/Block UI experience anyway; allowSave is the governed way to keep the rule active for interactive users while letting a service account save through an Alert.) FindDuplicates is capped at 50 sObjects per call and all must be the same type — batch parent lookups accordingly.

The Apex trigger approach is the authoritative pattern for FSC financial record deduplication. It must be bulk-safe (process Trigger.new as a list, not record-by-record), governor-limit-aware, and bypass-capable for integration contexts.

Complementary batch layer — Duplicate Jobs for records at rest: The three approaches above catch duplicates at insert time. For the household-level Account, Contact, and Lead records that already exist and do support standard matching, Duplicate Jobs run a matching rule as a batch over existing records, produce Duplicate Record Sets for review and merge, and email results on completion. Use them as a periodic retroactive sweep alongside insert-time detection — noting the feature is available only in Performance and Unlimited Editions.

FSC Rollup Batch — Failure Modes and Reconciliation

The FSC rollup engine is an asynchronous batch process that reads all FinancialAccount and AssetsAndLiabilities records associated with a household and writes aggregated values (Total Assets, Total Liabilities, Net Worth, Total AUM) to fields on the household Account. Key behaviors affecting data quality:

Monitoring pattern: A scheduled Apex job that reads the last rollup completion timestamp and fires a Platform Event or sends an alert if the timestamp is older than the expected batch interval is the standard operational monitoring approach.

Source System Reconciliation Pattern

When FSC financial data must stay synchronized with an external core banking or custodial system, a reconciliation process is required because the FSC rollup alone cannot detect discrepancies originating in the source system.

Recommended reconciliation architecture:

  1. Source system exports a daily or intraday reconciliation feed (CSV or JSON) containing account-level balance and status snapshots.
  2. An integration layer (MuleSoft, Apex scheduled batch, or external ETL) loads the feed into a staging object (e.g., AccountReconciliationStaging__c) with fields: ExternalAccountId, SourceBalance, SourceStatus, FeedTimestamp.
  3. A reconciliation Apex batch compares each staging record against the corresponding FinancialAccount in FSC, computing variance (abs(FSC_Balance - SourceBalance)).
  4. Records with variance above a configurable threshold are written to a FinancialAccountDiscrepancy__c object for review and resolution.
  5. A reconciliation dashboard in CRM Analytics or a report highlights open discrepancies by advisor, branch, or account type.

Common Patterns

Pattern: Apex Trigger Duplicate Check on FinancialAccount

When to use: Any time a new FinancialAccount record could be a duplicate of an existing record — most commonly during core banking integration loads and bulk migrations.

How it works:

trigger FinancialAccountDuplicateCheck on FinancialAccount (before insert) {
    // Collect external account numbers from incoming records
    Set<String> externalIds = new Set<String>();
    for (FinancialAccount fa : Trigger.new) {
        if (fa.ExternalAccountNumber__c != null) {
            externalIds.add(fa.ExternalAccountNumber__c);
        }
    }

    if (externalIds.isEmpty()) return;

    // Query existing FinancialAccount records with matching external IDs
    Map<String, FinancialAccount> existingByExtId = new Map<String, FinancialAccount>();
    for (FinancialAccount existing : [
        SELECT Id, ExternalAccountNumber__c, Name
        FROM FinancialAccount
        WHERE ExternalAccountNumber__c IN :externalIds
    ]) {
        existingByExtId.put(existing.ExternalAccountNumber__c, existing);
    }

    // Block duplicates with an error on the trigger record
    for (FinancialAccount fa : Trigger.new) {
        if (fa.ExternalAccountNumber__c != null
                && existingByExtId.containsKey(fa.ExternalAccountNumber__c)) {
            fa.addError('Duplicate FinancialAccount: ExternalAccountNumber '
                + fa.ExternalAccountNumber__c
                + ' already exists on record '
                + existingByExtId.get(fa.ExternalAccountNumber__c).Id);
        }
    }
}

Why not standard Duplicate Rules: FinancialAccount has no standard Matching Rule support. A standard Duplicate Rule cannot be configured for this object in Setup. The trigger approach is the only in-platform mechanism for blocking duplicate financial records at insert time.

Pattern: Validation Rule with RecordType Guard and Integration Bypass

When to use: Enforcing required fields on FinancialAccount that vary by account type (Investment vs. Bank vs. Loan) while allowing integration users to bypass during bulk loads.

How it works:

Validation rule formula (example: Investment account requires ExternalAccountNumber):

AND(
    NOT($Permission.Bypass_FSC_Validation),
    ISPICKVAL(RecordType.DeveloperName, 'Investment'),
    ISBLANK(ExternalAccountNumber__c)
)

Error message: Investment FinancialAccount records require an External Account Number from the custodial system.

Why not a universally required field: Making ExternalAccountNumber__c universally required blocks valid record creation for account types where the field is not applicable (e.g., a manually tracked Insurance policy that has no external system identifier). RecordType-scoped validation rules express this conditional logic declaratively without Apex.

Pattern: Rollup Batch Health Monitor

When to use: Any FSC org where household KPI accuracy is business-critical (wealth management, banking advisory dashboards).

How it works:

global class FSCRollupHealthMonitor implements Schedulable {
    global void execute(SchedulableContext sc) {
        // Read last rollup completion time from FSC Settings custom metadata
        // or from a custom Rollup_Audit__c record updated by a post-rollup Flow
        Rollup_Audit__c audit = [
            SELECT Last_Successful_Run__c
            FROM Rollup_Audit__c
            ORDER BY Last_Successful_Run__c DESC
            LIMIT 1
        ];
        DateTime threshold = DateTime.now().addHours(-25); // Expected: daily rollup
        if (audit.Last_Successful_Run__c < threshold) {
            // Fire alert — send email to ops team or publish Platform Event
            Messaging.SingleEmailMessage alert = new Messaging.SingleEmailMessage();
            alert.setToAddresses(new List<String>{'sfops@example.com'});
            alert.setSubject('FSC Rollup Batch Overdue — Household KPIs May Be Stale');
            alert.setPlainTextBody(
                'The FSC rollup batch last completed at '
                + audit.Last_Successful_Run__c.format()
                + '. Expected completion by '
                + threshold.format()
                + '. Investigate via FSC Settings > Rollup Configuration.'
            );
            Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ alert });
        }
    }
}

Why not rely on FSC Settings alone: FSC Settings shows the rollup configuration and a last-run timestamp but does not alert on failure. Advisors and operations teams never see the Settings page. The monitor bridges this gap and feeds alerting into the org's operational workflow.


Decision Guidance

SituationRecommended ApproachReason
Need to block duplicate FinancialAccount on insertApex before-insert trigger with SOQL duplicate checkStandard Duplicate Rules have no FinancialAccount Matching Rule; trigger is the only blocking mechanism
Need to report on potential FinancialAccount duplicates without blockingApex scheduled batch writing to FinancialAccountDuplicate__c review objectNon-blocking detection separates review from load; allows stewardship workflow
Duplicate signal lives on the related Account/Contact, not FinancialAccountCall Datacloud.FindDuplicates from the trigger to run the parent object's active duplicate rules (≤50 sObjects/call)Reuses configured matching rules instead of re-implementing them; returns matched records for decisioning
Governed integration must save parent records that would trigger a duplicate AlertSet Database.DMLOptions.DuplicateRuleHeader.allowSave = true (and runAsCurrentUser for sharing) on the DMLPer-DML bypass of the Alert action only; it does not override a Block action, so a blocking rule still stops the save regardless of allowSave
Need to sweep existing Account/Contact/Lead records at rest for duplicatesDuplicate Jobs (Performance/Unlimited Editions) producing Duplicate Record SetsBatch retroactive detection over records already saved; insert-time rules do not re-scan history
Match key holds non-Latin names or Shield-encrypted PIIUse exact (not fuzzy) matching; confirm fields are deterministically encryptedFuzzy methods support Latin characters only; probabilistic encryption is unsupported and Shield caps candidates at 100
FinancialAccount field required only for specific account typeValidation rule with RecordType guard and Custom Permission bypassUniversal required fields cannot express per-type conditionality
Household totals don't match source system after bulk loadCheck FSC rollup batch last-run timestamp; trigger manual re-runRollup is async and does not self-heal; stale values persist until re-run
Need to detect balance discrepancies vs. core banking feedReconciliation staging object + Apex batch with configurable variance thresholdFSC has no native source-system reconciliation; custom batch is the standard pattern
Validation rules blocking integration loadAdd Custom Permission bypass (NOT($Permission.Bypass_FSC_Validation)) to all FSC validation rulesIntegration users need a governed bypass; profile-based bypasses are too broad
FinancialHolding data quality check requires parent account fieldsOne-level cross-object formula in validation rule for simple checks; Apex trigger for deeper traversalPlatform cross-object limit is one relationship level in validation rule formulas

Step-by-step instructions for an AI agent or practitioner working on FSC financial data quality:

  1. Confirm FSC deployment type and object namespace — verify managed-package (FinServ__FinancialAccount__c) vs. Core FSC (FinancialAccount) before writing any Apex, validation rules, or SOQL. All object references depend on this.
  2. Audit FinancialAccount validation coverage — review existing validation rules for missing RecordType guards, missing integration bypass conditions, and cross-field consistency checks. Run check_financial_data_quality.py against the org metadata.
  3. Assess duplicate detection posture — confirm that no standard Duplicate Rule is being relied on for FinancialAccount. If no Apex trigger exists for duplicate blocking, design and implement the trigger using external account number or owner + account type combination as the deduplication key.
  4. Verify FSC rollup batch health — check the last-run timestamp in FSC Settings > Rollup Configuration. If household KPIs appear stale, trigger a manual re-run before diagnosing further. Implement a scheduled Apex rollup health monitor if one does not exist.
  5. Design source system reconciliation if required — if FSC data must reconcile against a core banking or custodial feed, design the staging object, reconciliation batch, discrepancy object, and variance threshold configuration. Confirm the feed format and delivery schedule with the source system team.
  6. Test all validation rules and triggers in sandbox — include both UI-triggered and API-triggered scenarios. Verify that integration users with the Custom Permission can bypass validation rules. Verify that the duplicate trigger fires on batch inserts (bulk-safe behavior).
  7. Document operational runbook — record how to manually trigger the FSC rollup batch, how to investigate rollup failures, and how to resolve reconciliation discrepancies. Ensure any admin can execute recovery without developer involvement.

Review Checklist

Run through these before marking FSC financial data quality work complete:


Salesforce-Specific Gotchas

Non-obvious platform behaviors that cause real production problems:

  1. Standard Duplicate Rules do not cover FinancialAccount — Standard Duplicate Management supports Account, Contact, and Lead. There is no out-of-box Matching Rule for FinancialAccount (Core FSC) or FinServ__FinancialAccount__c (managed-package). Attempting to configure a standard Duplicate Rule for these objects through Setup fails. Duplicate detection must be custom-built via Apex trigger or Matching Rules on the related Account/Contact with indirect signal fields.
  2. FSC rollup batch does not self-heal after failure — stale household KPIs persist silently — The FSC async rollup batch aggregates household financial KPIs. When the batch fails (governor limit, data integrity issue, maintenance window), stale values persist on the household Account indefinitely. There is no platform alert and no automatic retry. Advisors see incorrect totals without any visible error. Recovery requires a manual re-run via FSC Settings or Apex. This is the most common source of FSC data integrity incidents in production.
  3. RecordType guards are required on FinancialAccount validation rules — FSC ships multiple FinancialAccount RecordTypes (Investment, Bank, Loan, Insurance, and custom types). A validation rule written without a RecordType guard will fire on all types, blocking legitimate record creation for types where the validated field is not applicable. Always scope validation rule formulas to the relevant RecordType using ISPICKVAL(RecordType.DeveloperName, 'TargetType').
  4. Apex duplicate triggers must be explicitly bulk-safe — During core banking integration loads (which commonly use Bulk API or batched REST calls), Trigger.new contains up to 200 records per execution. A trigger that runs a SOQL query inside a for-loop over Trigger.new will hit governor limits immediately. The correct pattern is to collect all deduplication keys into a Set before the SOQL query, execute one query for the entire batch, and check results in memory.
  5. FinancialHolding validation rules cannot traverse two relationship levels — Validation rules on FinancialHolding can access parent FinancialAccount fields in one-level cross-object formulas (e.g., FinancialAccount.Status). Attempting to access grandparent fields (e.g., FinancialAccount.PrimaryOwner__r.Type) will cause a compile error. For checks that require deeper traversal, implement a before insert/update Apex trigger on FinancialHolding.
  6. Data import tools and the API silently bypass Duplicate Rule alerts and Block actions — On the related Account, Contact, and Lead records that do support standard Duplicate Rules, no alert is shown and a Block action is skipped when records arrive through the data import tools or through Salesforce APIs — the duplicate saves without error. Since core banking and custodial feeds write through the API, do not assume a Duplicate Rule on the parent object protects an integration load; enforce detection in the load path (Apex, Datacloud.FindDuplicates) or sweep afterward with Duplicate Jobs.
  7. Matching rules for financial data hit hard caps and character/encryption limits — Only five matching rules can be active per object and 25 org-wide, a budget shared with FSC and other managed-package rules. Fuzzy matching methods support Latin characters only, so account-holder names with diacritics or non-Latin scripts need exact matching. Custom matching rules require deterministically encrypted fields (probabilistic Shield encryption is unsupported), and with Shield enabled the candidate search shrinks from 200 to 100 — encrypted PII match keys can therefore under-detect. A Contact-lookup matching rule is also skipped entirely when the looked-up record is a Person Account.

Output Artifacts

ArtifactDescription
FinancialAccount validation rule inventoryPer-RecordType table of validation rules with bypass condition status and API-trigger safety flag
Duplicate detection Apex triggerBulk-safe before-insert trigger for FinancialAccount with external account number deduplication key
FSC rollup health monitorScheduled Apex class that checks rollup last-run timestamp and fires alert on overdue condition
Reconciliation staging designObject model and batch Apex skeleton for comparing FSC FinancialAccount balances against source system feed
check_financial_data_quality.py outputStatic metadata analysis issue list covering missing bypasses, RecordType guard gaps, and duplicate rule coverage

Dependencies

Package files