@porulle/plugin-pos-restaurant 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -0
- package/dist/hooks/modifier-validation.d.ts +21 -0
- package/dist/hooks/modifier-validation.d.ts.map +1 -0
- package/dist/hooks/modifier-validation.js +40 -0
- package/dist/hooks/table-lifecycle.d.ts +15 -0
- package/dist/hooks/table-lifecycle.d.ts.map +1 -0
- package/dist/hooks/table-lifecycle.js +40 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/dist/routes/kds.d.ts +9 -0
- package/dist/routes/kds.d.ts.map +1 -0
- package/dist/routes/kds.js +112 -0
- package/dist/routes/modifiers.d.ts +15 -0
- package/dist/routes/modifiers.d.ts.map +1 -0
- package/dist/routes/modifiers.js +117 -0
- package/dist/routes/operations.d.ts +34 -0
- package/dist/routes/operations.d.ts.map +1 -0
- package/dist/routes/operations.js +249 -0
- package/dist/routes/tables.d.ts +9 -0
- package/dist/routes/tables.d.ts.map +1 -0
- package/dist/routes/tables.js +110 -0
- package/dist/schema.d.ts +3831 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +418 -0
- package/dist/services/alert-service.d.ts +56 -0
- package/dist/services/alert-service.d.ts.map +1 -0
- package/dist/services/alert-service.js +173 -0
- package/dist/services/analytics-service.d.ts +60 -0
- package/dist/services/analytics-service.d.ts.map +1 -0
- package/dist/services/analytics-service.js +178 -0
- package/dist/services/checklist-service.d.ts +66 -0
- package/dist/services/checklist-service.d.ts.map +1 -0
- package/dist/services/checklist-service.js +106 -0
- package/dist/services/kds-service.d.ts +77 -0
- package/dist/services/kds-service.d.ts.map +1 -0
- package/dist/services/kds-service.js +246 -0
- package/dist/services/modifier-service.d.ts +74 -0
- package/dist/services/modifier-service.d.ts.map +1 -0
- package/dist/services/modifier-service.js +180 -0
- package/dist/services/recipe-deduction-service.d.ts +64 -0
- package/dist/services/recipe-deduction-service.d.ts.map +1 -0
- package/dist/services/recipe-deduction-service.js +144 -0
- package/dist/services/recipe-service.d.ts +43 -0
- package/dist/services/recipe-service.d.ts.map +1 -0
- package/dist/services/recipe-service.js +85 -0
- package/dist/services/table-service.d.ts +61 -0
- package/dist/services/table-service.d.ts.map +1 -0
- package/dist/services/table-service.js +206 -0
- package/dist/types.d.ts +35 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/package.json +61 -0
- package/src/hooks/modifier-validation.ts +60 -0
- package/src/hooks/table-lifecycle.ts +50 -0
- package/src/index.ts +218 -0
- package/src/routes/kds.ts +121 -0
- package/src/routes/modifiers.ts +129 -0
- package/src/routes/operations.ts +276 -0
- package/src/routes/tables.ts +117 -0
- package/src/schema.ts +462 -0
- package/src/services/alert-service.ts +234 -0
- package/src/services/analytics-service.ts +242 -0
- package/src/services/checklist-service.ts +139 -0
- package/src/services/kds-service.ts +340 -0
- package/src/services/modifier-service.ts +251 -0
- package/src/services/recipe-deduction-service.ts +221 -0
- package/src/services/recipe-service.ts +115 -0
- package/src/services/table-service.ts +260 -0
- package/src/types.ts +63 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Operational routes: checklists, alerts, recipes, combos, menu availability,
|
|
3
|
+
* analytics, customer favorites.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { router } from "@porulle/core";
|
|
7
|
+
import { z } from "@hono/zod-openapi";
|
|
8
|
+
import type { ChecklistService } from "../services/checklist-service.js";
|
|
9
|
+
import type { AlertService } from "../services/alert-service.js";
|
|
10
|
+
import type { RecipeService } from "../services/recipe-service.js";
|
|
11
|
+
import type { RestaurantAnalyticsService } from "../services/analytics-service.js";
|
|
12
|
+
import type { PluginRouteRegistration } from "@porulle/core";
|
|
13
|
+
|
|
14
|
+
export function buildChecklistRoutes(
|
|
15
|
+
service: ChecklistService,
|
|
16
|
+
ctx: { services?: Record<string, unknown>; database?: { db: unknown } },
|
|
17
|
+
): PluginRouteRegistration[] {
|
|
18
|
+
const r = router("POS Restaurant Checklists", "/pos/restaurant/checklists", ctx);
|
|
19
|
+
|
|
20
|
+
r.post("/")
|
|
21
|
+
.summary("Create checklist")
|
|
22
|
+
.permission("pos-restaurant:admin")
|
|
23
|
+
.input(z.object({
|
|
24
|
+
name: z.string().min(1),
|
|
25
|
+
type: z.enum(["pre_billing", "shift_open", "shift_close"]),
|
|
26
|
+
items: z.array(z.object({ label: z.string().min(1), isRequired: z.boolean().optional() })).min(1),
|
|
27
|
+
}))
|
|
28
|
+
.handler(async ({ input, orgId }) => {
|
|
29
|
+
const body = input as { name: string; type: "pre_billing" | "shift_open" | "shift_close"; items: Array<{ label: string; isRequired?: boolean }> };
|
|
30
|
+
const result = await service.createChecklist(orgId, body);
|
|
31
|
+
if (!result.ok) throw new Error(result.error);
|
|
32
|
+
return result.value;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
r.get("/")
|
|
36
|
+
.summary("List checklists")
|
|
37
|
+
.permission("pos:operate")
|
|
38
|
+
.query(z.object({ type: z.enum(["pre_billing", "shift_open", "shift_close"]).optional() }))
|
|
39
|
+
.handler(async ({ query, orgId }) => {
|
|
40
|
+
const q = query as { type?: "pre_billing" | "shift_open" | "shift_close" };
|
|
41
|
+
const result = await service.listChecklists(orgId, q.type);
|
|
42
|
+
if (!result.ok) throw new Error(result.error);
|
|
43
|
+
return result.value;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
r.get("/{id}")
|
|
47
|
+
.summary("Get checklist with items")
|
|
48
|
+
.permission("pos:operate")
|
|
49
|
+
.handler(async ({ params, orgId }) => {
|
|
50
|
+
const result = await service.getChecklistWithItems(params.id!, orgId);
|
|
51
|
+
if (!result.ok) throw new Error(result.error);
|
|
52
|
+
return result.value;
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
r.post("/{id}/complete")
|
|
56
|
+
.summary("Complete a checklist")
|
|
57
|
+
.permission("pos:operate")
|
|
58
|
+
.input(z.object({
|
|
59
|
+
referenceType: z.enum(["transaction", "shift"]),
|
|
60
|
+
referenceId: z.string().uuid(),
|
|
61
|
+
completedItems: z.array(z.object({
|
|
62
|
+
itemId: z.string().uuid(),
|
|
63
|
+
checked: z.boolean(),
|
|
64
|
+
note: z.string().optional(),
|
|
65
|
+
})),
|
|
66
|
+
}))
|
|
67
|
+
.handler(async ({ params, input, actor }) => {
|
|
68
|
+
const body = input as { referenceType: "transaction" | "shift"; referenceId: string; completedItems: Array<{ itemId: string; checked: boolean; note?: string }> };
|
|
69
|
+
const result = await service.completeChecklist({
|
|
70
|
+
checklistId: params.id!,
|
|
71
|
+
...body,
|
|
72
|
+
operatorId: actor!.userId,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
if (!result.ok) throw new Error(result.error);
|
|
76
|
+
return result.value;
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
return r.routes();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function buildAlertRoutes(
|
|
83
|
+
service: AlertService,
|
|
84
|
+
ctx: { services?: Record<string, unknown>; database?: { db: unknown } },
|
|
85
|
+
): PluginRouteRegistration[] {
|
|
86
|
+
const r = router("POS Restaurant Alerts", "/pos/restaurant/alerts", ctx);
|
|
87
|
+
|
|
88
|
+
r.get("/")
|
|
89
|
+
.summary("List active alerts")
|
|
90
|
+
.permission("pos:operate")
|
|
91
|
+
.query(z.object({
|
|
92
|
+
type: z.string().optional(),
|
|
93
|
+
unresolvedOnly: z.string().optional(),
|
|
94
|
+
}))
|
|
95
|
+
.handler(async ({ query, orgId }) => {
|
|
96
|
+
const q = query as { type?: string; unresolvedOnly?: string };
|
|
97
|
+
const result = await service.listAlerts(orgId, {
|
|
98
|
+
type: q.type as Parameters<typeof service.listAlerts>[1] extends { type?: infer T } ? T : never,
|
|
99
|
+
unresolvedOnly: q.unresolvedOnly === "true",
|
|
100
|
+
});
|
|
101
|
+
if (!result.ok) throw new Error(result.error);
|
|
102
|
+
return result.value;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
r.post("/detect")
|
|
106
|
+
.summary("Run alert detection scan")
|
|
107
|
+
.permission("pos-restaurant:admin")
|
|
108
|
+
.handler(async ({ orgId }) => {
|
|
109
|
+
const result = await service.runAllDetections(orgId);
|
|
110
|
+
if (!result.ok) throw new Error(result.error);
|
|
111
|
+
return result.value;
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
r.post("/{id}/resolve")
|
|
115
|
+
.summary("Resolve an alert")
|
|
116
|
+
.permission("pos:operate")
|
|
117
|
+
.handler(async ({ params, actor }) => {
|
|
118
|
+
const result = await service.resolveAlert(params.id!, actor!.userId);
|
|
119
|
+
if (!result.ok) throw new Error(result.error);
|
|
120
|
+
return result.value;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
r.post("/config")
|
|
124
|
+
.summary("Set alert threshold")
|
|
125
|
+
.permission("pos-restaurant:admin")
|
|
126
|
+
.input(z.object({
|
|
127
|
+
alertType: z.string().min(1),
|
|
128
|
+
thresholdMinutes: z.number().int().min(1),
|
|
129
|
+
notifyRoles: z.array(z.string()).optional(),
|
|
130
|
+
}))
|
|
131
|
+
.handler(async ({ input, orgId }) => {
|
|
132
|
+
const body = input as { alertType: string; thresholdMinutes: number; notifyRoles?: string[] };
|
|
133
|
+
const result = await service.setThreshold(orgId, body.alertType, body.thresholdMinutes, body.notifyRoles);
|
|
134
|
+
if (!result.ok) throw new Error(result.error);
|
|
135
|
+
return result.value;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
r.get("/config")
|
|
139
|
+
.summary("Get alert configuration")
|
|
140
|
+
.permission("pos-restaurant:admin")
|
|
141
|
+
.handler(async ({ orgId }) => {
|
|
142
|
+
const result = await service.getConfig(orgId);
|
|
143
|
+
if (!result.ok) throw new Error(result.error);
|
|
144
|
+
return result.value;
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
return r.routes();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function buildRecipeRoutes(
|
|
151
|
+
service: RecipeService,
|
|
152
|
+
ctx: { services?: Record<string, unknown>; database?: { db: unknown } },
|
|
153
|
+
): PluginRouteRegistration[] {
|
|
154
|
+
const r = router("POS Restaurant Recipes", "/pos/restaurant/recipes", ctx);
|
|
155
|
+
|
|
156
|
+
r.post("/")
|
|
157
|
+
.summary("Create recipe (BOM)")
|
|
158
|
+
.permission("pos-restaurant:admin")
|
|
159
|
+
.input(z.object({
|
|
160
|
+
entityId: z.string().uuid(),
|
|
161
|
+
name: z.string().min(1),
|
|
162
|
+
yieldQuantity: z.number().int().min(1).optional(),
|
|
163
|
+
ingredients: z.array(z.object({
|
|
164
|
+
ingredientName: z.string().min(1),
|
|
165
|
+
quantity: z.number().int().positive(),
|
|
166
|
+
unit: z.string().min(1),
|
|
167
|
+
costPerUnit: z.number().int().min(0),
|
|
168
|
+
})).min(1),
|
|
169
|
+
}))
|
|
170
|
+
.handler(async ({ input, orgId }) => {
|
|
171
|
+
const body = input as { entityId: string; name: string; yieldQuantity?: number; ingredients: Array<{ ingredientName: string; quantity: number; unit: string; costPerUnit: number }> };
|
|
172
|
+
const result = await service.createRecipe(orgId, body);
|
|
173
|
+
if (!result.ok) throw new Error(result.error);
|
|
174
|
+
return result.value;
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
r.get("/")
|
|
178
|
+
.summary("List recipes")
|
|
179
|
+
.permission("pos-restaurant:admin")
|
|
180
|
+
.handler(async ({ orgId }) => {
|
|
181
|
+
const result = await service.listRecipes(orgId);
|
|
182
|
+
if (!result.ok) throw new Error(result.error);
|
|
183
|
+
return result.value;
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
r.get("/{id}")
|
|
187
|
+
.summary("Get recipe with ingredients")
|
|
188
|
+
.permission("pos-restaurant:admin")
|
|
189
|
+
.handler(async ({ params }) => {
|
|
190
|
+
const result = await service.getRecipeWithIngredients(params.id!);
|
|
191
|
+
if (!result.ok) throw new Error(result.error);
|
|
192
|
+
return result.value;
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return r.routes();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function buildAnalyticsRoutes(
|
|
199
|
+
service: RestaurantAnalyticsService,
|
|
200
|
+
ctx: { services?: Record<string, unknown>; database?: { db: unknown } },
|
|
201
|
+
): PluginRouteRegistration[] {
|
|
202
|
+
const r = router("POS Restaurant Analytics", "/pos/restaurant/analytics", ctx);
|
|
203
|
+
|
|
204
|
+
r.post("/daily-pnl")
|
|
205
|
+
.summary("Create daily P&L")
|
|
206
|
+
.permission("pos-restaurant:admin")
|
|
207
|
+
.input(z.object({
|
|
208
|
+
date: z.string(),
|
|
209
|
+
grossSales: z.number().int(),
|
|
210
|
+
netSales: z.number().int(),
|
|
211
|
+
costOfGoods: z.number().int(),
|
|
212
|
+
directExpenses: z.number().int(),
|
|
213
|
+
indirectExpenses: z.number().int(),
|
|
214
|
+
employeeCosts: z.number().int(),
|
|
215
|
+
transactionCount: z.number().int(),
|
|
216
|
+
expenses: z.array(z.object({
|
|
217
|
+
category: z.enum(["cogs", "direct", "indirect", "employee"]),
|
|
218
|
+
name: z.string(),
|
|
219
|
+
amount: z.number().int(),
|
|
220
|
+
percentage: z.number().int().optional(),
|
|
221
|
+
})).optional(),
|
|
222
|
+
}))
|
|
223
|
+
.handler(async ({ input, orgId }) => {
|
|
224
|
+
const body = input as { date: string; grossSales: number; netSales: number; costOfGoods: number; directExpenses: number; indirectExpenses: number; employeeCosts: number; transactionCount: number; expenses?: Array<{ category: "cogs" | "direct" | "indirect" | "employee"; name: string; amount: number; percentage?: number }> };
|
|
225
|
+
const result = await service.createDailyPnl(orgId, {
|
|
226
|
+
...body,
|
|
227
|
+
date: new Date(body.date),
|
|
228
|
+
});
|
|
229
|
+
if (!result.ok) throw new Error(result.error);
|
|
230
|
+
return result.value;
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
r.get("/daily-pnl")
|
|
234
|
+
.summary("List daily P&L records")
|
|
235
|
+
.permission("pos-restaurant:admin")
|
|
236
|
+
.handler(async ({ orgId }) => {
|
|
237
|
+
const result = await service.listDailyPnl(orgId);
|
|
238
|
+
if (!result.ok) throw new Error(result.error);
|
|
239
|
+
return result.value;
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
r.get("/station-performance/{id}")
|
|
243
|
+
.summary("Station performance metrics")
|
|
244
|
+
.permission("pos-restaurant:admin")
|
|
245
|
+
.query(z.object({ from: z.string(), to: z.string() }))
|
|
246
|
+
.handler(async ({ params, query, orgId }) => {
|
|
247
|
+
const q = query as { from: string; to: string };
|
|
248
|
+
const result = await service.getStationPerformance(orgId, params.id!, new Date(q.from), new Date(q.to));
|
|
249
|
+
if (!result.ok) throw new Error(result.error);
|
|
250
|
+
return result.value;
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
r.get("/course-performance")
|
|
254
|
+
.summary("Course-wise performance")
|
|
255
|
+
.permission("pos-restaurant:admin")
|
|
256
|
+
.query(z.object({ from: z.string(), to: z.string() }))
|
|
257
|
+
.handler(async ({ query, orgId }) => {
|
|
258
|
+
const q = query as { from: string; to: string };
|
|
259
|
+
const result = await service.getCoursePerformance(orgId, new Date(q.from), new Date(q.to));
|
|
260
|
+
if (!result.ok) throw new Error(result.error);
|
|
261
|
+
return result.value;
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
r.get("/operator-performance")
|
|
265
|
+
.summary("Staff/captain performance")
|
|
266
|
+
.permission("pos-restaurant:admin")
|
|
267
|
+
.query(z.object({ from: z.string(), to: z.string() }))
|
|
268
|
+
.handler(async ({ query, orgId }) => {
|
|
269
|
+
const q = query as { from: string; to: string };
|
|
270
|
+
const result = await service.getOperatorPerformance(orgId, new Date(q.from), new Date(q.to));
|
|
271
|
+
if (!result.ok) throw new Error(result.error);
|
|
272
|
+
return result.value;
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return r.routes();
|
|
276
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { router } from "@porulle/core";
|
|
2
|
+
import { z } from "@hono/zod-openapi";
|
|
3
|
+
import type { TableService } from "../services/table-service.js";
|
|
4
|
+
import type { PluginRouteRegistration } from "@porulle/core";
|
|
5
|
+
|
|
6
|
+
export function buildTableRoutes(
|
|
7
|
+
service: TableService,
|
|
8
|
+
ctx: { services?: Record<string, unknown>; database?: { db: unknown } },
|
|
9
|
+
): PluginRouteRegistration[] {
|
|
10
|
+
const r = router("POS Restaurant Tables", "/pos/restaurant/tables", ctx);
|
|
11
|
+
|
|
12
|
+
r.post("/")
|
|
13
|
+
.summary("Create table")
|
|
14
|
+
.permission("pos-restaurant:admin")
|
|
15
|
+
.input(z.object({
|
|
16
|
+
number: z.string().min(1).max(50),
|
|
17
|
+
zone: z.string().min(1).max(100),
|
|
18
|
+
capacity: z.number().int().min(1).max(100).optional(),
|
|
19
|
+
minimumSeats: z.number().int().min(1).optional(),
|
|
20
|
+
shape: z.enum(["rectangle", "square", "circle"]).optional(),
|
|
21
|
+
isTakeaway: z.boolean().optional(),
|
|
22
|
+
layoutX: z.number().int().optional(),
|
|
23
|
+
layoutY: z.number().int().optional(),
|
|
24
|
+
}))
|
|
25
|
+
.handler(async ({ input, orgId }) => {
|
|
26
|
+
const body = input as { number: string; zone: string; capacity?: number; minimumSeats?: number; shape?: "rectangle" | "square" | "circle"; isTakeaway?: boolean; layoutX?: number; layoutY?: number };
|
|
27
|
+
const result = await service.create(orgId, body);
|
|
28
|
+
if (!result.ok) throw new Error(result.error);
|
|
29
|
+
return result.value;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
r.get("/zones")
|
|
33
|
+
.summary("List zones with table counts")
|
|
34
|
+
.permission("pos:operate")
|
|
35
|
+
.handler(async ({ orgId }) => {
|
|
36
|
+
const result = await service.listZones(orgId);
|
|
37
|
+
if (!result.ok) throw new Error(result.error);
|
|
38
|
+
return result.value;
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
r.get("/")
|
|
42
|
+
.summary("List tables")
|
|
43
|
+
.permission("pos:operate")
|
|
44
|
+
.query(z.object({ zone: z.string().optional() }))
|
|
45
|
+
.handler(async ({ query, orgId }) => {
|
|
46
|
+
const q = query as { zone?: string };
|
|
47
|
+
const result = await service.list(orgId, q.zone);
|
|
48
|
+
if (!result.ok) throw new Error(result.error);
|
|
49
|
+
return result.value;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
r.patch("/{id}")
|
|
53
|
+
.summary("Update table")
|
|
54
|
+
.permission("pos-restaurant:admin")
|
|
55
|
+
.input(z.object({
|
|
56
|
+
number: z.string().min(1).max(50).optional(),
|
|
57
|
+
zone: z.string().min(1).max(100).optional(),
|
|
58
|
+
capacity: z.number().int().min(1).optional(),
|
|
59
|
+
shape: z.enum(["rectangle", "square", "circle"]).optional(),
|
|
60
|
+
assignedOperatorId: z.string().nullable().optional(),
|
|
61
|
+
}))
|
|
62
|
+
.handler(async ({ params, input, orgId }) => {
|
|
63
|
+
const body = input as { number?: string; zone?: string; capacity?: number; shape?: "rectangle" | "square" | "circle"; assignedOperatorId?: string | null };
|
|
64
|
+
const result = await service.update(orgId, params.id!, body);
|
|
65
|
+
if (!result.ok) throw new Error(result.error);
|
|
66
|
+
return result.value;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
r.post("/{id}/assign")
|
|
70
|
+
.summary("Assign table to transaction")
|
|
71
|
+
.permission("pos:operate")
|
|
72
|
+
.input(z.object({ transactionId: z.string().uuid() }))
|
|
73
|
+
.handler(async ({ params, input, orgId }) => {
|
|
74
|
+
const body = input as { transactionId: string };
|
|
75
|
+
const result = await service.assignToTransaction(orgId, params.id!, body.transactionId);
|
|
76
|
+
if (!result.ok) throw new Error(result.error);
|
|
77
|
+
return result.value;
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
r.post("/{id}/clear")
|
|
81
|
+
.summary("Clear table")
|
|
82
|
+
.permission("pos:operate")
|
|
83
|
+
.handler(async ({ params, orgId }) => {
|
|
84
|
+
const result = await service.clear(orgId, params.id!);
|
|
85
|
+
if (!result.ok) throw new Error(result.error);
|
|
86
|
+
return result.value;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
r.post("/{id}/transfer")
|
|
90
|
+
.summary("Transfer to another table")
|
|
91
|
+
.permission("pos:operate")
|
|
92
|
+
.input(z.object({ toTableId: z.string().uuid() }))
|
|
93
|
+
.handler(async ({ params, input, orgId }) => {
|
|
94
|
+
const body = input as { toTableId: string };
|
|
95
|
+
const result = await service.transfer(orgId, params.id!, body.toTableId);
|
|
96
|
+
if (!result.ok) throw new Error(result.error);
|
|
97
|
+
return result.value;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
r.patch("/{id}/layout")
|
|
101
|
+
.summary("Update floor plan layout")
|
|
102
|
+
.permission("pos-restaurant:admin")
|
|
103
|
+
.input(z.object({
|
|
104
|
+
layoutX: z.number().int().optional(),
|
|
105
|
+
layoutY: z.number().int().optional(),
|
|
106
|
+
layoutWidth: z.number().int().optional(),
|
|
107
|
+
layoutHeight: z.number().int().optional(),
|
|
108
|
+
}))
|
|
109
|
+
.handler(async ({ params, input, orgId }) => {
|
|
110
|
+
const body = input as { layoutX?: number; layoutY?: number; layoutWidth?: number; layoutHeight?: number };
|
|
111
|
+
const result = await service.updateLayout(orgId, params.id!, body);
|
|
112
|
+
if (!result.ok) throw new Error(result.error);
|
|
113
|
+
return result.value;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return r.routes();
|
|
117
|
+
}
|