Assets and locations
A location hierarchy (site, building, unit) and an asset registry with tags, status, coordinates and assignment to members or records. Optional PostGIS stores points and areas as geography; booking resources and work items can name an asset.
assets keeps two registries per organization. Locations form a tree, such
as a site with buildings and units inside it, and assets (a laptop, a van, a
meeting room, a machine) sit at a location, carry a tag and a serial number,
and are assigned to a member or to a record such as a party.
Reading needs assets.read, creating and changing locations and assets needs
assets.manage, and assigning needs assets.assign. Admins get all three and
members get assets.read. A member also reads the assets assigned to them
without assets.read.
better-supabase add assetsThe module needs tenant and access. It works with
bookings and work items
when they are installed, and records asset.* events to the outbox and the
audit log.
import { createAssets } from "better-supabase/blocks/assets";
const assets = createAssets({ transport, tenant: organizationId });
const site = await assets
.createLocation({
kind: "site",
name: "Amsterdam office",
code: "AMS",
point: { latitude: 52.37, longitude: 4.89 },
})
.orThrow();
const laptop = await assets
.createAsset({
kind: "laptop",
name: "MacBook Pro 14",
locationId: site.id,
tag: "IT-0042",
serialNumber: "C02XYZ",
})
.orThrow();
await assets
.assign(laptop.id, { type: "member", id: userId }, { note: "New hire" })
.orThrow();Locations
A location has a free-text kind (site, building, floor, unit), a
name, an optional code that is unique in the organization, an address
object and an optional point. parentId places it in the tree: the parent
must belong to the same organization, and a move that would put a location
inside one of its own sublocations fails with ASSETS_LOCATION_CYCLE.
location(id) returns the location with its path from the top, and how
many open sublocations and assets in service it holds. locations() lists the
top locations, and locations({ parentId }) the children of one.
archiveLocation(id) archives a location once it has no open sublocations
and no assets in service.
Assets
An asset has a kind, a name, an optional locationId, a tag that is
unique in the organization, a serialNumber, a point and a status:
active, maintenance or retired. Retiring an asset ends its assignment,
and a retired asset can't be assigned again.
assets(filter) lists assets newest first, filtered by location (with
includeSublocations for everything under it), kind, status, assignee or a
search on name, tag and serial number, and pages with before. asset(id)
adds the location path and the booking resources and open work items that
name the asset.
Assignment
assign(assetId, assignee) assigns an asset to a member
({ type: "member", id: userId }) or to any record ({ type, id }), and
ends the previous assignment. A member must be able to read the
organization's assets. release(assetId) ends the assignment, and
assignments(assetId) lists the history newest first.
assignedTo(subject) lists the assets a member or record holds; the
assets section renders them on the record page. The
before_asset_assign and after_asset_assign hooks run around an
assignment.
Coordinates and PostGIS
Locations and assets store latitude and longitude. near({ latitude, longitude, radiusMeters }) returns the assets and locations within the
radius, nearest first, each with distanceMeters. Without PostGIS this is a
haversine distance on the stored coordinates.
The postgis option installs the postgis extension in the extensions
schema and adds geography columns: a generated point on locations and
assets, and an area on locations (a GeoJSON polygon or multipolygon, stored
as a multipolygon). Both get GiST indexes, near uses ST_DWithin, and
locationsAt(point) returns the locations whose area contains a point.
Without the option, locationsAt fails with ASSETS_POSTGIS_REQUIRED.
blocks: {
assets: { options: { postgis: true } },
}Bookings and work items
With bookings installed, booking resources get an
asset_id column, so a meeting room or vehicle you book is an asset. With
work items installed, work items get one for the
asset a job is about. link({ type: "booking_resource" | "work_item", id }, assetId) sets it, and null clears it. Locations and assets are registered
subjects, so relations, comments and attachments
work on them.
Errors
| Hint | Cause |
|---|---|
ASSETS_FORBIDDEN | the caller lacks the permission for this |
ASSETS_LOCATION_NOT_FOUND | no location with this id in the organization |
ASSETS_NOT_FOUND | no asset with this id in the organization |
ASSETS_CODE_TAKEN | a location with this code already exists |
ASSETS_TAG_TAKEN | an asset with this tag already exists |
ASSETS_LOCATION_CYCLE | the parent is one of the location's own sublocations |
ASSETS_LOCATION_IN_USE | the location still holds open sublocations or assets |
ASSETS_RETIRED | the asset is retired |
ASSETS_ASSIGNEE_INVALID | the assignee isn't valid, or the member can't read assets |
ASSETS_POINT_INVALID | a latitude or longitude is missing or out of range |
ASSETS_RADIUS_INVALID | the radius isn't a positive number of meters |
ASSETS_PATCH_INVALID | the patch names a key that can't be changed |
ASSETS_POSTGIS_REQUIRED | locationsAt needs the postgis option |
ASSETS_LINK_UNSUPPORTED | the link target isn't a booking resource or a work item |
ASSETS_LINK_TARGET_NOT_FOUND | no booking resource or work item with this id |
Events
asset_location.created, asset_location.updated and
asset_location.archived, and asset.created, asset.updated,
asset.assigned and asset.released. The
events page lists their payloads.
Last updated on
Ledger
Double-entry accounts in a currency or a custom unit, balanced transactions, running balances and holds. Inventory stock, leave balances and invoice payments post through it, and credits, loyalty points, gift cards and trust balances are account kinds.
Drive
Folders and files on the attachments module. Access is granted to users or teams and inherited down the tree, storage policies enforce it on the objects too, and versions, trash and share-links add revisions, a bin, stars, recent items and folder links.