@teincfood/core 0.7.8 → 0.8.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 +26 -21
- package/dist/adapters/auth.d.ts +10 -0
- package/dist/adapters/auth.js +43 -0
- package/dist/adapters/devices.d.ts +3 -1
- package/dist/adapters/devices.js +187 -3
- package/dist/adapters/filesystem.d.ts +4 -0
- package/dist/adapters/kv.d.ts +10 -0
- package/dist/adapters/kv.js +23 -7
- package/dist/adapters/local-node.d.ts +4 -6
- package/dist/adapters/local-node.js +9 -32
- package/dist/adapters/notify.d.ts +12 -0
- package/dist/adapters/notify.js +33 -0
- package/dist/adapters/query-keys.d.ts +4 -0
- package/dist/adapters/query-keys.js +4 -0
- package/dist/adapters/services.d.ts +99 -15
- package/dist/adapters/services.js +244 -4
- package/dist/auth/session.d.ts +66 -0
- package/dist/auth/session.js +172 -0
- package/dist/catalog/hooks.d.ts +10 -0
- package/dist/catalog/hooks.js +82 -11
- package/dist/db/migrations.js +274 -0
- package/dist/db/operations.d.ts +53 -2
- package/dist/db/operations.js +177 -8
- package/dist/domain/business/index.d.ts +5 -0
- package/dist/domain/business/index.js +5 -0
- package/dist/domain/orders/lifecycle.d.ts +6 -0
- package/dist/domain/orders/lifecycle.js +6 -0
- package/dist/domain/orders/service.d.ts +24 -0
- package/dist/domain/orders/service.js +242 -0
- package/dist/domain/payments/index.d.ts +6 -0
- package/dist/domain/payments/index.js +5 -0
- package/dist/domain/reference/index.d.ts +8 -0
- package/dist/domain/reference/index.js +8 -0
- package/dist/e2e/harness.d.ts +1 -0
- package/dist/e2e/harness.js +61 -0
- package/dist/hooks/analytics.hooks.d.ts +25 -0
- package/dist/hooks/analytics.hooks.js +214 -0
- package/dist/hooks/business-hours.hooks.d.ts +8 -0
- package/dist/hooks/business-hours.hooks.js +45 -0
- package/dist/hooks/dashboard.hooks.d.ts +30 -0
- package/dist/hooks/dashboard.hooks.js +132 -0
- package/dist/hooks/delivery.hooks.d.ts +225 -0
- package/dist/hooks/delivery.hooks.js +80 -0
- package/dist/hooks/fleet.hooks.d.ts +37 -0
- package/dist/hooks/fleet.hooks.js +229 -0
- package/dist/hooks/image.hooks.d.ts +20 -0
- package/dist/hooks/image.hooks.js +62 -0
- package/dist/hooks/kiosk.hooks.d.ts +22 -5
- package/dist/hooks/kiosk.hooks.js +74 -5
- package/dist/hooks/members.hooks.d.ts +48 -0
- package/dist/hooks/members.hooks.js +302 -0
- package/dist/hooks/offline-orders.hooks.d.ts +9 -0
- package/dist/hooks/offline-orders.hooks.js +51 -0
- package/dist/hooks/orders.hooks.d.ts +19 -39
- package/dist/hooks/orders.hooks.js +126 -321
- package/dist/hooks/places.hooks.d.ts +21 -0
- package/dist/hooks/places.hooks.js +112 -0
- package/dist/hooks/realtime-stubs.d.ts +28 -0
- package/dist/hooks/realtime-stubs.js +85 -0
- package/dist/hooks/rider.hooks.d.ts +470 -0
- package/dist/hooks/rider.hooks.js +373 -0
- package/dist/hooks/use-infinite-list.d.ts +243 -0
- package/dist/hooks/use-infinite-list.js +58 -0
- package/dist/hooks/wallet.hooks.d.ts +219 -0
- package/dist/hooks/wallet.hooks.js +56 -0
- package/dist/image-cache/service.d.ts +8 -0
- package/dist/image-cache/service.js +15 -3
- package/dist/index.d.ts +60 -5
- package/dist/index.js +78 -5
- package/dist/local-node/adapters.d.ts +87 -0
- package/dist/local-node/adapters.js +4 -0
- package/dist/local-node/index.d.ts +5 -0
- package/dist/local-node/index.js +5 -0
- package/dist/local-node/security.d.ts +25 -0
- package/dist/local-node/security.js +149 -0
- package/dist/local-node/service.d.ts +83 -0
- package/dist/local-node/service.js +706 -0
- package/dist/local-node/store.d.ts +50 -0
- package/dist/local-node/store.js +284 -0
- package/dist/local-node/types.d.ts +92 -0
- package/dist/local-node/types.js +4 -0
- package/dist/printer/receipt.d.ts +4 -0
- package/dist/printer/receipt.js +75 -0
- package/dist/printer/service.js +1 -1
- package/dist/providers/query-client.d.ts +13 -0
- package/dist/providers/query-client.js +58 -0
- package/dist/realtime/events.service.d.ts +18 -0
- package/dist/realtime/events.service.js +76 -0
- package/dist/realtime/order-events.service.d.ts +6 -0
- package/dist/realtime/order-events.service.js +17 -0
- package/dist/realtime/phoenix-socket.d.ts +55 -0
- package/dist/realtime/phoenix-socket.js +269 -0
- package/dist/realtime/useEventsConnection.d.ts +1 -0
- package/dist/realtime/useEventsConnection.js +50 -0
- package/dist/reference/api.d.ts +2 -0
- package/dist/reference/api.js +2 -0
- package/dist/reference/capabilities.d.ts +12 -7
- package/dist/reference/capabilities.js +28 -13
- package/dist/reference/hooks.d.ts +34 -0
- package/dist/reference/hooks.js +124 -0
- package/dist/reference/menu-apply.d.ts +29 -0
- package/dist/reference/menu-apply.js +289 -0
- package/dist/reference/menu-record.d.ts +69 -0
- package/dist/reference/menu-record.js +163 -0
- package/dist/reference/operations.d.ts +3 -0
- package/dist/reference/operations.js +35 -1
- package/dist/reference/service.js +36 -9
- package/dist/reference/types.d.ts +1 -1
- package/dist/repositories/order.repository.d.ts +8 -0
- package/dist/repositories/order.repository.js +24 -2
- package/dist/repositories/pos.repository.d.ts +4 -0
- package/dist/repositories/pos.repository.js +4 -0
- package/dist/sync/backoff.d.ts +21 -0
- package/dist/sync/backoff.js +42 -0
- package/dist/sync/command-builder.js +10 -2
- package/dist/sync/command-queue.service.d.ts +12 -0
- package/dist/sync/command-queue.service.js +115 -18
- package/dist/sync/device-lease.service.d.ts +3 -3
- package/dist/sync/device-lease.service.js +80 -20
- package/dist/sync/engine.d.ts +3 -1
- package/dist/sync/engine.js +44 -4
- package/dist/sync/full-sync.d.ts +6 -0
- package/dist/sync/full-sync.js +72 -0
- package/dist/sync/health-probe.d.ts +24 -0
- package/dist/sync/health-probe.js +138 -0
- package/dist/sync/offline-capable.js +25 -0
- package/dist/sync/online-gates.d.ts +17 -0
- package/dist/sync/online-gates.js +55 -0
- package/dist/sync/order-number.d.ts +4 -4
- package/dist/sync/order-number.js +33 -13
- package/dist/sync/order-snapshots.service.d.ts +1 -0
- package/dist/sync/order-snapshots.service.js +47 -2
- package/dist/sync/pending-orders.service.d.ts +18 -0
- package/dist/sync/pending-orders.service.js +148 -3
- package/dist/sync/reconciliation/index.d.ts +5 -0
- package/dist/sync/reconciliation/index.js +5 -0
- package/dist/sync/snapshots/entity.d.ts +6 -0
- package/dist/sync/snapshots/entity.js +6 -0
- package/dist/sync/sync-jobs.service.d.ts +18 -0
- package/dist/sync/sync-jobs.service.js +539 -0
- package/dist/sync/transition-endpoints.d.ts +11 -0
- package/dist/sync/transition-endpoints.js +30 -0
- package/dist/sync/transport-local-node.js +3 -2
- package/dist/sync/transport-rest.d.ts +7 -0
- package/dist/sync/transport-rest.js +90 -25
- package/dist/sync/use-sync-reconciliation.js +58 -16
- package/dist/test-helpers/memory-driver.js +182 -4
- package/dist/test-helpers/seed-hours.d.ts +1 -0
- package/dist/test-helpers/seed-hours.js +23 -0
- package/dist/types/api/business.api.d.ts +42 -0
- package/dist/types/api/menu.api.d.ts +15 -0
- package/dist/types/api/seller.api.d.ts +20 -0
- package/dist/types/places.d.ts +19 -0
- package/dist/types/places.js +2 -0
- package/dist/types/rider.d.ts +67 -0
- package/dist/types/rider.js +2 -0
- package/dist/utils/attempt-stats.d.ts +23 -0
- package/dist/utils/attempt-stats.js +61 -0
- package/dist/utils/business-hours.d.ts +37 -0
- package/dist/utils/business-hours.js +96 -0
- package/dist/utils/debug-log.d.ts +23 -0
- package/dist/utils/debug-log.js +153 -0
- package/dist/utils/order-filter.d.ts +1 -1
- package/dist/utils/order-filter.js +1 -1
- package/dist/utils/order-transitions.d.ts +30 -0
- package/dist/utils/order-transitions.js +185 -0
- package/dist/utils/sync-logger.js +5 -4
- package/dist/validation/offline.d.ts +46 -0
- package/dist/validation/offline.js +79 -0
- package/package.json +6 -1
- package/dist/pricing/engine.test.d.ts +0 -1
- package/dist/pricing/engine.test.js +0 -142
- package/dist/reference/lww.test.d.ts +0 -1
- package/dist/reference/lww.test.js +0 -23
- package/dist/reference/operations.test.d.ts +0 -1
- package/dist/reference/operations.test.js +0 -66
- package/dist/standalone.integration.test.d.ts +0 -1
- package/dist/standalone.integration.test.js +0 -235
- package/dist/sync/device-lease.test.d.ts +0 -1
- package/dist/sync/device-lease.test.js +0 -38
- package/dist/sync/offline-integration.test.d.ts +0 -1
- package/dist/sync/offline-integration.test.js +0 -52
- package/dist/sync/order-number.test.d.ts +0 -1
- package/dist/sync/order-number.test.js +0 -51
- package/dist/sync/perms.test.d.ts +0 -1
- package/dist/sync/perms.test.js +0 -15
- package/dist/utils/currency.test.d.ts +0 -1
- package/dist/utils/currency.test.js +0 -17
- package/dist/utils/hlc.test.d.ts +0 -1
- package/dist/utils/hlc.test.js +0 -29
- package/dist/utils/phone.test.d.ts +0 -1
- package/dist/utils/phone.test.js +0 -19
- package/dist/utils/responsive.d.ts +0 -13
- package/dist/utils/responsive.js +0 -18
- package/dist/utils/uuid.test.d.ts +0 -1
- package/dist/utils/uuid.test.js +0 -13
- package/dist/utils/validation.test.d.ts +0 -1
- package/dist/utils/validation.test.js +0 -23
package/dist/catalog/hooks.js
CHANGED
|
@@ -7,13 +7,13 @@ import { useMemo } from "react";
|
|
|
7
7
|
import { useInfiniteQuery, useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
8
8
|
import { useSyncExternalStore } from "react";
|
|
9
9
|
import { getActiveBusiness, subscribeBusiness } from "../adapters/business";
|
|
10
|
-
import { getConnectivityState, subscribeConnectivity } from "../adapters/connectivity";
|
|
11
10
|
import { menuService } from "../adapters/services";
|
|
12
11
|
import { getReferenceData } from "../reference/operations";
|
|
13
12
|
import { openSyncDatabase } from "../db/connection";
|
|
14
13
|
import { referenceSyncService } from "../reference/service";
|
|
15
14
|
import { emitReferenceChanged } from "../reference/events";
|
|
16
15
|
import { runOfflineCapableMutation } from "../sync/offline-capable";
|
|
16
|
+
import { useIsEffectivelyOnline } from "../hooks/use-infinite-list";
|
|
17
17
|
export const CATALOG_KEYS = {
|
|
18
18
|
all: ["menu"],
|
|
19
19
|
items: (businessId) => [...CATALOG_KEYS.all, "items", businessId],
|
|
@@ -23,13 +23,9 @@ function useBusinessId() {
|
|
|
23
23
|
const id = useSyncExternalStore(subscribeBusiness, () => getActiveBusiness()?.id ?? null, () => null);
|
|
24
24
|
return id ?? undefined;
|
|
25
25
|
}
|
|
26
|
-
function useIsOnline() {
|
|
27
|
-
const s = useSyncExternalStore(subscribeConnectivity, getConnectivityState, getConnectivityState);
|
|
28
|
-
return s.isOnline;
|
|
29
|
-
}
|
|
30
26
|
export function useMenuItemsQuery(filters, enabled = true) {
|
|
31
27
|
const businessId = useBusinessId();
|
|
32
|
-
const isOnline =
|
|
28
|
+
const isOnline = useIsEffectivelyOnline();
|
|
33
29
|
// Cached reference query — synchronous from SQLite via query
|
|
34
30
|
const cachedQuery = useQuery({
|
|
35
31
|
queryKey: [...CATALOG_KEYS.items(businessId), "cached", filters],
|
|
@@ -97,7 +93,7 @@ export function useMenuItemsQuery(filters, enabled = true) {
|
|
|
97
93
|
}
|
|
98
94
|
export function useMenuCategoriesQuery() {
|
|
99
95
|
const businessId = useBusinessId();
|
|
100
|
-
const isOnline =
|
|
96
|
+
const isOnline = useIsEffectivelyOnline();
|
|
101
97
|
const cachedQuery = useQuery({
|
|
102
98
|
queryKey: [...CATALOG_KEYS.categories(businessId), "cached"],
|
|
103
99
|
queryFn: async () => {
|
|
@@ -118,15 +114,28 @@ export function useMenuCategoriesQuery() {
|
|
|
118
114
|
retry: false,
|
|
119
115
|
networkMode: "offlineFirst",
|
|
120
116
|
});
|
|
121
|
-
const
|
|
122
|
-
|
|
117
|
+
const usingNetwork = isOnline && networkQuery.data;
|
|
118
|
+
const data = usingNetwork ? networkQuery.data : cachedQuery.data;
|
|
119
|
+
const hasCache = cachedQuery.data != null;
|
|
120
|
+
return {
|
|
121
|
+
...networkQuery,
|
|
122
|
+
data,
|
|
123
|
+
isPending: cachedQuery.isPending && !networkQuery.data,
|
|
124
|
+
isError: hasCache ? false : networkQuery.isError,
|
|
125
|
+
error: hasCache ? null : networkQuery.error,
|
|
126
|
+
};
|
|
123
127
|
}
|
|
124
128
|
// Mutations — offline-capable
|
|
125
129
|
export function useCreateMenuItemMutation() {
|
|
126
130
|
const businessId = useBusinessId();
|
|
127
131
|
const qc = useQueryClient();
|
|
128
132
|
return useMutation({
|
|
129
|
-
mutationFn: (payload) =>
|
|
133
|
+
mutationFn: (payload) => {
|
|
134
|
+
// cacheRecord is local-cache only — never on the wire.
|
|
135
|
+
const { cacheRecord: _omit, ...wire } = payload;
|
|
136
|
+
void _omit;
|
|
137
|
+
return runOfflineCapableMutation({ run: () => menuService.createMenuItem(wire), commandType: "menu:create_item", businessId: businessId, entityId: payload.id, payload });
|
|
138
|
+
},
|
|
130
139
|
onSuccess: () => {
|
|
131
140
|
qc.invalidateQueries({ queryKey: CATALOG_KEYS.items(businessId) });
|
|
132
141
|
if (businessId)
|
|
@@ -138,7 +147,11 @@ export function useUpdateMenuItemMutation() {
|
|
|
138
147
|
const businessId = useBusinessId();
|
|
139
148
|
const qc = useQueryClient();
|
|
140
149
|
return useMutation({
|
|
141
|
-
mutationFn: ({ id, payload }) =>
|
|
150
|
+
mutationFn: ({ id, payload }) => {
|
|
151
|
+
const { cacheRecord: _omit, ...wire } = payload;
|
|
152
|
+
void _omit;
|
|
153
|
+
return runOfflineCapableMutation({ run: () => menuService.updateMenuItem(id, wire), commandType: "menu:update_item", businessId: businessId, entityId: id, payload });
|
|
154
|
+
},
|
|
142
155
|
onSuccess: () => {
|
|
143
156
|
qc.invalidateQueries({ queryKey: CATALOG_KEYS.items(businessId) });
|
|
144
157
|
if (businessId)
|
|
@@ -158,3 +171,61 @@ export function useDeleteMenuItemMutation() {
|
|
|
158
171
|
},
|
|
159
172
|
});
|
|
160
173
|
}
|
|
174
|
+
export function useToggleItemAvailabilityMutation() {
|
|
175
|
+
const businessId = useBusinessId();
|
|
176
|
+
const qc = useQueryClient();
|
|
177
|
+
return useMutation({
|
|
178
|
+
// Availability is an update_item with an availability patch — same engine
|
|
179
|
+
// route (cloud → LAN → queue), same cache convergence.
|
|
180
|
+
mutationFn: ({ id, available }) => runOfflineCapableMutation({ run: () => menuService.updateMenuItem(id, { available }), commandType: "menu:update_item", businessId: businessId, entityId: id, payload: { id, available } }),
|
|
181
|
+
onSuccess: () => {
|
|
182
|
+
qc.invalidateQueries({ queryKey: CATALOG_KEYS.items(businessId) });
|
|
183
|
+
if (businessId)
|
|
184
|
+
emitReferenceChanged(businessId, ["menu:items"]);
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
export function useCreateMenuCategoryMutation() {
|
|
189
|
+
const businessId = useBusinessId();
|
|
190
|
+
const qc = useQueryClient();
|
|
191
|
+
return useMutation({
|
|
192
|
+
mutationFn: (payload) => {
|
|
193
|
+
const { cacheRecord: _omit, ...wire } = payload;
|
|
194
|
+
void _omit;
|
|
195
|
+
return runOfflineCapableMutation({ run: () => menuService.createCategory(businessId, wire), commandType: "menu:create_category", businessId: businessId, entityId: payload.id, payload });
|
|
196
|
+
},
|
|
197
|
+
onSuccess: () => {
|
|
198
|
+
qc.invalidateQueries({ queryKey: CATALOG_KEYS.categories(businessId) });
|
|
199
|
+
if (businessId)
|
|
200
|
+
emitReferenceChanged(businessId, ["menu:categories"]);
|
|
201
|
+
},
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
export function useUpdateMenuCategoryMutation() {
|
|
205
|
+
const businessId = useBusinessId();
|
|
206
|
+
const qc = useQueryClient();
|
|
207
|
+
return useMutation({
|
|
208
|
+
mutationFn: ({ id, payload }) => {
|
|
209
|
+
const { cacheRecord: _omit, ...wire } = payload;
|
|
210
|
+
void _omit;
|
|
211
|
+
return runOfflineCapableMutation({ run: () => menuService.updateCategory(id, wire), commandType: "menu:update_category", businessId: businessId, entityId: id, payload: { ...payload, id } });
|
|
212
|
+
},
|
|
213
|
+
onSuccess: () => {
|
|
214
|
+
qc.invalidateQueries({ queryKey: CATALOG_KEYS.categories(businessId) });
|
|
215
|
+
if (businessId)
|
|
216
|
+
emitReferenceChanged(businessId, ["menu:categories"]);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
export function useDeleteMenuCategoryMutation() {
|
|
221
|
+
const businessId = useBusinessId();
|
|
222
|
+
const qc = useQueryClient();
|
|
223
|
+
return useMutation({
|
|
224
|
+
mutationFn: (id) => runOfflineCapableMutation({ run: () => menuService.deleteCategory(id), commandType: "menu:delete_category", businessId: businessId, entityId: id, payload: { id } }),
|
|
225
|
+
onSuccess: () => {
|
|
226
|
+
qc.invalidateQueries({ queryKey: CATALOG_KEYS.categories(businessId) });
|
|
227
|
+
if (businessId)
|
|
228
|
+
emitReferenceChanged(businessId, ["menu:categories"]);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
package/dist/db/migrations.js
CHANGED
|
@@ -134,4 +134,278 @@ export const MIGRATIONS = [
|
|
|
134
134
|
catch { }
|
|
135
135
|
},
|
|
136
136
|
},
|
|
137
|
+
{
|
|
138
|
+
version: 5,
|
|
139
|
+
up: async (db) => {
|
|
140
|
+
// Real orders table — primary DB like backend Orders, device-partitioned by UUID + DD-RRR-CCC
|
|
141
|
+
await db.execAsync(`
|
|
142
|
+
CREATE TABLE IF NOT EXISTS orders (
|
|
143
|
+
id TEXT PRIMARY KEY,
|
|
144
|
+
business_id TEXT NOT NULL,
|
|
145
|
+
order_number TEXT NOT NULL,
|
|
146
|
+
callout_number INTEGER,
|
|
147
|
+
status TEXT NOT NULL,
|
|
148
|
+
order_type TEXT,
|
|
149
|
+
is_pos INTEGER NOT NULL DEFAULT 0,
|
|
150
|
+
items TEXT NOT NULL,
|
|
151
|
+
subtotal_minor INTEGER NOT NULL DEFAULT 0,
|
|
152
|
+
total_minor INTEGER NOT NULL DEFAULT 0,
|
|
153
|
+
tax_minor INTEGER NOT NULL DEFAULT 0,
|
|
154
|
+
tax_lines TEXT,
|
|
155
|
+
payment TEXT,
|
|
156
|
+
customer_name TEXT,
|
|
157
|
+
customer_phone TEXT,
|
|
158
|
+
pos_payment_method TEXT,
|
|
159
|
+
delivery_id TEXT,
|
|
160
|
+
business TEXT,
|
|
161
|
+
user TEXT,
|
|
162
|
+
inserted_at TEXT NOT NULL,
|
|
163
|
+
hlc TEXT,
|
|
164
|
+
deleted_at TEXT
|
|
165
|
+
);
|
|
166
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_orders_order_number_business ON orders(business_id, order_number);
|
|
167
|
+
CREATE INDEX IF NOT EXISTS idx_orders_business_status ON orders(business_id, status);
|
|
168
|
+
CREATE INDEX IF NOT EXISTS idx_orders_business_ispos ON orders(business_id, is_pos);
|
|
169
|
+
CREATE INDEX IF NOT EXISTS idx_orders_inserted ON orders(business_id, inserted_at DESC);
|
|
170
|
+
CREATE INDEX IF NOT EXISTS idx_orders_hlc ON orders(hlc);
|
|
171
|
+
`);
|
|
172
|
+
// Backfill from existing entity_snapshots type='order' if any (best-effort, ignore failures on fresh DB)
|
|
173
|
+
try {
|
|
174
|
+
await db.execAsync(`
|
|
175
|
+
INSERT OR IGNORE INTO orders (id, business_id, order_number, callout_number, status, order_type, is_pos, items, subtotal_minor, total_minor, tax_minor, tax_lines, payment, customer_name, customer_phone, pos_payment_method, delivery_id, business, user, inserted_at, hlc)
|
|
176
|
+
SELECT
|
|
177
|
+
json_extract(payload, '$.id') as id,
|
|
178
|
+
json_extract(payload, '$.business_id') as business_id,
|
|
179
|
+
json_extract(payload, '$.order_number') as order_number,
|
|
180
|
+
json_extract(payload, '$.callout_number') as callout_number,
|
|
181
|
+
json_extract(payload, '$.status') as status,
|
|
182
|
+
json_extract(payload, '$.order_type') as order_type,
|
|
183
|
+
CASE WHEN json_extract(payload, '$.is_pos') THEN 1 ELSE 0 END as is_pos,
|
|
184
|
+
json_extract(payload, '$.items') as items,
|
|
185
|
+
json_extract(payload, '$.subtotal_minor') as subtotal_minor,
|
|
186
|
+
json_extract(payload, '$.total_minor') as total_minor,
|
|
187
|
+
json_extract(payload, '$.tax_minor') as tax_minor,
|
|
188
|
+
json_extract(payload, '$.tax_lines') as tax_lines,
|
|
189
|
+
json_extract(payload, '$.payment') as payment,
|
|
190
|
+
json_extract(payload, '$.customer_name') as customer_name,
|
|
191
|
+
json_extract(payload, '$.customer_phone') as customer_phone,
|
|
192
|
+
json_extract(payload, '$.pos_payment_method') as pos_payment_method,
|
|
193
|
+
json_extract(payload, '$.delivery_id') as delivery_id,
|
|
194
|
+
json_extract(payload, '$.business') as business,
|
|
195
|
+
json_extract(payload, '$.user') as user,
|
|
196
|
+
json_extract(payload, '$.inserted_at') as inserted_at,
|
|
197
|
+
json_extract(payload, '$.hlc') as hlc
|
|
198
|
+
FROM entity_snapshots WHERE entity_type='order' AND json_extract(payload, '$.id') IS NOT NULL;
|
|
199
|
+
`);
|
|
200
|
+
}
|
|
201
|
+
catch { }
|
|
202
|
+
// Sync outbox for background backend sync — secondary, not UI-blocking
|
|
203
|
+
await db.execAsync(`
|
|
204
|
+
CREATE TABLE IF NOT EXISTS sync_jobs (
|
|
205
|
+
id TEXT PRIMARY KEY,
|
|
206
|
+
business_id TEXT NOT NULL,
|
|
207
|
+
command_type TEXT NOT NULL,
|
|
208
|
+
entity_id TEXT,
|
|
209
|
+
payload TEXT NOT NULL,
|
|
210
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
211
|
+
attempts INTEGER NOT NULL DEFAULT 0,
|
|
212
|
+
last_error TEXT,
|
|
213
|
+
created_at TEXT NOT NULL,
|
|
214
|
+
updated_at TEXT NOT NULL
|
|
215
|
+
);
|
|
216
|
+
CREATE INDEX IF NOT EXISTS idx_sync_jobs_business ON sync_jobs(business_id, status);
|
|
217
|
+
`);
|
|
218
|
+
},
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
version: 6,
|
|
222
|
+
up: async (db) => {
|
|
223
|
+
// v5 backfilled only entity_type='order' which never exists — real data is
|
|
224
|
+
// entity_type='seller_orders' with {data: SellerOrder[]}. Backfill those via json_each.
|
|
225
|
+
// idempotent INSERT OR IGNORE; also handles legacy 'order' type with COALESCE timestamps.
|
|
226
|
+
try {
|
|
227
|
+
await db.execAsync(`
|
|
228
|
+
INSERT OR IGNORE INTO orders (id, business_id, order_number, callout_number, status, order_type, is_pos, items, subtotal_minor, total_minor, tax_minor, tax_lines, payment, customer_name, customer_phone, pos_payment_method, delivery_id, business, user, inserted_at, hlc)
|
|
229
|
+
SELECT
|
|
230
|
+
json_extract(value, '$.id') as id,
|
|
231
|
+
COALESCE(json_extract(value, '$.business_id'), json_extract(value, '$.business.id'), '') as business_id,
|
|
232
|
+
json_extract(value, '$.order_number') as order_number,
|
|
233
|
+
json_extract(value, '$.callout_number') as callout_number,
|
|
234
|
+
json_extract(value, '$.status') as status,
|
|
235
|
+
json_extract(value, '$.order_type') as order_type,
|
|
236
|
+
CASE WHEN json_extract(value, '$.is_pos') THEN 1 ELSE 0 END as is_pos,
|
|
237
|
+
COALESCE(json_extract(value, '$.items'), '[]') as items,
|
|
238
|
+
COALESCE(json_extract(value, '$.subtotal_minor'), 0) as subtotal_minor,
|
|
239
|
+
COALESCE(json_extract(value, '$.total_minor'), 0) as total_minor,
|
|
240
|
+
COALESCE(json_extract(value, '$.tax_minor'), 0) as tax_minor,
|
|
241
|
+
json_extract(value, '$.tax_lines') as tax_lines,
|
|
242
|
+
json_extract(value, '$.payment') as payment,
|
|
243
|
+
json_extract(value, '$.customer_name') as customer_name,
|
|
244
|
+
json_extract(value, '$.customer_phone') as customer_phone,
|
|
245
|
+
json_extract(value, '$.pos_payment_method') as pos_payment_method,
|
|
246
|
+
json_extract(value, '$.delivery_id') as delivery_id,
|
|
247
|
+
json_extract(value, '$.business') as business,
|
|
248
|
+
json_extract(value, '$.user') as user,
|
|
249
|
+
COALESCE(json_extract(value, '$.inserted_at'), json_extract(value, '$.created_at'), last_event_timestamp, datetime('now')) as inserted_at,
|
|
250
|
+
COALESCE(json_extract(value, '$.hlc'), last_event_timestamp, datetime('now')) as hlc
|
|
251
|
+
FROM entity_snapshots, json_each(json_extract(payload, '$.data'))
|
|
252
|
+
WHERE entity_type='seller_orders' AND json_extract(value, '$.id') IS NOT NULL AND json_extract(value, '$.order_number') IS NOT NULL;
|
|
253
|
+
`);
|
|
254
|
+
}
|
|
255
|
+
catch { }
|
|
256
|
+
try {
|
|
257
|
+
await db.execAsync(`
|
|
258
|
+
INSERT OR IGNORE INTO orders (id, business_id, order_number, callout_number, status, order_type, is_pos, items, subtotal_minor, total_minor, tax_minor, tax_lines, payment, customer_name, customer_phone, pos_payment_method, delivery_id, business, user, inserted_at, hlc)
|
|
259
|
+
SELECT
|
|
260
|
+
json_extract(payload, '$.id') as id,
|
|
261
|
+
COALESCE(json_extract(payload, '$.business_id'), '') as business_id,
|
|
262
|
+
json_extract(payload, '$.order_number') as order_number,
|
|
263
|
+
json_extract(payload, '$.callout_number') as callout_number,
|
|
264
|
+
json_extract(payload, '$.status') as status,
|
|
265
|
+
json_extract(payload, '$.order_type') as order_type,
|
|
266
|
+
CASE WHEN json_extract(payload, '$.is_pos') THEN 1 ELSE 0 END as is_pos,
|
|
267
|
+
COALESCE(json_extract(payload, '$.items'), '[]') as items,
|
|
268
|
+
COALESCE(json_extract(payload, '$.subtotal_minor'), 0) as subtotal_minor,
|
|
269
|
+
COALESCE(json_extract(payload, '$.total_minor'), 0) as total_minor,
|
|
270
|
+
COALESCE(json_extract(payload, '$.tax_minor'), 0) as tax_minor,
|
|
271
|
+
json_extract(payload, '$.tax_lines') as tax_lines,
|
|
272
|
+
json_extract(payload, '$.payment') as payment,
|
|
273
|
+
json_extract(payload, '$.customer_name') as customer_name,
|
|
274
|
+
json_extract(payload, '$.customer_phone') as customer_phone,
|
|
275
|
+
json_extract(payload, '$.pos_payment_method') as pos_payment_method,
|
|
276
|
+
json_extract(payload, '$.delivery_id') as delivery_id,
|
|
277
|
+
json_extract(payload, '$.business') as business,
|
|
278
|
+
json_extract(payload, '$.user') as user,
|
|
279
|
+
COALESCE(json_extract(payload, '$.inserted_at'), last_event_timestamp, datetime('now')) as inserted_at,
|
|
280
|
+
COALESCE(json_extract(payload, '$.hlc'), last_event_timestamp, datetime('now')) as hlc
|
|
281
|
+
FROM entity_snapshots WHERE entity_type='order' AND json_extract(payload, '$.id') IS NOT NULL;
|
|
282
|
+
`);
|
|
283
|
+
}
|
|
284
|
+
catch { }
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
version: 7,
|
|
289
|
+
up: async (db) => {
|
|
290
|
+
// Fix [object Object] order_number bug from 0.7.13 domain/service toString() on object
|
|
291
|
+
try {
|
|
292
|
+
await db.execAsync(`DELETE FROM orders WHERE order_number = '[object Object]';`);
|
|
293
|
+
}
|
|
294
|
+
catch { }
|
|
295
|
+
try {
|
|
296
|
+
await db.execAsync(`DELETE FROM sync_jobs WHERE payload LIKE '%[object Object]%';`);
|
|
297
|
+
}
|
|
298
|
+
catch { }
|
|
299
|
+
try {
|
|
300
|
+
await db.execAsync(`DELETE FROM entity_snapshots WHERE payload LIKE '%[object Object]%';`);
|
|
301
|
+
}
|
|
302
|
+
catch { }
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
version: 8,
|
|
307
|
+
up: async (db) => {
|
|
308
|
+
// Full drop legacy — new DB, no fallback (confirmed). Keep only orders, sync_jobs, reference_data, migrations.
|
|
309
|
+
// Drops commands, events, entity_snapshots (replaced by orders primary).
|
|
310
|
+
try {
|
|
311
|
+
await db.execAsync(`DROP TABLE IF EXISTS commands;`);
|
|
312
|
+
}
|
|
313
|
+
catch { }
|
|
314
|
+
try {
|
|
315
|
+
await db.execAsync(`DROP TABLE IF EXISTS events;`);
|
|
316
|
+
}
|
|
317
|
+
catch { }
|
|
318
|
+
// entity_snapshots kept for reference_data fallback? No — new DB no old stuff, drop all snapshots
|
|
319
|
+
try {
|
|
320
|
+
await db.execAsync(`DROP TABLE IF EXISTS entity_snapshots;`);
|
|
321
|
+
}
|
|
322
|
+
catch { }
|
|
323
|
+
// Recreate minimal empty entity_snapshots if later code expects it (reference fallback disabled for orders)
|
|
324
|
+
try {
|
|
325
|
+
await db.execAsync(`
|
|
326
|
+
CREATE TABLE IF NOT EXISTS entity_snapshots (
|
|
327
|
+
entity_type TEXT NOT NULL,
|
|
328
|
+
entity_id TEXT NOT NULL,
|
|
329
|
+
business_id TEXT,
|
|
330
|
+
last_event_id TEXT NOT NULL,
|
|
331
|
+
last_event_timestamp TEXT NOT NULL,
|
|
332
|
+
payload TEXT NOT NULL,
|
|
333
|
+
PRIMARY KEY (entity_type, entity_id)
|
|
334
|
+
);
|
|
335
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_entity_id ON entity_snapshots(entity_id);
|
|
336
|
+
CREATE INDEX IF NOT EXISTS idx_snapshots_business ON entity_snapshots(business_id, entity_type);
|
|
337
|
+
`);
|
|
338
|
+
}
|
|
339
|
+
catch { }
|
|
340
|
+
// Clean any stale TFL remnants that survived
|
|
341
|
+
try {
|
|
342
|
+
await db.execAsync(`DELETE FROM sync_jobs WHERE payload LIKE '%TFL-%';`);
|
|
343
|
+
}
|
|
344
|
+
catch { }
|
|
345
|
+
},
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
version: 9,
|
|
349
|
+
up: async (db) => {
|
|
350
|
+
// v8 dropped commands/events as "legacy" while the engine outbox
|
|
351
|
+
// (commandQueueService.enqueue) and the event journal (insertEvent,
|
|
352
|
+
// hydrate, replay) still depend on them. On any DB that ran v8, every
|
|
353
|
+
// queue/journal write throws "no such table", breaking offline queue
|
|
354
|
+
// on all transports. Recreate exactly (IF NOT EXISTS: healthy DBs no-op).
|
|
355
|
+
await db.execAsync(`
|
|
356
|
+
CREATE TABLE IF NOT EXISTS commands (
|
|
357
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
358
|
+
command_id TEXT NOT NULL UNIQUE,
|
|
359
|
+
command_type TEXT NOT NULL,
|
|
360
|
+
command_version TEXT NOT NULL,
|
|
361
|
+
business_id TEXT NOT NULL,
|
|
362
|
+
entity_id TEXT,
|
|
363
|
+
user_id TEXT NOT NULL,
|
|
364
|
+
device_id TEXT NOT NULL,
|
|
365
|
+
payload TEXT NOT NULL,
|
|
366
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
367
|
+
error TEXT,
|
|
368
|
+
retry_count INTEGER NOT NULL DEFAULT 0,
|
|
369
|
+
last_attempted_at TEXT,
|
|
370
|
+
created_at TEXT NOT NULL,
|
|
371
|
+
updated_at TEXT NOT NULL
|
|
372
|
+
);
|
|
373
|
+
|
|
374
|
+
CREATE INDEX IF NOT EXISTS idx_commands_status ON commands(status);
|
|
375
|
+
CREATE INDEX IF NOT EXISTS idx_commands_business ON commands(business_id);
|
|
376
|
+
CREATE INDEX IF NOT EXISTS idx_commands_entity ON commands(entity_id);
|
|
377
|
+
CREATE INDEX IF NOT EXISTS idx_commands_created_at ON commands(created_at);
|
|
378
|
+
`);
|
|
379
|
+
await db.execAsync(`
|
|
380
|
+
CREATE TABLE IF NOT EXISTS events (
|
|
381
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
382
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
383
|
+
event_type TEXT NOT NULL,
|
|
384
|
+
event_version TEXT NOT NULL,
|
|
385
|
+
entity_id TEXT NOT NULL,
|
|
386
|
+
user_id TEXT NOT NULL,
|
|
387
|
+
device_id TEXT NOT NULL,
|
|
388
|
+
payload TEXT NOT NULL,
|
|
389
|
+
origin TEXT NOT NULL,
|
|
390
|
+
synced_to_cloud INTEGER NOT NULL DEFAULT 0,
|
|
391
|
+
timestamp TEXT NOT NULL,
|
|
392
|
+
persisted_at TEXT NOT NULL
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
|
|
396
|
+
CREATE INDEX IF NOT EXISTS idx_events_entity ON events(entity_id);
|
|
397
|
+
CREATE INDEX IF NOT EXISTS idx_events_synced ON events(synced_to_cloud);
|
|
398
|
+
CREATE INDEX IF NOT EXISTS idx_events_timestamp ON events(timestamp);
|
|
399
|
+
CREATE INDEX IF NOT EXISTS idx_events_origin ON events(origin);
|
|
400
|
+
`);
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
version: 10,
|
|
405
|
+
up: async (db) => {
|
|
406
|
+
await db.execAsync(`
|
|
407
|
+
ALTER TABLE orders ADD COLUMN updated_at TEXT;
|
|
408
|
+
`);
|
|
409
|
+
},
|
|
410
|
+
},
|
|
137
411
|
];
|
package/dist/db/operations.d.ts
CHANGED
|
@@ -39,10 +39,61 @@ export interface UpsertEntitySnapshotInput<T = unknown> {
|
|
|
39
39
|
payload: T;
|
|
40
40
|
}
|
|
41
41
|
export declare function upsertEntitySnapshot<T>(db: SQLiteDatabase, input: UpsertEntitySnapshotInput<T>): Promise<void>;
|
|
42
|
-
export declare function getEntitySnapshot<T>(db: SQLiteDatabase, entityType: string, entityId: string): Promise<EntitySnapshot<T> | null>;
|
|
43
|
-
export declare function getEntitySnapshotsByType<T>(db: SQLiteDatabase, entityType: string): Promise<EntitySnapshot<T>[]>;
|
|
42
|
+
export declare function getEntitySnapshot<T>(db: SQLiteDatabase, entityType: string, entityId: string, businessId?: string | null): Promise<EntitySnapshot<T> | null>;
|
|
43
|
+
export declare function getEntitySnapshotsByType<T>(db: SQLiteDatabase, entityType: string, businessId?: string | null): Promise<EntitySnapshot<T>[]>;
|
|
44
44
|
export declare function deleteEntitySnapshot(db: SQLiteDatabase, entityType: string, entityId: string): Promise<void>;
|
|
45
45
|
export declare function pruneEntitySnapshots(db: SQLiteDatabase, entityType: string, cutoffIso: string): Promise<void>;
|
|
46
|
+
export interface OrderRow {
|
|
47
|
+
id: string;
|
|
48
|
+
business_id: string;
|
|
49
|
+
order_number: string;
|
|
50
|
+
callout_number: number | null;
|
|
51
|
+
status: string;
|
|
52
|
+
order_type: string | null;
|
|
53
|
+
is_pos: number;
|
|
54
|
+
items: string;
|
|
55
|
+
subtotal_minor: number;
|
|
56
|
+
total_minor: number;
|
|
57
|
+
tax_minor: number;
|
|
58
|
+
tax_lines: string | null;
|
|
59
|
+
payment: string | null;
|
|
60
|
+
customer_name: string | null;
|
|
61
|
+
customer_phone: string | null;
|
|
62
|
+
pos_payment_method: string | null;
|
|
63
|
+
delivery_id: string | null;
|
|
64
|
+
business: string | null;
|
|
65
|
+
user: string | null;
|
|
66
|
+
inserted_at: string;
|
|
67
|
+
updated_at: string | null;
|
|
68
|
+
hlc: string | null;
|
|
69
|
+
deleted_at: string | null;
|
|
70
|
+
}
|
|
71
|
+
export declare function insertOrder(db: SQLiteDatabase, order: import("../types/api/seller.api").SellerOrder & {
|
|
72
|
+
hlc?: string;
|
|
73
|
+
}): Promise<void>;
|
|
74
|
+
export declare function getOrderById(db: SQLiteDatabase, businessId: string, idOrNumber: string): Promise<import("../types/api/seller.api").SellerOrder | null>;
|
|
75
|
+
export declare function listOrders(db: SQLiteDatabase, businessId: string, filters?: {
|
|
76
|
+
status?: string;
|
|
77
|
+
q?: string;
|
|
78
|
+
is_pos?: string;
|
|
79
|
+
order_type?: string;
|
|
80
|
+
date_from?: string;
|
|
81
|
+
date_to?: string;
|
|
82
|
+
}, page?: number, pageSize?: number): Promise<{
|
|
83
|
+
data: import("../types/api/seller.api").SellerOrder[];
|
|
84
|
+
total: number;
|
|
85
|
+
}>;
|
|
86
|
+
export declare function updateOrderStatus(db: SQLiteDatabase, businessId: string, orderId: string, toStatus: string, hlc?: string): Promise<void>;
|
|
87
|
+
export declare function updateOrderPayment(db: SQLiteDatabase, businessId: string, orderId: string, patch: {
|
|
88
|
+
is_paid: boolean;
|
|
89
|
+
paid_at: string | null;
|
|
90
|
+
}): Promise<void>;
|
|
46
91
|
export declare function clearAllSyncData(db: SQLiteDatabase): Promise<void>;
|
|
92
|
+
export declare function wipeForNewInstall(db: SQLiteDatabase): Promise<void>;
|
|
47
93
|
export declare function clearReferenceData(db: SQLiteDatabase): Promise<void>;
|
|
94
|
+
export declare function subscribeDbChanges(listener: (info: {
|
|
95
|
+
businessId?: string;
|
|
96
|
+
table: string;
|
|
97
|
+
}) => void): () => void;
|
|
98
|
+
export declare function notifyDbChange(businessId: string | undefined, table: string): void;
|
|
48
99
|
export declare function clearCompletedCommandsAndOrphans(db: SQLiteDatabase): Promise<void>;
|