Skip to main content

Place and Catalog

The place and catalog system in upp-data manages business entities, their product catalogs, pricing modifiers (offers, extras, discounts), and the QR code table infrastructure. The primary classes are Place (DataObject), _PlaceView (ViewObject), and the supporting Catalog and SearchTool utilities.

PlaceView

_PlaceView extends ViewObject<Place> and is the main interface for working with a business location. It wraps the Place DataObject with computed collections, configuration accessors, and management actions.

Initialization

The PlaceView waits for the underlying Place object's OnInitialize event before performing its own initialization. This ensures all related objects (products, offers, etc.) are loaded before the view attempts to compute its collections.

constructor(object: Place, data: dataService) {
super(object, data);
this.place.OnInitialize.pipe(take(1)).subscribe(() => {
this.Initialize();
});
}

Lazy Services

Services are resolved lazily from the Angular injector to avoid circular dependencies:

ServicePurpose
adhocServiceAd-hoc HTTP requests (catalog loading, QR code printing)
clockServiceTime management and refresh ticks
languageServiceTranslations
toastServiceUser notifications
alertServiceModal alerts
configServiceLocal device configuration (ticket numbering)

Children Collections

All collections are cached and invalidated when the corresponding entity type changes (via RefreshView(targets)). Each collection filters for IsValid objects and wraps them as ViewObjects:

GetterTypeSourceInvalidated by
userUserViewplace.user via alives.get()
tablesQrCodeView[]place.qrcodesQRCODE
productsProductView[]place.productsPRODUCT
offersOfferView[]place.offersOFFER
extrasExtraView[]place.extrasEXTRA
discountsDiscountView[]place.discountsDISCOUNT
employeesEmployeeView[]place.employeesSTAFF
ticketsTicketView[]place.tickets (sorted by updated desc)TICKET

The products getter has special logic: it skips catalog items that have been overridden (i.e., IsCatalog && next && next.IsValid), showing only the effective version.

ProductsLength returns the count of non-group products (excludes containers).

Refresh Mechanism

The RefreshView(targets: string[]) method is called when any child entity changes. It invalidates the corresponding cached collection and triggers DoRefreshView():

private RefreshView(targets: string[]) {
if (targets.includes('PRODUCT')) {
this._validproducts = null;
this._groupproducts = null;
this.DoRefreshView();
}
// ... similar for QRCODE, OFFER, EXTRA, DISCOUNT, STAFF, TICKET
}

doRegister

Subscribes to the Place object's OnRefresh and each QR code's OnRefresh (for alert tracking). Uses WeakRef(this) to prevent the subscription from keeping the PlaceView alive.

Alert System

Tracks QR code tables that require waiter attention:

PropertyTypeDescription
AlertSizenumberNumber of tables currently needing attention
AlertTablesQrCode[]Array of QrCode objects needing attention

RefreshAlerts(qrcode) checks qrcode.IsValid && qrcode.Attention to decide whether a table should be in the alert set.

Place Configuration

The PlaceView exposes configuration properties that control ticket and payment behavior. These are intended to read from place-level configuration (currently returning defaults as TODOs):

PropertyTypeDefaultDescription
taxratenumberAppConstants.defaultTaxRateDefault tax rate for the place
TicketPrepaymentbooleanfalseIf true, tickets must be paid before they can be "opened"
TicketPreparationbooleanfalseIf true, tickets go through PR→AC→RD; if false, auto-ready
CanCashbooleantrueCash payment is enabled
CanCardbooleantrueCard payment is enabled
CanAccountbooleanfalseAccount payment is enabled
OpenOnCashbooleantrueOpen cash drawer when paying with cash
OpenOnCardbooleanfalseOpen cash drawer when paying with card
CashModalbooleantrueShow cash payment modal
CardModalbooleantrueShow card payment modal
OptionSendstring''Fiscal system to send tickets to: '' (none), 'BAI' (TicketBAI), 'VFCT' (Verifactu). Stored as PLACEOPT optionSend.
SendVFTEnabledbooleanVerifactu fiscal integration is active (OptionSend === 'VFCT')
SendBAIEnabledbooleanTicketBAI fiscal integration is active (OptionSend === 'BAI')

Ticket Numbering

The PlaceView manages per-device sequential numbering for ticket series and invoices, stored locally in configService.

TicketDeviceInfo

interface TicketDeviceInfo {
X: number | null; // last provisional (unpaid) ticket number
T: number | null; // last ticket number
F: number | null; // last invoice number
R: number | null; // last rectificative number
A: number | null; // last ticket (recapitulative) number
C: number | null; // last ticket (with invoice) number
bai: string | null; // last sent ticketbai signature
}

InitializeTickets()

Called during PlaceView initialization:

  1. Loads the last ticket numbers from the local configService (the ticketstore property).
  2. Calls _LastServerTickets() to fetch the latest numbers from the server.
  3. Reconciles local and server numbers, taking the greater value for each type.

NextDeviceTicket(serialType, invoiceType): Promise<[string | null, string | null]>

Generates the next serial and invoice number:

  1. Reads the current counter from TicketDeviceInfo for the given invoiceType.
  2. Increments the counter.
  3. Formats the serial as {SerialType}{padded_number} (e.g., S00000042).
  4. Formats the invoice similarly.
  5. Saves the updated TicketDeviceInfo to configService.
  6. Returns [serial, invoice].

Product Catalog

Product Hierarchy

Products form a tree structure through the parent relation:

FlagMeaning
isgroupProduct is a container (folder) for other products
isfamilyProduct has associated Family objects for time-based grouping
IsCatalogProduct comes from a shared catalog template

Catalog Items

Products with status='CT' are catalog templates loaded from the server. When a place customizes a catalog product, a new Product is created with next pointing to the original catalog product. The PlaceView's products getter filters out overridden catalog items.

GroupedProducts

The GroupedProducts getter organizes products into display groups:

type GroupedInfo = {
parent: ProductView | null;
children: ProductView[];
};
type CatalogGrouped = GroupedInfo[];

Products are grouped by their parent:

  • Root products (no parent or parent is the place) form standalone groups.
  • Products whose parent has isgroup=true are nested under their parent group.
  • The result is an array of GroupedInfo, each with an optional parent and an array of children.

Catalog Loading

get catalog(): Catalog

The Catalog class (lazy-initialized) handles loading product templates from the server via adhocService. It provides products that can be added to the place's catalog.

Backend endpoints:

  • GET catalog/catalog — Returns the list of demo catalogs (JSON).
  • POST catalog/import — Imports a catalog ZIP via chunked transfer (base64 chunks).
  • resources/catalog/images/ — Static path for catalog product images (relative to baseURL).
get search(): SearchTool

The SearchTool (lazy-initialized) provides text search over products and preselects. Used by the UI for product lookup by name or code.

doSearch(criteria: string): ProductView[] | PreselectView[]

Table Management

MethodDescription
CreateTables(count)Creates the specified number of new QrCode objects for the place
DeleteTables(tables)Marks the specified QrCode objects as DE
PrintQrCodes(tables)Sends a request to the server to generate a PDF with QR codes for download

Offers System

Offers represent special pricing for product bundles (e.g., a menu).

Offer Structure

EntityTableDescription
OfferOFFERThe offer definition: name, price, ismenu, allday
OfferProductOFFERPRODUCTLinks an offer to a product, with grouped count and menuctg
OfferPeriodOFFERPERIODDefines when the offer is valid (weekdays, time range)
OfferOptionOFFERPRODUCTOPTLinks an OfferProduct to a specific ProductOpt

Offer Application

Offers are auto-applied during price calculation (_PriceInfo). The algorithm:

  1. Collects all active offers from the place.
  2. For each offer, calculates the potential savings if applied to the current ticket.
  3. Picks the offer with the greatest savings.
  4. Creates a TicketOffer and assigns matching products to it.
  5. Repeats until no more offers improve savings.

An offer's AppliesTo(ticketView) method checks:

  • The offer's periods are currently active.
  • The ticket contains the required products (matching the offer's OfferProduct entries).
  • The required product amounts are available.

When ismenu=true, the offer represents a fixed-price menu. Products in the menu are organized by menuctg (menu category), and each grouped value indicates how many items from that product can be selected.

Extras System

Extras are additional charges applied to tickets automatically (e.g., table service charge, terrace surcharge).

Extra Structure

EntityTableDescription
ExtraEXTRAThe extra definition: name, type, price, allday, allprd
ExtraProductEXTRAPRODUCTLinks an extra to a specific product (when allprd=false)
ExtraPeriodEXTRAPERIODDefines when the extra is valid
ExtraTableEXTRATABLELinks an extra to a specific QR code table
ExtraOptionEXTRAPRODUCTOPTLinks an ExtraProduct to a specific ProductOpt

Extra Application

Extras are auto-applied during price calculation:

  1. For each existing TicketExtra, check if its source Extra still applies. Remove if not.
  2. For each active Extra in the place, check if it applies to this ticket. Add if applicable and not already present.

An extra applies when:

  • Its periods are currently active (or allday=true).
  • It applies to all products (allprd=true) or the ticket contains a matching product.
  • It applies to all tables or the ticket's QR code matches an ExtraTable.

Discounts System

Discounts reduce the ticket total, either as a fixed amount or a percentage.

Discount Structure

EntityTableDescription
DiscountDISCOUNTThe discount definition: name, type, value, allday, allprd, cumulative
DiscountProductDISCOUNTPRODUCTLinks a discount to a specific product
DiscountPeriodDISCOUNTPERIODDefines when the discount is valid

Discount Properties

PropertyDescription
typeDiscount type (percentage or fixed amount)
valueThe discount value
cumulativeIf true, can be combined with other discounts
allprdIf true, applies to all products; if false, only to linked products
alldayIf true, always active; if false, follows period schedules

Discount Application

Discounts are manually added by the user (via AddDiscount()), not auto-applied. During price calculation, ApplyDiscounts() triggers calcinfo on each TicketDiscountView, which computes the charge based on the discount's type and value against the current ticket total.

Period Objects (Shared Pattern)

All period objects (OfferPeriod, ExtraPeriod, DiscountPeriod, FamilyPeriod) share the same structure for defining time-based validity:

FieldTypeDescription
statusstring'AC' for active, 'DE' for deleted
weekdaysstringBitmask or comma-separated list of active days
inistringStart time (e.g., '09:00')
endstringEnd time (e.g., '14:00')
timezonestringTimezone for evaluation

Each period object references its parent (offer, extra, discount, or family) and follows the standard status setter pattern with parent notification on 'DE'.

Family System

Families group products with time-based activation, typically for daily menus or special schedules.

Family Structure

EntityTableDescription
FamilyFAMILYThe family definition, linked to a product with isfamily=true
FamilyProductFAMILYPRODUCTLinks a family to a product or preselect, with sort order
FamilyPeriodFAMILYPERIODDefines when the family is active

A product's family getter finds its first valid Family child. When isfamily=true, the product acts as a family container, and its FamilyProduct children define the actual items available during the family's active periods.

FamilyProduct

Each FamilyProduct can link to either:

  • A Product directly, or
  • A Preselect (which itself groups product options).

IsValid on FamilyProduct checks both its own status and the validity of the linked product/preselect.