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());
