How do I add a posting module with posting rules to a chart of accounts?

Prev Next

You can add one or more posting modules, each containing any number of posting rules, to an existing chart of accounts (CoA).

Note the order in which posting rules are specified in a module is significant. For each journal entry line, LUSID uses the first matching posting rule found to post the debit or credit amount to a particular general ledger account.

Methods

Call the CreatePostingModule API, for example:

curl -X POST 'https://<your-domain>.lusid.com/api/api/chartofaccounts/CoAs/DailyCoA/postingmodules'
  -H 'Content-Type: application/json-patch+json' 
  -H 'Authorization: Bearer <your-API-access-token>'
  -d '{
  "code": "DailyPM",
  "displayName": "Daily NAV posting module",
  "description": "This is a posting module for daily NAV",
  "rules": [
    {
      "ruleId": "rule_0001",
      "account": "Other-Investments",
      "ruleFilter": "EconomicBucket startswith 'NA' and HoldType neq 'P'"
    },
    {
      "ruleId": "rule_0002",
      "account": "Cash",
      "ruleFilter": "EconomicBucket startswith 'NA' and HoldType eq 'B'"
    },
    {
      "ruleId": "rule_0003",
      "account": "Commitments",
      "ruleFilter": "EconomicBucket eq 'NA_Cost' and HoldType eq 'C'"
    }
  ]
}'

Navigate to Fund Accounting > Chart of Accounts, open the Posting Modules tab, select the parent CoA and click the Create posting module button.

You can either add posting rules to the module using the Add button in this dialog, or alternatively navigate to the Posting Rules tab.

Write a Luminesce SQL query using the Lusid.PostingModule.Writer provider to create a posting module and the Lusid.PostingModule.Rule.Writer provider to set posting rules, and execute it using a tool such as the LUSID web app, for example:

Data fields

This section supplements the API documentation and on-screen help text in the LUSID web app.

A posting module is identified by a code that must be unique within the parent CoA.

You can specify any number of posting rules. Note the order is significant. Each posting rule consists of:

Subsequent updates

Once created, you can edit an existing posting module as follows:

  • To change the displayName or description of the module, call the SetPostingModuleDetails API. You cannot change the code.

  • To change the order of existing rules in the module, call the SetPostingModuleRules API. Note you must specify all the rules again, this time in the required order; any omitted from the request are removed.

  • To insert one or more new posting rules at a specific order, call the PatchPostingModule API. For example, the following request adds a new rule as the second in the list (the index starts at 0):

    curl -X PATCH 'https://<your-domain>.lusid.com/api/api/chartofaccounts/CoAs/DailyCoA/postingmodules/DailyPM'
      -H 'Content-Type: application/json-patch+json' 
      -H 'Authorization: Bearer <your-API-access-token>'
      -d '[
      {
        "value": {
          "ruleId": "rule_0001a",
          "generalLedgerAccountCode": "Error",
          "ruleFilter": "EconomicBucket eq 'PL_Other'"
        },
        "path": "/rules/1",
        "op": "add"
      }
    ]'

    To add a new rule as the first in the list, specify a path of /rules/0. To add a new rule last, specify /rules/-. LUSID automatically moves the index position of other rules.

  • To delete a posting module, call the DeletePostingModule API.

Note it may be easier to change the order of posting rules in the LUSID web app using the ‘grab handles’ (in yellow) or up/down arrows (in red):

Parameterising account codes to reduce posting rules

You can use Mustache template syntax in the general ledger account field of a posting rule to derive account codes from data attributes of journal entry lines, potentially reducing the number of posting rules.

For example, imagine you have four general ledger accounts in your CoA that share the same purpose but with codes that distinguish between portfolio currency and strategy:

  1. 1101-GBP-Growth

  2. 1101-USD-Growth

  3. 1101-GBP-Income

  4. 1101-USD-Income

The following posting rule examines each journal entry line matching the ruleFilter expression and posts the amount to an appropriate account:

"rules": [
  {
    "ruleId": "NA-Subscriptions",
    "account": "1101-{{Portfolio.BaseCurrency}}-{{Properties[Transaction/SHKs/Strategy]}}",
    "ruleFilter": "MovementName eq 'Subscriptions'"
  }
]

This might replace the following four posting rules with more complicated ruleFilter expressions:

"rules": [
  {
    "ruleId": "NA-Subscriptions-01",
    "account": "1101-GBP-Growth",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'GBP' and Properties[Transaction/SHKs/Strategy] eq 'Growth'"
  },
  {
    "ruleId": "NA-Subscriptions-02",
    "account": "1101-USD-Growth",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'USD' and Properties[Transaction/SHKs/Strategy] eq 'Growth'"
  },
  {
    "ruleId": "NA-Subscriptions-03",
    "account": "1101-GBP-Income",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'GBP' and Properties[Transaction/SHKs/Strategy] eq 'Income'"
  },
  {
    "ruleId": "NA-Subscriptions-04",
    "account": "1101-USD-Income",
    "ruleFilter": "MovementName eq 'Subscriptions' and Portfolio.BaseCurrency eq 'USD' and Properties[Transaction/SHKs/Strategy] eq 'Income'"
  }
]

Syntax of a posting rule

A posting rule consists of:

  • A ruleId that uniquely identifies the posting rule in the posting module.

  • An account that is the code of an account in the parent chart of accounts (CoA).

  • A ruleFilter expression that describes criteria for matching journal entry lines to the account.

The syntax of a ruleFilter expression is:

<attribute> <operator> <value>

...where:

Note the following:

  • A ruleFilter expression is case-insensitive.

  • A string value must be enclosed in single straight quote marks (the %27 UTF-8 encoding).

  • You can concatenate expressions using the and and or operators, for example:
    MovementName eq 'Side1' and subholdingkeys[Transaction/Ibor/Strategy] eq 'Income'

    If you use both, standard boolean operator precedence applies:
    InstrumentScope startswith 'Custom' or (LocalAmount lte 1000 and HoldType eq 'P')

<attribute> of a journal entry line

Data type

Example expression

Explanation/origin of <value>

SourceType

String

SourceType eq 'LusidTransaction'

Either LusidTransaction or LusidValuation. Use LusidValuation to map LUSID's automatic mark-to-market valuation activity to an account.

ActivityDate

DateTime

ActivityDate gt 2023-01-01

For a transaction, this is the trade date. For a valuation, this is the date of the latest detected change to market data.

EconomicBucket
EconomicBucketComponent
EconomicBucketVariant

String

EconomicBucket startswith 'PL_Real'

LUSID automatically categorises every journal entry line into a broad economic bucket. Some economic buckets are further categorised into components and variants. More information.

SourceId

String

SourceId eq 'Txn01'

For a transaction, this is the unique identifier in the portfolio (txnId).
For a valuation, this is the date and time of the latest change to market data (stored as a string, so only string operators are available).

TaxLotId

String

SourceId eq 'Txn01'

For a transaction, if the underlying instrument is a currency and the trade is settled (holding type of B), the value is 1 (stored as a string). Otherwise, this is the same as the SourceId.

LocalAmount

Decimal

LocalAmount lte 1000

The amount in the transaction currency.

BaseAmount

Decimal

BaseAmount gt 450000

The amount in the portfolio base currency.

DefaultCurrency

String

DefaultCurrency in 'GBP','USD'

The currency of the underlying instrument, if specified.

InstrumentScope

String

InstrumentScope startswith 'Custom'

The instrument scope in which the underlying instrument is mastered.

LusidInstrumentId

String

LusidInstrumentId not startswith 'CCY'

The LUID (globally unique ID) of the underlying instrument.

HoldType

String

HoldType eq 'P'

One of the LUSID holding type codes.

MovementName

String

MovementName eq 'Side1'

For a transaction, this is the name of the movement in the transaction type to which the transaction belongs. If the movement does not have a name (and note its name field is optional), then this is the name of the movement's side.

For a valuation, this is always MarkToMarket.

MovementSign

String

MovementSign eq 'Long'

Either Long or Short.

HoldingSign

String

HoldingSign eq 'Short'

Either Long or Short. You might use this to post amounts of a single transaction that moves from long to short to different accounts. For example, if you have a holding of £100 but a transaction for -£120 then the following journal entry lines are produced:

EconomicBucket

Amount

MovementSign

HoldingSign

NA_COST

-100

Short

Long

NA_COST

-20

Short

Short

A sub-holding key on the portfolio

User-defined

subholdingkeys[Transaction/Ibor/Strategy] eq 'Income'

Under-the-hood, a SHK is defined as a custom property with a 3-stage key in the Transaction domain.

A portfolio field

System-defined

Portfolio.displayName eq 'UK-Equities'

Portfolio.parentPortfolioId.Code eq 'FixedIncome'

This can be any of the stored fields for a portfolio except the href, links and version fields, prefixed by Portfolio. Nested fields can be accessed using dot notation.

A portfolio property

User-defined

properties[Portfolio
/Ibor/Manager] in 'John Smith','Jane Jones'

This can be any property with a 3-stage key in the Portfolio domain.

An instrument field (see also a transaction instrument field, below)

System-defined

Instrument.identifiers['LusidInstrumentId'] eq 'LUID_ZZZZZZZZ'

Instrument.instrumentDefinition.instrumentType in 'Equity','Bond'

This can be any of the stored fields for an instrument except the href, links and version fields, prefixed by Instrument. Nested fields can be accessed using dot notation.

An instrument property (see also a transaction instrument property, below)

User-defined

properties[Instrument/Ibor/AnalystRating] any (~ startswith 'A')

This can be any property with a 3-stage key in the Instrument domain.

A transaction field

System-defined

Transaction.totalConsideration.amount gt 2000

Transaction.instrumentScope eq 'ProtectedScope'

This can be any of the stored fields for a transaction, prefixed by Transaction. Nested fields can be accessed using dot notation.

A transaction property

User-defined

properties[Transaction/Ibor/Broker] eq 'AcmeCorp'

This can be any property with a 3-stage key in the Transaction domain.

An instrument field on a transaction

System-defined

Transaction.Instrument.instrumentDefinition.instrumentType in 'Equity','Bond'

If a journal entry line is generated in response to a currency movement, then the instrument of the line itself is a currency. The instrument of the original transaction, however, may be (for example) an equity. This gives you access to the instrument fields and properties of the equity.

The available fields and properties are the same as those for the instrument of the line itself, above.

An instrument property on a transaction

User-defined

Transaction.Properties[Instrument/Ibor/AnalystRating] any (~ startswith 'A')

An account field

System-defined

Account.type in 'Asset','Liabilities'

This can be any of the stored fields for an account, prefixed by Account. Nested fields can be accessed using dot notation.

An account property

User-defined

properties[Account/Type/AssetClass] eq 'Equity'

This can be any property with a 3-stage key in the Account domain.

A fund property

User-defined

properties[Fund/Managers/Name] eq 'Fred Bloggs'

This can be any property with a 3-stage key in the Fund domain.

An ABOR property (V1 funds only)

User-defined

properties[Abor/Client/Accountant] not in 'Fred Bloggs', 'Sarah Smart'

This can be any property with a 3-stage key in the Abor domain.