Skip to content

Frontend Restrictions on Integration Positions

Can users modify custom_security for positions from integration sources (Yodlee, etc.)?

File: frontend/app/src/application/shared/components/financial-item-drawer/accounts/investment/position-list/investment-position-list.component.tsx

The frontend determines edit permissions based on account.source:

FieldManual Account (source = null)Linked Account (source != null)
QuantityEditableRead-only
PriceEditable (custom security only)Read-only
ValueEditable (generic only)Read-only
Cost BasisEditableEditable (except Cash)
Asset ClassificationEditableEditable

Conditions (line 508-519):

{PositionModel.isAssetClassification(record, securities) &&
AccountModel.isAssetClassification(account) && (
<AssetClassificationDrawerButton .../>
)}

File: frontend/app/src/application/shared/resources/position.model.ts:31-39

public static isAssetClassification(
position: RecursivePartial<IPositionPayload>,
securities: ISecurityPayload[],
): boolean {
return (
position.securityId === null || // Standalone position (including UNRECOGNIZED from integration)
securities.map(({ id }) => id).includes(position.securityId as string) // Unknown securities (!?E, !?F)
);
}

File: frontend/app/src/application/shared/resources/account.model.ts:402-409

public static isAssetClassification<T extends IVirtualAnnuityAccount>(
account: T,
): boolean {
return (
account.type === 'investment' &&
account.investmentAccount?.allocationMode === 'auto'
);
}

Critical: No check for account.source! Integration accounts can edit asset classification.

File: investment-position-list.component.tsx:313-327

const rowExpandable = (record) => {
// 1. Morningstar asset classification - always expandable
if (checkIfHasMorningstarAssetClassification(account, record)) {
return true;
}
// 2. Asset classification (Unknown/standalone) - always expandable
if (
PositionModel.isAssetClassification(record, securities) &&
AccountModel.isAssetClassification(account)
) {
return true; // ✅ Integration UNRECOGNIZED positions can expand
}
// 3. Other cases
return (
_.isNil(account.source) || // Manual account
(hasCostBasis && !checkIfPositionIsCash(record)) // Linked with cost basis
);
};

Your Yodlee integration position:

  • isManual: false (integration account)
  • securityId: null (UNRECOGNIZED, standalone position)
  • providerAccountId: 29116571 (Yodlee)

Can the user modify asset classification?

YES, if:

  1. account.investmentAccount.allocationMode === 'auto'
  2. Position will be expandable (line 317-321)
  3. Asset Classification Drawer button will appear

For integration UNRECOGNIZED positions, users can modify:

PropertyCan ModifyPreserved on Sync
custom_security.typeNo - overwritten by integration
custom_security.symbolNo - overwritten by integration
custom_security.nameNo - overwritten by integration
custom_security.categoryIdYes - preserved
custom_security.percentagesByCategoryIdYes - preserved
custom_security.id (linked security)Yes - preserved
costBasisYes - preserved

File: backend/packages/libs/integrations-core/src/Models/HoldingTrait.php:115-126

$custom_security = array_filter(($position->custom_security ?? []) + [
Security::COLUMN_SYMBOL => $this->symbol,
Security::COLUMN_NAME => $this->name,
Security::COLUMN_TYPE => $security_type,
]);
// Custom security ID and type cannot co-exist
if (array_key_exists(Security::COLUMN_ID, $custom_security)) {
unset($custom_security[Security::COLUMN_TYPE]);
}
$position->custom_security = $custom_security;

The + operator is left-associative (existing values take precedence):

  • Existing categoryId, percentagesByCategoryId, idpreserved
  • New symbol, name, typeoverwrite if not present

File: HoldingTrait.php:283-286

If the integration later matches this position to a security:

if ($custom_security !== [] && !Security::isUnknown($this->matched_security_id)) {
// Custom security must be removed if the position were to be securitized to anything other than unknown
$custom_security = [];
}

All user customizations will be lost when position transitions from UNRECOGNIZED to RECOGNIZED.

Frontend allows editing asset classification for integration UNRECOGNIZED positions ✅ Backend preserves categoryId and percentagesByCategoryId on sync ❌ Backend overwrites type, symbol, name on sync ❌ All customizations lost if position becomes recognized

Consider adding a user warning in the UI:

⚠️ This holding is from an aggregated account. Asset classification will be preserved,
but may be removed if the holding is later recognized by the data provider.