Developer Documentation
Torro's data model is built on three simple concepts borrowed from language — nouns, adjectives, and verbs. Understanding this vocabulary is the foundation for everything else.
Section 01
Nouns represent the things that a business has — people, jobs, inventory, suppliers. They form the foundation of every system built on Torro. Every significant entity in the business is a noun.
The first objects you see when logging in. Accessed from the home screen for daily work — customers, jobs, inventory items. They grow continuously as the business expands and progress through states across their lifecycle.
Can be added, but the process is controlled. Standard managed allows general staff to add items (e.g. new inventory). Admin managed requires elevated permissions (e.g. adding branches).
Similar to primary nouns but not on the home screen. Low volume (10–40 items) displays all options. High volume (100,000+) uses search and filter. The treatment evolves naturally as the noun grows.
Section 02
Adjectives describe nouns and add information around them — just as in English. Big or small, male or female, good customer or at-risk customer, buying group membership, vehicle type.
In Torro's definition, adjectives also include what would traditionally be called adverbs — things that describe activities or verbs — because they behave identically in the data model.
Adjectives come in basic forms: some with just a description, others with a code and description (for example M for Male, F for Female). The choice depends on how the data will be filtered and used in code.
Design Note
Adjectives are typically small, stable lists. When a list starts growing significantly or items need their own lifecycle, consider whether it should be promoted to a noun instead.Section 03
Classes have two categories of members: properties — data that sits directly on the object such as a name, phone number or date — and references — links to other objects such as a customer's buying group, or a sale line's item.
In Jade, class references can point to collections. A sale order can have a collection of sale order lines. Typically the child manages the relationship by declaring its parent, and Jade automatically maintains the parent's collection.
One-to-many relationships are significantly easier to manage than many-to-many. When possible, engineer your design as one-to-many using an intersection object rather than a direct many-to-many link.
Section 04
| Type | Description | Example |
|---|---|---|
| Parent–Child | The child has no meaning without the parent. Lifecycle is tied — deleting the parent removes the children. | SaleOrder_Line → SaleOrder |
| Required One-to-Many | The object must have this reference. Many objects can share the same referenced object. | Customer must have a buying group |
| Optional One-to-Many | The reference may or may not be set. The object is valid either way. | Customer may or may not have a buying group |
| One-to-One Required | This side must have the reference, but the other side may not have a back-reference. | BankAccount must have a GL Account |
| One-to-One Optional | This side may or may not have the reference. | GL Account may or may not have a BankAccount |
| Intersection (Many-to-Many) | Direct many-to-many is avoided. An intersection object creates two one-to-many relationships instead, with its own properties and lifecycle. | CustomerPricingGroup between Customer and PricingGroup |
Section 05
Every property and reference in Torro is prefixed with one or two characters that encode its nature. By reading the prefix, a developer immediately knows where the value comes from, whether it can be set, how it relates to other objects, and what validation applies — without reading any documentation.
Reference Prefixes
Reference to a parent object. Implies ownership and lifecycle. Set once on creation, never changed. e.g. sSaleOrder on SaleOrder_Line
Must be set. The object is invalid without it. e.g. rItem
May or may not be set. The object is valid either way. e.g. oDiscount
A collection of related objects. Always the inverse side of another reference. e.g. nrSaleOrder_Line on Item
Reference used in an intersection object to manage a many-to-many relationship.
Relationship Modifiers — Combining Prefixes
Required reference that has a collection on the other side. e.g. rnItem on SaleOrder_Line
The collection side of an rn relationship. e.g. nrSaleOrder_Line on Item
Optional reference that has a collection on the other side.
This side is required, the other side is optional. e.g. roGLAccount on BankAccount
This side is optional, the other side is required. e.g. orBankAccount on GLAccount
System-calculated reference with a collection on the other side. e.g. fnState on SaleOrder
Attribute Prefixes
Must have a meaningful value. Not null, not zero, not blank.
May be null, zero, or blank. The object is valid without it.
The system provides a default value. Can be overridden by the user. e.g. dOrderDate defaults to today.
Must be unique within a defined context (not necessarily globally). e.g. invoice number unique per Supplier.
Can be set on creation but cannot be changed thereafter.
Only the person who created the record can change this value. e.g. hNote
There are ristrictions as to who can do what with this property ie Approving a Price Increase.
A bitmask flag used for relationship identification. Multiple flags combine into a single value.
Behavioural Prefixes
Value populated automatically through a relationship. Not settable externally. e.g. iSellPrice populated when Item is set.
Inferred automatically but can be overridden by the user. e.g. eSellPrice
The system calculates this value. Can be a number, a state, or any type. Not settable externally.
Lazily calculated and cached. The system marks it expired when dependencies change, and recalculates only when requested.
Method & Code Prefixes
A standard method on a class.
A method defined at the class type level.
A parameter passed into a method.
A variable scoped to the current method.
A system or method-level constant. Fixed value, never changes.
Used on JU objects for data transformation — maps external inputs to internal representations.
Section 06
Inferences explain how data is automatically populated through relationships. When a user selects an Item on a Sale Order Line, a chain of values is inferred from that Item automatically.
When the selected item changes, inferences generally update to match the new item. However, there are cases — such as quoting with a generic item before converting to a specific one — where you may not want prices to reset. Every situation has unique requirements, and inference update behaviour is configurable.
Section 07
Calculations produce values that the system computes at the time of update — before the database commit, all related calculations are resolved. There are two types.
All the information needed is on the object itself or a directly related object. For example: net total = sell price × quantity, optionally adjusted by a discount. Simple, fast, and executed as part of the standard update cycle.
The parent object holds a total that is the sum of its children. When a child is added, edited or removed, the parent's total must update. For example, a Sale Order total is the sum of all its Sale Order Line totals.
In Torro, this cascade is automatic. mUpdateCalculations on SaleOrder_Line calls mUpdateCalculations on SaleOrder. The developer updates the calculation logic in one place — not in every event handler that might touch a contributing value.
Key Principle
If you add a discount field to SaleOrder_Line, you update the maths in one place. You do not touch qty-change handlers, price-change handlers, or discount-change handlers separately. The single mUpdateCalculations method is called for any property change.Section 08
Derived values differ from calculations in when they are computed. Rather than updating immediately on every change, derived values use a lazy evaluation and caching mechanism — marked with the x prefix.
When contributing data changes, the cached derived value is marked as expired — but not recalculated.
When something asks for the derived value, the system checks whether its cached version is still valid.
If the edition number matches, the cached value is returned immediately — no calculation required.
If expired or unknown, the system calculates, stores the result with the current edition number, and returns it.
Jade automatically manages edition numbers for all objects. Every object begins at edition 1. Each edit increments the edition. When contributing data changes, the derived value object's edition is incremented — expiring its cache automatically.
Significantly, if you change the calculation method itself, Jade increments the edition — automatically expiring all cached values and preventing stale results from persisting.
Example
"Total iPhones sold" — if 1,000 iPhones are sold but nobody requests this total, the calculation never runs. Only when requested does the system compute, cache, and return the result. This minimises database activity, especially for complex queries that may never actually be needed.