@porulle/plugin-pos-restaurant 0.8.0 → 0.9.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/dist/services/kds-service.d.ts.map +1 -1
- package/dist/services/kds-service.js +3 -3
- package/dist/services/recipe-deduction-service.d.ts +2 -1
- package/dist/services/recipe-deduction-service.d.ts.map +1 -1
- package/dist/services/recipe-deduction-service.js +19 -8
- package/package.json +5 -5
- package/src/services/kds-service.ts +3 -2
- package/src/services/recipe-deduction-service.ts +30 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"kds-service.d.ts","sourceRoot":"","sources":["../../src/services/kds-service.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"kds-service.d.ts","sourceRoot":"","sources":["../../src/services/kds-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -112,7 +112,7 @@ export class KDSService {
|
|
|
112
112
|
.from(kdsTickets)
|
|
113
113
|
.where(and(eq(kdsTickets.transactionId, input.transactionId), eq(kdsTickets.stationId, station.id)));
|
|
114
114
|
const ticketType = existingTickets.length > 0 ? "modified" : "new_order";
|
|
115
|
-
const ticketNumber = await this.generateTicketNumber(station.id);
|
|
115
|
+
const ticketNumber = await this.generateTicketNumber(orgId, station.id);
|
|
116
116
|
const ticketRows = await this.db
|
|
117
117
|
.insert(kdsTickets)
|
|
118
118
|
.values({
|
|
@@ -232,14 +232,14 @@ export class KDSService {
|
|
|
232
232
|
return Ok(result);
|
|
233
233
|
}
|
|
234
234
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
235
|
-
async generateTicketNumber(stationId) {
|
|
235
|
+
async generateTicketNumber(orgId, stationId) {
|
|
236
236
|
const today = new Date();
|
|
237
237
|
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
238
238
|
const startOfDayISO = startOfDay.toISOString();
|
|
239
239
|
const countRows = await this.db
|
|
240
240
|
.select({ count: sql `COUNT(*)`.as("count") })
|
|
241
241
|
.from(kdsTickets)
|
|
242
|
-
.where(and(eq(kdsTickets.stationId, stationId), sql `${kdsTickets.createdAt} >= ${startOfDayISO}::timestamptz`));
|
|
242
|
+
.where(and(eq(kdsTickets.organizationId, orgId), eq(kdsTickets.stationId, stationId), sql `${kdsTickets.createdAt} >= ${startOfDayISO}::timestamptz`));
|
|
243
243
|
const seq = Number(countRows[0]?.count ?? 0) + 1;
|
|
244
244
|
return `KDS-${String(seq).padStart(4, "0")}`;
|
|
245
245
|
}
|
|
@@ -48,7 +48,8 @@ export declare class RecipeDeductionService {
|
|
|
48
48
|
* - Used when services are not available (standalone scripts, tests)
|
|
49
49
|
* - Same oversell protection via UPDATE ... WHERE quantity_on_hand >= N
|
|
50
50
|
*/
|
|
51
|
-
applyDeductions(tx: Db, deductions: DeductionItem[], warehouseId: string, referenceType: string, referenceId: string, performedBy: string): Promise<PluginResult<number>>;
|
|
51
|
+
applyDeductions(tx: Db, deductions: DeductionItem[], warehouseId: string, referenceType: string, referenceId: string, performedBy: string, orgId?: string): Promise<PluginResult<number>>;
|
|
52
|
+
private resolveOrgIdFromWarehouse;
|
|
52
53
|
/**
|
|
53
54
|
* Strategy 1: Deduct via kernel.services.inventory.adjust().
|
|
54
55
|
* Each deduction becomes a negative adjustment with a reason.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"recipe-deduction-service.d.ts","sourceRoot":"","sources":["../../src/services/recipe-deduction-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAUtC,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,EAAE;IAHZ,OAAO,CAAC,YAAY,CAAC,CAAoB;gBAG/B,EAAE,EAAE,EAAE,EACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQpC;;;OAGG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC;IAwCzC;;;;;;;;;;;OAWG;IACG,eAAe,CACnB,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,aAAa,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"recipe-deduction-service.d.ts","sourceRoot":"","sources":["../../src/services/recipe-deduction-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AAUtC,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,sBAAsB;IAI/B,OAAO,CAAC,EAAE;IAHZ,OAAO,CAAC,YAAY,CAAC,CAAoB;gBAG/B,EAAE,EAAE,EAAE,EACd,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAQpC;;;OAGG;IACG,iBAAiB,CACrB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GACnD,OAAO,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC;IAwCzC;;;;;;;;;;;OAWG;IACG,eAAe,CACnB,EAAE,EAAE,EAAE,EACN,UAAU,EAAE,aAAa,EAAE,EAC3B,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAmBlB,yBAAyB;IAUvC;;;OAGG;YACW,eAAe;IAwC7B;;;OAGG;YACW,cAAc;CA+C7B"}
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* falls back to raw SQL with the same atomic guard.
|
|
16
16
|
*/
|
|
17
17
|
import { eq, and, sql } from "@porulle/core/drizzle";
|
|
18
|
-
import { Ok } from "@porulle/core";
|
|
18
|
+
import { Ok, Err } from "@porulle/core";
|
|
19
19
|
import { posRecipes, posRecipeIngredients } from "../schema.js";
|
|
20
20
|
export class RecipeDeductionService {
|
|
21
21
|
db;
|
|
@@ -73,11 +73,21 @@ export class RecipeDeductionService {
|
|
|
73
73
|
* - Used when services are not available (standalone scripts, tests)
|
|
74
74
|
* - Same oversell protection via UPDATE ... WHERE quantity_on_hand >= N
|
|
75
75
|
*/
|
|
76
|
-
async applyDeductions(tx, deductions, warehouseId, referenceType, referenceId, performedBy) {
|
|
76
|
+
async applyDeductions(tx, deductions, warehouseId, referenceType, referenceId, performedBy, orgId) {
|
|
77
77
|
if (this.inventorySvc) {
|
|
78
78
|
return this.applyViaService(deductions, warehouseId, performedBy);
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
const resolvedOrgId = orgId ?? await this.resolveOrgIdFromWarehouse(tx, warehouseId);
|
|
81
|
+
if (!resolvedOrgId)
|
|
82
|
+
return Err("Warehouse not found");
|
|
83
|
+
return this.applyViaRawSQL(tx, deductions, warehouseId, referenceType, referenceId, performedBy, resolvedOrgId);
|
|
84
|
+
}
|
|
85
|
+
async resolveOrgIdFromWarehouse(tx, warehouseId) {
|
|
86
|
+
const result = await tx.execute(sql `SELECT organization_id FROM warehouses WHERE id = ${warehouseId} LIMIT 1`);
|
|
87
|
+
const rows = Array.isArray(result)
|
|
88
|
+
? result
|
|
89
|
+
: result.rows;
|
|
90
|
+
return rows[0]?.organization_id ?? null;
|
|
81
91
|
}
|
|
82
92
|
/**
|
|
83
93
|
* Strategy 1: Deduct via kernel.services.inventory.adjust().
|
|
@@ -115,7 +125,7 @@ export class RecipeDeductionService {
|
|
|
115
125
|
* Strategy 2: Raw SQL fallback with atomic oversell guard.
|
|
116
126
|
* Used when ServiceRegistry is not available.
|
|
117
127
|
*/
|
|
118
|
-
async applyViaRawSQL(tx, deductions, warehouseId, referenceType, referenceId, performedBy) {
|
|
128
|
+
async applyViaRawSQL(tx, deductions, warehouseId, referenceType, referenceId, performedBy, orgId) {
|
|
119
129
|
let applied = 0;
|
|
120
130
|
// PluginDb (PgDatabase) has .execute() — no cast needed
|
|
121
131
|
const exec = tx;
|
|
@@ -127,16 +137,17 @@ export class RecipeDeductionService {
|
|
|
127
137
|
const updateResult = await exec.execute(sql `UPDATE inventory_levels
|
|
128
138
|
SET quantity_on_hand = quantity_on_hand - ${d.quantity}, updated_at = NOW()
|
|
129
139
|
WHERE entity_id = ${d.entityId} AND warehouse_id = ${warehouseId} AND ${variantClause}
|
|
140
|
+
AND organization_id = ${orgId}
|
|
130
141
|
AND quantity_on_hand >= ${d.quantity}
|
|
131
142
|
RETURNING quantity_on_hand`);
|
|
132
143
|
const rows = Array.isArray(updateResult) ? updateResult : updateResult.rows;
|
|
133
144
|
if (rows.length === 0) {
|
|
134
|
-
await exec.execute(sql `INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
135
|
-
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, 'sale', ${0}, ${referenceType}, ${referenceId}, ${"SKIPPED: insufficient stock for " + d.reason}, ${performedBy})`);
|
|
145
|
+
await exec.execute(sql `INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, organization_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
146
|
+
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, ${orgId}, 'sale', ${0}, ${referenceType}, ${referenceId}, ${"SKIPPED: insufficient stock for " + d.reason}, ${performedBy})`);
|
|
136
147
|
continue;
|
|
137
148
|
}
|
|
138
|
-
await exec.execute(sql `INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
139
|
-
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, 'sale', ${-d.quantity}, ${referenceType}, ${referenceId}, ${d.reason}, ${performedBy})`);
|
|
149
|
+
await exec.execute(sql `INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, organization_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
150
|
+
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, ${orgId}, 'sale', ${-d.quantity}, ${referenceType}, ${referenceId}, ${d.reason}, ${performedBy})`);
|
|
140
151
|
applied++;
|
|
141
152
|
}
|
|
142
153
|
return Ok(applied);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@porulle/plugin-pos-restaurant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -19,16 +19,16 @@
|
|
|
19
19
|
"dependencies": {
|
|
20
20
|
"@hono/zod-openapi": "^1.2.2",
|
|
21
21
|
"hono": "^4.12.5",
|
|
22
|
-
"@porulle/
|
|
23
|
-
"@porulle/
|
|
22
|
+
"@porulle/plugin-pos": "0.9.0",
|
|
23
|
+
"@porulle/core": "0.9.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
26
|
"@types/node": "^24.5.2",
|
|
27
27
|
"eslint": "^9.39.1",
|
|
28
28
|
"typescript": "5.9.2",
|
|
29
29
|
"vitest": "^3.2.4",
|
|
30
|
-
"@porulle/
|
|
31
|
-
"@porulle/
|
|
30
|
+
"@porulle/eslint-config": "0.1.0",
|
|
31
|
+
"@porulle/typescript-config": "0.1.0"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
@@ -175,7 +175,7 @@ export class KDSService {
|
|
|
175
175
|
));
|
|
176
176
|
|
|
177
177
|
const ticketType = existingTickets.length > 0 ? "modified" : "new_order";
|
|
178
|
-
const ticketNumber = await this.generateTicketNumber(station.id);
|
|
178
|
+
const ticketNumber = await this.generateTicketNumber(orgId, station.id);
|
|
179
179
|
|
|
180
180
|
const ticketRows = await this.db
|
|
181
181
|
.insert(kdsTickets)
|
|
@@ -321,7 +321,7 @@ export class KDSService {
|
|
|
321
321
|
|
|
322
322
|
// ─── Helpers ───────────────────────────────────────────────────────
|
|
323
323
|
|
|
324
|
-
private async generateTicketNumber(stationId: string): Promise<string> {
|
|
324
|
+
private async generateTicketNumber(orgId: string, stationId: string): Promise<string> {
|
|
325
325
|
const today = new Date();
|
|
326
326
|
const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate());
|
|
327
327
|
const startOfDayISO = startOfDay.toISOString();
|
|
@@ -330,6 +330,7 @@ export class KDSService {
|
|
|
330
330
|
.select({ count: sql<number>`COUNT(*)`.as("count") })
|
|
331
331
|
.from(kdsTickets)
|
|
332
332
|
.where(and(
|
|
333
|
+
eq(kdsTickets.organizationId, orgId),
|
|
333
334
|
eq(kdsTickets.stationId, stationId),
|
|
334
335
|
sql`${kdsTickets.createdAt} >= ${startOfDayISO}::timestamptz`,
|
|
335
336
|
));
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { eq, and, sql } from "@porulle/core/drizzle";
|
|
19
|
-
import { Ok } from "@porulle/core";
|
|
19
|
+
import { Ok, Err } from "@porulle/core";
|
|
20
20
|
import type { PluginResult } from "@porulle/core";
|
|
21
21
|
import { posRecipes, posRecipeIngredients } from "../schema.js";
|
|
22
22
|
import type { Db } from "../types.js";
|
|
@@ -117,12 +117,34 @@ export class RecipeDeductionService {
|
|
|
117
117
|
referenceType: string,
|
|
118
118
|
referenceId: string,
|
|
119
119
|
performedBy: string,
|
|
120
|
+
orgId?: string,
|
|
120
121
|
): Promise<PluginResult<number>> {
|
|
121
122
|
if (this.inventorySvc) {
|
|
122
123
|
return this.applyViaService(deductions, warehouseId, performedBy);
|
|
123
124
|
}
|
|
124
125
|
|
|
125
|
-
|
|
126
|
+
const resolvedOrgId = orgId ?? await this.resolveOrgIdFromWarehouse(tx, warehouseId);
|
|
127
|
+
if (!resolvedOrgId) return Err("Warehouse not found");
|
|
128
|
+
|
|
129
|
+
return this.applyViaRawSQL(
|
|
130
|
+
tx,
|
|
131
|
+
deductions,
|
|
132
|
+
warehouseId,
|
|
133
|
+
referenceType,
|
|
134
|
+
referenceId,
|
|
135
|
+
performedBy,
|
|
136
|
+
resolvedOrgId,
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private async resolveOrgIdFromWarehouse(tx: Db, warehouseId: string): Promise<string | null> {
|
|
141
|
+
const result = await tx.execute(
|
|
142
|
+
sql`SELECT organization_id FROM warehouses WHERE id = ${warehouseId} LIMIT 1`,
|
|
143
|
+
);
|
|
144
|
+
const rows = Array.isArray(result)
|
|
145
|
+
? result as Array<{ organization_id: string }>
|
|
146
|
+
: (result as { rows: Array<{ organization_id: string }> }).rows;
|
|
147
|
+
return rows[0]?.organization_id ?? null;
|
|
126
148
|
}
|
|
127
149
|
|
|
128
150
|
/**
|
|
@@ -180,6 +202,7 @@ export class RecipeDeductionService {
|
|
|
180
202
|
referenceType: string,
|
|
181
203
|
referenceId: string,
|
|
182
204
|
performedBy: string,
|
|
205
|
+
orgId: string,
|
|
183
206
|
): Promise<PluginResult<number>> {
|
|
184
207
|
let applied = 0;
|
|
185
208
|
// PluginDb (PgDatabase) has .execute() — no cast needed
|
|
@@ -195,6 +218,7 @@ export class RecipeDeductionService {
|
|
|
195
218
|
sql`UPDATE inventory_levels
|
|
196
219
|
SET quantity_on_hand = quantity_on_hand - ${d.quantity}, updated_at = NOW()
|
|
197
220
|
WHERE entity_id = ${d.entityId} AND warehouse_id = ${warehouseId} AND ${variantClause}
|
|
221
|
+
AND organization_id = ${orgId}
|
|
198
222
|
AND quantity_on_hand >= ${d.quantity}
|
|
199
223
|
RETURNING quantity_on_hand`,
|
|
200
224
|
);
|
|
@@ -202,15 +226,15 @@ export class RecipeDeductionService {
|
|
|
202
226
|
const rows = Array.isArray(updateResult) ? updateResult : (updateResult as { rows: unknown[] }).rows;
|
|
203
227
|
if (rows.length === 0) {
|
|
204
228
|
await exec.execute(
|
|
205
|
-
sql`INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
206
|
-
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, 'sale', ${0}, ${referenceType}, ${referenceId}, ${"SKIPPED: insufficient stock for " + d.reason}, ${performedBy})`,
|
|
229
|
+
sql`INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, organization_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
230
|
+
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, ${orgId}, 'sale', ${0}, ${referenceType}, ${referenceId}, ${"SKIPPED: insufficient stock for " + d.reason}, ${performedBy})`,
|
|
207
231
|
);
|
|
208
232
|
continue;
|
|
209
233
|
}
|
|
210
234
|
|
|
211
235
|
await exec.execute(
|
|
212
|
-
sql`INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
213
|
-
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, 'sale', ${-d.quantity}, ${referenceType}, ${referenceId}, ${d.reason}, ${performedBy})`,
|
|
236
|
+
sql`INSERT INTO inventory_movements (entity_id, variant_id, warehouse_id, organization_id, type, quantity, reference_type, reference_id, reason, performed_by)
|
|
237
|
+
VALUES (${d.entityId}, ${d.variantId}, ${warehouseId}, ${orgId}, 'sale', ${-d.quantity}, ${referenceType}, ${referenceId}, ${d.reason}, ${performedBy})`,
|
|
214
238
|
);
|
|
215
239
|
|
|
216
240
|
applied++;
|