glanzlicht / GastroOS
Verified secure showcase
500 Files 1 MB Public size 31 Redacted TypeScript · SQL · JSON · Markdown · DOCKERIGNORE · EXAMPLE

Project overview

GastroOS is a verified code showcase with 500 visible project entries across 3 top-level modules. The primary detected language is TypeScript; protected files remain visible in the structure while their contents stay private.

README

GastravaOS

GastravaOS is a modular Hospitality Operational Intelligence Platform.

The technical repository name remains GastroOS for compatibility. The product is presented publicly as GastravaOS.

Product preview

GastravaOS operations dashboard

GastravaOS is designed for hospitality operations ranging from small venues to larger multi-location organizations. The platform combines operational workflows, resilient data handling and role-specific user experiences without exposing internal complexity to operators.

What this repository demonstrates

This repository is presented as a technical portfolio showcase, not as a complete product blueprint.

It demonstrates work across:

  • backend engineering with Node.js, TypeScript, NestJS and REST/OpenAPI;
  • PostgreSQL data modeling, explicit SQL migrations and transactional consistency;
  • Flutter application architecture with Riverpod, go_router, Drift/SQLite and Dio;
  • operational workflows across hospitality domains;
  • offline-capable client foundations and synchronization patterns;
  • real-time state reconciliation and event-driven integration patterns;
  • integrations for storage, payments, fiscal/accounting systems and external platforms;
  • Docker-based development, CI, observability and runtime verification;
  • tenant-aware security, audit-oriented design and permission boundaries;
  • applied AI around deterministic domain data and human-controlled actions.

Technology stack

AreaTechnology
BackendNode.js 24 LTS, TypeScript, NestJS 11
APIREST / OpenAPI
DatabasePostgreSQL 18, Drizzle ORM, explicit SQL migrations
FrontendFlutter, Riverpod, go_router
Local dataDrift / SQLite
NetworkingDio, json_serializable
InfrastructureDocker Compose, S3-compatible storage
ArchitectureModular monolith, transactional outbox
QualityAutomated tests, CI, observability and runtime verification

Engineering approach

The codebase is organized around clear boundaries and reusable platform capabilities rather than one large feature layer.

Recurring engineering principles include:

  • deterministic and auditable domain state;
  • separation of operational truth from presentation;
  • role- and context-specific interfaces;
  • resilience under intermittent connectivity;
  • explicit integration adapters;
  • composable capabilities instead of duplicated vertical implementations;
  • controlled exposure of sensitive business, security and product logic.

Portfolio boundary

The public showcase intentionally omits detailed roadmaps, ADRs, security internals, commercial logic, AI prompts, operational playbooks and reconstructable product strategy.

Those materials remain in the private repository. The public view is optimized so technical reviewers can evaluate engineering breadth and implementation quality without receiving a reconstruction guide for the product.

Local development

The repository uses a versioned pnpm dependency graph.

corepack enable
pnpm install --frozen-lockfile

Project-specific runtime and verification details remain in the private engineering documentation.

Status

GastravaOS is under active development. The repository contains a broad implementation foundation and ongoing product work rather than a finished commercial release.

The public showcase focuses on engineering capability, architecture quality and implementation breadth while deliberately omitting core IP.

apps/backend/src/common/validation/critical-write.schemas.tsTypeScript
import { z } from 'zod';
import { SYNTHETIC_UI_FIXTURE_ROUTE_PATTERN, SYNTHETIC_UI_ROUTES, SYNTHETIC_UI_SURFACES } from '../synthetic-operations/synthetic-ui-catalog';
export const uuid=z.string().uuid();
export const currency=z.string().regex(/^[A-Z]{3}$/);
export const integerMinor=z.number().int().safe();
export const positiveMinor=integerMinor.positive();
export const decimalString=z.string().regex(/^-?\d+(?:\.\d+)?$/);
export const positiveDecimalString=z.string().regex(/^\d+(?:\.\d+)?$/).refine((v:string)=>Number(v)>0);
export const nonNegativeDecimalString=z.string().regex(/^\d+(?:\.\d+)?$/);
export const cashOpenSchema=z.object({
tillId:uuid,
openingFloatMinor:integerMinor.nonnegative().default(0),
currencyCode:currency.default('EUR'),
}).strict();
export const fulfillmentGuestContextSchema=z.object({
guestId:uuid.optional(),
reservationId:uuid.optional(),
expectedVersion:z.number().int().positive().optional(),
}).refine(v=>Boolean(v.guestId||v.reservationId),{message:'guest_or_reservation_required'}).strict();
export const fulfillmentPromiseSchema=z.object({
status:z.enum(['proposed','committed','at_risk','fulfilled','missed']),
source:z.enum(['capacity','manual','provider']),
promiseAt:z.string().datetime().optional(),
evidence:z.record(z.string(),z.unknown()).optional(),
expectedVersion:z.number().int().positive().optional(),
}).strict();
export const telemetrySettingsSchema=z.object({
automaticTechnicalDiagnostics:z.boolean(),
crashReporting:z.boolean(),
performanceTelemetry:z.boolean(),
productWorkflowAnalytics:z.boolean(),
extendedPilotDiagnostics:z.boolean(),
manualSupportBundleUploads:z.boolean(),
pilotMode:z.boolean(),
}).strict();
export const telemetrySupportBundlePreviewSchema=z.object({
confirmed:z.boolean().default(false),
entries:z.array(z.object({
id:z.string().trim().min(1).max(120),
category:z.enum(['technical_diagnostics','crash_report','performance','audit']),
bytes:z.number().int().nonnegative().safe(),
containsPersonalData:z.boolean(),
}).strict()).max(1000),
}).strict();
const syntheticScenarioSchema=z.object({
scenarioId:z.string().trim().min(1).max(120),
scenarioVersion:z.string().trim().min(1).max(40),
seed:z.number().int().nonnegative().safe(),
businessType:z.enum(['fast_food','imbiss','cafe','casual_restaurant','fine_dining','bar','cocktail_bar','beer_garden','hotel_restaurant','multi_outlet_hotel','multi_location_group']),
tenantTemplate:z.string().trim().min(1).max(120),
virtualTimeWindow:z.object({start:z.string().min(1).max(80),end:z.string().min(1).max(80)}).strict(),
loadProfile:z.record(z.string().min(1).max(80),z.number().finite().nonnegative()),
actors:z.array(z.string().trim().min(1).max(120)).max(1000),
devices:z.array(z.string().trim().min(1).max(120)).max(1000),
networkProfile:z.enum(['online','offline','intermittent','high_latency','packet_loss','reconnect']),
failureProfile:z.array(z.string().trim().min(1).max(120)).max(1000),
expectedInvariants:z.array(z.string().trim().min(1).max(160)).max(1000),
successCriteria:z.array(z.string().trim().min(1).max(160)).max(1000),
}).strict();
const syntheticResourcesSchema=z.object({
places:z.array(z.string().trim().min(1).max(120)).max(1000),
roles:z.array(z.string().trim().min(1).max(120)).max(1000),
devices:z.array(z.string().trim().min(1).max(120)).max(1000),
kitchenStations:z.array(z.string().trim().min(1).max(120)).max(1000),
integrations:z.array(z.string().trim().min(1).max(120)).max(1000),
}).strict();
const syntheticAgentSchema=z.object({
agentId:z.string().trim().min(1).max(120),
role:z.enum(['waiter','kitchen','bar','manager','inventory','purchasing','hotel_outlet','customer_demand']),
persona:z.enum(['experienced_user','new_employee','power_user','impatient_user','occasional_user','manager']),
controlMode:z.enum(['deterministic','exploratory']),
actionBudget:z.number().int().min(1).max(1000000),
}).strict();
const syntheticActionSchema=z.object({actor:z.string().trim().min(1).max(120),action:z.string().trim().min(1).max(160),payload:z.record(z.string(),z.unknown()).optional()}).strict();
const syntheticUiActionSchema=z.object({
actor:z.string().trim().min(1).max(120),
surface:z.enum(SYNTHETIC_UI_SURFACES),
route:z.union([z.enum(SYNTHETIC_UI_ROUTES), z.string().regex(SYNTHETIC_UI_FIXTURE_ROUTE_PATTERN)]),
intent:z.enum(['navigate','tap','input','select','back']),
target:z.string().trim().regex(/^[A-Za-z][A-Za-z0-9_.:-]{0,159}$/),
}).strict();
export const syntheticAgentsSchema=z.object({agents:z.array(syntheticAgentSchema).min(1).max(1000)}).strict();
export const syntheticHumanErrorSchema=z.object({
actions:z.array(syntheticActionSchema).max(100000),
seed:z.number().int().nonnegative().safe(),
profile:z.object({
duplicateClickRate:z.number().finite().min(0).max(1).optional(),
backtrackingRate:z.number().finite().min(0).max(1).optional(),
cancellationRate:z.number().finite().min(0).max(1).optional(),
}).strict(),
}).strict();
export const syntheticTenantManifestSchema=z.object({tenantId:z.string().trim().min(1).max(120),template:z.string().trim().min(1).max(120),seed:z.number().int().nonnegative().safe(),resources:syntheticResourcesSchema,externalAdapters:z.literal('simulation-only'),dataOrigin:z.literal('synthetic')}).strict();
export const syntheticRunPlanSchema=z.object({
scenario:syntheticScenarioSchema,
tenant:syntheticTenantManifestSchema,
agents:z.array(syntheticAgentSchema).min(1).max(1000),
actions:z.array(syntheticActionSchema).max(100000),
virtualStartMs:z.number().int().nonnegative().safe().optional(),
stepMs:z.number().int().positive().safe().optional(),
}).strict();
export const syntheticRuntimeExecuteSchema=syntheticRunPlanSchema;
export const syntheticUiPlanSchema=z.object({
scenario:syntheticScenarioSchema,
tenant:syntheticTenantManifestSchema,
agents:z.array(syntheticAgentSchema).min(1).max(1000),
actions:z.array(syntheticUiActionSchema).max(100000),
}).strict();
export const syntheticMaterializeSchema=z.object({tenant:syntheticTenantManifestSchema}).strict();
const syntheticExecutionContextSchema=z.object({
mode:z.literal('synthetic'),
tenantId:z.string().trim().regex(/^synthetic[-_][a-z0-9._-]+$/i).max(120),
scenarioId:z.string().trim().min(1).max(120),
scenarioVersion:z.string().trim().min(1).max(40),
seed:z.number().int().nonnegative().safe(),
dataOrigin:z.literal('synthetic'),
externalSideEffects:z.literal('blocked'),
}).strict();
const syntheticEvidenceTenantSchema=syntheticTenantManifestSchema.extend({
tenantId:z.string().trim().regex(/^synthetic[-_][a-z0-9._-]+$/i).max(120),
}).strict();
const syntheticTraceEventSchema=z.object({
sequence:z.number().int().nonnegative().safe(),
virtualTimeMs:z.number().int().safe(),
actor:z.string().trim().min(1).max(120),
action:z.string().trim().min(1).max(160),
payload:z.record(z.string(),z.unknown()).optional(),
networkProfile:z.enum(['online','offline','intermittent','high_latency','packet_loss','reconnect']),
dataOrigin:z.literal('synthetic'),
}).strict();
const syntheticRunPlanEvidenceSchema=z.object({
executionMode:z.literal('plan-only'),
context:syntheticExecutionContextSchema,
tenant:syntheticEvidenceTenantSchema,
agents:z.array(syntheticAgentSchema).min(1).max(1000),
trace:z.array(syntheticTraceEventSchema).max(100000),
}).strict();
const syntheticInvariantResultSchema=z.object({
key:z.string().trim().min(1).max(160),
passed:z.boolean(),
details:z.string().max(500).optional(),
}).strict();
const syntheticFailureArtifactSchema=z.object({
artifactType:z.literal('synthetic.failure'),
context:syntheticExecutionContextSchema,
failedInvariants:z.array(syntheticInvariantResultSchema).max(1000),
actions:z.array(z.unknown()).max(100000),
stateSnapshot:z.unknown(),
}).strict();
const syntheticRunResultEvidenceSchema=z.object({
status:z.enum(['passed','failed']),
plan:syntheticRunPlanEvidenceSchema,
invariants:z.array(syntheticInvariantResultSchema).max(1000),
failureArtifact:syntheticFailureArtifactSchema.optional(),
}).strict();
const syntheticTransportEvidenceSchema=syntheticTraceEventSchema.extend({
delivery:z.enum(['immediate','deferred','delayed','dropped']),
fault:z.enum(['duplicate_transport','backend_timeout']).optional(),
sourceSequence:z.number().int().nonnegative().safe(),
}).strict();
export const syntheticReleaseGateSchema=z.object({
run:syntheticRunResultEvidenceSchema,
transport:z.array(syntheticTransportEvidenceSchema).max(100000).optional(),
requiredInvariantKeys:z.array(z.string().trim().min(1).max(160)).max(1000).optional(),
}).strict();
export const syntheticPerformanceBudgetSchema=z.object({
durationsMs:z.array(z.number().finite().nonnegative()).min(1).max(100000),
maxAverageMs:z.number().finite().nonnegative(),
maxP95Ms:z.number().finite().nonnegative(),
}).strict();
export const syntheticCatalogSchema=z.object({
entries:z.array(z.object({scenario:syntheticScenarioSchema,owner:z.string().trim().min(1).max(120),enabled:z.boolean()}).strict()).min(1).max(1000),
}).strict();
export const syntheticCatalogCompareSchema=z.object({left:syntheticScenarioSchema,right:syntheticScenarioSchema}).strict();
export const syntheticHistorySchema=z.object({results:z.array(syntheticRunResultEvidenceSchema).min(1).max(1000)}).strict();
export const syntheticTenantPersistenceSchema=z.object({tenant:syntheticTenantManifestSchema}).strict();
export const syntheticRunEvidenceSchema=z.object({
tenantId:z.string().trim().regex(/^synthetic[-_]/i).max(120),
result:syntheticRunResultEvidenceSchema,
}).strict();
export const syntheticSoakPlanSchema=z.object({
context:syntheticExecutionContextSchema,
durationMs:z.number().int().positive().safe(),
stepMs:z.number().int().positive().safe(),
}).strict();
export const syntheticTransportSimulationSchema=z.object({
trace:z.array(syntheticTraceEventSchema).max(100000),
seed:z.number().int().nonnegative().safe(),
faultProfile:z.object({
packetLossRate:z.number().finite().min(0).max(1).optional(),
duplicateTransportRate:z.number().finite().min(0).max(1).optional(),
outOfOrderRate:z.number().finite().min(0).max(1).optional(),
backendTimeoutRate:z.number().finite().min(0).max(1).optional(),
}).strict().optional(),
}).strict();
export const syntheticConcurrencySchema=z.object({
traces:z.record(z.string().trim().min(1).max(120),z.array(syntheticTraceEventSchema).max(100000)),
seed:z.number().int().nonnegative().safe(),
}).strict().superRefine((value, context) => {
if (Object.keys(value.traces).length === 0) context.addIssue({code:z.ZodIssueCode.custom,path:['traces'],message:'at_least_one_lane_required'});
if (Object.values(value.traces).reduce((total, trace) => total + trace.length, 0) > 100000) context.addIssue({code:z.ZodIssueCode.custom,path:['traces'],message:'trace_budget_exceeded'});
});
export const syntheticStateMachineSchema=z.object({
machineId:z.string().trim().min(1).max(120),
initialState:z.string().trim().min(1).max(120),
transitions:z.array(z.object({from:z.string().trim().min(1).max(120),to:z.string().trim().min(1).max(120),event:z.string().trim().min(1).max(160)}).strict()).max(10000),
allowed:z.record(z.string().trim().min(1).max(120),z.array(z.string().trim().min(1).max(120)).max(1000)),
}).strict();
const distributionChannelSchema=z.object({
channel:z.string().trim().min(1).max(120),
requestedUnits:z.number().int().nonnegative().safe(),
unitValue:z.number().finite().nonnegative(),
acquisitionCost:z.number().finite().nonnegative(),
eligible:z.boolean().optional(),
maxAllocatableUnits:z.number().int().nonnegative().safe().optional(),
}).strict();
export const distributionPlanningSchema=z.object({
factors:z.array(z.object({
key:z.enum(['reservations','space','staff','skills','production','prep','inventory','resources','service_intent']),
available:z.number().finite(),
considered:z.boolean(),
}).strict()).max(100),
channels:z.array(distributionChannelSchema).max(1000),
operationalReady:z.boolean().optional(),
}).strict().superRefine((value, context) => {
const seen = new Set<string>();
value.channels.forEach((channel, index) => {
const key = channel.channel.trim().toLowerCase();
if (seen.has(key)) context.addIssue({code: z.ZodIssueCode.custom, path: ['channels', index, 'channel'], message: 'duplicate_channel'});
seen.add(key);
});
});
export const distributionReviewSchema=z.object({
decisionFingerprint:z.string().regex(/^[a-f0-9]{24}$/),
decision:z.enum(['accepted','rejected']),
reason:z.string().trim().min(1).max(1000).optional(),
}).strict().superRefine((value, context) => {
if (value.decision === 'rejected' && !value.reason) {
context.addIssue({code: z.ZodIssueCode.custom, path: ['reason'], message: 'reason_required_for_rejection'});
}
});
export const distributionOutcomeSchema=z.object({
decisionFingerprint:z.string().regex(/^[a-f0-9]{24}$/),
requestedUnits:z.number().int().nonnegative().safe(),
allocatedUnits:z.number().int().nonnegative().safe(),
fulfilledUnits:z.number().int().nonnegative().safe(),
overrideUsed:z.boolean().optional(),
}).strict().superRefine((value, context) => {
if (value.allocatedUnits > value.requestedUnits) {
context.addIssue({code: z.ZodIssueCode.custom, path: ['allocatedUnits'], message: 'allocated_exceeds_requested'});
}
if (value.fulfilledUnits > value.allocatedUnits) {
context.addIssue({code: z.ZodIssueCode.custom, path: ['fulfilledUnits'], message: 'fulfilled_exceeds_allocated'});
}
});
const placeEventScenarioSchema=z.object({
scenarioId:z.string().trim().min(1).max(120),
placeType:z.string().trim().min(1).max(120),
eventType:z.string().trim().min(1).max(120).optional(),
attendance:z.number().int().nonnegative().safe(),
stockUnits:z.number().int().nonnegative().safe(),
staffingUnits:z.number().int().nonnegative().safe(),
prepUnits:z.number().int().nonnegative().safe(),
contributionMinor:z.number().int().safe().optional(),
}).strict();
export const placeEventLearningSchema=z.object({
current:z.object({
placeType:z.string().trim().min(1).max(120),
eventType:z.string().trim().min(1).max(120).optional(),
attendance:z.number().int().nonnegative().safe(),
stockUnits:z.number().int().nonnegative().safe(),
staffingUnits:z.number().int().nonnegative().safe(),
prepUnits:z.number().int().nonnegative().safe(),
contributionMinor:z.number().int().safe().optional(),
}).strict(),
historical:z.array(placeEventScenarioSchema).min(1).max(1000),
}).strict().superRefine((value, context) => {
const seen = new Set<string>();
value.historical.forEach((scenario, index) => {
const key = scenario.scenarioId.trim().toLowerCase();
if (seen.has(key)) context.addIssue({code: z.ZodIssueCode.custom, path: ['historical', index, 'scenarioId'], message: 'duplicate_scenario'});
seen.add(key);
});
});
const placeEventMetricSetSchema=z.object({
attendance:z.number().int().nonnegative().safe(),
stockUnits:z.number().int().nonnegative().safe(),
staffingUnits:z.number().int().nonnegative().safe(),
prepUnits:z.number().int().nonnegative().safe(),
contributionMinor:z.number().int().safe().optional(),
}).strict();
export const placeEventOutcomeSchema=z.object({
decisionFingerprint:z.string().regex(/^[a-f0-9]{24}$/),
forecast:placeEventMetricSetSchema,
actual:placeEventMetricSetSchema,
economicsSource:z.enum(['canonical_event_economics']).optional(),
}).strict().superRefine((value, context) => {
const forecastHasContribution = value.forecast.contributionMinor !== undefined;
const actualHasContribution = value.actual.contributionMinor !== undefined;
if (forecastHasContribution !== actualHasContribution) {
context.addIssue({code: z.ZodIssueCode.custom, path: ['actual', 'contributionMinor'], message: 'contribution_required_on_both_sides'});
}
if ((forecastHasContribution || actualHasContribution) && value.economicsSource !== 'canonical_event_economics') {
context.addIssue({code: z.ZodIssueCode.custom, path: ['economicsSource'], message: 'canonical_economics_source_required'});
}
});
export const cashMovementSchema=z.object({
movementType:z.enum(['pay_in','pay_out','safe_drop','correction']),
amountMinor:positiveMinor,
currencyCode:currency.default('EUR'),
reason:z.string().trim().min(1).max(500),
}).strict();
export const cashCountSchema=z.object({
totalMinor:integerMinor.nonnegative(),
currencyCode:currency.default('EUR'),
denominationCounts:z.record(z.string(),z.number().int().nonnegative()).default({}),
}).strict();
export const cashCloseSchema=z.object({
notes:z.string().max(2000).optional(),
}).strict();
export const reservationCreateSchema=z.object({
guestId:uuid.optional(),
guestName:z.string().trim().min(1).max(200),
guestPhone:z.string().max(100).optional(),
guestEmail:z.string().email().max(320).optional(),
requestedLocale:z.string().max(20).optional(),
startAt:z.string().datetime({offset:true}),
endAt:z.string().datetime({offset:true}),
partySize:z.number().int().min(1).max(1000),
notes:z.string().max(4000).optional(),
source:z.string().max(100).optional(),
}).strict();
export const tableAssignmentSchema=z.object({
tableIds:z.array(uuid).min(1).max(50),
}).strict();
export const reservationCancelSchema=z.object({
reason:z.string().trim().min(1).max(1000).optional(),
}).strict();
export const inventoryAdjustmentSchema=z.object({
stockItemId:uuid,
storageLocationId:uuid,
quantityDelta:decimalString,
unitCode:z.string().min(1).max(32),
normalizedQuantityDelta:decimalString,
normalizedUnitCode:z.string().min(1).max(32),
reason:z.string().trim().min(1).max(1000),
employeeId:uuid.optional(),
}).strict();
export const inventoryWasteSchema=z.object({
stockItemId:uuid,
storageLocationId:uuid,
quantity:positiveDecimalString,
unitCode:z.string().min(1).max(32),
normalizedQuantity:positiveDecimalString,
normalizedUnitCode:z.string().min(1).max(32),
reason:z.string().trim().min(1).max(100),
notes:z.string().max(2000).optional(),
sourceType:z.string().max(100).optional(),
sourceId:uuid.optional(),
employeeId:uuid.optional(),
}).strict();
export const inventoryTransferSchema=z.object({
stockItemId:uuid,
fromStorageLocationId:uuid,
toStorageLocationId:uuid,
quantity:positiveDecimalString,
unitCode:z.string().min(1).max(32),
normalizedQuantity:positiveDecimalString,
normalizedUnitCode:z.string().min(1).max(32),
reason:z.string().max(1000).optional(),
employeeId:uuid.optional(),
}).strict();
export const inventoryCountPostSchema=z.object({
employeeId:uuid.optional(),
}).strict();
export const purchaseReceiveSchema=z.object({
receiptNumber:z.string().max(100).optional(),
lines:z.array(z.object({
purchaseOrderLineId:uuid,
receivedQuantity:positiveDecimalString,
storageLocationId:uuid,
lotNumber:z.string().max(200).optional(),
bestBeforeDate:z.string().date().optional(),
notes:z.string().max(2000).optional(),
}).strict()).min(1).max(1000),
idempotencyKey:z.string().min(1).max(200).optional(),
}).strict();
export const paymentIntentSchema=z.object({
checkId:uuid,
tenderId:uuid,
amountMinor:positiveMinor,
currencyCode:currency,
}).strict();
export const paymentReconcileSchema=z.object({
observedStatus:z.string().min(1).max(100),
observedAmountMinor:z.union([integerMinor,z.string().regex(/^\d+$/)]).optional(),
providerReference:z.string().max(500).optional(),
payload:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const refundSchema=z.object({
paymentId:uuid,
amountMinor:positiveMinor,
reason:z.string().trim().min(1).max(1000),
idempotencyKey:z.string().min(1).max(200).optional(),
}).strict();
export const realPaymentStartSchema=z.object({
checkId:uuid,
tenderId:uuid,
amountMinor:positiveMinor,
currencyCode:currency,
channel:z.enum(['terminal','online']),
terminalMappingId:uuid.optional(),
idempotencyKey:z.string().min(1).max(200),
}).strict();
export const terminalCancelSchema=z.object({
idempotencyKey:z.string().min(1).max(200),
}).strict();
export const billingOpenSchema=z.object({
serviceSessionId:uuid,
currencyCode:currency.default('EUR'),
}).strict();
export const billingSplitItemsSchema=z.object({
orderItemIds:z.array(uuid).min(1).max(500),
}).strict();
export const billingSplitSeatsSchema=z.object({
seatNumbers:z.array(z.number().int().min(1).max(1000)).min(1).max(100),
}).strict();
export const billingSplitEqualSchema=z.object({
parts:z.number().int().min(2).max(20),
}).strict();
export const billingSplitCustomSchema=z.object({
amountsMinor:z.array(positiveMinor).min(2).max(20),
}).strict();
export const inventoryTrustBudgetPolicySchema=z.object({
enabled:z.boolean(),
period:z.enum(['day','week','month']),
budgetMinor:integerMinor.nonnegative(),
currencyCode:currency.default('EUR'),
overLimitBehavior:z.enum(['notify_only','require_reason','manager_approval']).default('require_reason'),
allowedReasonKeys:z.array(z.enum([
'garnish','guest_return','spill','prep','staff_consumption',
'complimentary','overproduction','other'
])).min(1).max(20),
}).strict();
export const inventoryTrustConsumptionSchema=z.object({
stockItemId:uuid,
storageLocationId:uuid,
quantity:positiveDecimalString,
unitCode:z.string().min(1).max(32),
normalizedQuantity:positiveDecimalString,
normalizedUnitCode:z.string().min(1).max(32),
reasonKey:z.enum([
'garnish','guest_return','spill','prep','staff_consumption',
'complimentary','overproduction','other'
]),
detailNote:z.string().trim().max(1000).optional(),
employeeId:uuid.optional(),
}).strict();
export const roleAssignmentSchema=z.object({
membershipId:uuid,
roleId:uuid,
locationId:uuid.optional(),
}).strict();
export const locationScopesSchema=z.object({
locationIds:z.array(uuid).max(500).default([]),
}).strict();
export const membershipInviteSchema=z.object({
userAccountId:uuid,
employeeId:uuid.optional(),
}).strict();
export const knowledgeDraftSchema=z.object({
slug:z.string().trim().min(1).max(160).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/),
title:z.string().trim().min(1).max(300),
bodyMarkdown:z.string().min(1).max(200000),
articleType:z.enum(['sop','policy','howto','reference','safety','other']).optional(),
sourceLocale:z.enum(['de-DE','en-GB']).default('de-DE'),
}).strict();
export const knowledgeAcknowledgeSchema=z.object({
employeeId:uuid,
localeShown:z.enum(['de-DE','en-GB']),
}).strict();
export const trainingAssignSchema=z.object({
employeeId:uuid,
trainingModuleId:uuid,
dueAt:z.string().datetime({offset:true}).optional(),
}).strict();
export const trainingProgressSchema=z.object({
employeeId:uuid,
localeShown:z.enum(['de-DE','en-GB']),
}).strict();
export const trainingQuizSchema=trainingProgressSchema.extend({
answers:z.record(uuid,z.string().min(1).max(100)),
}).strict();
export const fileUploadSessionSchema=z.object({
ownerEntityType:z.string().min(1).max(100).optional(),
ownerEntityId:uuid.optional(),
purpose:z.enum([
'recipe_media','training_media','task_evidence','incident_evidence',
'safety_evidence','employee_document','supplier_document','invoice_document',
'import_source','export_result','other'
]),
fileName:z.string().trim().min(1).max(255),
mimeType:z.string().trim().min(1).max(255),
sizeBytes:z.number().int().safe().min(0),
checksumSha256:z.string().regex(/^[a-f0-9]{64}$/i).optional(),
}).strict();
export const notificationPreferenceSchema=z.object({
notificationType:z.string().trim().min(1).max(160),
inAppEnabled:z.boolean().default(true),
emailEnabled:z.boolean().default(false),
pushEnabled:z.boolean().default(false),
quietHoursPolicy:z.object({
enabled:z.boolean(),
start:z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
end:z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
}).strict().nullable().optional(),
}).strict();
export const serviceOpenSchema=z.object({
reservationId:uuid.optional(),
tableIds:z.array(uuid).min(1).max(50),
partySize:z.number().int().min(1).max(1000),
}).strict();
export const serviceMoveTableSchema=z.object({
fromTableId:uuid,
toTableId:uuid,
}).strict();
export const fulfillmentContextSchema=z.object({
orderChannel:z.enum(['pos','counter','takeaway','delivery','roomservice','event','retail','staff_meal']).default('pos'),
fulfillmentMode:z.enum(['table_service','counter_service','takeaway','delivery','bar_tab','roomservice','event','retail','staff_meal']).default('table_service'),
fulfillmentTargetAt:z.string().datetime().optional(),
placeId:uuid.optional(),
operationalUnitId:uuid.optional(),
expectedVersion:z.number().int().positive().optional(),
}).strict();
export const barTabContextSchema=z.object({
orderChannel:z.enum(['pos','counter']).default('pos'),
fulfillmentTargetAt:z.string().datetime().optional(),
placeId:uuid.optional(),
operationalUnitId:uuid.optional(),
expectedVersion:z.number().int().positive().optional(),
}).strict();
export const barTabOpenSchema=z.object({
orderChannel:z.enum(['pos','counter']).default('counter'),
fulfillmentTargetAt:z.string().datetime().optional(),
placeId:uuid.optional(),
operationalUnitId:uuid.optional(),
}).strict();
export const orderCreateSchema=z.object({
serviceSessionId:uuid,
}).strict();
export const orderAddItemSchema=z.object({
productId:uuid,
seatId:uuid.optional(),
courseNumber:z.number().int().min(1).max(50).optional(),
quantity:positiveDecimalString,
modifierIds:z.array(uuid).max(100).optional(),
notes:z.string().max(2000).optional(),
}).strict();
export const orderReasonSchema=z.object({
reason:z.string().trim().min(1).max(1000),
}).strict();
export const operationalTaskTemplateSchema=z.object({
name:z.string().trim().min(1).max(250),
description:z.string().max(4000).optional(),
category:z.string().trim().min(1).max(100),
recurrenceRule:z.string().max(1000).optional(),
defaultAssigneeRoleId:uuid.optional(),
evidenceRequired:z.boolean().default(false),
}).strict();
export const operationalTaskCreateSchema=z.object({
taskTemplateId:uuid.optional(),
occurrenceKey:z.string().max(250).optional(),
title:z.string().trim().min(1).max(500),
description:z.string().max(4000).optional(),
category:z.string().trim().min(1).max(100),
priority:z.enum(['low','normal','high','urgent']).default('normal'),
assignedEmployeeId:uuid.optional(),
assignedRoleId:uuid.optional(),
dueAt:z.string().datetime({offset:true}).optional(),
relatedEntityType:z.string().max(100).optional(),
relatedEntityId:uuid.optional(),
}).strict();
export const employeeActionSchema=z.object({
employeeId:uuid.optional(),
}).strict();
export const operationalEvidenceSchema=z.object({
evidenceType:z.string().trim().min(1).max(100),
fileAssetId:uuid.optional(),
textValue:z.string().max(4000).optional(),
numericValue:decimalString.optional(),
employeeId:uuid.optional(),
}).strict().refine(
(v:any)=>Boolean(v.fileAssetId||v.textValue||v.numericValue),
{message:'at least one evidence value required'}
);
export const handoverCreateSchema=z.object({
fromEmployeeId:uuid.optional(),
toEmployeeId:uuid.optional(),
summary:z.string().max(4000).optional(),
}).strict();
export const handoverItemSchema=z.object({
itemType:z.string().trim().min(1).max(100),
title:z.string().trim().min(1).max(500),
body:z.string().max(4000).optional(),
priority:z.enum(['low','normal','high','urgent']).default('normal'),
relatedEntityType:z.string().max(100).optional(),
relatedEntityId:uuid.optional(),
sortOrder:z.number().int().min(0).max(10000).default(0),
}).strict();
export const handoverAmendmentSchema=z.object({
body:z.string().trim().min(1).max(4000),
employeeId:uuid.optional(),
}).strict();
export const briefingDraftSchema=z.object({
title:z.string().trim().min(1).max(500),
body:z.string().min(1).max(20000),
sourceLocale:z.enum(['de-DE','en-GB']).optional(),
}).strict();
export const briefingAcknowledgeSchema=z.object({
employeeId:uuid,
localeShown:z.enum(['de-DE','en-GB']),
}).strict();
export const fiscalCreateTransactionSchema=z.object({
fiscalDeviceId:uuid,
checkId:uuid.optional(),
paymentId:uuid.optional(),
transactionType:z.enum(['sale','refund','cash_movement','training','other']),
clientTransactionRef:z.string().trim().min(1).max(250),
processData:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const fiscalStartedSchema=z.object({
providerReference:z.string().trim().min(1).max(500),
tseTransactionNumber:z.coerce.number().int().nonnegative().optional(),
tseStartAt:z.string().datetime({offset:true}).optional(),
}).strict();
export const fiscalCompletedSchema=z.object({
tseTransactionNumber:z.coerce.number().int().nonnegative(),
signatureCounter:z.coerce.number().int().safe().nonnegative(),
tseStartAt:z.string().datetime({offset:true}),
tseEndAt:z.string().datetime({offset:true}),
verificationValue:z.string().trim().min(1).max(4000),
providerReference:z.string().max(500).optional(),
}).strict();
export const fiscalUncertainSchema=z.object({
providerStatus:z.string().trim().min(1).max(200),
}).strict();
export const fiscalReceiptSchema=z.object({
receiptNumber:z.string().trim().min(1).max(200),
issuerName:z.string().trim().min(1).max(300),
issuerAddress:z.string().trim().min(1).max(1000),
locale:z.enum(['de-DE','en-GB']).optional(),
qrPayload:z.string().max(10000).optional(),
receiptPayload:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const fiscalPeriodSchema=z.object({
periodStart:z.string().date(),
periodEnd:z.string().date(),
}).strict().refine((v:any)=>v.periodStart<=v.periodEnd,{message:'periodStart must be <= periodEnd'});
export const fiscalExportCreateSchema=fiscalPeriodSchema.extend({
exportType:z.enum(['dsfinvk','tse','cash_journal']),
}).strict();
export const fiscalRealStartSchema=z.object({
checkId:uuid,
paymentId:uuid.optional(),
transactionType:z.enum(['sale','refund','cancellation','cash_movement']),
mappingId:uuid,
clientTransactionRef:z.string().trim().min(1).max(250),
}).strict();
export const fiscalConnectionSchema=z.object({
provider:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
environment:z.enum(['test','sandbox','production']).default('test'),
credentialReference:z.string().trim().min(1).max(500),
config:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const fiscalDeviceMapSchema=z.object({
fiscalDeviceId:uuid.optional(),
cashRegisterId:uuid.optional(),
providerDeviceId:z.string().trim().min(1).max(500),
providerClientId:z.string().max(500).optional(),
providerDeviceSerial:z.string().max(500).optional(),
providerClientSerial:z.string().max(500).optional(),
displayName:z.string().trim().min(1).max(250).optional(),
capabilities:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const fiscalConnectionTestSchema=z.object({
mappingId:uuid,
}).strict();
export const fiscalRecoveryResolveSchema=z.object({
action:z.enum(['refresh_provider','retry_finish','provider_support','ignore']),
note:z.string().max(2000).optional(),
}).strict();
export const fiscalProviderExportSchema=fiscalPeriodSchema.extend({
connectionId:uuid,
exportType:z.enum(['tse_export','dsfinvk']),
}).strict();
export const commercialCheckoutSchema=z.object({
planVersionId:uuid,
successUrl:z.string().url().max(2000),
cancelUrl:z.string().url().max(2000),
billingEmail:z.string().email().max(320),
companyName:z.string().trim().min(1).max(300),
}).strict();
export const commercialPortalSchema=z.object({
returnUrl:z.string().url().max(2000),
}).strict();
export const commercialPlanChangeSchema=z.object({
toPlanVersionId:uuid,
}).strict();
export const commercialCancelSchema=z.object({
atPeriodEnd:z.boolean().default(true),
}).strict();
export const commercialPlanCreateSchema=z.object({
code:z.string().trim().min(1).max(100),
name:z.string().trim().min(1).max(250),
description:z.string().max(4000).optional(),
}).strict();
export const commercialPlanVersionSchema=z.object({
currencyCode:currency,
billingInterval:z.enum(['month','year']),
basePriceMinor:integerMinor.nonnegative(),
providerPriceId:z.string().max(500).nullable().optional(),
entitlements:z.array(z.object({
featureKey:z.string().trim().min(1).max(160),
entitlementType:z.enum(['boolean','numeric_limit','string']),
booleanValue:z.boolean().optional(),
numericValue:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]).optional(),
stringValue:z.string().max(500).optional(),
}).strict()).max(500).default([]),
}).strict();
export const entitlementOverrideSchema=z.object({
featureKey:z.string().trim().min(1).max(160),
entitlementType:z.enum(['boolean','numeric_limit','string']),
booleanValue:z.boolean().optional(),
numericValue:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]).optional(),
stringValue:z.string().max(500).optional(),
startsAt:z.string().datetime({offset:true}).optional(),
endsAt:z.string().datetime({offset:true}).optional(),
reason:z.string().trim().min(1).max(1000),
}).strict();
export const externalIntegrationCreateSchema=z.object({
category:z.enum(['pms','external_pos','supplier']),
provider:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
mode:z.enum(['source','mirror','peer']).default('source'),
credentialReference:z.string().max(500).optional(),
config:z.record(z.string(),z.unknown()).default({}),
sourceOfTruth:z.record(z.string(),z.string()).default({}),
}).strict();
export const externalMappingSchema=z.object({
mappingType:z.string().trim().min(1).max(100),
localEntityType:z.string().trim().min(1).max(100),
localEntityId:uuid.optional(),
externalEntityType:z.string().trim().min(1).max(100),
externalId:z.string().trim().min(1).max(500),
externalParentId:z.string().max(500).optional(),
direction:z.enum(['inbound','outbound','bidirectional']).default('bidirectional'),
metadata:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const externalPollSchema=z.object({
streamKey:z.string().trim().min(1).max(200),
}).strict();
export const pmsFolioChargeSchema=z.object({
checkId:uuid,
externalFolioId:z.string().trim().min(1).max(500),
externalReservationId:z.string().max(500).optional(),
externalGuestId:z.string().max(500).optional(),
chargeCode:z.string().trim().min(1).max(100),
amountMinor:positiveMinor,
currencyCode:currency,
description:z.string().trim().min(1).max(500),
idempotencyKey:z.string().trim().min(1).max(200),
}).strict();
export const externalPosBatchSchema=z.object({
providerBatchId:z.string().trim().min(1).max(500),
businessDate:z.string().date(),
salesGrossMinor:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]),
salesNetMinor:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]),
taxMinor:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]),
payload:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const supplierCatalogPreviewSchema=z.object({
supplierId:uuid,
externalCatalogId:z.string().max(500).optional(),
sourceFileAssetId:uuid.optional(),
items:z.array(z.object({
externalId:z.string().trim().min(1).max(500),
sku:z.string().max(250).optional(),
name:z.string().trim().min(1).max(500),
purchaseUnitCode:z.string().trim().min(1).max(32),
normalizedUnitCode:z.string().trim().min(1).max(32),
unitsPerPurchaseUnit:positiveDecimalString,
priceMinor:z.union([integerMinor.nonnegative(),z.string().regex(/^\d+$/)]),
currencyCode:currency,
}).strict()).min(1).max(5000),
}).strict();
export const integrationConflictResolveSchema=z.object({
action:z.enum(['retry','use_provider','keep_local','ignore']),
note:z.string().max(2000).optional(),
result:z.record(z.string(),z.unknown()).optional(),
}).strict();
export const paymentConnectionSchema=z.object({
provider:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
environment:z.enum(['test','sandbox','production']).default('test'),
credentialReference:z.string().trim().min(1).max(500),
config:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const paymentIssueResolveSchema=z.object({
action:z.enum(['refresh_provider','mark_investigated','ignore']),
note:z.string().max(2000).optional(),
}).strict();
export const safetyPlanSchema=z.object({
name:z.string().trim().min(1).max(250),
description:z.string().max(4000).optional(),
planType:z.string().trim().min(1).max(100),
status:z.enum(['draft','active','retired']).optional(),
}).strict();
export const safetyTemplateSchema=z.object({
safetyPlanId:uuid,
name:z.string().trim().min(1).max(250),
checkType:z.string().trim().min(1).max(100),
instructions:z.string().max(4000).optional(),
targetEntityType:z.string().max(100).optional(),
targetEntityId:uuid.optional(),
measurementType:z.string().max(100).optional(),
minimumValue:decimalString.optional(),
maximumValue:decimalString.optional(),
unitCode:z.string().max(32).optional(),
recurrenceRule:z.string().max(250).optional(),
correctiveActionRequiredOnFailure:z.boolean().default(true),
}).strict();
export const safetyCheckCreateSchema=z.object({
safetyCheckTemplateId:uuid,
businessDate:z.string().date().optional(),
dueAt:z.string().datetime({offset:true}).optional(),
assignedEmployeeId:uuid.optional(),
}).strict();
export const safetyMeasurementSchema=z.object({
valueDecimal:decimalString,
unitCode:z.string().trim().min(1).max(32),
measurementType:z.string().max(100).optional(),
captureMethod:z.string().max(100).optional(),
deviceId:z.string().max(200).optional(),
employeeId:uuid.optional(),
notes:z.string().max(2000).optional(),
}).strict();
export const safetySubmitSchema=z.object({
result:z.enum(['pass','fail']).optional(),
notes:z.string().max(4000).optional(),
employeeId:uuid.optional(),
correctiveActionDescription:z.string().max(4000).optional(),
}).strict();
export const safetyScheduleGenerateSchema=z.object({
runKey:z.string().trim().min(1).max(250),
}).strict();
export const correctiveEvidenceSchema=z.object({
evidenceType:z.string().trim().min(1).max(100),
fileAssetId:uuid.optional(),
textValue:z.string().max(4000).optional(),
employeeId:uuid.optional(),
}).strict().refine(
(v:any)=>Boolean(v.fileAssetId||v.textValue),
{message:'fileAssetId or textValue required'}
);
export const publicBookingConfigSchema=z.object({
isEnabled:z.boolean().default(false),
confirmationMode:z.enum(['instant','request']).default('instant'),
requireEmail:z.boolean().default(true),
requirePhone:z.boolean().default(false),
guestNoteEnabled:z.boolean().default(true),
dietaryNoteEnabled:z.boolean().default(true),
defaultDurationMinutes:z.number().int().min(15).max(1440).optional(),
slotIntervalMinutes:z.number().int().min(5).max(240).optional(),
minimumPartySize:z.number().int().min(1).max(1000).optional(),
maximumPartySize:z.number().int().min(1).max(1000).optional(),
minimumLeadTimeMinutes:z.number().int().min(0).max(10080).optional(),
maximumAdvanceDays:z.number().int().min(1).max(3650).optional(),
cancellationCutoffMinutes:z.number().int().min(0).max(10080).optional(),
widgetTheme:z.record(z.string(),z.unknown()).optional(),
allowedOrigins:z.array(z.string().url()).optional(),
}).strict();
export const publicBookingProfileSchema=z.object({
publicLocationId:uuid.optional(),
publicName:z.string().trim().min(1).max(300),
address:z.record(z.string(),z.unknown()).optional(),
contact:z.record(z.string(),z.unknown()).optional(),
bookingPolicySummaryDe:z.string().max(4000).optional(),
bookingPolicySummaryEn:z.string().max(4000).optional(),
isPublished:z.boolean().default(true),
}).strict();
export const publicBookingWindowsSchema=z.object({
windows:z.array(z.object({
weekday:z.number().int().min(0).max(6),
startLocal:z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
endLocal:z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d$/),
isActive:z.boolean().default(true),
}).strict()).max(100),
}).strict();
export const publicMenuConfigSchema=z.object({
isEnabled:z.boolean().default(false),
showPrices:z.boolean().default(true),
showAllergens:z.boolean().default(true),
}).strict();
export const publicReservationCreateSchema=z.object({
idempotencyKey:z.string().min(1).max(200).optional(),
startsAtLocal:z.string().min(16).max(40),
partySize:z.number().int().min(1).max(1000),
guest:z.object({
name:z.string().trim().min(1).max(160),
email:z.string().email().max(320).optional(),
phone:z.string().max(100).optional(),
}).strict(),
occasion:z.string().max(120).optional(),
notes:z.string().max(1000).optional(),
dietaryNotes:z.array(z.string().max(240)).max(10).optional(),
locale:z.enum(['de-DE','en-GB']).default('de-DE'),
privacyAccepted:z.literal(true),
}).strict();
export const inventoryStorageSchema=z.object({
name:z.string().trim().min(1).max(250),
storageType:z.string().trim().min(1).max(100),
sortOrder:z.number().int().min(0).max(10000).default(0),
}).strict();
export const inventoryStockItemSchema=z.object({
ingredientId:uuid.optional(),
name:z.string().trim().min(1).max(300),
baseUnitCode:z.string().trim().min(1).max(32),
}).strict();
export const inventoryLocationConfigSchema=z.object({
isTracked:z.boolean().default(true),
isCountable:z.boolean().default(true),
defaultStorageLocationId:uuid,
}).strict();
export const inventoryReorderRuleSchema=z.object({
stockItemId:uuid,
reorderPoint:nonNegativeDecimalString,
targetStock:nonNegativeDecimalString,
unitCode:z.string().trim().min(1).max(32),
}).strict().refine(
value=>Number(value.targetStock)>=Number(value.reorderPoint),
{message:'target_stock_below_reorder_point',path:['targetStock']},
);
export const inventoryCountCreateSchema=z.object({
storageLocationId:uuid.optional(),
scope:z.record(z.string(),z.unknown()).optional(),
}).strict();
export const inventoryCountLineSchema=z.object({
countedQuantity:nonNegativeDecimalString,
normalizedCountedQuantity:nonNegativeDecimalString,
notes:z.string().max(2000).optional(),
}).strict();
export const supplierCreateSchema=z.object({
name:z.string().trim().min(1).max(300),
email:z.string().email().max(320).optional(),
phone:z.string().max(100).optional(),
supplierNumber:z.string().max(100).optional(),
notes:z.string().max(4000).optional(),
}).strict();
export const supplierCatalogItemSchema=z.object({
stockItemId:uuid,
supplierSku:z.string().max(250).optional(),
supplierProductName:z.string().trim().min(1).max(500),
purchaseUnitCode:z.string().trim().min(1).max(32),
unitsPerPurchaseUnit:positiveDecimalString,
normalizedUnitCode:z.string().trim().min(1).max(32),
unitPriceMinor:integerMinor.nonnegative(),
currencyCode:currency,
minimumOrderQuantity:nonNegativeDecimalString.optional(),
isActive:z.boolean().default(true),
}).strict();
export const purchaseOrderCreateSchema=z.object({
supplierId:uuid,
poNumber:z.string().trim().min(1).max(160),
currencyCode:currency,
expectedDeliveryDate:z.string().date().optional(),
notes:z.string().max(4000).optional(),
}).strict();
export const purchaseOrderLineSchema=z.object({
supplierCatalogItemId:uuid,
orderedQuantity:positiveDecimalString,
notes:z.string().max(2000).optional(),
}).strict();
export const purchaseOrderConfirmSchema=z.object({
expectedDeliveryDate:z.string().date().optional(),
}).strict();
export const purchaseOrderCancelSchema=z.object({
reason:z.string().trim().min(1).max(1000),
}).strict();
export const purchaseSuggestionAcceptSchema=z.object({
suggestionIds:z.array(uuid).min(1).max(1000),
poNumberPrefix:z.string().trim().min(1).max(50).default('AUTO'),
}).strict();
export const walkInSchema=z.object({
guestName:z.string().trim().min(1).max(200),
partySize:z.number().int().min(1).max(1000),
requestedLocale:z.enum(['de-DE','en-GB']).optional(),
}).strict();
export const waitlistCreateSchema=z.object({
guestName:z.string().trim().min(1).max(200),
partySize:z.number().int().min(1).max(1000),
guestPhone:z.string().max(100).optional(),
notes:z.string().max(2000).optional(),
}).strict();
export const waitlistStatusSchema=z.object({
status:z.enum(['notified','seated','cancelled','left']),
note:z.string().max(1000).optional(),
}).strict();
export const providerReferenceSchema=z.object({
providerReference:z.string().trim().min(1).max(500),
}).strict();
export const providerStatusSchema=z.object({
providerStatus:z.string().trim().min(1).max(200),
}).strict();
export const providerWebhookSchema=z.object({
providerKey:z.string().trim().min(1).max(100),
providerEventId:z.string().trim().min(1).max(500),
providerReference:z.string().max(500).optional(),
eventType:z.string().trim().min(1).max(200),
observedStatus:z.string().trim().min(1).max(100),
observedAmountMinor:z.union([integerMinor,z.string().regex(/^\d+$/)]).optional(),
payload:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const providerWebhookCompletionSchema=z.object({
providerReference:z.string().trim().min(1).max(500),
}).strict();
export const accountingConnectionSchema=z.object({
provider:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
credentialReference:z.string().max(500).optional(),
config:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const accountingInvoiceTransferSchema=z.object({
connectionId:uuid,
invoiceId:uuid,
}).strict();
export const accountingConfigSchema=z.record(z.string(),z.unknown());
export const accountingRuleSchema=z.record(z.string(),z.unknown());
export const accountingExportSchema=z.object({
exportType:z.string().trim().min(1).max(100),
periodStart:z.string().date(),
periodEnd:z.string().date(),
config:z.record(z.string(),z.unknown()).optional(),
}).strict();
export const accountingRunExportSchema=z.object({
exportJobId:uuid,
}).strict();
export const invoiceFromCheckSchema=z.object({
checkId:uuid,
customer:z.record(z.string(),z.unknown()).optional(),
dueDate:z.string().date().optional(),
notes:z.string().max(4000).optional(),
}).strict();
export const creditNoteSchema=z.object({
reason:z.string().trim().min(1).max(2000),
}).strict();
export const einvoiceValidateSchema=z.object({
profile:z.enum(['xrechnung','zugferd']),
validatorConnectionId:uuid.optional(),
}).strict();
export const invoiceRenderSchema=z.object({
renderType:z.enum(['pdf','zugferd_pdf','xml']),
}).strict();
export const reservationIntegrationConnectionSchema=z.object({
provider:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
credentialReference:z.string().max(500).optional(),
config:z.record(z.string(),z.unknown()).default({}),
syncDirection:z.enum(['inbound_only','outbound_only','bidirectional']).default('inbound_only'),
}).strict();
export const reservationVenueMappingSchema=z.object({
providerVenueId:z.string().trim().min(1).max(500),
providerVenueName:z.string().max(250).optional(),
locationId:uuid,
capacityAuthority:z.enum(['shared_manual','provider','local']).default('shared_manual'),
}).strict();
export const reservationStateMappingSchema=z.object({
providerState:z.string().trim().min(1).max(200),
canonicalState:z.string().trim().min(1).max(100),
isActive:z.boolean().default(true),
}).strict();
export const integrationIssueResolveSchema=z.object({
action:z.enum(['retry_provider','use_provider','keep_local','ignore']),
note:z.string().max(2000).optional(),
}).strict();
export const wordpressConnectionSchema=z.object({
siteUrl:z.string().url().max(2000),
displayName:z.string().trim().min(1).max(250),
secretReference:z.string().trim().min(1).max(500),
allowedOrigins:z.array(z.string().url().max(2000)).max(100).default([]),
}).strict();
export const wordpressFormMappingSchema=z.object({
connectionId:uuid,
formKey:z.string().trim().min(1).max(250),
mapping:z.record(z.string(),z.unknown()),
}).strict();
export const floorAreaSchema=z.object({
name:z.string().trim().min(1).max(250),
sortOrder:z.number().int().min(0).max(10000).default(0),
}).strict();
export const floorTableSchema=z.object({
areaId:uuid,
tableNumber:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
minCapacity:z.number().int().min(1).max(1000),
maxCapacity:z.number().int().min(1).max(1000),
}).strict();
export const floorLinkSchema=z.object({
tableIds:z.array(uuid).min(2).max(50),
}).strict();
export const employeeCreateSchema=z.object({
firstName:z.string().trim().min(1).max(200),
lastName:z.string().trim().min(1).max(200),
displayName:z.string().trim().min(1).max(250).optional(),
employeeNumber:z.string().max(100).optional(),
preferredLocale:z.enum(['de-DE','en-GB']).default('de-DE'),
}).strict();
export const employeeUpdateSchema=employeeCreateSchema.partial().strict();
export const guestCreateSchema=z.object({
displayName:z.string().trim().min(1).max(250),
email:z.string().email().max(320).optional(),
phone:z.string().max(100).optional(),
notes:z.string().max(4000).optional(),
}).strict();
export const guestDietarySchema=z.object({
guestId:uuid,
note:z.string().trim().min(1).max(1000),
}).strict();
export const incidentCreateSchema=z.object({
incidentType:z.string().trim().min(1).max(100),
severity:z.string().trim().min(1).max(100),
title:z.string().trim().min(1).max(500),
description:z.string().max(10000).optional(),
employeeId:uuid.optional(),
}).strict();
export const incidentAssignSchema=z.object({
employeeId:uuid,
}).strict();
export const incidentResolveSchema=z.object({
resolution:z.string().trim().min(1).max(10000),
}).strict();
export const operationalCaseCreateSchema=z.object({
caseType:z.string().trim().min(1).max(100),
severity:z.enum(['low','medium','high','critical']),
title:z.string().trim().min(1).max(500),
summary:z.string().trim().min(1).max(10000),
affectedEntityType:z.string().trim().min(1).max(100).optional(),
affectedEntityId:uuid.optional(),
}).strict();
export const operationalCaseEvidenceSchema=z.object({
evidence:z.record(z.string().trim().min(1).max(80),z.unknown())
.refine((value)=>Object.keys(value).length<=20,'too many evidence fields'),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const operationalCaseResolveSchema=z.object({
outcome:z.string().trim().min(1).max(10000),
decision:z.record(z.string().trim().min(1).max(80),z.unknown())
.refine((value)=>Object.keys(value).length<=20,'too many decision fields').optional(),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const kitchenStationSchema=z.object({
code:z.string().trim().min(1).max(100),
displayName:z.string().trim().min(1).max(250),
targetSeconds:z.number().int().min(1).max(86400),
stationKind:z.enum(['kitchen','bar','pass','expedite']).default('kitchen'),
}).strict();
export const kitchenRouteSchema=z.object({
productId:uuid,
stationId:uuid,
priority:z.number().int().min(0).max(10000).default(0),
}).strict();
export const kitchenTicketTransitionSchema=z.object({
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const costingConfigSchema=z.object({
method:z.enum(['weighted_average','last_purchase_price','standard_cost']),
currencyCode:currency.default('EUR'),
includeWasteFactor:z.boolean().default(true),
targetFoodCostPercent:decimalString.optional(),
minimumGrossMarginPercent:decimalString.optional(),
}).strict();
export const costingRecalculateSchema=z.object({
scopeType:z.enum(['all','stock_item','ingredient','recipe','product']).default('all'),
scopeId:uuid.optional(),
reason:z.string().trim().min(1).max(1000).default('manual_recalculation'),
}).strict();
export const organizationUnitBindingSchema=z.object({
organizationUnitId:uuid.nullable(),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const organizationUnitCreateSchema=z.object({
name:z.string().trim().min(1).max(250),
code:z.string().trim().min(1).max(100),
unitType:z.enum(['group','brand','region','property','cluster','custom']).default('custom'),
parentUnitId:uuid.optional(),
}).strict();
export const legalEntityCreateSchema=z.object({
legalName:z.string().trim().min(1).max(300),
legalForm:z.string().trim().max(120).optional(),
countryCode:z.string().regex(/^[A-Z]{2}$/).default('DE'),
registrationNumber:z.string().trim().max(120).optional(),
}).strict();
export const legalEntityBindingSchema=z.object({
legalEntityId:uuid.nullable(),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const operationalUnitCreateSchema=z.object({
name:z.string().trim().min(1).max(250),
code:z.string().trim().min(1).max(100),
archetype:z.enum(['restaurant','bar','banquet','roomservice','central_kitchen','bakery','catering_team','other']).default('other'),
}).strict();
export const operationalUnitLifecycleSchema=z.object({
status:z.enum(['active','inactive','archived']),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const placeCreateSchema=z.object({
name:z.string().trim().min(1).max(250),
code:z.string().trim().min(1).max(100).optional(),
operationalUnitId:uuid.optional(),
placeType:z.enum(['permanent','temporary_fixed','seasonal','recurring','mobile','external']).default('permanent'),
startsAt:z.string().datetime({offset:true}).optional(),
endsAt:z.string().datetime({offset:true}).optional(),
}).strict();
export const placeLifecycleSchema=z.object({
status:z.enum(['active','closing','closed','archived']),
expectedVersion:z.number().int().safe().positive().optional(),
}).strict();
export const locationCreateSchema=z.object({
name:z.string().trim().min(1).max(250),
code:z.string().trim().min(1).max(100),
timezone:z.string().trim().min(1).max(100),
defaultLocale:z.enum(['de-DE','en-GB']).default('de-DE'),
businessDayCutoff:z.string().regex(/^(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/),
}).strict();
export const locationOperatingProfilePutSchema=z.object({
conceptCode:z.enum([
'restaurant',
'italian_restaurant',
'asian_restaurant',
'chinese_restaurant',
'cafe',
'bar',
'quick_service',
'snack_bar',
'hotel_restaurant',
'fine_dining',
'catering',
'bakery_cafe',
'other',
]),
serviceStyleCode:z.enum([
'table_service',
'counter_service',
'self_service',
'delivery',
'takeaway',
'mixed',
]),
productionModelCode:z.enum([
'cook_to_order',
'batch_prep',
'central_production',
'assembly',
'hybrid',
]),
seatCapacity:z.number().int().safe().nonnegative().nullable().optional(),
takeawaySharePercent:z.union([
z.string().regex(/^(?:100(?:\.0{1,2})?|\d{1,2}(?:\.\d{1,2})?)$/),
z.null(),
]).optional(),
expectedVersion:z.number().int().safe().nonnegative().optional(),
}).strict();
export const productionEvidenceSchema=z.object({
environment:z.enum(['test','sandbox','staging','production']),
checkKey:z.string().trim().min(1).max(200).regex(/^[a-z][a-z0-9_:-]*$/),
status:z.enum(['pass','warn','fail']),
evidenceType:z.enum([
'automated_test','manual_test','runbook','staging_log','trace',
'restore_drill','security_review','performance_run','operational_signoff'
]),
evidenceReference:z.string().max(2000).optional(),
details:z.record(z.string(),z.unknown()).optional(),
}).strict();
export const revokeAllSessionsSchema=z.object({
reason:z.string().trim().min(1).max(1000).default('administrative_revocation'),
}).strict();
export const revokeSessionSchema=z.object({
sessionIdentifier:z.string().trim().min(1).max(1000),
reason:z.string().trim().min(1).max(1000).default('administrative_revocation'),
expiresAt:z.string().datetime({offset:true}).optional(),
}).strict();
export const requireStepUpSchema=z.object({
reason:z.string().trim().min(1).max(1000).default('security_policy'),
}).strict();
export const syncClientRegisterSchema=z.object({
deviceId:z.string().trim().min(1).max(500),
platform:z.string().trim().min(1).max(100),
appVersion:z.string().max(100).optional(),
deviceName:z.string().max(250).optional(),
}).strict();
export const syncCommandSchema=z.object({
syncClientId:uuid,
clientCommandId:z.string().trim().min(1).max(500),
commandType:z.string().trim().min(1).max(200),
aggregateType:z.string().trim().min(1).max(200).optional(),
aggregateId:uuid.optional(),
baseVersion:z.number().int().safe().nonnegative().optional(),
payload:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const syncConflictResolveSchema=z.object({
resolution:z.enum(['discard_client','retry_against_latest']),
}).strict();
export const equipmentCreateSchema=z.object({
name:z.string().trim().min(1).max(300),
equipmentType:z.string().trim().min(1).max(100),
serialNumber:z.string().max(250).optional(),
manufacturer:z.string().max(250).optional(),
model:z.string().max(250).optional(),
notes:z.string().max(4000).optional(),
}).strict();
export const equipmentMaintenanceSchema=z.object({
maintenanceType:z.string().trim().min(1).max(100),
description:z.string().trim().min(1).max(4000),
performedAt:z.string().datetime({offset:true}).optional(),
dueAt:z.string().datetime({offset:true}).optional(),
employeeId:uuid.optional(),
}).strict();
export const reportingRebuildSchema=z.object({
reportDate:z.string().date().optional(),
scope:z.enum(['daily_location','product_mix','hourly_sales','organization_rollup','all']).default('all'),
reason:z.string().trim().min(1).max(1000).default('manual'),
}).strict();
export const reportingSavedSchema=z.object({
name:z.string().trim().min(1).max(250),
reportType:z.string().trim().min(1).max(100),
filters:z.record(z.string(),z.unknown()).default({}),
locale:z.enum(['de-DE','en-GB']).optional(),
isShared:z.boolean().optional(),
}).strict();
export const exportCreateSchema=z.object({
exportType:z.string().trim().min(1).max(100),
format:z.string().trim().min(1).max(50),
parameters:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const importCreateSchema=z.object({
importType:z.string().trim().min(1).max(100),
fileAssetId:uuid,
mapping:z.record(z.string(),z.unknown()).default({}),
options:z.record(z.string(),z.unknown()).default({}),
}).strict();
export const wordpressSubmissionSchema=z.record(z.string(),z.unknown());
export const providerWebhookTriggerSchema=z.record(z.string(),z.unknown());

Architecture

High-level modules detected from the verified public snapshot. Protected implementation details are intentionally omitted.

appsmodule
.vscodemodule
.githubmodule

Technology stack

TypeScriptSQLMarkdownJSONGITIGNOREDOCKERIGNORENode.js / JavaScript ecosystem
TypeScript68.8%
SQL20.0%
Markdown10.9%
JSON0.3%
GITIGNORE0.1%
DOCKERIGNORE0.0%
CJS0.0%
MJS0.0%