AppConstants Reference
Introduction
AppConstants is a static class that centralises every application-wide constant into a single, predictable location. Instead of scattering magic numbers, URLs, and configuration values across the codebase, every library and application reads them from AppConstants. This guarantees consistency: when the backend URL changes, it changes in one place; when a tax rate is updated, every price calculation picks it up automatically.
Values in AppConstants come from two sources:
- Hard-coded defaults -- values that are inherent to the business domain (tax rates, printer widths, the application name) and do not vary between environments.
- Build-time environment -- values injected from
environments/environment.tsat build time (URLs, sandbox flags, API keys, version strings). This allows the same source code to serve development, staging, and production without runtime conditionals.
import { AppConstants } from '@unpispas/upp-defs';
When to use AppConstants
Use AppConstants whenever you need a value that:
- Is shared across multiple files, libraries, or features.
- Might change between environments (development vs production).
- Represents a business rule (tax rates, invoice limits, cache durations).
- Is an external key or identifier (API keys, Firebase VAPID key, OAuth client ID).
Never hard-code these values locally. If you find yourself writing const BASE_URL = 'https://...' or const VAT = 21, use the corresponding AppConstants getter instead.
Application identity
These constants identify the application itself. They are used in headers, about screens, version checks, and build metadata.
| Getter | Type | Value / Source | Description |
|---|---|---|---|
Appname | string | 'UnPisPas' | The display name of the application. Used in UI headers, log prefixes, and TicketBAI registration. |
Version | string | environment.appVersion | Semantic version string injected at build time. Appears in the about screen, is sent to the backend during sync, and is reported in TicketBAI submissions. |
Build | string | '0' | Legacy build number from before the Nx migration. Kept for backward compatibility but no longer incremented. |
isProduction | boolean | environment.production | Whether the current build targets production. Guards behaviours like verbose logging, sandbox APIs, and development-only UI. |
Why these exist: The application needs a single source of truth for its own identity. The version string, in particular, must match exactly between the frontend and the TicketBAI fiscal submissions, so reading it from one place prevents mismatches.
Example
if (!AppConstants.isProduction) {
console.warn(`[DEV] Running ${AppConstants.Appname} v${AppConstants.Version}`);
}
URLs
The URL constants build full endpoint addresses from environment fragments (protocol, path, domain, port, route). This decomposition allows the same codebase to target different servers (local Docker, staging, production) by changing only the environment file.
| Getter | Type | Description |
|---|---|---|
baseURL | string | Backend REST API base URL. Built from environment.wwwProt, wwwPath, wwwDomain, wwwPort, wwwRoute. Every HTTP request to the backend starts with this. |
thisURL | string | Current application (frontend) URL. Built from environment.appProt, appPath, appDomain, appPort, appRoute. Used for generating links that point back to the app (e.g. QR codes, sharing). |
restURL | string | WebSocket relay server URL. Built from environment.wsrProt, wsrPath, wsrDomain, wsrPort, wsrRoute. Used by the real-time sync layer. |
apppath | string | environment.appPath -- the sub-path portion of the application URL (e.g. '/app/'). Used when constructing relative links. |
domain | string | environment.appDomain -- the domain portion of the application URL (e.g. 'unpispas.com'). |
uploadPath | string | 'upload/' -- relative path appended to baseURL for file uploads (images, attachments). |
qrcodePath | string | 'qrcode/' -- relative path appended to baseURL for QR-code image assets. |
Why these exist: A POS system talks to multiple backend endpoints (REST API, WebSocket relay, file uploads, QR codes). Centralising the base URLs ensures every service call targets the correct server for the current environment. The decomposed fragments (prot, domain, port, path, route) give deployment flexibility without code changes.
How URLs are constructed
baseURL = wwwProt + "://" + wwwPath + wwwDomain + [":" + wwwPort] + wwwRoute
For example, with a local Docker environment:
http:// localhost :8080 /api/
^prot ^domain ^port ^route
Examples
// Build a full upload URL for an image stored in the database
const imageUrl = AppConstants.baseURL + AppConstants.uploadPath + filename;
// e.g. "https://api.unpispas.com/upload/photo_12345.jpg"
// Build the avatar URL for a product
const avatarUrl = AppConstants.baseURL + 'avatar?size=128&name=' + encodeURIComponent(name);
// Check if an image URL belongs to our backend
const isOwnImage = imageUrl.startsWith(AppConstants.baseURL);
Tax rates
Spain has three VAT (IVA) rates. These constants encode the current legal rates and are used by PriceInfo, the ticket system, and any component that displays or calculates prices.
| Getter | Type | Value | Description |
|---|---|---|---|
TaxRate1 | number | 4 | Super-reduced VAT rate (4%). Applies to basic necessities (bread, milk, books, medicine). |
TaxRate2 | number | 10 | Reduced VAT rate (10%). Applies to food, water, hospitality, passenger transport, and similar categories. |
TaxRate3 | number | 21 | General VAT rate (21%). The default rate for most goods and services. |
defaultTaxRate | number | 10 (= TaxRate2) | The fallback tax rate when a product or line item does not specify one. Set to the reduced rate because the primary use case is hospitality (restaurants, bars). |
Why these exist: Tax rates are mandated by Spanish law and appear in dozens of calculations across the system -- product prices, ticket totals, discount breakdowns, TicketBAI submissions, and printed receipts. Having them as named constants prevents errors (nobody accidentally writes 22 instead of 21) and makes future rate changes a single-line edit.
Example
// In PriceInfo, the default tax rate is used when no rate is provided
const price = new PriceInfo(50); // 50 EUR at defaultTaxRate (10%)
// Explicitly use the general rate
const price21 = new PriceInfo(100, AppConstants.TaxRate3); // 100 EUR at 21%
// A product view falls back to the default rate when the product has no rate set
const rate = product.taxrate || AppConstants.defaultTaxRate;
Timings and thresholds
These constants control time-based behaviours and search sensitivity throughout the application.
| Getter | Type | Value | Description |
|---|---|---|---|
CacheExpiration | number | 432 000 000 (5 days in ms) | Maximum age of cached session data before it is considered stale and discarded. When the sync module recovers stored data from IndexedDB, it checks whether the data is older than this threshold. If it is, the cache is invalidated and a full sync is triggered. |
recentMs | number | 28 800 000 (8 hours in ms) | Time window for considering tickets "recent". The ticket list UI uses this to highlight or filter tickets from the current work shift. A ticket is "recent" if now - ticket.updated < recentMs. |
MainRefresh | number | 500 | Interval in milliseconds for the main sync refresh tick. The sync module uses setInterval with this value to periodically check and push pending changes. 500ms gives near-real-time sync without overwhelming the system. |
SearchThreshold | number | 80 | Minimum fuzzy-search score (0-100) for a result to be considered a match. Used by the product search in the catalog view. A score of 80 means only close matches appear, reducing noise. |
invoiceMaxPrice | number | 3000 | Maximum price (in EUR) for a simplified invoice under Spanish regulation. When a ticket exceeds this amount, the system requires a full invoice with the customer's tax ID. This is a legal requirement, not a business preference. |
Why these exist: Each of these values encodes either a business rule or a UX decision that affects multiple parts of the system. Centralising them makes it easy to tune behaviour (e.g. change the search sensitivity) without hunting through the codebase.
Examples
// Sync module: check if cached data is still fresh
const elapsed = Date.now() - metadata.timestamp;
if (elapsed > AppConstants.CacheExpiration) {
console.warn('[STORAGE] Stored data are outdated!');
return false;
}
// Ticket view: determine if a ticket is recent (current shift)
get isRecent(): boolean {
return (Date.now() - this.ticket.updated.getTime()) < AppConstants.recentMs;
}
// Ticket view: check if a full invoice is required
get requiresInvoice(): boolean {
return this.priceinfo.val >= AppConstants.invoiceMaxPrice;
}
// Product catalog: filter search results
const results = search.doSearch(criteria, products, AppConstants.SearchThreshold);
Printing defaults
These constants configure thermal receipt printing, the primary output format for a POS system.
| Getter | Type | Value | Description |
|---|---|---|---|
defaultPrintWidth | number | 58 | Default thermal-printer paper width in millimetres. 58mm is the standard width for most POS receipt printers. |
defaultPrintFont | number | 12 | Default font size in pixels for printed ticket text. Balanced for readability on 58mm paper. |
thumbnailWidth | number | 140 | Pixel width for image thumbnails (product images, logos). Used when resizing images for display in the catalog grid. |
thumbnailColumns | number | 24 | Column count used for thumbnail grid layouts. Determines how many characters fit across a receipt when rendering grid-based content. |
Why these exist: Receipt printers have strict physical constraints. Hard-coding 58 in every print-related component would scatter knowledge about the hardware across the codebase. These constants let the printing subsystem adapt if a different printer width is needed in the future.
Language
| Getter | Type | Value | Description |
|---|---|---|---|
defaultLanguage | string | 'es' | Fallback language code when the browser locale is not supported. Spanish is the default because the primary market is Spain. |
Why this exists: The language service in upp-base uses this as the ultimate fallback. If a user's browser reports a language that the application does not support, the UI falls back to Spanish rather than showing raw translation keys.
Service Worker
These getters control the Progressive Web App (PWA) service worker registration. The application runs in two modes -- full POS mode and guest (QR-access) mode -- and each requires a different service worker with a different scope and registration timing.
| Getter | Type | Description |
|---|---|---|
EnableSW | boolean | Whether to register a service worker at all. Returns false for static pages (no qr-access or index in the URL) and native environments (http://localhost/). This prevents SW interference during development and on landing pages that do not need offline capabilities. |
TargetSW | string | Which service worker file to register: 'firebase-messaging-sw.js' for guest (QR-access) mode (handles push notifications only), 'unpispas-application-sw.js' for full POS mode (handles offline caching, background sync). |
ScopeSW | string | The scope of the service worker: '/qr-access' for guest mode, '/' for full POS mode. Scoping ensures the guest SW does not intercept POS requests and vice versa. |
DelaySW | string | Delay in milliseconds before registering the SW: '2000' for guest mode (fast startup, push notifications needed quickly), '30000' for full POS mode (let the app finish loading and hydrating before the SW starts caching). |
Why these exist: Service worker registration is sensitive to timing and context. Registering too early blocks the main thread during app startup. Registering the wrong SW file can break offline behaviour. These constants encode the correct decisions based on the running mode, so the SW registration logic does not need to understand the business context.
How mode detection works
The mode is determined by inspecting document.URL:
- Contains
qr-access-> guest mode (customer scanning a QR code) - Contains
index-> static page (landing page, does not need a SW but is not "static" in the IsStatic sense) - Neither -> static context (development, direct file access) -- SW disabled
// SW registration logic (simplified)
if (AppConstants.EnableSW) {
setTimeout(() => {
navigator.serviceWorker.register(AppConstants.TargetSW, {
scope: AppConstants.ScopeSW
});
}, parseInt(AppConstants.DelaySW));
}
TicketBAI / Verifactu (Spanish fiscal compliance)
TicketBAI es un sistema de facturacion obligatorio en el Pais Vasco (Bizkaia, Gipuzkoa, Araba) que requiere que todo software de facturacion se registre ante las haciendas forales con licencias, nombres de desarrollador y NIFs especificos por territorio. Verifactu (SII) es el equivalente para el resto de Espana.
These constants manage the registration data required by the Basque Country's TicketBAI fiscal compliance system. Every software that generates invoices in the Basque Country must be registered with the regional tax authority and must include its licence code, developer identity, and NIF in every fiscal submission.
| Getter / Method | Type | Description |
|---|---|---|
TicketBaiSandbox | boolean | Whether to use the TicketBAI sandbox (test) environment. Read from environment.ticketbai_sandbox. When true, all TicketBAI methods return test credentials instead of production ones. |
TicketSiiSandbox | boolean | Whether to use the SII/Verifactu sandbox environment. Read from environment.ticketsii_sandbox. |
TicketBaiAppname(region) | string | Returns the registered application name for the given region. The name is registered with the regional tax authority and must match exactly in submissions. |
TicketBaiAppvers(region) | string | Returns the application version string for TicketBAI. Returns AppConstants.Version if a region is provided, '0.0.0.0' otherwise (indicating no TicketBAI region is configured). |
TicketBaiLicense(region) | string | Returns the TicketBAI software licence code for the region. Each region issues a unique licence code during the registration process. |
TicketBaiDeveloper(region) | string | Returns the registered developer name for the region. |
TicketBaiNIF(region) | string | Returns the developer's NIF (tax identification number) for the region. |
Supported regions: 'Bizkaia', 'Gipuzkoa', 'Araba'.
How it works internally: The class maintains a private TicketBaiRegistry object with two keys: PRD (production) and DEV (development/sandbox). Each contains region-specific registration data. The public methods select the correct environment based on TicketBaiSandbox and then look up the requested field for the given region.
Example
// When generating a TicketBAI-compliant invoice for Bizkaia
const region = 'Bizkaia';
const submission = {
licence: AppConstants.TicketBaiLicense(region),
appName: AppConstants.TicketBaiAppname(region),
appVersion: AppConstants.TicketBaiAppvers(region),
developer: AppConstants.TicketBaiDeveloper(region),
developerNIF: AppConstants.TicketBaiNIF(region),
sandbox: AppConstants.TicketBaiSandbox
};
// In development, TicketBaiSandbox is true, so these return test credentials
// In production, they return the real registered credentials
API keys
These constants hold external API keys and identifiers for third-party services.
| Getter | Type | Description |
|---|---|---|
googleApiKey | string | Google Maps / Geocoding API key. Used by the geocode service in upp-base to resolve addresses to coordinates and vice versa. |
GoogleMapKey | string | Google Maps Android SDK key. Used when the application runs as a native Android app via Capacitor. |
googleOAuth2Key | string | Google OAuth 2.0 client ID. Used for "Sign in with Google" flows. |
GoogleAnalitics | string | Google Analytics tracking ID. Read from environment.googleAnalytics, so it can differ between environments (e.g. a separate analytics property for staging). |
FCMVapidKey | string | Firebase Cloud Messaging VAPID key for web push notifications. Required to subscribe the browser to push notifications from Firebase. |
Why these exist: API keys must be consistent across the application. If the geocode service and the map component used different Google API keys, quota tracking would be split and billing would be unpredictable. Centralising them also makes key rotation straightforward -- update one constant, rebuild, and every consumer picks up the new key.
Example
// Geocode service uses the Google API key
const geocodeUrl = `https://maps.googleapis.com/maps/api/geocode/json?address=${encodeURIComponent(address)}&key=${AppConstants.googleApiKey}`;
// Firebase push notification subscription
const subscription = await messaging.getToken({ vapidKey: AppConstants.FCMVapidKey });
Common patterns
Reading environment-dependent values
Many AppConstants values delegate to environment.*. The pattern is always:
// In AppConstants
public static get baseURL(): string {
return environment.wwwProt + '://' + environment.wwwPath + environment.wwwDomain + ...;
}
You never import environment directly outside of upp-defs. Always go through AppConstants.
Combining URL constants
The most common pattern in the codebase is building full URLs by concatenating base URLs with path segments:
// File upload
const url = AppConstants.baseURL + AppConstants.uploadPath + filename;
// Avatar generation
const url = AppConstants.baseURL + 'avatar?size=128&name=' + encodeURIComponent(name);
// Catalog resource
const url = AppConstants.baseURL + 'catalog/' + resourcePath;
Conditional behaviour by environment
if (AppConstants.isProduction) {
// Production-only: register analytics, enable error reporting
} else {
// Development-only: verbose logging, sandbox APIs
}
Using tax rates with PriceInfo
const price = new PriceInfo();
price.Add(menuPrice, AppConstants.TaxRate2); // Food at 10%
price.Add(beveragePrice, AppConstants.TaxRate2); // Beverage at 10%
price.Add(serviceCharge, AppConstants.TaxRate3); // Service at 21%
Related
- upp-defs Overview -- what the library provides and where it sits in the dependency graph
- Utilities Reference -- GenericUtils, PriceInfo, TaxIdValidators, GlobalMutex
- Ticket System -- the primary consumer of tax rates and
invoiceMaxPrice - Sync and Cache -- uses
CacheExpirationandMainRefresh - HTTP Service -- uses
baseURLfor all backend communication