Skip to main content

Data Service

The dataService is the central injectable service in upp-data, managing the data lifecycle, object storage, view caching, synchronization, and session state. It acts as the hub connecting all other components.

dataService

Injectable: providedIn: 'root'

Constructor Dependencies

constructor(
public injector: Injector,
public clock: clockService,
private lang: languageService,
private adhoc: adhocService,
private logs: logsService,
private state: stateService,
private view: viewService,
private sync: syncService
)

Properties

PropertyTypeDescription
storedataStorageLazy-initialized two-tiered object storage
alivesaliveItemsWeakRef-based ViewObject cache with automatic GC cleanup
eventseventsNotifierEvent logging system for drawer, login, and guest actions
sessionSession | nullThe current active session
userUser | nullThe currently authenticated user
placePlace | nullThe currently active place/business
qrcodeQrCode | nullThe currently active QR code (guest mode)
serialnumberCRC32-based serial from place.objid + device for ticket numbering
injectorInjectorAngular injector exposed for ViewObjects to resolve services lazily

Observables

ObservableTypeDescription
OnSessionChangedSubject<void>Emitted when the session is established or changed
OnSessionReleasedSubject<boolean>Emitted when the session ends; boolean indicates whether it expired
OnReloadRequestedSubject<void>Emitted when the server requests a full reload
OnUserChangedSubject<void>Emitted when the user entity is set or changed
OnPlaceChangedSubject<void>Emitted when the place entity is set or changed
OnQrCodeChangedSubject<void>Emitted when the QR code entity is set or changed
OnRefreshPercentObservable<number>Proxied from syncService — sync progress (0–100)
OnRefreshCompletedObservable<void>Proxied from syncService — sync cycle completed
OnCommitCompletedObservable<void>Proxied from syncService — commit acknowledged by server

Session Lifecycle

Start(session, access)

Begins a data session:

  1. Creates a Session object in the store.
  2. Starts the sync service: sync.Start(session).
  3. Based on access mode:
    • LOGIN: Waits for sync.SetUser(user), then calls SetUser().
    • GUEST: Waits for sync.SetQrCode(qrcode), then calls SetQrCode().
  4. Emits OnSessionChanged.

Stop(expired)

Ends the data session:

  1. Releases the current session.
  2. Stops the sync service: sync.Stop().
  3. Clears the object storage: store.Clear().
  4. Emits OnSessionReleased(expired).

Ready(): Promise<void>

Waits until the sync service signals readiness (sync.IsReady), using firstValueFrom with take(1).

Entity Management

Each setter follows the same pattern:

async SetUser(user: User): Promise<void>
async SetPlace(place: Place): Promise<void>
async SetQrCode(qrcode: QrCode): Promise<void>

The flow:

  1. Wait for the entity to be fully loaded (its OnInitialize event).
  2. Update the internal reference (this._user, this._place, this._qrcode).
  3. Add the entity to the store.
  4. Reset the sync stage: sync.ResetStage().
  5. Emit the corresponding change observable.
  6. SetPlace additionally: logs a LOGIN event, preloads the place's language pack.

Commit Flow

The commit flow is the path data changes take from a DataObject modification to the server:

Commit(changes: CommitChange[])

Validates and queues changes for submission:

  1. For each CommitChange, checks that all requires (dependencies) are safe:
    • The dependency has an objid (already resolved), or
    • The dependency is already in the ToCommit map, or
    • The dependency is in the store and currently InCommit.
  2. Adds validated changes to the internal _tocommit map (keyed by table@objid or table@uuid).

Flush(force: boolean): Promise<boolean>

Sends accumulated changes to the server:

  1. Collects all entries from the _tocommit map.
  2. Delegates to sync.Flush(changes, force).
  3. Returns true on success.

FetchByObjid(table, objid): Promise<BaseObject>

Ad-hoc server request to fetch a single object by table and objid, independent of the sync cycle.

Transaction Support

get transaction(): dataTransaction

Creates a new dataTransaction for batching multiple changes atomically.

dataStorage

A two-tiered object store that separates objects by resolution state:

Internal Structure

type StorageMap = { [table: string]: Map<string, BaseObject> };

private _objects: {
resolved: StorageMap; // objects with numeric objid from server
awaiting: StorageMap; // objects with temporary _uuid (client-created)
};

Methods

MethodSignatureDescription
GetByRef(table, objid, uuid) → BaseObject | nullSearches by uuid in awaiting first, then by objid in resolved
GetObject(dataobject) → BaseObject | nullSearches by objref in both tiers
AddObject(dataobject) → booleanRoutes to resolved (if numeric objid) or awaiting (otherwise). Skips copies (CopyOf is set)
DelObject(dataobject) → voidRemoves from the appropriate tier based on objid
SetObject(dataobject) → voidDelete + Add (update in place)
Resolve(dataobject) → voidMoves an object from awaiting to resolved after the server assigns an objid. Calls OnResolve() on any displaced object
Replace(dataobject, uuid) → voidReplaces an existing entry in resolved with a new version. Handles uuid-based lookups in awaiting
Clear() → voidEmpties both maps completely

Resolution Flow

When a client-created object (e.g., a new Ticket) is committed to the server:

  1. The object starts in awaiting with a temporary _uuid.
  2. The server responds with a permanent objid.
  3. Resolve() is called:
    • Removes the object from awaiting.
    • Sets objid from the server response.
    • If an object with the same objid already exists in resolved, it is removed and its OnResolve() is called to update any references.
    • The object is added to resolved.

dataTransaction

Manages atomic groups of changes to be committed together:

class dataTransaction {
private _trstatus: 'OPEN' | 'CLOSED' = 'OPEN';
private _tocommit = new Map<string, CommitChange>();
}

Methods

push(item: BaseObject, force?: boolean): Promise<boolean>

Adds an object and its dependent changes to the transaction:

  1. If force is true, calls item.ForceUpdate() to mark it for update.
  2. Collects all CommitChange objects from item.Commit (includes children and dependencies).
  3. Adds each change to the transaction's _tocommit map.
  4. Validates that all dependencies are safe:
    • The dependency has an objid, or
    • The dependency is in this transaction's _tocommit, or
    • The dependency is in dataService.ToCommit, or
    • The dependency is in the store and InCommit.
  5. Throws if any dependency is unresolved.

flush(force?: boolean): Promise<boolean>

Commits all accumulated changes:

  1. Merges the transaction's _tocommit into dataService.ToCommit.
  2. Calls dataService.Flush(force).
  3. Returns true on success.

Usage Example

const tx = this.data.transaction;

const ticket = new Ticket(null, this.data);
ticket.place = this.data.place;
ticket.status = 'AC';

await tx.push(ticket);

const product = new TicketProduct(null, this.data);
product.ticket = ticket;
product.product = someProduct;
product.amount = 2;

await tx.push(product);
await tx.flush();

aliveItems

Manages ViewObject instances using weak references to allow garbage collection when views are no longer needed by the UI.

Internal Structure

class aliveItems {
private _alivemap = new WeakMap<DataObject, WeakRef<ViewObject<DataObject>>>();
private finalizationRegistry = new FinalizationRegistry<DataObject>(...);
}

Methods

MethodDescription
get(object)Returns the existing ViewObject if still alive (via WeakRef.deref()), or creates a new one by calling object.View, stores it as a WeakRef, registers it with FinalizationRegistry, and calls doRegister(). Returns null if object is null.
has(object)Returns true if a WeakRef exists and the referenced ViewObject is still alive. Logs a warning if the ref exists but the object has been GC'd.
set(object)(Private) Creates and stores a new ViewObject via object.View, registers it for finalization tracking.

Lifecycle

Debug Information

get itemssize(): number    // _register_count - _released_count
logstatus(): void // Logs total registered and released counts