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