@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
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @porulle/plugin-pos-restaurant
|
|
2
|
+
|
|
3
|
+
Restaurant layer on top of `@porulle/plugin-pos`: tables, floor zones, optional modifiers and KDS, checklists, alerts, recipes (food BOM), and analytics.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @porulle/plugin-pos @porulle/plugin-pos-restaurant
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Add to `commerce.config.ts`:
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { posPlugin } from "@porulle/plugin-pos";
|
|
15
|
+
import { posRestaurantPlugin } from "@porulle/plugin-pos-restaurant";
|
|
16
|
+
|
|
17
|
+
export default defineConfig({
|
|
18
|
+
plugins: [posPlugin(), posRestaurantPlugin()],
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
(`posRestaurantPlugin` declares `requires: ["pos"]`.)
|
|
23
|
+
|
|
24
|
+
Add to `drizzle.config.ts`:
|
|
25
|
+
|
|
26
|
+
```typescript
|
|
27
|
+
schema: [
|
|
28
|
+
"./node_modules/@porulle/plugin-pos-restaurant/src/schema.ts",
|
|
29
|
+
// include plugin-pos schema as well
|
|
30
|
+
],
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## What it does
|
|
34
|
+
|
|
35
|
+
Adds restaurant schema (modifiers, tables, KDS, checklists, alerts, recipes/combos/menu availability, P&L, favorites). Modifier and KDS route groups mount only when the corresponding options are enabled.
|
|
36
|
+
|
|
37
|
+
## Routes exposed
|
|
38
|
+
|
|
39
|
+
- **`/pos/restaurant/tables`** — CRUD tables, zones, assign/clear/transfer, layout
|
|
40
|
+
- **`/pos/restaurant/modifier-groups`** (+ **`/pos/restaurant/modifier-options`**) — when `enableModifiers`
|
|
41
|
+
- **`/pos/restaurant/kds`** — stations, tickets, transitions — when `enableKDS`
|
|
42
|
+
- **`/pos/restaurant/checklists`**, **`/pos/restaurant/alerts`**, **`/pos/restaurant/recipes`**, **`/pos/restaurant/analytics`** — operational and reporting endpoints (see route files for full paths)
|
|
43
|
+
|
|
44
|
+
## Hooks
|
|
45
|
+
|
|
46
|
+
**Emitted:** none.
|
|
47
|
+
|
|
48
|
+
**Consumed:** **`cart.beforeAddItem`** — validates modifier selections when `enableModifiers`. **`checkout.afterCreate`** — for POS transactions with table assignments, moves tables to `cleaning` and clears assignments (see `buildTableClearOnCompleteHook`).
|
|
49
|
+
|
|
50
|
+
## MCP tools
|
|
51
|
+
|
|
52
|
+
- **`restaurant_tables`** — `list`, `set_status`, `transfer`
|
|
53
|
+
- **`restaurant_kds`** — `list_tickets`, `transition`, `list_zones`
|
|
54
|
+
|
|
55
|
+
## Configuration options
|
|
56
|
+
|
|
57
|
+
`POSRestaurantPluginOptions`: `enableKDS`, `enableTips`, `enableModifiers`, `kdsAlertMinutes`.
|
|
58
|
+
|
|
59
|
+
## License
|
|
60
|
+
|
|
61
|
+
MIT
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifier Validation Hook — cart.beforeAddItem
|
|
3
|
+
*
|
|
4
|
+
* Validates modifier selections when items are added to a POS cart.
|
|
5
|
+
* URY has no modifier validation (modifiers are just flat item links).
|
|
6
|
+
* This hook enforces:
|
|
7
|
+
* - Required modifier groups must have at least minSelect selections
|
|
8
|
+
* - No group may exceed maxSelect selections
|
|
9
|
+
* - Unavailable (86'd) options are rejected
|
|
10
|
+
* - Price adjustments are summed into cart line item metadata
|
|
11
|
+
*/
|
|
12
|
+
import type { ModifierService } from "../services/modifier-service.js";
|
|
13
|
+
export declare function buildModifierValidationHook(getService: () => ModifierService): {
|
|
14
|
+
key: string;
|
|
15
|
+
handler: (...args: unknown[]) => Promise<{
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
entityId?: string;
|
|
18
|
+
metadata?: Record<string, unknown>;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=modifier-validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modifier-validation.d.ts","sourceRoot":"","sources":["../../src/hooks/modifier-validation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAEvE,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,MAAM,eAAe;;uBAGhD,OAAO,EAAE;;mBAGjB,MAAM;mBACN,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;;EAsC3C"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modifier Validation Hook — cart.beforeAddItem
|
|
3
|
+
*
|
|
4
|
+
* Validates modifier selections when items are added to a POS cart.
|
|
5
|
+
* URY has no modifier validation (modifiers are just flat item links).
|
|
6
|
+
* This hook enforces:
|
|
7
|
+
* - Required modifier groups must have at least minSelect selections
|
|
8
|
+
* - No group may exceed maxSelect selections
|
|
9
|
+
* - Unavailable (86'd) options are rejected
|
|
10
|
+
* - Price adjustments are summed into cart line item metadata
|
|
11
|
+
*/
|
|
12
|
+
export function buildModifierValidationHook(getService) {
|
|
13
|
+
return {
|
|
14
|
+
key: "cart.beforeAddItem",
|
|
15
|
+
handler: async (...args) => {
|
|
16
|
+
const hook = args[0];
|
|
17
|
+
const { data, context } = hook;
|
|
18
|
+
const modifiers = data.metadata?.modifiers;
|
|
19
|
+
// If no modifiers provided, skip validation (non-restaurant items)
|
|
20
|
+
if (!modifiers || modifiers.length === 0)
|
|
21
|
+
return data;
|
|
22
|
+
if (!data.entityId)
|
|
23
|
+
return data;
|
|
24
|
+
const { resolveOrgId } = await import("@porulle/core");
|
|
25
|
+
const orgId = resolveOrgId(context.actor);
|
|
26
|
+
const service = getService();
|
|
27
|
+
const result = await service.validateModifiers(orgId, data.entityId, modifiers);
|
|
28
|
+
if (!result.ok) {
|
|
29
|
+
throw new Error(result.error);
|
|
30
|
+
}
|
|
31
|
+
// Inject validated modifiers and price adjustment into metadata
|
|
32
|
+
data.metadata = {
|
|
33
|
+
...data.metadata,
|
|
34
|
+
validatedModifiers: result.value.validatedModifiers,
|
|
35
|
+
modifierPriceAdjustment: result.value.totalAdjustment,
|
|
36
|
+
};
|
|
37
|
+
return data;
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table Lifecycle Hooks
|
|
3
|
+
*
|
|
4
|
+
* URY equivalent: table_status_delete() in ury_pos_invoice.py.
|
|
5
|
+
* URY clears occupied=0 on POS Invoice delete/cancel/submit.
|
|
6
|
+
*
|
|
7
|
+
* We hook into checkout.afterCreate to update table status when a
|
|
8
|
+
* POS transaction completes, and pos transaction void to clear tables.
|
|
9
|
+
*/
|
|
10
|
+
import type { Db } from "../types.js";
|
|
11
|
+
export declare function buildTableClearOnCompleteHook(getDb: () => Db): {
|
|
12
|
+
key: string;
|
|
13
|
+
handler: (...args: unknown[]) => Promise<void>;
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=table-lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-lifecycle.d.ts","sourceRoot":"","sources":["../../src/hooks/table-lifecycle.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAEtC,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,EAAE;;uBAGhC,OAAO,EAAE;EAgCrC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Table Lifecycle Hooks
|
|
3
|
+
*
|
|
4
|
+
* URY equivalent: table_status_delete() in ury_pos_invoice.py.
|
|
5
|
+
* URY clears occupied=0 on POS Invoice delete/cancel/submit.
|
|
6
|
+
*
|
|
7
|
+
* We hook into checkout.afterCreate to update table status when a
|
|
8
|
+
* POS transaction completes, and pos transaction void to clear tables.
|
|
9
|
+
*/
|
|
10
|
+
import { eq } from "@porulle/core/drizzle";
|
|
11
|
+
import { posTableAssignments, posTables } from "../schema.js";
|
|
12
|
+
export function buildTableClearOnCompleteHook(getDb) {
|
|
13
|
+
return {
|
|
14
|
+
key: "checkout.afterCreate",
|
|
15
|
+
handler: async (...args) => {
|
|
16
|
+
const hook = args[0];
|
|
17
|
+
const metadata = hook.result?.metadata;
|
|
18
|
+
const posTransactionId = metadata?.posTransactionId;
|
|
19
|
+
if (!posTransactionId)
|
|
20
|
+
return;
|
|
21
|
+
const db = getDb();
|
|
22
|
+
// Find table assignments for this transaction
|
|
23
|
+
const assignments = await db
|
|
24
|
+
.select()
|
|
25
|
+
.from(posTableAssignments)
|
|
26
|
+
.where(eq(posTableAssignments.transactionId, posTransactionId));
|
|
27
|
+
// Set tables to "cleaning" (staff will set to "available" after bussing)
|
|
28
|
+
for (const assignment of assignments) {
|
|
29
|
+
await db
|
|
30
|
+
.update(posTables)
|
|
31
|
+
.set({ status: "cleaning", updatedAt: new Date() })
|
|
32
|
+
.where(eq(posTables.id, assignment.tableId));
|
|
33
|
+
}
|
|
34
|
+
// Remove assignments
|
|
35
|
+
await db
|
|
36
|
+
.delete(posTableAssignments)
|
|
37
|
+
.where(eq(posTableAssignments.transactionId, posTransactionId));
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POS Restaurant Extension — RFC-024a (Complete Feature Set)
|
|
3
|
+
*
|
|
4
|
+
* Extends @porulle/plugin-pos with all restaurant-specific features
|
|
5
|
+
* informed by URY Restaurant ERP (ury-erp/ury) production patterns.
|
|
6
|
+
*
|
|
7
|
+
* Feature coverage (maps to URY FEATURES.md):
|
|
8
|
+
*
|
|
9
|
+
* POS & Billing:
|
|
10
|
+
* - Pre-billing checklists (compliance enforcement)
|
|
11
|
+
* - Table service, QSR, and takeaway (order types)
|
|
12
|
+
* - Multi-cashier handling (via Tier 0 terminals)
|
|
13
|
+
* - Shift opening/closing with cash reconciliation (via Tier 0)
|
|
14
|
+
*
|
|
15
|
+
* Menu & Recipe Management:
|
|
16
|
+
* - Item modifiers (groups, options, required/optional, price adjustments)
|
|
17
|
+
* - Recipe/BOM mapping for COGS calculation
|
|
18
|
+
* - Combos and item bundles
|
|
19
|
+
* - Menu availability per outlet (86'd items)
|
|
20
|
+
*
|
|
21
|
+
* Table Order Management:
|
|
22
|
+
* - Table management with zones, floor plan, status lifecycle
|
|
23
|
+
* - Server section assignment
|
|
24
|
+
* - Table/captain transfer
|
|
25
|
+
* - Customer favorites (top ordered items)
|
|
26
|
+
*
|
|
27
|
+
* Kitchen Display & KOT Management:
|
|
28
|
+
* - Multi-station KDS with item-group routing
|
|
29
|
+
* - 4-state ticket flow (pending -> preparing -> ready -> served)
|
|
30
|
+
* - Item-level completion tracking (persistent, not localStorage)
|
|
31
|
+
* - Course sequencing with priority-based ordering
|
|
32
|
+
* - Delay and modification tracking
|
|
33
|
+
* - KOT reprint support
|
|
34
|
+
*
|
|
35
|
+
* Operational Red Flags & Alerts:
|
|
36
|
+
* - Delayed order alerts
|
|
37
|
+
* - KOT not started alerts
|
|
38
|
+
* - Prolonged table occupancy alerts
|
|
39
|
+
* - Excessive cancellation tracking
|
|
40
|
+
* - Configurable thresholds per alert type
|
|
41
|
+
*
|
|
42
|
+
* Reports & Analytics:
|
|
43
|
+
* - Daily Profit & Loss (gross sales, COGS, expenses, net profit)
|
|
44
|
+
* - Station performance (prep time, throughput)
|
|
45
|
+
* - Course-wise performance
|
|
46
|
+
* - Staff/captain performance
|
|
47
|
+
* - P&L expense breakdown
|
|
48
|
+
*/
|
|
49
|
+
import type { POSRestaurantPluginOptions } from "./types.js";
|
|
50
|
+
export type { POSRestaurantPluginOptions, Db } from "./types.js";
|
|
51
|
+
export { ModifierService } from "./services/modifier-service.js";
|
|
52
|
+
export { TableService } from "./services/table-service.js";
|
|
53
|
+
export { KDSService } from "./services/kds-service.js";
|
|
54
|
+
export { ChecklistService } from "./services/checklist-service.js";
|
|
55
|
+
export { AlertService } from "./services/alert-service.js";
|
|
56
|
+
export { RecipeService } from "./services/recipe-service.js";
|
|
57
|
+
export { RestaurantAnalyticsService } from "./services/analytics-service.js";
|
|
58
|
+
export { RecipeDeductionService } from "./services/recipe-deduction-service.js";
|
|
59
|
+
export declare function posRestaurantPlugin(userOptions?: POSRestaurantPluginOptions): import("@porulle/core").CommercePlugin;
|
|
60
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AA6CH,OAAO,KAAK,EAAE,0BAA0B,EAAM,MAAM,YAAY,CAAC;AAGjE,YAAY,EAAE,0BAA0B,EAAE,EAAE,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAC7D,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAEhF,wBAAgB,mBAAmB,CAAC,WAAW,GAAE,0BAA+B,0CAgH/E"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* POS Restaurant Extension — RFC-024a (Complete Feature Set)
|
|
3
|
+
*
|
|
4
|
+
* Extends @porulle/plugin-pos with all restaurant-specific features
|
|
5
|
+
* informed by URY Restaurant ERP (ury-erp/ury) production patterns.
|
|
6
|
+
*
|
|
7
|
+
* Feature coverage (maps to URY FEATURES.md):
|
|
8
|
+
*
|
|
9
|
+
* POS & Billing:
|
|
10
|
+
* - Pre-billing checklists (compliance enforcement)
|
|
11
|
+
* - Table service, QSR, and takeaway (order types)
|
|
12
|
+
* - Multi-cashier handling (via Tier 0 terminals)
|
|
13
|
+
* - Shift opening/closing with cash reconciliation (via Tier 0)
|
|
14
|
+
*
|
|
15
|
+
* Menu & Recipe Management:
|
|
16
|
+
* - Item modifiers (groups, options, required/optional, price adjustments)
|
|
17
|
+
* - Recipe/BOM mapping for COGS calculation
|
|
18
|
+
* - Combos and item bundles
|
|
19
|
+
* - Menu availability per outlet (86'd items)
|
|
20
|
+
*
|
|
21
|
+
* Table Order Management:
|
|
22
|
+
* - Table management with zones, floor plan, status lifecycle
|
|
23
|
+
* - Server section assignment
|
|
24
|
+
* - Table/captain transfer
|
|
25
|
+
* - Customer favorites (top ordered items)
|
|
26
|
+
*
|
|
27
|
+
* Kitchen Display & KOT Management:
|
|
28
|
+
* - Multi-station KDS with item-group routing
|
|
29
|
+
* - 4-state ticket flow (pending -> preparing -> ready -> served)
|
|
30
|
+
* - Item-level completion tracking (persistent, not localStorage)
|
|
31
|
+
* - Course sequencing with priority-based ordering
|
|
32
|
+
* - Delay and modification tracking
|
|
33
|
+
* - KOT reprint support
|
|
34
|
+
*
|
|
35
|
+
* Operational Red Flags & Alerts:
|
|
36
|
+
* - Delayed order alerts
|
|
37
|
+
* - KOT not started alerts
|
|
38
|
+
* - Prolonged table occupancy alerts
|
|
39
|
+
* - Excessive cancellation tracking
|
|
40
|
+
* - Configurable thresholds per alert type
|
|
41
|
+
*
|
|
42
|
+
* Reports & Analytics:
|
|
43
|
+
* - Daily Profit & Loss (gross sales, COGS, expenses, net profit)
|
|
44
|
+
* - Station performance (prep time, throughput)
|
|
45
|
+
* - Course-wise performance
|
|
46
|
+
* - Staff/captain performance
|
|
47
|
+
* - P&L expense breakdown
|
|
48
|
+
*/
|
|
49
|
+
import { defineCommercePlugin } from "@porulle/core";
|
|
50
|
+
import { posModifierGroups, posModifierOptions, posTables, posTableAssignments, kdsStations, kdsStationItemGroups, kdsTickets, kdsTicketItems, posChecklists, posChecklistItems, posChecklistCompletions, posRestaurantAlerts, posAlertConfig, posRecipes, posRecipeIngredients, posCombos, posComboGroups, posComboItems, posMenuAvailability, posDailyPnl, posPnlExpenses, posCustomerFavorites, } from "./schema.js";
|
|
51
|
+
import { ModifierService } from "./services/modifier-service.js";
|
|
52
|
+
import { TableService } from "./services/table-service.js";
|
|
53
|
+
import { KDSService } from "./services/kds-service.js";
|
|
54
|
+
import { ChecklistService } from "./services/checklist-service.js";
|
|
55
|
+
import { AlertService } from "./services/alert-service.js";
|
|
56
|
+
import { RecipeService } from "./services/recipe-service.js";
|
|
57
|
+
import { RestaurantAnalyticsService } from "./services/analytics-service.js";
|
|
58
|
+
import { buildModifierRoutes, buildModifierOptionRoutes } from "./routes/modifiers.js";
|
|
59
|
+
import { buildTableRoutes } from "./routes/tables.js";
|
|
60
|
+
import { buildKDSRoutes } from "./routes/kds.js";
|
|
61
|
+
import { buildChecklistRoutes, buildAlertRoutes, buildRecipeRoutes, buildAnalyticsRoutes, } from "./routes/operations.js";
|
|
62
|
+
import { buildModifierValidationHook } from "./hooks/modifier-validation.js";
|
|
63
|
+
import { buildTableClearOnCompleteHook } from "./hooks/table-lifecycle.js";
|
|
64
|
+
import { DEFAULT_RESTAURANT_OPTIONS } from "./types.js";
|
|
65
|
+
export { ModifierService } from "./services/modifier-service.js";
|
|
66
|
+
export { TableService } from "./services/table-service.js";
|
|
67
|
+
export { KDSService } from "./services/kds-service.js";
|
|
68
|
+
export { ChecklistService } from "./services/checklist-service.js";
|
|
69
|
+
export { AlertService } from "./services/alert-service.js";
|
|
70
|
+
export { RecipeService } from "./services/recipe-service.js";
|
|
71
|
+
export { RestaurantAnalyticsService } from "./services/analytics-service.js";
|
|
72
|
+
export { RecipeDeductionService } from "./services/recipe-deduction-service.js";
|
|
73
|
+
export function posRestaurantPlugin(userOptions = {}) {
|
|
74
|
+
const options = {
|
|
75
|
+
...DEFAULT_RESTAURANT_OPTIONS,
|
|
76
|
+
...userOptions,
|
|
77
|
+
};
|
|
78
|
+
const dbRef = { current: null };
|
|
79
|
+
const modifierServiceRef = { current: null };
|
|
80
|
+
return defineCommercePlugin({
|
|
81
|
+
id: "pos-restaurant",
|
|
82
|
+
version: "1.0.0",
|
|
83
|
+
requires: ["pos"],
|
|
84
|
+
permissions: [
|
|
85
|
+
{
|
|
86
|
+
scope: "pos-restaurant:admin",
|
|
87
|
+
description: "Create/edit modifier groups, tables, KDS stations, checklists, recipes, alert config, analytics, floor plan layout.",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
schema: () => ({
|
|
91
|
+
// Core restaurant tables
|
|
92
|
+
posModifierGroups,
|
|
93
|
+
posModifierOptions,
|
|
94
|
+
posTables,
|
|
95
|
+
posTableAssignments,
|
|
96
|
+
kdsStations,
|
|
97
|
+
kdsStationItemGroups,
|
|
98
|
+
kdsTickets,
|
|
99
|
+
kdsTicketItems,
|
|
100
|
+
// Operational features
|
|
101
|
+
posChecklists,
|
|
102
|
+
posChecklistItems,
|
|
103
|
+
posChecklistCompletions,
|
|
104
|
+
posRestaurantAlerts,
|
|
105
|
+
posAlertConfig,
|
|
106
|
+
// Menu & recipe management
|
|
107
|
+
posRecipes,
|
|
108
|
+
posRecipeIngredients,
|
|
109
|
+
posCombos,
|
|
110
|
+
posComboGroups,
|
|
111
|
+
posComboItems,
|
|
112
|
+
posMenuAvailability,
|
|
113
|
+
// Analytics
|
|
114
|
+
posDailyPnl,
|
|
115
|
+
posPnlExpenses,
|
|
116
|
+
posCustomerFavorites,
|
|
117
|
+
}),
|
|
118
|
+
hooks: () => {
|
|
119
|
+
const hooks = [];
|
|
120
|
+
if (options.enableModifiers) {
|
|
121
|
+
hooks.push(buildModifierValidationHook(() => {
|
|
122
|
+
if (!modifierServiceRef.current)
|
|
123
|
+
throw new Error("ModifierService not initialized");
|
|
124
|
+
return modifierServiceRef.current;
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
hooks.push(buildTableClearOnCompleteHook(() => {
|
|
128
|
+
if (!dbRef.current)
|
|
129
|
+
throw new Error("Restaurant plugin DB not initialized");
|
|
130
|
+
return dbRef.current;
|
|
131
|
+
}));
|
|
132
|
+
return hooks;
|
|
133
|
+
},
|
|
134
|
+
routes: (ctx) => {
|
|
135
|
+
const db = ctx.database.db;
|
|
136
|
+
if (!db)
|
|
137
|
+
return [];
|
|
138
|
+
dbRef.current = db;
|
|
139
|
+
// Initialize all services
|
|
140
|
+
const modifierService = new ModifierService(db);
|
|
141
|
+
const tableService = new TableService(db);
|
|
142
|
+
const kdsService = new KDSService(db);
|
|
143
|
+
const checklistService = new ChecklistService(db);
|
|
144
|
+
const alertService = new AlertService(db);
|
|
145
|
+
const recipeService = new RecipeService(db);
|
|
146
|
+
const analyticsService = new RestaurantAnalyticsService(db);
|
|
147
|
+
modifierServiceRef.current = modifierService;
|
|
148
|
+
const routes = [
|
|
149
|
+
// Table management (always enabled)
|
|
150
|
+
...buildTableRoutes(tableService, ctx),
|
|
151
|
+
// Checklists (always enabled)
|
|
152
|
+
...buildChecklistRoutes(checklistService, ctx),
|
|
153
|
+
// Alerts (always enabled)
|
|
154
|
+
...buildAlertRoutes(alertService, ctx),
|
|
155
|
+
// Recipes/BOM (always enabled)
|
|
156
|
+
...buildRecipeRoutes(recipeService, ctx),
|
|
157
|
+
// Analytics (always enabled)
|
|
158
|
+
...buildAnalyticsRoutes(analyticsService, ctx),
|
|
159
|
+
];
|
|
160
|
+
if (options.enableModifiers) {
|
|
161
|
+
routes.push(...buildModifierRoutes(modifierService, ctx), ...buildModifierOptionRoutes(modifierService, ctx));
|
|
162
|
+
}
|
|
163
|
+
if (options.enableKDS) {
|
|
164
|
+
routes.push(...buildKDSRoutes(kdsService, ctx));
|
|
165
|
+
}
|
|
166
|
+
return routes;
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { KDSService } from "../services/kds-service.js";
|
|
2
|
+
import type { PluginRouteRegistration } from "@porulle/core";
|
|
3
|
+
export declare function buildKDSRoutes(service: KDSService, ctx: {
|
|
4
|
+
services?: Record<string, unknown>;
|
|
5
|
+
database?: {
|
|
6
|
+
db: unknown;
|
|
7
|
+
};
|
|
8
|
+
}): PluginRouteRegistration[];
|
|
9
|
+
//# sourceMappingURL=kds.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kds.d.ts","sourceRoot":"","sources":["../../src/routes/kds.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,cAAc,CAC5B,OAAO,EAAE,UAAU,EACnB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CAgH3B"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { router } from "@porulle/core";
|
|
2
|
+
import { z } from "@hono/zod-openapi";
|
|
3
|
+
export function buildKDSRoutes(service, ctx) {
|
|
4
|
+
const r = router("POS Restaurant KDS", "/pos/restaurant/kds", ctx);
|
|
5
|
+
// ─── Stations ──────────────────────────────────────────────────────
|
|
6
|
+
r.post("/stations")
|
|
7
|
+
.summary("Create KDS station")
|
|
8
|
+
.permission("pos-restaurant:admin")
|
|
9
|
+
.input(z.object({
|
|
10
|
+
name: z.string().min(1).max(100),
|
|
11
|
+
alertThresholdMinutes: z.number().int().min(1).optional(),
|
|
12
|
+
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
13
|
+
}))
|
|
14
|
+
.handler(async ({ input, orgId }) => {
|
|
15
|
+
const body = input;
|
|
16
|
+
const result = await service.createStation(orgId, body);
|
|
17
|
+
if (!result.ok)
|
|
18
|
+
throw new Error(result.error);
|
|
19
|
+
return result.value;
|
|
20
|
+
});
|
|
21
|
+
r.get("/stations")
|
|
22
|
+
.summary("List KDS stations")
|
|
23
|
+
.permission("pos:operate")
|
|
24
|
+
.handler(async ({ orgId }) => {
|
|
25
|
+
const result = await service.listStations(orgId);
|
|
26
|
+
if (!result.ok)
|
|
27
|
+
throw new Error(result.error);
|
|
28
|
+
return result.value;
|
|
29
|
+
});
|
|
30
|
+
r.patch("/stations/{id}")
|
|
31
|
+
.summary("Update KDS station")
|
|
32
|
+
.permission("pos-restaurant:admin")
|
|
33
|
+
.input(z.object({
|
|
34
|
+
name: z.string().min(1).max(100).optional(),
|
|
35
|
+
isActive: z.boolean().optional(),
|
|
36
|
+
alertThresholdMinutes: z.number().int().min(1).optional(),
|
|
37
|
+
}))
|
|
38
|
+
.handler(async ({ params, input, orgId }) => {
|
|
39
|
+
const body = input;
|
|
40
|
+
const result = await service.updateStation(orgId, params.id, body);
|
|
41
|
+
if (!result.ok)
|
|
42
|
+
throw new Error(result.error);
|
|
43
|
+
return result.value;
|
|
44
|
+
});
|
|
45
|
+
r.post("/stations/{id}/item-groups")
|
|
46
|
+
.summary("Add item group to station")
|
|
47
|
+
.permission("pos-restaurant:admin")
|
|
48
|
+
.input(z.object({ itemGroup: z.string().min(1) }))
|
|
49
|
+
.handler(async ({ params, input }) => {
|
|
50
|
+
const body = input;
|
|
51
|
+
const result = await service.addItemGroup(params.id, body.itemGroup);
|
|
52
|
+
if (!result.ok)
|
|
53
|
+
throw new Error(result.error);
|
|
54
|
+
return result.value;
|
|
55
|
+
});
|
|
56
|
+
r.delete("/stations/{id}/item-groups/{group}")
|
|
57
|
+
.summary("Remove item group from station")
|
|
58
|
+
.permission("pos-restaurant:admin")
|
|
59
|
+
.handler(async ({ params }) => {
|
|
60
|
+
const result = await service.removeItemGroup(params.id, params.group);
|
|
61
|
+
if (!result.ok)
|
|
62
|
+
throw new Error(result.error);
|
|
63
|
+
return result.value;
|
|
64
|
+
});
|
|
65
|
+
// ─── Tickets ───────────────────────────────────────────────────────
|
|
66
|
+
r.get("/stations/{id}/tickets")
|
|
67
|
+
.summary("List pending tickets for station")
|
|
68
|
+
.permission("pos:operate")
|
|
69
|
+
.handler(async ({ params, orgId }) => {
|
|
70
|
+
const result = await service.listPendingTickets(orgId, params.id);
|
|
71
|
+
if (!result.ok)
|
|
72
|
+
throw new Error(result.error);
|
|
73
|
+
return result.value;
|
|
74
|
+
});
|
|
75
|
+
r.post("/tickets/{id}/start")
|
|
76
|
+
.summary("Mark ticket as preparing")
|
|
77
|
+
.permission("pos:operate")
|
|
78
|
+
.handler(async ({ params }) => {
|
|
79
|
+
const result = await service.startTicket(params.id);
|
|
80
|
+
if (!result.ok)
|
|
81
|
+
throw new Error(result.error);
|
|
82
|
+
return result.value;
|
|
83
|
+
});
|
|
84
|
+
r.post("/tickets/{id}/ready")
|
|
85
|
+
.summary("Mark ticket as ready")
|
|
86
|
+
.permission("pos:operate")
|
|
87
|
+
.handler(async ({ params }) => {
|
|
88
|
+
const result = await service.readyTicket(params.id);
|
|
89
|
+
if (!result.ok)
|
|
90
|
+
throw new Error(result.error);
|
|
91
|
+
return result.value;
|
|
92
|
+
});
|
|
93
|
+
r.post("/tickets/{id}/serve")
|
|
94
|
+
.summary("Mark ticket as served")
|
|
95
|
+
.permission("pos:operate")
|
|
96
|
+
.handler(async ({ params }) => {
|
|
97
|
+
const result = await service.serveTicket(params.id);
|
|
98
|
+
if (!result.ok)
|
|
99
|
+
throw new Error(result.error);
|
|
100
|
+
return result.value;
|
|
101
|
+
});
|
|
102
|
+
r.post("/tickets/{id}/items/{itemId}/done")
|
|
103
|
+
.summary("Mark ticket item as done")
|
|
104
|
+
.permission("pos:operate")
|
|
105
|
+
.handler(async ({ params }) => {
|
|
106
|
+
const result = await service.markItemDone(params.id, params.itemId);
|
|
107
|
+
if (!result.ok)
|
|
108
|
+
throw new Error(result.error);
|
|
109
|
+
return result.value;
|
|
110
|
+
});
|
|
111
|
+
return r.routes();
|
|
112
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ModifierService } from "../services/modifier-service.js";
|
|
2
|
+
import type { PluginRouteRegistration } from "@porulle/core";
|
|
3
|
+
export declare function buildModifierRoutes(service: ModifierService, ctx: {
|
|
4
|
+
services?: Record<string, unknown>;
|
|
5
|
+
database?: {
|
|
6
|
+
db: unknown;
|
|
7
|
+
};
|
|
8
|
+
}): PluginRouteRegistration[];
|
|
9
|
+
export declare function buildModifierOptionRoutes(service: ModifierService, ctx: {
|
|
10
|
+
services?: Record<string, unknown>;
|
|
11
|
+
database?: {
|
|
12
|
+
db: unknown;
|
|
13
|
+
};
|
|
14
|
+
}): PluginRouteRegistration[];
|
|
15
|
+
//# sourceMappingURL=modifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"modifiers.d.ts","sourceRoot":"","sources":["../../src/routes/modifiers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAE7D,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CAqF3B;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,GAAG,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAAC,QAAQ,CAAC,EAAE;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAA;CAAE,GACtE,uBAAuB,EAAE,CA8B3B"}
|