@pisell/pisellos 2.3.58 → 2.3.59

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.
@@ -32,32 +32,49 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
32
32
  this.store = { list: [] };
33
33
  }
34
34
  async initialize(core, options) {
35
+ var _a, _b;
35
36
  this.core = core;
36
37
  this.request = core.getPlugin("request");
37
38
  if (!this.request)
38
39
  throw new Error("QuotationModule 需要 request 插件支持");
40
+ this.app = (_b = (_a = core.getPlugin("app")) == null ? void 0 : _a.getApp) == null ? void 0 : _b.call(_a);
39
41
  this.store = { list: [] };
40
42
  }
41
43
  async loadQuotations(params) {
42
- var _a;
43
44
  const query = {};
44
45
  if (params == null ? void 0 : params.channel)
45
46
  query.channel = params.channel;
46
47
  if ((params == null ? void 0 : params.channel) === "online_store") {
47
48
  query.channel = "online-store";
48
49
  }
50
+ let initialResultApplied = false;
51
+ const applyRemoteUpdate = (response) => {
52
+ this.applyQuotationResponse(response);
53
+ if (initialResultApplied) {
54
+ this.notifyQuotationListUpdated();
55
+ }
56
+ };
49
57
  const res = await this.request.get(
50
58
  "/quotation/available",
51
59
  query,
52
- { useCache: false, osServer: true }
60
+ {
61
+ cache: {
62
+ type: "indexDB",
63
+ updateCache: true,
64
+ cacheKeyData: this.getQuotationCacheKeyData(query)
65
+ },
66
+ cacheUpdateChange: applyRemoteUpdate
67
+ }
53
68
  );
54
- const list = ((_a = res == null ? void 0 : res.data) == null ? void 0 : _a.list) || (res == null ? void 0 : res.list) || [];
55
- list.sort((a, b) => b.sort - a.sort);
56
- this.store.list = list;
69
+ this.applyQuotationResponse(res);
70
+ initialResultApplied = true;
57
71
  }
58
72
  getQuotationList() {
59
73
  return this.store.list;
60
74
  }
75
+ setQuotationListUpdatedListener(listener) {
76
+ this.quotationListUpdatedListener = listener;
77
+ }
61
78
  getQuotationCustomerScopeInfo() {
62
79
  const customerById = /* @__PURE__ */ new Map();
63
80
  const quotationScopes = this.store.list.filter((quotation) => Array.isArray(quotation.customer) && quotation.customer.length > 0).map((quotation) => {
@@ -156,6 +173,33 @@ var QuotationModule = class extends import_BaseModule.BaseModule {
156
173
  setScheduleResolver(resolver) {
157
174
  this.scheduleResolver = resolver;
158
175
  }
176
+ applyQuotationResponse(response) {
177
+ var _a;
178
+ const source = ((_a = response == null ? void 0 : response.data) == null ? void 0 : _a.list) || (response == null ? void 0 : response.list) || [];
179
+ this.store.list = [...source].sort((a, b) => Number(b.sort || 0) - Number(a.sort || 0));
180
+ }
181
+ notifyQuotationListUpdated() {
182
+ var _a;
183
+ try {
184
+ (_a = this.quotationListUpdatedListener) == null ? void 0 : _a.call(this);
185
+ } catch (error) {
186
+ console.warn("[QuotationModule] 报价单后台更新回调失败", error);
187
+ }
188
+ }
189
+ getQuotationCacheKeyData(query) {
190
+ var _a, _b, _c;
191
+ let shopId = "";
192
+ try {
193
+ const storedCore = (_c = (_b = (_a = this.app) == null ? void 0 : _a.storage) == null ? void 0 : _b.getStorage) == null ? void 0 : _c.call(_b, "core");
194
+ const coreInfo = typeof storedCore === "string" ? JSON.parse(storedCore || "{}") : storedCore || {};
195
+ shopId = String((coreInfo == null ? void 0 : coreInfo.id) || "");
196
+ } catch {
197
+ }
198
+ return {
199
+ ...query,
200
+ shop_id: shopId
201
+ };
202
+ }
159
203
  isQuotationVisibleForCustomer(quotation, customerId) {
160
204
  const customers = quotation.customer || [];
161
205
  if (customers.length === 0)
@@ -68,6 +68,7 @@ export interface RequestOptions {
68
68
  useCache?: boolean;
69
69
  customToast?: () => void;
70
70
  cache?: CacheProps;
71
+ cacheUpdateChange?: (data: any) => void;
71
72
  osServer?: boolean;
72
73
  prefix?: boolean;
73
74
  callback?: (res: any) => void;
@@ -263,6 +263,11 @@ declare class Server {
263
263
  * 对单个商品 query 订阅者重算并推送(定时路径;不新建 schedule timer)。
264
264
  */
265
265
  private recomputeAndNotifySubscriber;
266
+ /**
267
+ * 根据餐牌 ID 获取其绑定日程中的全部时间点。
268
+ * 商品查询定时器与 schedule-time-points 路由共用此逻辑。
269
+ */
270
+ private getMenuScheduleTimePoints;
266
271
  /**
267
272
  * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
268
273
  */
@@ -518,6 +523,10 @@ declare class Server {
518
523
  private handleOrderDraftSubmit;
519
524
  private handlePendingSyncCheckoutOrder;
520
525
  private buildPendingSyncCheckoutOrder;
526
+ /**
527
+ * Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
528
+ */
529
+ private shouldAutoPrintCheckoutReceipt;
521
530
  private shouldBuildSmallTicketData;
522
531
  private shouldPrintSyncedOrder;
523
532
  private hasPaymentStatus;
@@ -45,6 +45,7 @@ var import_floor_plan = require("./modules/floor-plan");
45
45
  var import_types = require("./modules/floor-plan/types");
46
46
  var import_payment = require("./modules/payment");
47
47
  var import_schedule2 = require("./utils/schedule");
48
+ var import_time = require("./utils/time");
48
49
  var import_types2 = require("./modules/products/types");
49
50
  var import_product = require("./utils/product");
50
51
  var import_order = require("./modules/order");
@@ -65,6 +66,7 @@ var PRODUCT_TITLE_SNAPSHOT_KEYS = [
65
66
  "kitchen",
66
67
  "original"
67
68
  ];
69
+ var AUTO_PRINT_STORAGE_KEY = "auto_print";
68
70
  var Server = class {
69
71
  constructor(core) {
70
72
  this.quotationAvailableCache = /* @__PURE__ */ new Map();
@@ -254,7 +256,11 @@ var Server = class {
254
256
  strategy_context
255
257
  });
256
258
  if (subscriberId && typeof callback === "function") {
257
- this.syncProductQueryScheduleTimers(subscriberId, strategy_context);
259
+ this.syncProductQueryScheduleTimers({
260
+ subscriberId,
261
+ menu_list_ids,
262
+ strategy_context
263
+ });
258
264
  }
259
265
  return result;
260
266
  };
@@ -1081,7 +1087,12 @@ var Server = class {
1081
1087
  };
1082
1088
  }
1083
1089
  try {
1084
- const menuList = this.menu.getMenuByIds(menu_list_ids);
1090
+ const {
1091
+ menuList,
1092
+ scheduleIds,
1093
+ scheduleList,
1094
+ timePoints
1095
+ } = this.getMenuScheduleTimePoints(menu_list_ids);
1085
1096
  console.log(`[Server] 找到 ${menuList.length} 个餐牌`);
1086
1097
  this.logInfo("handleGetScheduleTimePoints: 获取到餐牌列表", {
1087
1098
  requestedCount: menu_list_ids.length,
@@ -1089,7 +1100,6 @@ var Server = class {
1089
1100
  menu_list_ids,
1090
1101
  menuList
1091
1102
  });
1092
- const scheduleIds = (0, import_schedule2.extractScheduleIdsFromMenus)(menuList);
1093
1103
  console.log(`[Server] 提取到 ${scheduleIds.length} 个日程ID:`, scheduleIds);
1094
1104
  this.logInfo("handleGetScheduleTimePoints: 提取到日程 IDs", {
1095
1105
  scheduleCount: scheduleIds.length
@@ -1103,13 +1113,11 @@ var Server = class {
1103
1113
  status: true
1104
1114
  };
1105
1115
  }
1106
- const scheduleList = this.schedule.getScheduleByIds(scheduleIds);
1107
1116
  console.log(`[Server] 找到 ${scheduleList.length} 个日程`);
1108
1117
  this.logInfo("handleGetScheduleTimePoints: 获取到日程详情", {
1109
1118
  requestedCount: scheduleIds.length,
1110
1119
  foundCount: scheduleList.length
1111
1120
  });
1112
- const timePoints = (0, import_schedule2.extractTimePointsFromSchedules)(scheduleList);
1113
1121
  console.log(`[Server] 提取到 ${timePoints.length} 个时间点:`, timePoints);
1114
1122
  this.logInfo("handleGetScheduleTimePoints 处理完成", {
1115
1123
  menuListIdsCount: menu_list_ids.length,
@@ -1680,11 +1688,22 @@ var Server = class {
1680
1688
  return;
1681
1689
  }
1682
1690
  try {
1683
- await this.schedule.loadAllSchedule();
1684
1691
  this.products.clearPriceCache();
1685
1692
  await this.products.setupQuotationPriceBridge({
1686
1693
  scheduleModule: this.schedule,
1687
- channel: (_a = this.core.context) == null ? void 0 : _a.channel
1694
+ channel: (_a = this.core.context) == null ? void 0 : _a.channel,
1695
+ onQuotationUpdated: () => {
1696
+ void this.recomputeAndNotifyProductQuery();
1697
+ }
1698
+ });
1699
+ void this.schedule.loadAllSchedule().then(() => {
1700
+ var _a2;
1701
+ (_a2 = this.products) == null ? void 0 : _a2.clearPriceCache();
1702
+ void this.recomputeAndNotifyProductQuery();
1703
+ }).catch((error) => {
1704
+ this.logWarning("Products 报价单价格桥接后台刷新 Schedule 失败,继续使用本地数据", {
1705
+ error: error instanceof Error ? error.message : String(error)
1706
+ });
1688
1707
  });
1689
1708
  this.logInfo("Products 报价单价格桥接初始化完成");
1690
1709
  } catch (error) {
@@ -2319,16 +2338,44 @@ var Server = class {
2319
2338
  });
2320
2339
  }
2321
2340
  }
2341
+ /**
2342
+ * 根据餐牌 ID 获取其绑定日程中的全部时间点。
2343
+ * 商品查询定时器与 schedule-time-points 路由共用此逻辑。
2344
+ */
2345
+ getMenuScheduleTimePoints(menuListIds) {
2346
+ var _a, _b;
2347
+ const menuList = ((_a = this.menu) == null ? void 0 : _a.getMenuByIds(menuListIds)) ?? [];
2348
+ const scheduleIds = (0, import_schedule2.extractScheduleIdsFromMenus)(menuList);
2349
+ const scheduleList = scheduleIds.length > 0 ? ((_b = this.schedule) == null ? void 0 : _b.getScheduleByIds(scheduleIds)) ?? [] : [];
2350
+ const timePoints = (0, import_schedule2.extractTimePointsFromSchedules)(scheduleList);
2351
+ return {
2352
+ menuList,
2353
+ scheduleIds,
2354
+ scheduleList,
2355
+ timePoints
2356
+ };
2357
+ }
2322
2358
  /**
2323
2359
  * query 完成后为 subscriber 同步 schedule 定时器(先 clear 再 schedule)。
2324
2360
  */
2325
- syncProductQueryScheduleTimers(subscriberId, strategy_context) {
2361
+ syncProductQueryScheduleTimers({
2362
+ subscriberId,
2363
+ menu_list_ids,
2364
+ strategy_context
2365
+ }) {
2326
2366
  var _a, _b;
2327
2367
  this.clearSubscriberScheduleTimers(subscriberId);
2328
2368
  if (!this.shouldScheduleProductQueryReload(strategy_context)) {
2329
2369
  return;
2330
2370
  }
2331
- const timePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
2371
+ const productTimePoints = ((_b = (_a = this.products) == null ? void 0 : _a.getLastScheduleTimePoints) == null ? void 0 : _b.call(_a)) ?? [];
2372
+ const menuListIds = Array.isArray(menu_list_ids) ? Array.from(new Set(
2373
+ menu_list_ids.map((id) => Number(id)).filter((id) => Number.isFinite(id))
2374
+ )) : [];
2375
+ const menuTimePoints = this.getMenuScheduleTimePoints(menuListIds).timePoints;
2376
+ const timePoints = (0, import_time.sortTimePoints)(
2377
+ Array.from(/* @__PURE__ */ new Set([...productTimePoints, ...menuTimePoints]))
2378
+ );
2332
2379
  if (timePoints.length > 0) {
2333
2380
  this.scheduleSubscriberTimePointReloads(subscriberId, timePoints);
2334
2381
  }
@@ -3791,7 +3838,6 @@ var Server = class {
3791
3838
  });
3792
3839
  }
3793
3840
  async handleOrderCheckoutSubmit(params) {
3794
- var _a;
3795
3841
  const { backendPath, data, title } = params;
3796
3842
  this.logInfo(`${title}: 开始处理`, {
3797
3843
  backendPath,
@@ -3816,15 +3862,14 @@ var Server = class {
3816
3862
  });
3817
3863
  if ((pendingResult == null ? void 0 : pendingResult.status) === true) {
3818
3864
  const pendingOrder = pendingResult.data;
3819
- const platform = (_a = this.core.context) == null ? void 0 : _a.getPlatform();
3820
- const isAndroid = platform === "android";
3821
- if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && // TODO 这里先加个兼容,如果是安卓,则需要 type=virtual 才走打印,如果是苹果则无限制
3822
- (isAndroid ? checkoutData.type === "virtual" : true)) {
3865
+ if (pendingOrder && this.shouldBuildSmallTicketData(checkoutData) && this.isSalesOrderFullyPaid(pendingOrder) && this.shouldAutoPrintCheckoutReceipt()) {
3823
3866
  await this.dispatchLocalReceiptPrint({
3824
3867
  checkoutData,
3825
3868
  order: pendingOrder,
3826
3869
  response: pendingResult,
3827
- requireFullyPaid: true
3870
+ requireFullyPaid: true,
3871
+ // 自动小票固定使用 print_all 的收据类型 0,不随订单 type 改成券/手环类型 99。
3872
+ isManualPrint: true
3828
3873
  });
3829
3874
  }
3830
3875
  await this.dispatchCheckoutSyncTask({
@@ -4054,6 +4099,15 @@ var Server = class {
4054
4099
  { requireFullyPaid: true, productMap }
4055
4100
  );
4056
4101
  }
4102
+ /**
4103
+ * Android 结账自动小票由当前设备设置控制;其它平台保持原有行为。
4104
+ */
4105
+ shouldAutoPrintCheckoutReceipt() {
4106
+ var _a, _b, _c, _d, _e;
4107
+ if (((_b = (_a = this.core.context) == null ? void 0 : _a.getPlatform) == null ? void 0 : _b.call(_a)) !== "android")
4108
+ return true;
4109
+ return ((_e = (_d = (_c = this.app) == null ? void 0 : _c.storage) == null ? void 0 : _d.getStorage) == null ? void 0 : _e.call(_d, AUTO_PRINT_STORAGE_KEY)) === "true";
4110
+ }
4057
4111
  shouldBuildSmallTicketData(order) {
4058
4112
  if (!order || typeof order !== "object")
4059
4113
  return false;
@@ -10,6 +10,7 @@ export declare class FloorPlanModule extends BaseModule implements Module {
10
10
  private store;
11
11
  private request;
12
12
  private logger;
13
+ private dbManager;
13
14
  private floorPlanDataSource;
14
15
  private pendingSyncMessages;
15
16
  private syncTimer?;
@@ -28,6 +29,9 @@ export declare class FloorPlanModule extends BaseModule implements Module {
28
29
  private mergeItems;
29
30
  private removeByIds;
30
31
  private loadListFromServer;
32
+ private loadListFromSQLite;
33
+ private saveListToSQLite;
34
+ private refreshListFromServer;
31
35
  fetchByIdFromServer(id: number | string): Promise<FloorPlanItem | null>;
32
36
  fetchByCodeFromServer(code: string): Promise<FloorPlanItem | null>;
33
37
  preload(): Promise<void>;
@@ -25,6 +25,7 @@ module.exports = __toCommonJS(floor_plan_exports);
25
25
  var import_BaseModule = require("../../../modules/BaseModule");
26
26
  var import_types = require("./types");
27
27
  var FLOOR_PLAN_SYNC_DEBOUNCE_MS = 3e3;
28
+ var FLOOR_PLAN_STORE_NAME = "floor_plans";
28
29
  var FloorPlanModule = class extends import_BaseModule.BaseModule {
29
30
  constructor(name, version) {
30
31
  super(name, version);
@@ -50,6 +51,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
50
51
  if (appPlugin) {
51
52
  const app = appPlugin.getApp();
52
53
  this.logger = app.logger;
54
+ this.dbManager = app.sqlite || app.dbManager;
53
55
  }
54
56
  this.initFloorPlanDataSource();
55
57
  this.setupFloorPlanSync();
@@ -159,6 +161,45 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
159
161
  return [];
160
162
  }
161
163
  }
164
+ async loadListFromSQLite() {
165
+ if (!this.dbManager)
166
+ return [];
167
+ try {
168
+ const list = await this.dbManager.getAll(FLOOR_PLAN_STORE_NAME);
169
+ return Array.isArray(list) ? list : [];
170
+ } catch (error) {
171
+ this.logWarning("从 SQLite 读取平面图失败", {
172
+ error: error instanceof Error ? error.message : String(error)
173
+ });
174
+ return [];
175
+ }
176
+ }
177
+ async saveListToSQLite(list) {
178
+ if (!this.dbManager)
179
+ return;
180
+ try {
181
+ await this.dbManager.clear(FLOOR_PLAN_STORE_NAME);
182
+ if (list.length > 0) {
183
+ await this.dbManager.bulkAdd(FLOOR_PLAN_STORE_NAME, list);
184
+ }
185
+ } catch (error) {
186
+ this.logWarning("保存平面图到 SQLite 失败", {
187
+ count: list.length,
188
+ error: error instanceof Error ? error.message : String(error)
189
+ });
190
+ }
191
+ }
192
+ async refreshListFromServer() {
193
+ const list = await this.loadListFromServer();
194
+ if (list.length === 0) {
195
+ this.logWarning("远端平面图刷新完成但列表为空");
196
+ return;
197
+ }
198
+ this.replaceStore(list);
199
+ await this.saveListToSQLite(list);
200
+ this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
201
+ this.logInfo("远端平面图刷新完成", { count: list.length });
202
+ }
162
203
  async fetchByIdFromServer(id) {
163
204
  try {
164
205
  const res = await this.request.get(
@@ -190,14 +231,19 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
190
231
  }
191
232
  async preload() {
192
233
  this.logInfo("开始预加载平面图列表");
193
- const list = await this.loadListFromServer();
194
- if (list.length > 0) {
195
- this.replaceStore(list);
234
+ const cachedList = await this.loadListFromSQLite();
235
+ if (cachedList.length > 0) {
236
+ this.replaceStore(cachedList);
196
237
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
197
- this.logInfo("预加载完成", { count: list.length });
198
- } else {
199
- this.logWarning("预加载完成但列表为空");
238
+ this.logInfo("预加载完成(SQLite)", { count: cachedList.length });
239
+ void this.refreshListFromServer().catch((error) => {
240
+ this.logWarning("后台刷新平面图失败,继续使用本地数据", {
241
+ error: error instanceof Error ? error.message : String(error)
242
+ });
243
+ });
244
+ return;
200
245
  }
246
+ await this.refreshListFromServer();
201
247
  }
202
248
  initFloorPlanDataSource() {
203
249
  var _a, _b;
@@ -299,6 +345,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
299
345
  }
300
346
  }
301
347
  if (changed) {
348
+ await this.saveListToSQLite(this.store.list);
302
349
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlansChanged, this.getSortedList());
303
350
  this.core.effects.emit(import_types.FloorPlanHooks.onFloorPlanSyncCompleted, null);
304
351
  }
@@ -307,6 +354,7 @@ var FloorPlanModule = class extends import_BaseModule.BaseModule {
307
354
  this.store.list = [];
308
355
  this.store.map.clear();
309
356
  this.store.codeMap.clear();
357
+ await this.saveListToSQLite([]);
310
358
  }
311
359
  destroy() {
312
360
  var _a;
@@ -290,13 +290,14 @@ export declare class OrderModule extends BaseModule implements Module {
290
290
  private logDuplicateOrders;
291
291
  private compactOrderListByIdentity;
292
292
  /**
293
- * 当前订单拉取窗口:
294
- * - 优先使用 createdAtQuery.sales_time_between(已按营业日边界初始化)
295
- * - 无法解析时退化为自然日窗口
293
+ * 当前订单缓存可复用窗口。
294
+ * start_time 模式使用精确的营业日起止时间;配置缺失或异常时退化为自然日。
296
295
  */
297
- private getCurrentPullWindow;
296
+ private getCurrentOrderCacheWindow;
298
297
  private hasPulledOrdersToday;
299
298
  private markOrderPulledAtNow;
299
+ private clearOrderSnapshotMarker;
300
+ private getCurrentShopId;
300
301
  private getStorageItem;
301
302
  private setStorageItem;
302
303
  private loadOrdersFromSQLite;
@@ -334,7 +335,7 @@ export declare class OrderModule extends BaseModule implements Module {
334
335
  * 用于 SSE 全量拉取与营业日窗口裁剪。
335
336
  *
336
337
  * @example
337
- * await this.replaceOrdersSnapshotInSQLite(orderList)
338
+ * const { orders, persisted } = await this.replaceOrdersSnapshotInSQLite(orderList, 'refresh')
338
339
  */
339
340
  private replaceOrdersSnapshotInSQLite;
340
341
  /**