ViewObject Pattern
The ViewObject pattern in upp-data wraps DataObject instances with computed properties, event handling, and a unified Proxy interface. ViewObjects enhance the raw data model for UI consumption while keeping the DataObject as the single source of truth.
ViewObject Base Class (from base.ts)
The ViewObject<T extends DataObject> abstract class wraps a DataObject with computed properties and event handling.
Proxy Pattern
The get Proxy() method returns a JavaScript Proxy that:
- For get: Returns ViewObject properties first, then falls through to the underlying DataObject. Functions from DataObject are bound to the DataObject context. The
Viewproperty is explicitly excluded to avoid recursive access. - For set: Sets on ViewObject first if the property exists there, then on DataObject, then on ViewObject as fallback.
- For has: Checks both ViewObject and DataObject.
- For delete: Delegates to DataObject first, then ViewObject.
The exported type is defined as:
export type ViewType<T, U> = T & {
[K in keyof U as K extends keyof T ? never : K]: U[K];
};
This merges ViewObject and DataObject types while prioritizing ViewObject's properties (ViewObject keys are excluded from the DataObject contribution to avoid conflicts).
The factory function pattern:
export type PlaceView = ViewType<_PlaceView, Place>;
export function PlaceViewProxy(object: Place, data: dataService): PlaceView {
return new _PlaceView(object, data).Proxy as PlaceView;
}
Lifecycle
- DataObject's
get View()callsviewProxy(abstract, each class overrides) which creates the ViewObject. - The ViewObject is stored as a WeakRef on the DataObject (
_objectviewref). - When accessed via
aliveItems.get(dataObject), if no live reference exists, a new one is created anddoRegister()is called. doRegister()subscribes toOnRefreshof the underlying DataObject and its children.- Subscriptions use
WeakRef(this)to avoid preventing GC. OnDestroy()unsubscribes all subscriptions.
Refresh Flow
OnDataRefresh(Subject): Fires immediately on data changes.OnViewRefresh(Subject): Fires asynchronously (viaPromise.resolve().then()) for UI updates, with deduplication via the_scheduledflag.DoRefreshView(now?): Triggers both. Ifnow=false, waits for a clock tick viaclockService.OnRefreshTick.
aliveItems Class
Manages a WeakRef-based cache of ViewObjects:
- Uses
WeakMap<DataObject, WeakRef<ViewObject<DataObject>>>. FinalizationRegistrytracks when ViewObjects are garbage collected and cleans up the map.get(object): Returns the existing ViewObject or creates a new one and callsdoRegister().has(object): Checks if an alive reference still exists.- Debug:
itemssize,logstatus().
ViewObject Classes
PlaceView (wraps Place)
Computed properties:
user— UserViewtables— QrCodeView[]products— ProductView[]offers— OfferView[]extras— ExtraView[]discounts— DiscountView[]employees— EmployeeView[]tickets— TicketView[]
Features:
catalog— Catalog instance for loading product catalog.search— SearchTool for product search.GroupedProducts— Products grouped by parent (families, groups).AlertSize/AlertTables— Tables requiring waiter attention.
Configuration (getters on Place, visible on merged PlaceView):
taxrate,TicketPrepayment,TicketPreparationCanCash,CanCard,CanAccountOpenOnCash,OpenOnCard,CashModal,CardModal,FiscalWarningSendVFTEnabled,SendBAIEnabled,ProductCodes
Ticket generation:
NextDeviceTicket(series, invoice)— Series/invoice numbering on LOGIN terminals (not used for GUEST/QR).InitializeTickets— Initializes ticket store from server or local cache (LOGIN only).
Actions:
CreateTables(size)— Creates new QR code tables.DeleteTables(size)— Deletes tables.PrintQrCodes()— Generates and downloads QR stickers PDF.LoadCatalog()— Loads catalog if not already loaded.doSearch(criteria)— Searches products and preselects.
TicketView (wraps Ticket)
Computed properties:
place— PlaceViewqrcode— QrCodeViewproducts— TicketProductView[]offers— TicketOfferView[]extras— TicketExtraView[]discounts— TicketDiscountView[]mixed— MixedPayment[]PinpadPays/PinpadPending/PinpadApproved/PinpadToApply— PinpadPay (Paytef attempts; excludesDE)changes— TicketChange[]lastchange— Last TicketChangeinvoice— TicketInvoicepriceinfo— PriceType (calculated totals)
Status flags:
IsRecent,IsEmpty,IsOpened,IsClosedIsReady,IsToPay,IsPaid,IsCancel
Payment:
paymentgetter/setter with auto-print and cash drawer on payment.- Status transitions (AC, RD, PD, PP, CC).
ApplyPinpad()— closes with an unapplied approved terminal charge (PAYCARD).
Actions:
ToCart(product, options, amount, info)— Adds product to ticket.AmountOfProduct(product)— Units of a catalog product in the active cart (InCartlines); cached and invalidated withTICKET/TICKETPRODUCTrefresh.AddOneOf(ticketproduct)— Increments quantity.DelOneOf(ticketproduct)— Decrements quantity.AddSplit()— Splits payment.AddDiscount(discount)— Applies discount.OnCommit(payment, transaction)— Commits ticket (TicketCommit flow).OnOpen()— Reopens ticket for editing.OnCancel()— Cancels ticket.OnReady()— Marks ticket ready.OnMerge(merge)— Merges another ticket into this one.
Internal classes:
- TicketCommit — Handles CommitTicket flow (order number, TicketChange, Verifactu/Ticketbai, print, commit).
- TicketPrint — Manages print/drawer requests.
Price calculation (_PriceInfo):
- Clears offers and groups products.
- Applies offers.
- Groups products again.
- Applies extras.
- Applies discounts.
- Sums products, extras, subtracts discounts into
PriceInfo.
UserView (wraps User)
employees— EmployeeView[] (valid employees).places— PlaceView[] (owned places + staff places).IsActive— Whether user has an active session.IsPlaceOwner— Whether user owns the current place.IsAllowedTo(permission)— Permission check (owner or employee).
ProductView (wraps Product)
selects— PreselectView[]categories— CategoryView[]options— ProductOptView[]parent— ProductView (for grouped products).groupitems— ProductView[] (children of a group).ToCart— Whether product can be added to cart.calcinfo— Price calculation info.- Subscribes to
OnChildChangedto re-register when options/categories change.
CategoryView (wraps Category)
product— ProductViewoptions— ProductOptView[]enabledoptions— Options that are enabled.validdepends— CategoryDep[]checkoptions— ProductOptView[] for validation.IsSuitable— Whether category selection is valid.
QrCodeView (wraps QrCode)
place— PlaceViewtickets— TicketView[] (valid tickets).IsValid— Whether table is not deleted.Attention— Whether table needs waiter attention.IsConnected— Whether a guest QR session on the table is live (Session.IsActive: AC + freshtouch). Sessions stay onQRSCAN; the view listens only on thisQRCODE(DoRefresh('SESSION')). Updated from place_CHANGESLOG(Concat), not a 10s poll.TableStatus— wait | cnfg | goto | busy | work | free.ThumbInfo— icon and color for UI.QrCodeUrl(language)— Guest URL for this table in the given language.QrCodeImageDataUrl(language, options?)— PNG data URL of the table QR matrix.- Always uses error correction level
H. - Optional
options.logoDataUrl: when set to a valid image data URL, draws the Unpispas mark centered on the matrix (opaque light eye under the icon; mark ≤ ~22 % of QR side, total occlusion ≤ ~25 %). Preview, sticker PDF HTML, and table-edit download share this composed PNG. - If the logo fails to load or draw, returns a plain QR at ECL
Hand logs a warning (print must not fail). - Out of scope: fiscal QR images; embedding
Place.logoinside the matrix. - Mark asset (POS):
assets/image/unpispas-mark.png(fromlibs/feature/assets).
- Always uses error correction level
EmployeeView (wraps Employee)
user— UserViewplace— PlaceViewIsActive— Whether employee's user has active session.IsAllowedTo(permission)— Permission check.
OfferView (wraps Offer)
place— PlaceViewperiods— OfferPeriod[]products— OfferProduct[] with ProductView.GroupProducts— Products grouped for display.AppliesTo(ticket)— Whether offer applies to ticket.
ExtraView (wraps Extra)
place— PlaceViewperiods— ExtraPeriod[]products— ProductView[] (via ExtraProduct).tables— QrCodeView[] (via ExtraTable).AppliesTo(ticketproduct)— Whether extra applies.
DiscountView (wraps Discount)
place— PlaceViewperiods— DiscountPeriod[]products— ProductView[] (via DiscountProduct).AppliesTo(ticket)— Whether discount applies to ticket.
FamilyView (wraps Family)
periods— FamilyPeriod[]products— FamilyProduct[]ProductsLength— Count of products in family.
PreselectView (wraps Preselect)
options— ProductOptView[]price— Calculated preselect price.IsInvalid— Whether selection is invalid.IsCompleted— Whether all required options are selected.
ProductOptView (wraps ProductOpt)
product— ProductViewcategory— CategoryViewcalcinfo— Price calculation for option.
TicketProductView, TicketOfferView, TicketExtraView, TicketDiscountView
- Wrap ticket line items with computed properties (charge, amount, calcinfo).
- Provide
product,offer,extra,discountreferences. - Support grouping and display logic for ticket UI.
TicketProductViewbase unit price comes fromProductView.tableprice(ticket.qrcode)(zone/period overrides), then market/weight/options. Reassigningticket.qrcoderefreshes line and ticket price caches (RN-PZ-11).TicketProductView.shortcutrecovers the matchingPreselectby option coincidence (IsDirect).labelis the preparation-order text source: shortcutalias→ productalias→ commercial productname. The print class consumeslabel.tr(lang); the customer receipt does not.