@freedom_sky/feige-lib 5.18.0 → 5.20.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/src/index.ts CHANGED
@@ -1,612 +1,612 @@
1
- import { Order } from './interfaces/order/order.interface';
2
- import { Process, Content, Checkout, Task } from './interfaces/order/baseOrder.interface';
3
- import { BuffetCombosItems, Store } from './interfaces/store/store.interface';
4
- import { VisitTask } from './interfaces/visit-task/visit-task.interface';
5
- import { User, UserSafe, CertificatePath } from './interfaces/user/user.interface';
6
- import { DishSnapshot } from './interfaces/store/dishSnapshot.interface';
7
- import { CONST } from './const/const';
8
- import { PrintStatusEnum, PrinterDeviceTypeEnum, PrinterWidthEnum } from './enum/printer.enum';
9
- import { DishType } from './enum/dish.enum';
10
- export * from './enum/common.enum';
11
- export * from './enum/time-menu.enum';
12
- export * from './enum/printer.enum';
13
- export * from './enum/member.enum';
14
- export * from './enum/sms.enum';
15
- export * from './enum/ai-token.enum';
16
- export * from './enum/checkout.enum';
17
- export * from './enum/yeepay.enum';
18
- export * from './enum/timezone.emum';
19
- export * from './enum/order.enum';
20
- export * from './enum/tax.enum';
21
- export * from './enum/dish.enum';
22
- export * from './enum/user-status.enum';
23
- export * from './enum/visit-task.enum';
24
- export * from './enum/receptionist.enum';
25
- export * from './enum/from.enum';
26
- export * from './enum/role.enum';
27
- export * from './enum/continent.enum';
28
- export * from './announcement/enum/announcement.enum';
29
- export * from './interfaces/member/member.interface';
30
- export * from './interfaces/member/member-transaction.interface';
31
- export * from './interfaces/store/store.interface';
32
- export * from './interfaces/common/language.interface';
33
- export * from './interfaces/store/subscription.interface';
34
- export * from './interfaces/store/dish.interface';
35
- export * from './interfaces/store/classification.interface';
36
- export * from './interfaces/store/specification.interface';
37
- export * from './interfaces/store/menu-export.interface';
38
- export { MenuImportModeEnum } from './enum/menu-io.enum';
39
- export * from './interfaces/order';
40
- export { PayCompanyEnum } from './enum/pay-company.enum';
41
- import * as moment from 'moment';
42
- import Big from 'big.js';
43
- import { TaxTypeEnum } from './enum/tax.enum';
44
- import { OrderTypeEnum } from './enum/order.enum';
45
- import { calcBuffetTotalPrice, getDishItemInBuffet, getMaxBuffetDishUpperLimit } from './buffet/index';
46
- export * from './buffet/index';
47
- export * from './order/index';
48
- export {
49
- Order,
50
- Process,
51
- Content,
52
- Checkout,
53
- CONST,
54
- DishSnapshot,
55
- User,
56
- UserSafe,
57
- CertificatePath,
58
- Task,
59
- VisitTask,
60
- moment,
61
- PrintStatusEnum,
62
- PrinterDeviceTypeEnum,
63
- PrinterWidthEnum,
64
- Big,
65
- };
66
-
67
- /**
68
- * 小数转化为金额,去除分位之后的尾数
69
- *
70
- * @param {number} num 需要转换的数字
71
- * @returns {number} 返回只保留两位小数的money
72
- */
73
- export function parseMoney(num: number): number {
74
- return Math.round(num * 100) / 100;
75
- }
76
-
77
- /**
78
- * 计算加了规格后的菜品单价
79
- *
80
- * @param {Content} conent 单个菜品内容
81
- * @returns {number} 返回订单总金额
82
- *
83
- */
84
- export function calcDishFinalPrice(conent: Content): number {
85
- let addPrice = 0;
86
- const originPrice =
87
- conent.dishSnapshot.type === DishType.Weigh
88
- ? conent.weight * conent.dishSnapshot.price
89
- : conent.dishSnapshot.price;
90
- const selectedSpecifications = conent.dishSnapshot.selectedSpecifications || [];
91
- selectedSpecifications.forEach((specification) => {
92
- if (!specification) {
93
- return;
94
- }
95
- specification.content.forEach((item) => {
96
- if (item.fareType == CONST.FARE_TYPE.FIXED) {
97
- addPrice += item.fare;
98
- } else if (item.fareType == CONST.FARE_TYPE.PERCENTAGE) {
99
- addPrice += (originPrice * item.fare) / 100;
100
- }
101
- });
102
- });
103
- let dishPrice = originPrice + addPrice;
104
- if (dishPrice < 0) {
105
- dishPrice = 0;
106
- }
107
- return parseMoney(dishPrice);
108
- }
109
-
110
- /**
111
- * 利用订单计算总价
112
- *
113
- * @param {object} order 订单
114
- * @returns {number} 返回订单总金额
115
- *
116
- */
117
- export function calcTotalPrice(order: Order): number {
118
- let totalPrice = 0;
119
- order.content.forEach((orderContentItem) => {
120
- totalPrice += calcContentPrice({
121
- orderContentItem,
122
- buffetItem: order.buffet?.snapshot,
123
- orderType: order.type,
124
- adultNum: order.adultNum,
125
- childNum: order.childNum,
126
- contents: order.content,
127
- });
128
- });
129
- if (order.deliveryFee && !order.selfPickUp) {
130
- totalPrice += order.deliveryFee;
131
- }
132
- if (order.type === OrderTypeEnum.Buffet) {
133
- totalPrice += calcBuffetTotalPrice(order.buffet.snapshot, order.adultNum, order.childNum);
134
- }
135
- return parseMoney(totalPrice);
136
- }
137
-
138
- /**
139
- *
140
- */
141
- export function calcContentPrice({
142
- orderContentItem,
143
- buffetItem,
144
- orderType,
145
- adultNum,
146
- childNum,
147
- contents,
148
- }: {
149
- orderContentItem: Content;
150
- buffetItem: BuffetCombosItems;
151
- orderType: OrderTypeEnum;
152
- adultNum: number;
153
- childNum: number;
154
- contents: Content[];
155
- }): number {
156
- if (orderType === OrderTypeEnum.Buffet) {
157
- const buffetDishItem = getDishItemInBuffet({ dishId: orderContentItem.dishSnapshot._id, buffet: buffetItem });
158
- if (buffetDishItem) {
159
- const maxUpperLimit = getMaxBuffetDishUpperLimit({
160
- buffetDishItem,
161
- adultNum: adultNum,
162
- childNum: childNum,
163
- });
164
- const beforeCount = getOrderedCountByDishId(
165
- orderContentItem.dishSnapshot._id,
166
- contents,
167
- orderContentItem._id,
168
- );
169
- const maxUpperLimitForThisItem = Math.max(maxUpperLimit - beforeCount, 0);
170
- if (maxUpperLimitForThisItem >= orderContentItem.count) {
171
- return 0;
172
- } else {
173
- return (orderContentItem.count - maxUpperLimitForThisItem) * calcDishFinalPrice(orderContentItem);
174
- }
175
- } else {
176
- return orderContentItem.count * calcDishFinalPrice(orderContentItem);
177
- }
178
- } else {
179
- return orderContentItem.count * calcDishFinalPrice(orderContentItem);
180
- }
181
- }
182
-
183
- /**
184
- * @param dishId
185
- * @param contents
186
- * @param beforeKey
187
- */
188
- export function getOrderedCountByDishId(dishId, contents: Content[], beforeKey?: string): number {
189
- let count = 0;
190
- for (const content of contents.filter((content) => String(content.dishSnapshot._id) === String(dishId))) {
191
- if (beforeKey && String(content._id) === String(beforeKey)) break;
192
- count += content.count;
193
- }
194
- return count;
195
- }
196
-
197
- /**
198
- * 计算处理后的应收金额
199
- *
200
- * @param {object} order 订单
201
- * @param {Array} processArr 结账处理过程
202
- * @param noTax
203
- * @param taxEnable
204
- * @returns {object} 返回应收订单总金额,以及处理计算过程
205
- */
206
- export function calcReceivablePrice(
207
- order: Order,
208
- processArr: Process[],
209
- noTax = false,
210
- ): {
211
- resultProcessArr: Process[];
212
- receivablePrice: number;
213
- taxArr: {
214
- name: string;
215
- volume: number;
216
- }[];
217
- totalPrice: number;
218
- totalDiscountPrice: number;
219
- } {
220
- let totalDiscountPrice = 0;
221
- const resultProcessArr = [];
222
- const totalPrice = calcTotalPrice(order);
223
- let receivablePrice = totalPrice;
224
- const sortedProcessArr = processArr.sort((a, b) => {
225
- return (
226
- CONST.RECEIVABLE_PROCESSING_TYPE[b.type.toUpperCase()].SORT -
227
- CONST.RECEIVABLE_PROCESSING_TYPE[a.type.toUpperCase()].SORT
228
- );
229
- });
230
- let totalMarkDown = 0;
231
- let receivablePriceAfterDiscount = receivablePrice;
232
- sortedProcessArr.forEach((process) => {
233
- if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE) {
234
- const rate = -(10 - process.value) / 10;
235
- let volume = 0;
236
- order.content.forEach((orderContentItem) => {
237
- if (orderContentItem.dishSnapshot.noFullOrderDiscount) {
238
- return;
239
- }
240
- const dishTotalPrice = calcContentPrice({
241
- orderContentItem,
242
- buffetItem: order.buffet?.snapshot,
243
- orderType: order.type,
244
- adultNum: order.adultNum,
245
- childNum: order.childNum,
246
- contents: order.content,
247
- });
248
- totalDiscountPrice += dishTotalPrice;
249
- volume += dishTotalPrice * rate;
250
- });
251
- if (order.type === OrderTypeEnum.Buffet) {
252
- const buffetTotalPrice = calcBuffetTotalPrice(order.buffet.snapshot, order.adultNum, order.childNum);
253
- totalDiscountPrice += buffetTotalPrice;
254
- volume += buffetTotalPrice * rate;
255
- }
256
- resultProcessArr.push({
257
- type: process.type,
258
- value: process.value,
259
- volume,
260
- });
261
- receivablePrice += volume;
262
- receivablePriceAfterDiscount = receivablePrice;
263
- } else if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.MARKDOWN.TYPE) {
264
- const volume = -process.value;
265
- resultProcessArr.push({
266
- type: process.type,
267
- value: process.value,
268
- volume,
269
- });
270
- receivablePrice += volume;
271
- totalMarkDown += -volume;
272
- } else if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.REMOVE_TAILS.TYPE) {
273
- const pow = Math.pow(10, process.value - 2);
274
- const result = Math.floor(receivablePrice / pow) * pow;
275
- const volume = -(receivablePrice - result);
276
- resultProcessArr.push({
277
- type: process.type,
278
- value: process.value,
279
- volume,
280
- });
281
- receivablePrice += volume;
282
- totalMarkDown += -volume;
283
- }
284
- });
285
- const taxObj = {};
286
- if (order.taxType === TaxTypeEnum.PriceNoTaxBindDish && !noTax) {
287
- order.content.forEach((orderContentItem) => {
288
- if (!orderContentItem.dishSnapshot.taxes || orderContentItem.dishSnapshot.taxes.length === 0) {
289
- return;
290
- }
291
- const discountValue = sortedProcessArr.find(
292
- (process) => process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE,
293
- )?.value;
294
- let dishTotalPrice = calcContentPrice({
295
- orderContentItem,
296
- buffetItem: order.buffet?.snapshot,
297
- orderType: order.type,
298
- adultNum: order.adultNum,
299
- childNum: order.childNum,
300
- contents: order.content,
301
- });
302
- if (discountValue && !orderContentItem.dishSnapshot.noFullOrderDiscount) {
303
- const rate = -(10 - discountValue) / 10;
304
- dishTotalPrice += dishTotalPrice * rate;
305
- }
306
- const sharedMarkDown = (totalMarkDown * dishTotalPrice) / receivablePriceAfterDiscount;
307
- orderContentItem.dishSnapshot.taxes.forEach((tax) => {
308
- const taxFare = (dishTotalPrice - sharedMarkDown) * tax.rate;
309
- const taxKey = `${tax.name}(${Big(tax.rate).times(100)}%)`;
310
- taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
311
- });
312
- });
313
- // buffet price
314
- if (order.type === OrderTypeEnum.Buffet) {
315
- const discountValue = sortedProcessArr.find(
316
- (process) => process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE,
317
- )?.value;
318
- let buffetTotalPrice = calcBuffetTotalPrice(order.buffet?.snapshot, order.adultNum, order.childNum);
319
- if (discountValue) {
320
- const rate = -(10 - discountValue) / 10;
321
- buffetTotalPrice += buffetTotalPrice * rate;
322
- }
323
- const sharedMarkDown = (totalMarkDown * buffetTotalPrice) / receivablePriceAfterDiscount;
324
- order.buffet.taxes.forEach((tax) => {
325
- const taxFare = (buffetTotalPrice - sharedMarkDown) * tax.rate;
326
- const taxKey = `${tax.name}(${Big(tax.rate).times(100)}%)`;
327
- taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
328
- });
329
- }
330
- } else if (order.taxType === TaxTypeEnum.PriceHaveTaxBindOrder && !noTax) {
331
- let totalTaxRate = 0;
332
- order.priceHaveTaxBindOrderTaxes.forEach((item) => {
333
- totalTaxRate += item.rate;
334
- });
335
- const totalTaxFare = receivablePrice - receivablePrice / (totalTaxRate + 1);
336
- order.priceHaveTaxBindOrderTaxes.forEach((item) => {
337
- const taxFare = (item.rate / totalTaxRate) * totalTaxFare;
338
- const taxKey = `${item.name}(${Big(item.rate).times(100)}%)`;
339
- taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
340
- });
341
- }
342
- const taxArr = [];
343
- for (const name in taxObj) {
344
- taxArr.push({
345
- name,
346
- volume: taxObj[name],
347
- });
348
- if (order.taxType === TaxTypeEnum.PriceNoTaxBindDish) {
349
- receivablePrice += taxObj[name];
350
- }
351
- }
352
- taxArr.sort((item1, item2) => item1.name - item2.name);
353
- return {
354
- resultProcessArr,
355
- receivablePrice: parseMoney(receivablePrice),
356
- taxArr,
357
- totalPrice,
358
- totalDiscountPrice,
359
- };
360
- }
361
-
362
- /**
363
- * 计算已收金额
364
- *
365
- * @param {Array} checkoutArr 结账记录
366
- * @returns {number} 返回已收总金额
367
- */
368
- export function calcReceived(checkoutArr: Checkout[]): number {
369
- if (!checkoutArr) {
370
- checkoutArr = [];
371
- }
372
- let totalPaid = 0;
373
- checkoutArr.forEach((checkout) => {
374
- if (!checkout.retreated) {
375
- totalPaid += checkout.amount;
376
- }
377
- });
378
- return parseMoney(totalPaid);
379
- }
380
-
381
- /**
382
- * 计算剩余金额,去除尾数
383
- *
384
- * @param {number} receivablePrice 应收
385
- * @param {number} received 已收
386
- * @returns {number} 返回已收总金额
387
- */
388
- export function calcLeft(receivablePrice: number, received: number): number {
389
- const left = receivablePrice - received;
390
- return parseMoney(left);
391
- }
392
-
393
- /**
394
- * 获得可读的订单处理过程
395
- *
396
- * @param {Process} process 订单处理过程
397
- * @returns {string} 返回可读字符串
398
- */
399
- export function getReadbleProcess(process: Process): string {
400
- let result = '';
401
- if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE) {
402
- result = '打' + process.value + '折';
403
- } else {
404
- result = CONST.RECEIVABLE_PROCESSING_TYPE[process.type.toUpperCase()].TEXT;
405
- }
406
- return result;
407
- }
408
-
409
- export enum CHECK_STORE_SUBSCRIPTION_REASON {
410
- NeedBuy = 1,
411
- Expires = 2,
412
- }
413
- /**
414
- * 检查店铺是否开通了特定的功能
415
- *
416
- * @param {Store} store 店铺
417
- * @param {string} moduleId 功能id
418
- * @param {string | Date | number} now 对比时间
419
- * @returns {string} 返回可读字符串
420
- */
421
- export function checkStoreSubscription(
422
- store: Store,
423
- moduleId: string,
424
- now?: string | Date | number,
425
- ): { result: boolean; reason: CHECK_STORE_SUBSCRIPTION_REASON } {
426
- let result;
427
- let reason: CHECK_STORE_SUBSCRIPTION_REASON;
428
- // 检查永久开通
429
- if (store.subscription && store.subscription[moduleId] && store.subscription[moduleId].permanent) {
430
- return {
431
- result: true,
432
- reason,
433
- };
434
- }
435
- // 非永久开通,检查过期时间
436
- if (!(store.subscription && store.subscription[moduleId] && store.subscription[moduleId].expires)) {
437
- result = false;
438
- reason = CHECK_STORE_SUBSCRIPTION_REASON.NeedBuy;
439
- } else if (moment(store.subscription[moduleId].expires).isBefore(now ? moment(now) : moment())) {
440
- result = false;
441
- reason = CHECK_STORE_SUBSCRIPTION_REASON.Expires;
442
- } else {
443
- result = true;
444
- }
445
- return {
446
- result,
447
- reason,
448
- };
449
- }
450
-
451
- /**
452
- * 根据店铺获取当前应该用的语言
453
- *
454
- * @param {Store} store 店铺
455
- * @param {string} wantLang 想要的语言
456
- * @param {string | Date | number} now 对比时间
457
- * @returns {object} 返回当前语言和可用语言
458
- */
459
- export function getCurrentLangAndRange(
460
- store: Store,
461
- wantLang: string,
462
- now?: string | Date | number,
463
- ): { currentLang: string; availableLangs: string[] } {
464
- let result = store.defaultLang;
465
- let langs: string[] = [store.defaultLang]; //可用语言范围
466
- if (checkStoreSubscription(store, CONST.BUSINESS.SUPER_MULTI_LANGUAGE.ID, now).result) {
467
- langs = CONST.LANGUAGE.map((lang) => lang.code);
468
- } else if (checkStoreSubscription(store, CONST.BUSINESS.MULTI_LANGUAGE.ID, now).result) {
469
- langs = store.subscription.multiLanguage.allow;
470
- }
471
- if (wantLang && langs.filter((item) => wantLang.includes(item)).length > 0) {
472
- result = wantLang;
473
- }
474
- return {
475
- currentLang: result,
476
- availableLangs: langs,
477
- };
478
- }
479
-
480
- /**
481
- * 在语言对象中找到当前语言最匹配的值,
482
- * 1. 精确匹配
483
- * 2. 如果没有精确匹配,寻找默认语言
484
- * 3. 如果默认语言也没有匹配,则返回第一个不为空的语言
485
- * 4. 如果都为空,那么返回undefinded
486
- *
487
- * @param {object} names 语言
488
- * @param {string} currentLang 目标语言
489
- * @param {string} defaultLang 默认语言
490
- * @returns {string} 返回当前语言的值
491
- */
492
- export function getNameFromNames(names: object, currentLang: string, defaultLang: string): string {
493
- return (
494
- names &&
495
- (names[currentLang] || names[defaultLang] || names[Object.keys(names).filter((name) => names[name])[0]])
496
- );
497
- }
498
-
499
- /**
500
- * 根据语言返回处理过的店铺数据
501
- *
502
- * @param {Store} store 店铺
503
- * @param {string} currentLang 目标语言
504
- * @param {string} defaultLang 默认语言
505
- * @returns {object} 返回当前语言和可用语言
506
- */
507
- export function getStoreByLanguage(store: Store, currentLang: string, defaultLang: string): Store {
508
- store.name = getNameFromNames(store.names, currentLang, defaultLang) || store.name;
509
- store.description = getNameFromNames(store.descriptions, currentLang, defaultLang) || store.description;
510
- store.classifications.forEach((classification) => {
511
- classification.name = getNameFromNames(classification.names, currentLang, defaultLang) || classification.name;
512
- });
513
- store.dishes.forEach((dish) => {
514
- dish.name = getNameFromNames(dish.names, currentLang, defaultLang) || dish.name;
515
- dish.description = getNameFromNames(dish.descriptions, currentLang, defaultLang) || dish.description;
516
- });
517
- store.specifications.forEach((specifications) => {
518
- specifications.name = getNameFromNames(specifications.names, currentLang, defaultLang) || specifications.name;
519
- specifications.content.forEach((content) => {
520
- content.name = getNameFromNames(content.names, currentLang, defaultLang) || content.name;
521
- });
522
- });
523
- return store;
524
- }
525
-
526
- /**
527
- * 根据语言返回处理过的店铺数据
528
- *
529
- * @param {Content} content 订单内容
530
- * @param {string} currentLang 目标语言
531
- * @param {string} defaultLang 默认语言
532
- * @returns {Content} 返回当前语言的订单
533
- */
534
- function getContentByLanguage(content: Content, currentLang: string, defaultLang: string): Content {
535
- const dish = content.dishSnapshot;
536
- dish.name = getNameFromNames(dish.names, currentLang, defaultLang) || dish.name;
537
- if (dish.selectedSpecifications) {
538
- dish.selectedSpecifications &&
539
- dish.selectedSpecifications.forEach((specification) => {
540
- specification.name =
541
- getNameFromNames(specification.names, currentLang, defaultLang) || specification.name;
542
- specification.content.forEach((tag) => {
543
- tag.name = getNameFromNames(tag.names, currentLang, defaultLang) || tag.name;
544
- });
545
- });
546
- }
547
- return content;
548
- }
549
-
550
- /**
551
- * 根据语言返回处理过的订单数据
552
- *
553
- * @param {Order} order 店铺
554
- * @param {string} currentLang 目标语言
555
- * @param {string} defaultLang 默认语言
556
- * @returns {Order} 返回当前语言的订单
557
- */
558
- export function getOrderByLanguage(order: Order, currentLang: string, defaultLang: string): Order {
559
- if (order.content) {
560
- order.content.map((dishGroup) => {
561
- return getContentByLanguage(dishGroup, currentLang, defaultLang);
562
- });
563
- }
564
- if (order.tasks) {
565
- order.tasks.forEach((task) => {
566
- if (task.content) {
567
- task.content.map((dishGroup) => {
568
- return getContentByLanguage(dishGroup, currentLang, defaultLang);
569
- });
570
- }
571
- });
572
- }
573
- return order;
574
- }
575
-
576
- /**
577
- * 根据电话区号返回语言码,如果没找到,返回英文
578
- *
579
- * @param {string} countryCode 区号
580
- * @returns {string} 语言代码的小写格式
581
- */
582
- export function getLanguageFromCountryCode(countryCode): string {
583
- const data = {
584
- '86': 'zh',
585
- };
586
- const languageCode = data[String(countryCode)] || 'en';
587
- return languageCode;
588
- }
589
-
590
- /**
591
- * 根据dishID返回已经下单的数量
592
- *
593
- * @param {Order} order 店铺
594
- * @param dishId
595
- * @returns {number} 返回当前数量
596
- */
597
- export function getDishCountByIdInAllUncanceledTasks(order: Order, dishId: string): number {
598
- let count = 0;
599
- if (order.tasks) {
600
- count = order.tasks
601
- .filter((task) => task.status !== CONST.STATUS.CANCELLED)
602
- .reduce((count, task) => {
603
- return (
604
- count +
605
- task.content
606
- .filter((item) => String(item.dishSnapshot._id) === String(dishId))
607
- .reduce((value, item) => value + item.count, 0)
608
- );
609
- }, 0);
610
- }
611
- return count;
612
- }
1
+ import { Order } from './interfaces/order/order.interface';
2
+ import { Process, Content, Checkout, Task } from './interfaces/order/baseOrder.interface';
3
+ import { BuffetCombosItems, Store } from './interfaces/store/store.interface';
4
+ import { VisitTask } from './interfaces/visit-task/visit-task.interface';
5
+ import { User, UserSafe, CertificatePath } from './interfaces/user/user.interface';
6
+ import { DishSnapshot } from './interfaces/store/dishSnapshot.interface';
7
+ import { CONST } from './const/const';
8
+ import { PrintStatusEnum, PrinterDeviceTypeEnum, PrinterWidthEnum } from './enum/printer.enum';
9
+ import { DishType } from './enum/dish.enum';
10
+ export * from './enum/common.enum';
11
+ export * from './enum/time-menu.enum';
12
+ export * from './enum/printer.enum';
13
+ export * from './enum/member.enum';
14
+ export * from './enum/sms.enum';
15
+ export * from './enum/ai-token.enum';
16
+ export * from './enum/checkout.enum';
17
+ export * from './enum/yeepay.enum';
18
+ export * from './enum/timezone.emum';
19
+ export * from './enum/order.enum';
20
+ export * from './enum/tax.enum';
21
+ export * from './enum/dish.enum';
22
+ export * from './enum/user-status.enum';
23
+ export * from './enum/visit-task.enum';
24
+ export * from './enum/receptionist.enum';
25
+ export * from './enum/from.enum';
26
+ export * from './enum/role.enum';
27
+ export * from './enum/continent.enum';
28
+ export * from './announcement/enum/announcement.enum';
29
+ export * from './interfaces/member/member.interface';
30
+ export * from './interfaces/member/member-transaction.interface';
31
+ export * from './interfaces/store/store.interface';
32
+ export * from './interfaces/common/language.interface';
33
+ export * from './interfaces/store/subscription.interface';
34
+ export * from './interfaces/store/dish.interface';
35
+ export * from './interfaces/store/classification.interface';
36
+ export * from './interfaces/store/specification.interface';
37
+ export * from './interfaces/store/menu-export.interface';
38
+ export { MenuImportModeEnum } from './enum/menu-io.enum';
39
+ export * from './interfaces/order';
40
+ export { PayCompanyEnum } from './enum/pay-company.enum';
41
+ import * as moment from 'moment';
42
+ import Big from 'big.js';
43
+ import { TaxTypeEnum } from './enum/tax.enum';
44
+ import { OrderTypeEnum } from './enum/order.enum';
45
+ import { calcBuffetTotalPrice, getDishItemInBuffet, getMaxBuffetDishUpperLimit } from './buffet/index';
46
+ export * from './buffet/index';
47
+ export * from './order/index';
48
+ export {
49
+ Order,
50
+ Process,
51
+ Content,
52
+ Checkout,
53
+ CONST,
54
+ DishSnapshot,
55
+ User,
56
+ UserSafe,
57
+ CertificatePath,
58
+ Task,
59
+ VisitTask,
60
+ moment,
61
+ PrintStatusEnum,
62
+ PrinterDeviceTypeEnum,
63
+ PrinterWidthEnum,
64
+ Big,
65
+ };
66
+
67
+ /**
68
+ * 小数转化为金额,去除分位之后的尾数
69
+ *
70
+ * @param {number} num 需要转换的数字
71
+ * @returns {number} 返回只保留两位小数的money
72
+ */
73
+ export function parseMoney(num: number): number {
74
+ return Math.round(num * 100) / 100;
75
+ }
76
+
77
+ /**
78
+ * 计算加了规格后的菜品单价
79
+ *
80
+ * @param {Content} conent 单个菜品内容
81
+ * @returns {number} 返回订单总金额
82
+ *
83
+ */
84
+ export function calcDishFinalPrice(conent: Content): number {
85
+ let addPrice = 0;
86
+ const originPrice =
87
+ conent.dishSnapshot.type === DishType.Weigh
88
+ ? conent.weight * conent.dishSnapshot.price
89
+ : conent.dishSnapshot.price;
90
+ const selectedSpecifications = conent.dishSnapshot.selectedSpecifications || [];
91
+ selectedSpecifications.forEach((specification) => {
92
+ if (!specification) {
93
+ return;
94
+ }
95
+ specification.content.forEach((item) => {
96
+ if (item.fareType == CONST.FARE_TYPE.FIXED) {
97
+ addPrice += item.fare;
98
+ } else if (item.fareType == CONST.FARE_TYPE.PERCENTAGE) {
99
+ addPrice += (originPrice * item.fare) / 100;
100
+ }
101
+ });
102
+ });
103
+ let dishPrice = originPrice + addPrice;
104
+ if (dishPrice < 0) {
105
+ dishPrice = 0;
106
+ }
107
+ return parseMoney(dishPrice);
108
+ }
109
+
110
+ /**
111
+ * 利用订单计算总价
112
+ *
113
+ * @param {object} order 订单
114
+ * @returns {number} 返回订单总金额
115
+ *
116
+ */
117
+ export function calcTotalPrice(order: Order): number {
118
+ let totalPrice = 0;
119
+ order.content.forEach((orderContentItem) => {
120
+ totalPrice += calcContentPrice({
121
+ orderContentItem,
122
+ buffetItem: order.buffet?.snapshot,
123
+ orderType: order.type,
124
+ adultNum: order.adultNum,
125
+ childNum: order.childNum,
126
+ contents: order.content,
127
+ });
128
+ });
129
+ if (order.deliveryFee && !order.selfPickUp) {
130
+ totalPrice += order.deliveryFee;
131
+ }
132
+ if (order.type === OrderTypeEnum.Buffet) {
133
+ totalPrice += calcBuffetTotalPrice(order.buffet.snapshot, order.adultNum, order.childNum);
134
+ }
135
+ return parseMoney(totalPrice);
136
+ }
137
+
138
+ /**
139
+ *
140
+ */
141
+ export function calcContentPrice({
142
+ orderContentItem,
143
+ buffetItem,
144
+ orderType,
145
+ adultNum,
146
+ childNum,
147
+ contents,
148
+ }: {
149
+ orderContentItem: Content;
150
+ buffetItem: BuffetCombosItems;
151
+ orderType: OrderTypeEnum;
152
+ adultNum: number;
153
+ childNum: number;
154
+ contents: Content[];
155
+ }): number {
156
+ if (orderType === OrderTypeEnum.Buffet) {
157
+ const buffetDishItem = getDishItemInBuffet({ dishId: orderContentItem.dishSnapshot._id, buffet: buffetItem });
158
+ if (buffetDishItem) {
159
+ const maxUpperLimit = getMaxBuffetDishUpperLimit({
160
+ buffetDishItem,
161
+ adultNum: adultNum,
162
+ childNum: childNum,
163
+ });
164
+ const beforeCount = getOrderedCountByDishId(
165
+ orderContentItem.dishSnapshot._id,
166
+ contents,
167
+ orderContentItem._id,
168
+ );
169
+ const maxUpperLimitForThisItem = Math.max(maxUpperLimit - beforeCount, 0);
170
+ if (maxUpperLimitForThisItem >= orderContentItem.count) {
171
+ return 0;
172
+ } else {
173
+ return (orderContentItem.count - maxUpperLimitForThisItem) * calcDishFinalPrice(orderContentItem);
174
+ }
175
+ } else {
176
+ return orderContentItem.count * calcDishFinalPrice(orderContentItem);
177
+ }
178
+ } else {
179
+ return orderContentItem.count * calcDishFinalPrice(orderContentItem);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * @param dishId
185
+ * @param contents
186
+ * @param beforeKey
187
+ */
188
+ export function getOrderedCountByDishId(dishId, contents: Content[], beforeKey?: string): number {
189
+ let count = 0;
190
+ for (const content of contents.filter((content) => String(content.dishSnapshot._id) === String(dishId))) {
191
+ if (beforeKey && String(content._id) === String(beforeKey)) break;
192
+ count += content.count;
193
+ }
194
+ return count;
195
+ }
196
+
197
+ /**
198
+ * 计算处理后的应收金额
199
+ *
200
+ * @param {object} order 订单
201
+ * @param {Array} processArr 结账处理过程
202
+ * @param noTax
203
+ * @param taxEnable
204
+ * @returns {object} 返回应收订单总金额,以及处理计算过程
205
+ */
206
+ export function calcReceivablePrice(
207
+ order: Order,
208
+ processArr: Process[],
209
+ noTax = false,
210
+ ): {
211
+ resultProcessArr: Process[];
212
+ receivablePrice: number;
213
+ taxArr: {
214
+ name: string;
215
+ volume: number;
216
+ }[];
217
+ totalPrice: number;
218
+ totalDiscountPrice: number;
219
+ } {
220
+ let totalDiscountPrice = 0;
221
+ const resultProcessArr = [];
222
+ const totalPrice = calcTotalPrice(order);
223
+ let receivablePrice = totalPrice;
224
+ const sortedProcessArr = processArr.sort((a, b) => {
225
+ return (
226
+ CONST.RECEIVABLE_PROCESSING_TYPE[b.type.toUpperCase()].SORT -
227
+ CONST.RECEIVABLE_PROCESSING_TYPE[a.type.toUpperCase()].SORT
228
+ );
229
+ });
230
+ let totalMarkDown = 0;
231
+ let receivablePriceAfterDiscount = receivablePrice;
232
+ sortedProcessArr.forEach((process) => {
233
+ if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE) {
234
+ const rate = -(10 - process.value) / 10;
235
+ let volume = 0;
236
+ order.content.forEach((orderContentItem) => {
237
+ if (orderContentItem.dishSnapshot.noFullOrderDiscount) {
238
+ return;
239
+ }
240
+ const dishTotalPrice = calcContentPrice({
241
+ orderContentItem,
242
+ buffetItem: order.buffet?.snapshot,
243
+ orderType: order.type,
244
+ adultNum: order.adultNum,
245
+ childNum: order.childNum,
246
+ contents: order.content,
247
+ });
248
+ totalDiscountPrice += dishTotalPrice;
249
+ volume += dishTotalPrice * rate;
250
+ });
251
+ if (order.type === OrderTypeEnum.Buffet) {
252
+ const buffetTotalPrice = calcBuffetTotalPrice(order.buffet.snapshot, order.adultNum, order.childNum);
253
+ totalDiscountPrice += buffetTotalPrice;
254
+ volume += buffetTotalPrice * rate;
255
+ }
256
+ resultProcessArr.push({
257
+ type: process.type,
258
+ value: process.value,
259
+ volume,
260
+ });
261
+ receivablePrice += volume;
262
+ receivablePriceAfterDiscount = receivablePrice;
263
+ } else if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.MARKDOWN.TYPE) {
264
+ const volume = -process.value;
265
+ resultProcessArr.push({
266
+ type: process.type,
267
+ value: process.value,
268
+ volume,
269
+ });
270
+ receivablePrice += volume;
271
+ totalMarkDown += -volume;
272
+ } else if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.REMOVE_TAILS.TYPE) {
273
+ const pow = Math.pow(10, process.value - 2);
274
+ const result = Math.floor(receivablePrice / pow) * pow;
275
+ const volume = -(receivablePrice - result);
276
+ resultProcessArr.push({
277
+ type: process.type,
278
+ value: process.value,
279
+ volume,
280
+ });
281
+ receivablePrice += volume;
282
+ totalMarkDown += -volume;
283
+ }
284
+ });
285
+ const taxObj = {};
286
+ if (order.taxType === TaxTypeEnum.PriceNoTaxBindDish && !noTax) {
287
+ order.content.forEach((orderContentItem) => {
288
+ if (!orderContentItem.dishSnapshot.taxes || orderContentItem.dishSnapshot.taxes.length === 0) {
289
+ return;
290
+ }
291
+ const discountValue = sortedProcessArr.find(
292
+ (process) => process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE,
293
+ )?.value;
294
+ let dishTotalPrice = calcContentPrice({
295
+ orderContentItem,
296
+ buffetItem: order.buffet?.snapshot,
297
+ orderType: order.type,
298
+ adultNum: order.adultNum,
299
+ childNum: order.childNum,
300
+ contents: order.content,
301
+ });
302
+ if (discountValue && !orderContentItem.dishSnapshot.noFullOrderDiscount) {
303
+ const rate = -(10 - discountValue) / 10;
304
+ dishTotalPrice += dishTotalPrice * rate;
305
+ }
306
+ const sharedMarkDown = (totalMarkDown * dishTotalPrice) / receivablePriceAfterDiscount;
307
+ orderContentItem.dishSnapshot.taxes.forEach((tax) => {
308
+ const taxFare = (dishTotalPrice - sharedMarkDown) * tax.rate;
309
+ const taxKey = `${tax.name}(${Big(tax.rate).times(100)}%)`;
310
+ taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
311
+ });
312
+ });
313
+ // buffet price
314
+ if (order.type === OrderTypeEnum.Buffet) {
315
+ const discountValue = sortedProcessArr.find(
316
+ (process) => process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE,
317
+ )?.value;
318
+ let buffetTotalPrice = calcBuffetTotalPrice(order.buffet?.snapshot, order.adultNum, order.childNum);
319
+ if (discountValue) {
320
+ const rate = -(10 - discountValue) / 10;
321
+ buffetTotalPrice += buffetTotalPrice * rate;
322
+ }
323
+ const sharedMarkDown = (totalMarkDown * buffetTotalPrice) / receivablePriceAfterDiscount;
324
+ order.buffet.taxes.forEach((tax) => {
325
+ const taxFare = (buffetTotalPrice - sharedMarkDown) * tax.rate;
326
+ const taxKey = `${tax.name}(${Big(tax.rate).times(100)}%)`;
327
+ taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
328
+ });
329
+ }
330
+ } else if (order.taxType === TaxTypeEnum.PriceHaveTaxBindOrder && !noTax) {
331
+ let totalTaxRate = 0;
332
+ order.priceHaveTaxBindOrderTaxes.forEach((item) => {
333
+ totalTaxRate += item.rate;
334
+ });
335
+ const totalTaxFare = receivablePrice - receivablePrice / (totalTaxRate + 1);
336
+ order.priceHaveTaxBindOrderTaxes.forEach((item) => {
337
+ const taxFare = (item.rate / totalTaxRate) * totalTaxFare;
338
+ const taxKey = `${item.name}(${Big(item.rate).times(100)}%)`;
339
+ taxObj[taxKey] = (taxObj[taxKey] || 0) + parseMoney(taxFare);
340
+ });
341
+ }
342
+ const taxArr = [];
343
+ for (const name in taxObj) {
344
+ taxArr.push({
345
+ name,
346
+ volume: taxObj[name],
347
+ });
348
+ if (order.taxType === TaxTypeEnum.PriceNoTaxBindDish) {
349
+ receivablePrice += taxObj[name];
350
+ }
351
+ }
352
+ taxArr.sort((item1, item2) => item1.name - item2.name);
353
+ return {
354
+ resultProcessArr,
355
+ receivablePrice: parseMoney(receivablePrice),
356
+ taxArr,
357
+ totalPrice,
358
+ totalDiscountPrice,
359
+ };
360
+ }
361
+
362
+ /**
363
+ * 计算已收金额
364
+ *
365
+ * @param {Array} checkoutArr 结账记录
366
+ * @returns {number} 返回已收总金额
367
+ */
368
+ export function calcReceived(checkoutArr: Checkout[]): number {
369
+ if (!checkoutArr) {
370
+ checkoutArr = [];
371
+ }
372
+ let totalPaid = 0;
373
+ checkoutArr.forEach((checkout) => {
374
+ if (!checkout.retreated) {
375
+ totalPaid += checkout.amount;
376
+ }
377
+ });
378
+ return parseMoney(totalPaid);
379
+ }
380
+
381
+ /**
382
+ * 计算剩余金额,去除尾数
383
+ *
384
+ * @param {number} receivablePrice 应收
385
+ * @param {number} received 已收
386
+ * @returns {number} 返回已收总金额
387
+ */
388
+ export function calcLeft(receivablePrice: number, received: number): number {
389
+ const left = receivablePrice - received;
390
+ return parseMoney(left);
391
+ }
392
+
393
+ /**
394
+ * 获得可读的订单处理过程
395
+ *
396
+ * @param {Process} process 订单处理过程
397
+ * @returns {string} 返回可读字符串
398
+ */
399
+ export function getReadbleProcess(process: Process): string {
400
+ let result = '';
401
+ if (process.type === CONST.RECEIVABLE_PROCESSING_TYPE.DISCOUNT.TYPE) {
402
+ result = '打' + process.value + '折';
403
+ } else {
404
+ result = CONST.RECEIVABLE_PROCESSING_TYPE[process.type.toUpperCase()].TEXT;
405
+ }
406
+ return result;
407
+ }
408
+
409
+ export enum CHECK_STORE_SUBSCRIPTION_REASON {
410
+ NeedBuy = 1,
411
+ Expires = 2,
412
+ }
413
+ /**
414
+ * 检查店铺是否开通了特定的功能
415
+ *
416
+ * @param {Store} store 店铺
417
+ * @param {string} moduleId 功能id
418
+ * @param {string | Date | number} now 对比时间
419
+ * @returns {string} 返回可读字符串
420
+ */
421
+ export function checkStoreSubscription(
422
+ store: Store,
423
+ moduleId: string,
424
+ now?: string | Date | number,
425
+ ): { result: boolean; reason: CHECK_STORE_SUBSCRIPTION_REASON } {
426
+ let result;
427
+ let reason: CHECK_STORE_SUBSCRIPTION_REASON;
428
+ // 检查永久开通
429
+ if (store.subscription && store.subscription[moduleId] && store.subscription[moduleId].permanent) {
430
+ return {
431
+ result: true,
432
+ reason,
433
+ };
434
+ }
435
+ // 非永久开通,检查过期时间
436
+ if (!(store.subscription && store.subscription[moduleId] && store.subscription[moduleId].expires)) {
437
+ result = false;
438
+ reason = CHECK_STORE_SUBSCRIPTION_REASON.NeedBuy;
439
+ } else if (moment(store.subscription[moduleId].expires).isBefore(now ? moment(now) : moment())) {
440
+ result = false;
441
+ reason = CHECK_STORE_SUBSCRIPTION_REASON.Expires;
442
+ } else {
443
+ result = true;
444
+ }
445
+ return {
446
+ result,
447
+ reason,
448
+ };
449
+ }
450
+
451
+ /**
452
+ * 根据店铺获取当前应该用的语言
453
+ *
454
+ * @param {Store} store 店铺
455
+ * @param {string} wantLang 想要的语言
456
+ * @param {string | Date | number} now 对比时间
457
+ * @returns {object} 返回当前语言和可用语言
458
+ */
459
+ export function getCurrentLangAndRange(
460
+ store: Store,
461
+ wantLang: string,
462
+ now?: string | Date | number,
463
+ ): { currentLang: string; availableLangs: string[] } {
464
+ let result = store.defaultLang;
465
+ let langs: string[] = [store.defaultLang]; //可用语言范围
466
+ if (checkStoreSubscription(store, CONST.BUSINESS.SUPER_MULTI_LANGUAGE.ID, now).result) {
467
+ langs = CONST.LANGUAGE.map((lang) => lang.code);
468
+ } else if (checkStoreSubscription(store, CONST.BUSINESS.MULTI_LANGUAGE.ID, now).result) {
469
+ langs = store.subscription.multiLanguage.allow;
470
+ }
471
+ if (wantLang && langs.filter((item) => wantLang.includes(item)).length > 0) {
472
+ result = wantLang;
473
+ }
474
+ return {
475
+ currentLang: result,
476
+ availableLangs: langs,
477
+ };
478
+ }
479
+
480
+ /**
481
+ * 在语言对象中找到当前语言最匹配的值,
482
+ * 1. 精确匹配
483
+ * 2. 如果没有精确匹配,寻找默认语言
484
+ * 3. 如果默认语言也没有匹配,则返回第一个不为空的语言
485
+ * 4. 如果都为空,那么返回undefinded
486
+ *
487
+ * @param {object} names 语言
488
+ * @param {string} currentLang 目标语言
489
+ * @param {string} defaultLang 默认语言
490
+ * @returns {string} 返回当前语言的值
491
+ */
492
+ export function getNameFromNames(names: object, currentLang: string, defaultLang: string): string {
493
+ return (
494
+ names &&
495
+ (names[currentLang] || names[defaultLang] || names[Object.keys(names).filter((name) => names[name])[0]])
496
+ );
497
+ }
498
+
499
+ /**
500
+ * 根据语言返回处理过的店铺数据
501
+ *
502
+ * @param {Store} store 店铺
503
+ * @param {string} currentLang 目标语言
504
+ * @param {string} defaultLang 默认语言
505
+ * @returns {object} 返回当前语言和可用语言
506
+ */
507
+ export function getStoreByLanguage(store: Store, currentLang: string, defaultLang: string): Store {
508
+ store.name = getNameFromNames(store.names, currentLang, defaultLang) || store.name;
509
+ store.description = getNameFromNames(store.descriptions, currentLang, defaultLang) || store.description;
510
+ store.classifications.forEach((classification) => {
511
+ classification.name = getNameFromNames(classification.names, currentLang, defaultLang) || classification.name;
512
+ });
513
+ store.dishes.forEach((dish) => {
514
+ dish.name = getNameFromNames(dish.names, currentLang, defaultLang) || dish.name;
515
+ dish.description = getNameFromNames(dish.descriptions, currentLang, defaultLang) || dish.description;
516
+ });
517
+ store.specifications.forEach((specifications) => {
518
+ specifications.name = getNameFromNames(specifications.names, currentLang, defaultLang) || specifications.name;
519
+ specifications.content.forEach((content) => {
520
+ content.name = getNameFromNames(content.names, currentLang, defaultLang) || content.name;
521
+ });
522
+ });
523
+ return store;
524
+ }
525
+
526
+ /**
527
+ * 根据语言返回处理过的店铺数据
528
+ *
529
+ * @param {Content} content 订单内容
530
+ * @param {string} currentLang 目标语言
531
+ * @param {string} defaultLang 默认语言
532
+ * @returns {Content} 返回当前语言的订单
533
+ */
534
+ function getContentByLanguage(content: Content, currentLang: string, defaultLang: string): Content {
535
+ const dish = content.dishSnapshot;
536
+ dish.name = getNameFromNames(dish.names, currentLang, defaultLang) || dish.name;
537
+ if (dish.selectedSpecifications) {
538
+ dish.selectedSpecifications &&
539
+ dish.selectedSpecifications.forEach((specification) => {
540
+ specification.name =
541
+ getNameFromNames(specification.names, currentLang, defaultLang) || specification.name;
542
+ specification.content.forEach((tag) => {
543
+ tag.name = getNameFromNames(tag.names, currentLang, defaultLang) || tag.name;
544
+ });
545
+ });
546
+ }
547
+ return content;
548
+ }
549
+
550
+ /**
551
+ * 根据语言返回处理过的订单数据
552
+ *
553
+ * @param {Order} order 店铺
554
+ * @param {string} currentLang 目标语言
555
+ * @param {string} defaultLang 默认语言
556
+ * @returns {Order} 返回当前语言的订单
557
+ */
558
+ export function getOrderByLanguage(order: Order, currentLang: string, defaultLang: string): Order {
559
+ if (order.content) {
560
+ order.content.map((dishGroup) => {
561
+ return getContentByLanguage(dishGroup, currentLang, defaultLang);
562
+ });
563
+ }
564
+ if (order.tasks) {
565
+ order.tasks.forEach((task) => {
566
+ if (task.content) {
567
+ task.content.map((dishGroup) => {
568
+ return getContentByLanguage(dishGroup, currentLang, defaultLang);
569
+ });
570
+ }
571
+ });
572
+ }
573
+ return order;
574
+ }
575
+
576
+ /**
577
+ * 根据电话区号返回语言码,如果没找到,返回英文
578
+ *
579
+ * @param {string} countryCode 区号
580
+ * @returns {string} 语言代码的小写格式
581
+ */
582
+ export function getLanguageFromCountryCode(countryCode): string {
583
+ const data = {
584
+ '86': 'zh',
585
+ };
586
+ const languageCode = data[String(countryCode)] || 'en';
587
+ return languageCode;
588
+ }
589
+
590
+ /**
591
+ * 根据dishID返回已经下单的数量
592
+ *
593
+ * @param {Order} order 店铺
594
+ * @param dishId
595
+ * @returns {number} 返回当前数量
596
+ */
597
+ export function getDishCountByIdInAllUncanceledTasks(order: Order, dishId: string): number {
598
+ let count = 0;
599
+ if (order.tasks) {
600
+ count = order.tasks
601
+ .filter((task) => task.status !== CONST.STATUS.CANCELLED)
602
+ .reduce((count, task) => {
603
+ return (
604
+ count +
605
+ task.content
606
+ .filter((item) => String(item.dishSnapshot._id) === String(dishId))
607
+ .reduce((value, item) => value + item.count, 0)
608
+ );
609
+ }, 0);
610
+ }
611
+ return count;
612
+ }