docs
/
Appmint Mobile

Data models

The Dart models under lib/models, the server datatypes they map to, and the fields and enums that matter.

Every server record is a BaseModel<T> envelope — { sk, name, datatype, data, owner, createdate, modifydate } — and the app unwraps data on the client so screens see flat objects. Models exist only for the records the app edits with logic; everything else is handled as raw maps through DataProvider('<datatype>') and RepositoryService.

Models → datatypes

FileClassesDatatype / source
base_model.dartBaseModel<T>, BaseModelDTO<T>The generic envelope
user_model.dartUserModel/profile, auth responses
auth_response.dartAuthResponsesignin / signup / refresh / passcode responses
lead_model.dartLeadModellead via /crm/leads/detail
contact_model.dartContactModelcustomer
activity_model.dartActivityModel/crm/activity/*
message_model.dartMessageModel, ConversationModelmessage, /crm/inbox/conversations
inbox_message_model.dartInboxMessageModel, InboxMessageData, InboxConversationModelmessage plus server-threaded conversations (mirrors the web inbox-conversation-item.tsx)
chat_message_model.dartChatMessageModel, ChatSessionappengine ChatMessageModel; text, image, video, file
event_model.dartEventModel, EventData/events
operations_reservation_model.dartOpsReservation, OpsReservationCustomer, ServicePointreservation, service_point
pos_order_model.dartPosOrder, PosLineItem, PosLineFire, PosProduct, PosProductAttribute, PosProductAttributeOption, PosProductVariationsf_order, sf_product

Handled without a model class: task, ticket, bm_location_product, location, setting, social_activity, access_card, workflow records.

POS: sf_order and PosOrder

A POS tab is an sf_order, opened with status: 'new', so web orders and counter sales share one shape and reporting sees one model.

FieldNotes
businessLocationIdThe location slug
servicePointIdThe service point's name, filtered by location
aliasFree-form floor handle ("T7", "Smith — 4"); auto-numbered "Walk-up #N" when omitted
numberUnique, read-only human invoice number; settle refuses a tab without one
statusnew, awaiting-payment, paid-partial, paid, confirmed, processing, shipped, partially-shipped, delivered, partially-delivered, completed, failed, returned, refunded, cancelled
productItems[]OrderItemSchema: id (stable per line, used for fire bookkeeping), sku, name, quantity, unitPrice, amount, discount, itemType (product, rental, fee, addon), options[{name, value}] — prepStation rides here
subtotal, discount, tax, total, amountPaidServer-owned; re-priced on every item change and every settle. Open tabs carry subtotal; total is stamped at settle
payments[]{amount, gateway, ref, method, date, transactionId}
firedTaskIds[]Mirrors sf-order.ts:189-207; which lines went to which prep task
Numbers are parsed defensively

The server has returned money fields as strings (amountPaid: "5368" on a live order). Every money and quantity field in PosOrder and PosLineItem goes through a tolerant _num() parser. Before that, one bad record threw on as num and emptied the entire open-tabs list. Cards display displayTotal — total, else subtotal, else the line sum — because open tabs have no total yet.

PosProduct is built from sf_product: sku, price, taxable (drives the tax base), stock, attributes[] (option groups with priceDelta), variations[] (matched on every option group), image. Category chips come from /storefront/pos-categories.

service_point

FieldNotes
nameUnique key; orders and check-in reference points by name
displayName, kindkind is a free string in practice: table, chair, bay, room, seat, section
parentName, level, sortOrderHierarchy: floor → section → table → seat
capacity, minCapacity, maxCapacity
x, y, width, height, rotation, color, shapeFloor-plan geometry used by FloorPlanScreen
bookable, isActiveBoth default true
statusSDK enum available, occupied, reserved, dirty, blocked, closed; the storefront status endpoint writes it as a free string
currentReservationId, currentTabId, currentServerId, currentPartySize, seatedAt, lastStatusChangeAt, lastStatusReasonLive occupancy, owned by the tab and check-in flows

The Dart ServicePoint echoes currentTabId, lastStatusChangeAt, lastStatusReason and version in toCreateBody() so an edit from the form does not erase the table-to-tab link.

reservation and reservation_definition

OpsReservation (datatype: 'reservation'):

FieldNotes
statusRead-only on the server: new, pending, confirmed, used, rescheduled, cancelled, no_show
reservationDefinitionId, definition, serviceService is the definition's service name
targetTypeservice_point, pipeline, event, interval
servicePointId, workflowName, currentStageId
sourcewalk-in, phone, online, partner, kiosk, agent, email
customer{name, email, phone}, partySize, notes
startTime, endTime, timezoneSlot bookings store a UTC instant; older records a zone-less wall clock. The row formatter converts only values that carry a zone
checkedInAt, checkInTaskIdStamped by the server when the booking is handed to the check-in queue
businessLocationIdOften empty on older bookings; the operator's picker location wins at check-in

toCreateBody() wraps the record as {datatype, data} for updates; toCreatePayload() sends the flat fields to POST /crm/reservations/create, which builds the record server-side and rejects a pre-wrapped body.

reservation_definition fields the app reads: name, title, type (service, interval, event, service_point, pipeline), services[] {name, duration, price, breakAfter}, workDays[], officeHours[] with timezone, blockedTime[], spots, checkInBy, bookingType (unassigned, round-robin), notifications[] {offset, unit, channel}, notificationTemplate, cancellationTemplate, form. When a definition has no services, the app synthesizes {name: definition name, duration: definition.duration ?? 30} — the same implicit service the server uses.

task payloads

Workflow tasks share one schema (status: new, pending, inprogress, blocked, done, rejected, approved, canceled; stageId, dueDate, escalationTier, workflowName, history[]). What differs is payload:

WorkflowPayload
reservation-checkin-pipeline (stage 0 Waiting, stage 1 Assigned)customer{name, email, phone}, reservationId, servicePointId, partySize, preferences, businessLocationId, source, arrivalTime
prep-pipeline (Received → Prepping → Ready → Delivered)orderId, orderNumber, alias, businessLocationId, servicePointId, course, seat, specialInstructions, items[], itemRefs[]

Queue reads come back with an enrich block: waitedMs, isOverdue, queuePosition (1-indexed by createdate), displayName.

CRM records

DatatypeFields the app uses
crm_lead (LeadModel)fullName/firstName/lastName, email, phone, company, pipelineId, stageId, source, status (new, contacted, qualified, unqualified, disqualified, converted, lost, nurturing, follow_up), priority, temperature (cold, warm, hot), value, score, assignedTo
customer (ContactModel)email (identity key), firstName, lastName, phone, company, jobTitle, address, groups[], leadStatus, leadStage
crm_ticketstatus (new, open, pending, inprogress, blocked, done, canceled), priority (low…urgent), severity, channel, reportedBy, title, description, assignTo, resolution; replies are customer-facing, comments are internal
message (InboxMessageModel)Channel-tagged messages; inbox channels email, sms, whatsapp, internal, website; AI and chat types are filtered out
chat (ChatMessageModel, ChatSession)Socket messages with chatId, sender, type, status, AI stream segments

About bm_location_product

MenuManagementScreen and the POS read and write a per-location overlay named bm_location_product: eightySixed plus eightySixReason, priceOverride, and menuIds for day-parts. It behaves like any repository collection, but there is no schema for it in the SDK and no server code path reads it. Treat it as a mobile-side collection that the SDK has not published yet; the demo org has no records, which is why day-part chips show every product there.