Skip to main content

Extended properties & templates

Extended properties are Loca.fi's custom-field system: tenant-defined fields that attach to items, places, persons, collections, tasks, and services. An extended property defines a field once (name + data type); a template bundles a set of extended properties into a schema that entities are created against. There is no state machine here — templates and extended properties are plain definitions with no lifecycle states.

The two chains

Everything in this domain is one of two chains: the definition chain (what fields exist and which schema bundles them) and the instance chain (where a record's actual values live).

Definition chain

PieceWhat it is
Extended propertyAn org-scoped field definition: Name (unique per org + TemplateType), optional Description, a DataType, the TemplateType domain it belongs to, and — for SelectList fields — an EpOptions list of options. Created via POST api/extendedproperties/createextendedproperty.
TemplateAn org-scoped, named bundle of extended properties for one TemplateType. Created via POST api/templates/createtemplate; read back as a TemplateDetailDto via gettemplate/{id}.
Template membership rowEach extended property in a template carries per-template presentation data: IsRequired, Grouping (a UI sub-heading), and OrderNo (display order). The same extended property can appear in many templates with different settings.

DataType, TemplateType, and ReferenceTemplateId are immutable after creation. updateextendedproperty only accepts Name, Description, and EpOptions. If a field needs a different type, create a new extended property.

Instance chain

  • SKUs and items: a SKU is created against an Item template (SkuMaster.ItemTemplateId) — that template governs which extended properties every item of that SKU carries. See SKU lifecycle for the rest of what a SKU carries.
  • Places, persons, and collections each hang off a shared Entity host record whose required TemplateId points at their template. Their values live in EntityExtendedProperty junction rows.
  • Values are stored in ExtendedPropertyValue rows with one typed column per data-type family (date, string, number, decimal, bool, option selection, and GUID reference columns). On the wire, a value always travels as a string (WriteEntityExtendedPropertyDto: { ExtendedPropertyId, Value }); the server parses it into the right column per the field's DataType. Reads return ReadEntityExtendedPropertyDto, which adds the definition metadata (name, data type, IsRequired, options, grouping/order) alongside the value — dates come back as yyyy-MM-ddTHH:mm:ssZ.

The TemplateFor domains

TemplateType (a TemplateFor value, persisted as a string) says which domain a template or extended property belongs to:

TemplateTypeTemplate-driven?How values attach
ItemYes — via the SKU's ItemTemplateIdItem create/update extended-property lists
PlaceYes — TemplateId on place createPlace create/update
PersonYes — TemplateId on person createPerson create/update
CollectionYes — TemplateId on collection create; the only domain allowed reference data typesCollection create/update
TaskNo — template-lessExtended properties attach directly to tasks/schedules
ServiceNo — template-lessExtended properties attach directly to services/records

Templates can only be created for Item, Place, Person, and Collectioncreatetemplate rejects Task/Service with a validation error. Task and service extended properties skip the template layer entirely: you create them with TemplateType: "Task" or "Service" and supply them directly in the task/service write DTOs.

Data types

DataType is a TemplateDataTypes value (string on the wire). Which types a field may use depends on its TemplateType:

GroupData typesAllowed on
GeneralString, Date, DateTime, Number (integer), Decimal, Bool, SelectListAny TemplateType
Item-only (GS1-style)BatchNumber, ProductionDate, PackagingDate, SellByDate, ExpirationDate, SerialNumber, LotNumberItem only
Reference (advanced)Item, Place, Person, CollectionCollection only — the field's value is the referenced record's GUID
ReservedAutoIdReserved — not implemented; no generator exists. Do not use.

Parsing rules worth knowing (server-side, when a string Value is written): dates are parsed and stored UTC; an unparseable Bool coerces to false; reference-type values must be GUIDs. A SelectList selection is stored as the chosen option's value; the option catalogue itself lives on the definition as EpOptions ({ UserDefinedId, Value } pairs).

SelectList option edits do not migrate stored selections

Editing an extended property's EpOptions (adding, renaming, or removing options) does not touch values already stored on entities or items. Removing an option leaves existing records pointing at a no-longer-offered option. This is by design — clean up stored values yourself if an option is retired.

ReferenceTemplateId — scoping a reference field

A reference-type field (Item/Place/Person/Collection data type on a Collection template) may optionally set ReferenceTemplateId to constrain which kind of record it selects: the referenced template's TemplateType must match the field's DataType (e.g. a Place-typed field pointing at a Place template). Leaving it null allows any record of that type.

ReferenceTemplateId is only meaningful for these Collection-template reference fields. On any other TemplateType/DataType combination the value is stored but ignored — don't set it there.

Template update is a live cascading migration

POST api/templates/updatetemplate (reference) is not a simple rename. The request carries the full desired set of extended-property rows, and if the set (or any row's IsRequired/Grouping/OrderNo) changed, the server migrates every record using the template — in one transaction:

  1. The template's membership rows are reconciled: new rows added, omitted rows hard-deleted, kept rows updated.
  2. Every live entity (place/person/collection) on the template is re-synced: added fields appear with empty values; removed fields cascade-delete their stored values.
  3. For an Item template, every SKU on the template is re-synced, and then every item of those SKUs — potentially a very large write for big item populations. It runs atomically; any failure reverts the whole migration.

Two practical consequences: an update to a heavily-used Item template may be long-running, and values removed by the cascade are unrecoverable. See the Templates & extended properties guide for how to sequence this safely.

System templates

Loca.fi also auto-generates read-only system templates per organisation (device reader configs, portal rules, integration agents) on the internal api/systemplate/... surface. They reuse the same template/extended-property shapes but are not user-editable — you will encounter them only when provisioning devices or agents.