@chevre/domain 21.2.0-alpha.99 → 21.2.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 (162) hide show
  1. package/example/src/chevre/aggregateEventReservation.ts +1 -1
  2. package/example/src/chevre/aggregateSellerPaymentAccepted.ts +27 -0
  3. package/example/src/chevre/aggregation/aggregateSystem.ts +53 -22
  4. package/example/src/chevre/countDelayedTasks.ts +7 -2
  5. package/example/src/chevre/createManyEventsIfNotExist.ts +199 -46
  6. package/example/src/chevre/findScreeningRoomsByBranchCode.ts +4 -3
  7. package/example/src/chevre/giveUpStartDatePassedCertainPeriod.ts +56 -0
  8. package/example/src/chevre/lockStockHolder.ts +5 -2
  9. package/example/src/chevre/migrateCategoryCodeAdditionalProperties.ts +116 -0
  10. package/example/src/chevre/migrateScreeningEventSeriesVersion.ts +79 -0
  11. package/example/src/chevre/processRegisterMembership.ts +8 -4
  12. package/example/src/chevre/processRegisterPaymentCard.ts +8 -4
  13. package/example/src/chevre/processReserve.ts +1 -1
  14. package/example/src/chevre/searchAbortedTasks.ts +4 -6
  15. package/example/src/chevre/searchEventSeats.ts +5 -2
  16. package/example/src/chevre/searchHoldReservations.ts +38 -0
  17. package/example/src/chevre/searchPermissions.ts +54 -0
  18. package/example/src/chevre/searchScreeningRooms.ts +35 -0
  19. package/example/src/chevre/sendEmailMessage.ts +1 -2
  20. package/example/src/chevre/syncScreeningRooms.ts +22 -0
  21. package/example/src/chevre/syncScreeningRoomsAll.ts +44 -0
  22. package/example/src/chevre/unsetContainsInPlaceFromMovieTheater.ts +41 -0
  23. package/lib/chevre/factory/order.d.ts +4 -1
  24. package/lib/chevre/factory/order.js +19 -6
  25. package/lib/chevre/repo/account.js +3 -2
  26. package/lib/chevre/repo/accountTransaction.js +2 -1
  27. package/lib/chevre/repo/accountingReport.d.ts +1 -3
  28. package/lib/chevre/repo/action.d.ts +50 -1
  29. package/lib/chevre/repo/action.js +53 -2
  30. package/lib/chevre/repo/additionalProperty.js +2 -1
  31. package/lib/chevre/repo/aggregation.d.ts +3 -0
  32. package/lib/chevre/repo/aggregation.js +3 -0
  33. package/lib/chevre/repo/assetTransaction.js +6 -5
  34. package/lib/chevre/repo/categoryCode.d.ts +1 -1
  35. package/lib/chevre/repo/categoryCode.js +37 -25
  36. package/lib/chevre/repo/code.js +3 -2
  37. package/lib/chevre/repo/comment.js +2 -1
  38. package/lib/chevre/repo/creativeWork.d.ts +1 -1
  39. package/lib/chevre/repo/creativeWork.js +35 -12
  40. package/lib/chevre/repo/customer.js +2 -1
  41. package/lib/chevre/repo/emailMessage.js +2 -1
  42. package/lib/chevre/repo/event.d.ts +30 -2
  43. package/lib/chevre/repo/event.js +87 -36
  44. package/lib/chevre/repo/member.d.ts +14 -0
  45. package/lib/chevre/repo/member.js +31 -3
  46. package/lib/chevre/repo/merchantReturnPolicy.js +2 -1
  47. package/lib/chevre/repo/mongoose/schemas/assetTransaction.d.ts +3 -3
  48. package/lib/chevre/repo/mongoose/schemas/holdReservation.d.ts +75 -0
  49. package/lib/chevre/repo/mongoose/schemas/holdReservation.js +93 -0
  50. package/lib/chevre/repo/mongoose/schemas/offer.d.ts +3 -3
  51. package/lib/chevre/repo/mongoose/schemas/order.d.ts +21 -21
  52. package/lib/chevre/repo/mongoose/schemas/place.d.ts +6 -0
  53. package/lib/chevre/repo/mongoose/schemas/place.js +16 -1
  54. package/lib/chevre/repo/mongoose/schemas/product.js +6 -0
  55. package/lib/chevre/repo/mongoose/schemas/reservation.d.ts +9 -9
  56. package/lib/chevre/repo/mongoose/schemas/transaction.d.ts +3 -3
  57. package/lib/chevre/repo/offer.js +3 -2
  58. package/lib/chevre/repo/offerCatalog.js +0 -27
  59. package/lib/chevre/repo/offerItemCondition.js +2 -1
  60. package/lib/chevre/repo/order.js +8 -7
  61. package/lib/chevre/repo/ownershipInfo.js +2 -7
  62. package/lib/chevre/repo/paymentServiceProvider.d.ts +65 -0
  63. package/lib/chevre/repo/paymentServiceProvider.js +156 -0
  64. package/lib/chevre/repo/place.d.ts +203 -27
  65. package/lib/chevre/repo/place.js +1726 -694
  66. package/lib/chevre/repo/priceSpecification.js +3 -2
  67. package/lib/chevre/repo/product.d.ts +5 -3
  68. package/lib/chevre/repo/product.js +92 -5
  69. package/lib/chevre/repo/project.js +2 -1
  70. package/lib/chevre/repo/reservation.d.ts +9 -1
  71. package/lib/chevre/repo/reservation.js +29 -20
  72. package/lib/chevre/repo/role.d.ts +7 -0
  73. package/lib/chevre/repo/role.js +39 -2
  74. package/lib/chevre/repo/seller.d.ts +21 -1
  75. package/lib/chevre/repo/seller.js +59 -4
  76. package/lib/chevre/repo/serviceOutput.js +2 -1
  77. package/lib/chevre/repo/stockHolder.d.ts +143 -7
  78. package/lib/chevre/repo/stockHolder.js +622 -104
  79. package/lib/chevre/repo/task.d.ts +12 -1
  80. package/lib/chevre/repo/task.js +17 -7
  81. package/lib/chevre/repo/transaction.js +6 -5
  82. package/lib/chevre/repo/trip.js +2 -1
  83. package/lib/chevre/repository.d.ts +6 -0
  84. package/lib/chevre/repository.js +8 -1
  85. package/lib/chevre/service/aggregation/event/aggregateScreeningEvent.js +18 -8
  86. package/lib/chevre/service/aggregation/event/aggregateUseActionsOnEvent.js +19 -8
  87. package/lib/chevre/service/aggregation/system.d.ts +43 -1
  88. package/lib/chevre/service/aggregation/system.js +112 -2
  89. package/lib/chevre/service/assetTransaction/moneyTransfer.js +1 -1
  90. package/lib/chevre/service/assetTransaction/pay/account/validation.js +1 -1
  91. package/lib/chevre/service/assetTransaction/pay.js +2 -2
  92. package/lib/chevre/service/assetTransaction/refund.js +1 -1
  93. package/lib/chevre/service/assetTransaction/reserve.js +10 -3
  94. package/lib/chevre/service/event.d.ts +3 -0
  95. package/lib/chevre/service/event.js +52 -21
  96. package/lib/chevre/service/iam.d.ts +6 -2
  97. package/lib/chevre/service/iam.js +23 -9
  98. package/lib/chevre/service/offer/event/factory.js +22 -13
  99. package/lib/chevre/service/offer/event/importFromCOA.js +2 -2
  100. package/lib/chevre/service/offer/eventServiceByCOA/factory.js +15 -8
  101. package/lib/chevre/service/offer/eventServiceByCOA.js +7 -9
  102. package/lib/chevre/service/offer/product/factory.js +13 -6
  103. package/lib/chevre/service/offer/product.js +2 -2
  104. package/lib/chevre/service/offer.d.ts +16 -8
  105. package/lib/chevre/service/offer.js +71 -8
  106. package/lib/chevre/service/order/createAccountingReportIfNotExist.js +31 -17
  107. package/lib/chevre/service/order/onOrderStatusChanged/factory.js +1 -1
  108. package/lib/chevre/service/order/onOrderStatusChanged.js +2 -2
  109. package/lib/chevre/service/order/placeOrder.js +4 -4
  110. package/lib/chevre/service/order/returnOrder.js +1 -1
  111. package/lib/chevre/service/order/sendOrder.js +3 -3
  112. package/lib/chevre/service/payment/any/onPaymentStatusChanged.js +4 -6
  113. package/lib/chevre/service/payment/any/onRefund.js +46 -42
  114. package/lib/chevre/service/payment/creditCard.js +2 -2
  115. package/lib/chevre/service/payment/movieTicket/checkByIdentifier.d.ts +1 -1
  116. package/lib/chevre/service/payment/movieTicket/factory.d.ts +1 -1
  117. package/lib/chevre/service/payment/movieTicket/getCredentials.d.ts +1 -1
  118. package/lib/chevre/service/payment/movieTicket/getCredentials.js +1 -1
  119. package/lib/chevre/service/payment/movieTicket/validation.js +16 -9
  120. package/lib/chevre/service/payment/movieTicket.js +2 -2
  121. package/lib/chevre/service/reserve/cancelReservation.js +15 -3
  122. package/lib/chevre/service/reserve/confirmReservation.js +19 -11
  123. package/lib/chevre/service/task/aggregateScreeningEvent.js +1 -1
  124. package/lib/chevre/service/task/cancelPendingReservation.js +1 -1
  125. package/lib/chevre/service/task/cancelReservation.js +1 -1
  126. package/lib/chevre/service/task/deleteTransaction.js +2 -0
  127. package/lib/chevre/service/task/onEventChanged.d.ts +7 -0
  128. package/lib/chevre/service/task/onEventChanged.js +29 -0
  129. package/lib/chevre/service/task/onResourceUpdated.d.ts +7 -0
  130. package/lib/chevre/service/task/onResourceUpdated.js +396 -0
  131. package/lib/chevre/service/task/syncScreeningRooms.d.ts +7 -0
  132. package/lib/chevre/service/task/syncScreeningRooms.js +24 -0
  133. package/lib/chevre/service/task/voidReserveTransaction.js +1 -1
  134. package/lib/chevre/service/transaction/deleteTransaction.d.ts +2 -0
  135. package/lib/chevre/service/transaction/deleteTransaction.js +129 -1
  136. package/lib/chevre/service/transaction/moneyTransfer/exportTasks/factory.js +1 -0
  137. package/lib/chevre/service/transaction/moneyTransfer/factory.d.ts +1 -1
  138. package/lib/chevre/service/transaction/moneyTransfer.js +1 -1
  139. package/lib/chevre/service/transaction/orderProgramMembership/findCreditCard.js +1 -1
  140. package/lib/chevre/service/transaction/orderProgramMembership/findPaymentCardPermit.js +1 -1
  141. package/lib/chevre/service/transaction/orderProgramMembership.js +1 -1
  142. package/lib/chevre/service/transaction/placeOrder/exportTasks/factory.js +1 -0
  143. package/lib/chevre/service/transaction/placeOrderInProgress/potentialActions/givePointAward.js +1 -1
  144. package/lib/chevre/service/transaction/placeOrderInProgress/potentialActions/moneyTransfer.js +1 -1
  145. package/lib/chevre/service/transaction/placeOrderInProgress/potentialActions/registerService.js +1 -1
  146. package/lib/chevre/service/transaction/placeOrderInProgress/potentialActions/sendEmailMessage.js +4 -4
  147. package/lib/chevre/service/transaction/placeOrderInProgress/potentialActions.js +3 -4
  148. package/lib/chevre/service/transaction/placeOrderInProgress/result/acceptedOffers.js +8 -12
  149. package/lib/chevre/service/transaction/placeOrderInProgress.js +6 -4
  150. package/lib/chevre/service/transaction/returnOrder/exportTasks/factory.js +1 -0
  151. package/lib/chevre/service/transaction/returnOrder/potentialActions/returnMoneyTransfer.js +2 -2
  152. package/lib/chevre/service/transaction/returnOrder/potentialActions/returnPaymentMethod.js +5 -5
  153. package/lib/chevre/service/transaction/returnOrder/potentialActions/returnPointAward.js +2 -2
  154. package/lib/chevre/service/transaction/returnOrder/potentialActions/sendEmailMessage.js +3 -3
  155. package/lib/chevre/service/transaction/returnOrder/potentialActions.js +1 -1
  156. package/lib/chevre/service/transaction/returnOrder.js +3 -1
  157. package/lib/chevre/settings.d.ts +11 -0
  158. package/lib/chevre/settings.js +44 -2
  159. package/package.json +3 -3
  160. package/example/src/chevre/eventCatalog2eventService.ts +0 -123
  161. package/example/src/chevre/migrateOrderAdditionalProperties.ts +0 -85
  162. package/example/src/chevre/migrateStockHolderKeys.ts +0 -89
@@ -12,30 +12,60 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.StockHolderRepository = void 0;
13
13
  const createDebug = require("debug");
14
14
  const moment = require("moment");
15
+ const mongoose_1 = require("mongoose");
15
16
  const factory = require("../factory");
17
+ const holdReservation_1 = require("./mongoose/schemas/holdReservation");
16
18
  const settings_1 = require("../settings");
17
- const debug = createDebug('chevre-domain:repo');
19
+ const debug = createDebug('chevre-domain:repo:stockHolder');
18
20
  /**
19
21
  * イベントストックホルダーリポジトリ
20
22
  */
21
23
  class StockHolderRepository {
22
- constructor(redisClient) {
24
+ constructor(redisClient, connection) {
23
25
  this.redisClient = redisClient;
26
+ this.holdReservationModel = connection.model(holdReservation_1.modelName, holdReservation_1.schema);
24
27
  }
25
- // public static GET_KEY(params: {
26
- // eventId: string;
27
- // startDate: Date;
28
- // }) {
29
- // return StockHolderRepository.createKey(params);
30
- // }
31
- static offer2field(params) {
28
+ static offer2field(params, hasTicketedSeat) {
29
+ var _a, _b;
30
+ if (hasTicketedSeat) {
31
+ return `${params.seatSection}:${params.seatNumber}`;
32
+ }
33
+ else {
34
+ // 予約IDをfieldにする場合
35
+ const serviceOutputId = (_b = (_a = params.itemOffered) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.id;
36
+ if (typeof serviceOutputId === 'string') {
37
+ return serviceOutputId;
38
+ }
39
+ else {
40
+ throw new factory.errors.ServiceUnavailable('offer2field requires itemOffered.serviceOutput.id');
41
+ }
42
+ }
43
+ }
44
+ static offer2subReservation(params, hasTicketedSeat) {
32
45
  var _a, _b;
33
- // 予約IDをfieldにする場合
34
- const serviceOutputId = (_b = (_a = params.itemOffered) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.id;
35
- if (typeof serviceOutputId === 'string') {
36
- return serviceOutputId;
46
+ if (hasTicketedSeat) {
47
+ return {
48
+ identifier: `${params.seatSection}:${params.seatNumber}`,
49
+ reservedTicket: {
50
+ ticketedSeat: {
51
+ seatSection: params.seatSection,
52
+ seatNumber: params.seatNumber
53
+ }
54
+ }
55
+ };
56
+ }
57
+ else {
58
+ const serviceOutputId = (_b = (_a = params.itemOffered) === null || _a === void 0 ? void 0 : _a.serviceOutput) === null || _b === void 0 ? void 0 : _b.id;
59
+ if (typeof serviceOutputId === 'string') {
60
+ return {
61
+ id: serviceOutputId,
62
+ identifier: serviceOutputId
63
+ };
64
+ }
65
+ else {
66
+ throw new factory.errors.ServiceUnavailable('offer2subReservation requires itemOffered.serviceOutput.id');
67
+ }
37
68
  }
38
- return `${params.seatSection}:${params.seatNumber}`;
39
69
  }
40
70
  static createKey(params) {
41
71
  if (!(params.startDate instanceof Date)) {
@@ -50,69 +80,169 @@ class StockHolderRepository {
50
80
  return `${StockHolderRepository.KEY_PREFIX}:${params.eventId}`;
51
81
  }
52
82
  }
83
+ /**
84
+ * 新リポジトリを使用するかどうか
85
+ */
86
+ static useMongoose(params) {
87
+ if (!(params.startDate instanceof Date)) {
88
+ throw new factory.errors.Argument('startDate', 'must be Date');
89
+ }
90
+ if (params.useMongooseForcibly === true) {
91
+ return true;
92
+ }
93
+ // USE_NEW_STOCK_HOLDER_REPO_IDSに含まれれば強制的に使用
94
+ if (settings_1.USE_NEW_STOCK_HOLDER_REPO_IDS.includes(params.eventId)) {
95
+ return true;
96
+ }
97
+ // 座席有の場合のみ、USE_NEW_STOCK_HOLDER_REPO_FROMを検証
98
+ if (params.hasTicketedSeat) {
99
+ if (moment(params.startDate)
100
+ .isSameOrAfter(settings_1.USE_NEW_STOCK_HOLDER_REPO_FROM)) {
101
+ return true;
102
+ }
103
+ }
104
+ return false;
105
+ }
53
106
  /**
54
107
  * 座席をロックする(maxキャパシティチェック有)
55
108
  */
56
109
  lockIfNotLimitExceeded(lockKey, maximum) {
57
110
  return __awaiter(this, void 0, void 0, function* () {
58
- const key = StockHolderRepository.createKey({ eventId: lockKey.eventId, startDate: lockKey.startDate });
59
- yield this.redisClient.watch(key);
60
- const hashCount = yield this.redisClient.hLen(key);
61
- // Process result
62
- // Heavy and time consuming operation here
63
- debug('checking hash count...hashCount:', hashCount);
64
- if (hashCount + lockKey.offers.length > maximum) {
65
- throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
66
- }
67
- yield this.lock(lockKey);
111
+ const useMongoose = StockHolderRepository.useMongoose({
112
+ eventId: lockKey.eventId,
113
+ startDate: lockKey.startDate,
114
+ hasTicketedSeat: lockKey.hasTicketedSeat
115
+ });
116
+ if (useMongoose) {
117
+ if (!(lockKey.expires instanceof Date)) {
118
+ throw new factory.errors.Argument('expires', 'must be Date');
119
+ }
120
+ yield this.checkIfConflicted({ key: '', eventId: lockKey.eventId, useMongoose });
121
+ // reservationCount<=nであれば$push+$incする
122
+ const { id } = yield this.initializeHoldReservation({ project: lockKey.project, eventId: lockKey.eventId, startDate: lockKey.startDate });
123
+ const addedReservationCount = lockKey.offers.length;
124
+ const reservationCountLte = maximum - addedReservationCount;
125
+ const subReservations = lockKey.offers.map((offer) => StockHolderRepository.offer2subReservation(offer, lockKey.hasTicketedSeat));
126
+ const reservationPackage = {
127
+ // typeOf: factory.reservationType.ReservationPackage,
128
+ reservationNumber: lockKey.holder,
129
+ subReservation: subReservations
130
+ };
131
+ yield this.holdReservationModel.findOneAndUpdate({
132
+ _id: { $eq: id },
133
+ reservationCount: { $lte: reservationCountLte }
134
+ // 'reservationFor.id': { $eq: lockKey.eventId },
135
+ // 座席有無に関わらずsubReservation.identifierはuniqueであるはず
136
+ // 'reservations.subReservation.identifier': {
137
+ // $nin: subReservations.map((r) => r.identifier)
138
+ // }
139
+ }, {
140
+ $inc: { reservationCount: addedReservationCount },
141
+ $push: { reservations: reservationPackage }
142
+ }, { new: true })
143
+ .select({ _id: 1 })
144
+ .exec()
145
+ .then((doc) => {
146
+ if (doc === null) {
147
+ throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
148
+ }
149
+ });
150
+ // 重複検証
151
+ yield this.checkIfAlreadyInUse({ reservationPackage, id });
152
+ }
153
+ else {
154
+ const key = StockHolderRepository.createKey({ eventId: lockKey.eventId, startDate: lockKey.startDate });
155
+ yield this.redisClient.watch(key);
156
+ const hashCount = yield this.redisClient.hLen(key);
157
+ // Process result
158
+ // Heavy and time consuming operation here
159
+ debug('checking hash count...hashCount:', hashCount);
160
+ if (hashCount + lockKey.offers.length > maximum) {
161
+ throw new factory.errors.Argument('Event', 'maximumAttendeeCapacity exceeded');
162
+ }
163
+ yield this.lock(lockKey);
164
+ }
68
165
  });
69
166
  }
70
167
  /**
71
168
  * 座席をロックする
72
169
  */
170
+ // tslint:disable-next-line:max-func-body-length
73
171
  lock(lockKey) {
74
172
  return __awaiter(this, void 0, void 0, function* () {
75
173
  if (!(lockKey.expires instanceof Date)) {
76
174
  throw new factory.errors.Argument('expires', 'must be Date');
77
175
  }
78
- const key = StockHolderRepository.createKey({ eventId: lockKey.eventId, startDate: lockKey.startDate });
79
- yield this.checkIfConflicted({ key, eventId: lockKey.eventId });
80
- const value = lockKey.holder;
81
- const multi = this.redisClient.multi();
82
- const fields = lockKey.offers.map((offer) => StockHolderRepository.offer2field(offer));
83
- fields.forEach((field) => {
84
- multi.hSetNX(key, field, value);
176
+ const useMongoose = StockHolderRepository.useMongoose({
177
+ eventId: lockKey.eventId,
178
+ startDate: lockKey.startDate,
179
+ useMongooseForcibly: lockKey.useMongooseForcibly,
180
+ hasTicketedSeat: lockKey.hasTicketedSeat
85
181
  });
86
- const results = yield multi.expireAt(key, moment(lockKey.expires)
87
- .unix())
88
- .exec();
89
- const lockedFields = [];
90
- if (Array.isArray(results)) {
91
- results.slice(0, fields.length)
92
- .forEach((r, index) => {
93
- if (r === 1 || r === true) {
94
- lockedFields.push(fields[index]);
95
- }
96
- });
182
+ const key = StockHolderRepository.createKey({ eventId: lockKey.eventId, startDate: lockKey.startDate });
183
+ yield this.checkIfConflicted({ key, eventId: lockKey.eventId, useMongoose });
184
+ if (useMongoose) {
185
+ const { id } = yield this.initializeHoldReservation({ project: lockKey.project, eventId: lockKey.eventId, startDate: lockKey.startDate });
186
+ const addedReservationCount = lockKey.offers.length;
187
+ const subReservations = lockKey.offers.map((offer) => StockHolderRepository.offer2subReservation(offer, lockKey.hasTicketedSeat));
188
+ const reservationPackage = {
189
+ // typeOf: factory.reservationType.ReservationPackage,
190
+ reservationNumber: lockKey.holder,
191
+ subReservation: subReservations
192
+ };
193
+ yield this.holdReservationModel.updateOne({
194
+ _id: { $eq: id }
195
+ // 'reservationFor.id': { $eq: lockKey.eventId },
196
+ // 座席有無に関わらずsubReservation.identifierはuniqueであるはず
197
+ // 'reservations.subReservation.identifier': {
198
+ // $nin: subReservations.map((r) => r.identifier)
199
+ // }
200
+ }, {
201
+ $inc: { reservationCount: addedReservationCount },
202
+ $push: { reservations: reservationPackage }
203
+ })
204
+ .exec();
205
+ // 重複検証
206
+ yield this.checkIfAlreadyInUse({ reservationPackage, id });
97
207
  }
98
- const lockedAll = lockedFields.length === fields.length;
99
- debug('lockedAll?', lockedAll);
100
- // expireAtReplyの検証も追加する(2023-04-19~)
101
- const expiredAll = results.slice(fields.length)
102
- .every((r) => (r === 1 || r === true));
103
- debug('expiredAll?', expiredAll);
104
- if (!lockedAll || !expiredAll) {
105
- if (lockedFields.length > 0) {
106
- // 全て仮押さえできなければ仮押さえできたものは解除
107
- yield this.redisClient.multi()
108
- .hDel(key, lockedFields)
109
- .exec();
110
- }
111
- if (!lockedAll) {
112
- throw new factory.errors.AlreadyInUse(factory.reservationType.EventReservation, ['ticketedSeat'], 'Already hold');
208
+ else {
209
+ const value = lockKey.holder;
210
+ const multi = this.redisClient.multi();
211
+ const fields = lockKey.offers.map((offer) => StockHolderRepository.offer2field(offer, lockKey.hasTicketedSeat));
212
+ fields.forEach((field) => {
213
+ multi.hSetNX(key, field, value);
214
+ });
215
+ const results = yield multi.expireAt(key, moment(lockKey.expires)
216
+ .unix())
217
+ .exec();
218
+ const lockedFields = [];
219
+ if (Array.isArray(results)) {
220
+ results.slice(0, fields.length)
221
+ .forEach((r, index) => {
222
+ if (r === 1 || r === true) {
223
+ lockedFields.push(fields[index]);
224
+ }
225
+ });
113
226
  }
114
- else {
115
- throw new factory.errors.ServiceUnavailable('timeout cannot be set unexpectedly');
227
+ const lockedAll = lockedFields.length === fields.length;
228
+ debug('lockedAll?', lockedAll);
229
+ // expireAtReplyの検証も追加する(2023-04-19~)
230
+ const expiredAll = results.slice(fields.length)
231
+ .every((r) => (r === 1 || r === true));
232
+ debug('expiredAll?', expiredAll);
233
+ if (!lockedAll || !expiredAll) {
234
+ if (lockedFields.length > 0) {
235
+ // 全て仮押さえできなければ仮押さえできたものは解除
236
+ yield this.redisClient.multi()
237
+ .hDel(key, lockedFields)
238
+ .exec();
239
+ }
240
+ if (!lockedAll) {
241
+ throw new factory.errors.AlreadyInUse(factory.reservationType.EventReservation, ['ticketedSeat'], 'Already hold');
242
+ }
243
+ else {
244
+ throw new factory.errors.ServiceUnavailable('timeout cannot be set unexpectedly');
245
+ }
116
246
  }
117
247
  }
118
248
  });
@@ -122,55 +252,169 @@ class StockHolderRepository {
122
252
  */
123
253
  unlock(params) {
124
254
  return __awaiter(this, void 0, void 0, function* () {
255
+ const useMongoose = StockHolderRepository.useMongoose({
256
+ eventId: params.eventId,
257
+ startDate: params.startDate,
258
+ hasTicketedSeat: params.hasTicketedSeat
259
+ });
125
260
  const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
126
- yield this.checkIfConflicted({ key, eventId: params.eventId });
127
- const field = StockHolderRepository.offer2field(params.offer);
128
- yield this.redisClient.multi()
129
- .hDel(key, field)
130
- .exec();
261
+ yield this.checkIfConflicted({ key, eventId: params.eventId, useMongoose });
262
+ if (useMongoose) {
263
+ // [id]あるいは[seatNumber+seatSection]でreservations.subReservationsから$pull+$incする
264
+ const { id } = yield this.initializeHoldReservation({ project: params.project, eventId: params.eventId, startDate: params.startDate });
265
+ const subReservation = StockHolderRepository.offer2subReservation(params.offer, params.hasTicketedSeat);
266
+ const reservationNumber = params.holder;
267
+ const updateResult = yield this.holdReservationModel.updateOne({
268
+ _id: { $eq: id },
269
+ // 'reservationFor.id': { $eq: params.eventId },
270
+ 'reservations.reservationNumber': {
271
+ $exists: true,
272
+ $eq: reservationNumber
273
+ },
274
+ 'reservations.subReservation.identifier': {
275
+ $exists: true,
276
+ $eq: subReservation.identifier
277
+ }
278
+ }, {
279
+ $inc: { reservationCount: -1 },
280
+ $pull: { 'reservations.$[reservationPackage].subReservation': { identifier: { $eq: subReservation.identifier } } }
281
+ }, {
282
+ arrayFilters: [
283
+ { 'reservationPackage.reservationNumber': reservationNumber }
284
+ ]
285
+ })
286
+ .exec();
287
+ // docが存在しなくてもよい
288
+ debug('unlock processed. updateResult:', updateResult, 'reservationNumber:', reservationNumber, 'identifier:', subReservation.identifier);
289
+ // subReservationがemptyのreservationsをpull
290
+ const pullReservationPackageResult = yield this.holdReservationModel.updateOne({
291
+ _id: { $eq: id },
292
+ // 'reservationFor.id': { $eq: params.eventId },
293
+ 'reservations.reservationNumber': { $exists: true, $eq: reservationNumber }
294
+ }, {
295
+ $pull: {
296
+ reservations: {
297
+ reservationNumber: { $eq: reservationNumber },
298
+ subReservation: { $size: 0 }
299
+ }
300
+ }
301
+ })
302
+ .exec();
303
+ debug('unlock processed. pullReservationPackageResult:', pullReservationPackageResult, 'reservationNumber:', reservationNumber, 'identifier:', subReservation.identifier);
304
+ }
305
+ else {
306
+ const field = StockHolderRepository.offer2field(params.offer, params.hasTicketedSeat);
307
+ yield this.redisClient.multi()
308
+ .hDel(key, field)
309
+ .exec();
310
+ }
131
311
  });
132
312
  }
133
313
  /**
134
314
  * 空席でない座席を検索する
135
315
  */
136
- findUnavailableOffersByEventId(params) {
137
- return __awaiter(this, void 0, void 0, function* () {
138
- const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
139
- const reply = yield this.redisClient.hGetAll(key);
140
- let offers = [];
141
- if (reply !== null) {
142
- offers = Object.keys(reply)
143
- .map((field) => {
144
- const seatSection = field.split(':')[0];
145
- const seatNumber = field.split(':')[1];
146
- return { seatSection, seatNumber };
147
- });
148
- }
149
- return offers;
150
- });
151
- }
316
+ // public async findUnavailableOffersByEventId(params: { eventId: string; startDate: Date }): Promise<IOffer[]> {
317
+ // const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
318
+ // const reply = await this.redisClient.hGetAll(key);
319
+ // let offers: IOffer[] = [];
320
+ // if (reply !== null) {
321
+ // offers = Object.keys(reply)
322
+ // .map((field) => {
323
+ // const seatSection = field.split(':')[0];
324
+ // const seatNumber = field.split(':')[1];
325
+ // return { seatSection, seatNumber };
326
+ // });
327
+ // }
328
+ // return offers;
329
+ // }
152
330
  /**
153
331
  * 空席でない座席をカウントする
154
332
  */
155
333
  countUnavailableOffers(params) {
156
334
  return __awaiter(this, void 0, void 0, function* () {
157
- const key = StockHolderRepository.createKey({ eventId: params.event.id, startDate: params.event.startDate });
158
- const reply = yield this.redisClient.hLen(key);
159
- let fieldCount = 0;
160
- if (typeof reply === 'number') {
161
- fieldCount = Number(reply);
335
+ if (StockHolderRepository.useMongoose({
336
+ eventId: params.event.id,
337
+ startDate: params.event.startDate,
338
+ hasTicketedSeat: params.event.hasTicketedSeat
339
+ })) {
340
+ // reservationCountを返す
341
+ return this.holdReservationModel.findOne({
342
+ 'reservationFor.id': { $eq: params.event.id }
343
+ })
344
+ .select({ reservationCount: 1 })
345
+ .exec()
346
+ .then((doc) => {
347
+ if (doc === null) {
348
+ return 0;
349
+ }
350
+ else {
351
+ return doc.reservationCount;
352
+ }
353
+ });
354
+ }
355
+ else {
356
+ const key = StockHolderRepository.createKey({ eventId: params.event.id, startDate: params.event.startDate });
357
+ const reply = yield this.redisClient.hLen(key);
358
+ let fieldCount = 0;
359
+ if (typeof reply === 'number') {
360
+ fieldCount = Number(reply);
361
+ }
362
+ return fieldCount;
162
363
  }
163
- return fieldCount;
164
364
  });
165
365
  }
166
366
  /**
167
367
  * 保持者を取得する
168
368
  */
169
369
  getHolder(params) {
370
+ var _a;
170
371
  return __awaiter(this, void 0, void 0, function* () {
171
- const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
172
- const field = StockHolderRepository.offer2field(params.offer);
173
- return this.redisClient.hGet(key, field);
372
+ if (StockHolderRepository.useMongoose({
373
+ eventId: params.eventId,
374
+ startDate: params.startDate,
375
+ hasTicketedSeat: params.hasTicketedSeat
376
+ })) {
377
+ // [id]あるいは[seatNumber+seatSection]でreservationNumberを返す
378
+ const subReservation = StockHolderRepository.offer2subReservation(params.offer, params.hasTicketedSeat);
379
+ const matchStages = [
380
+ {
381
+ $match: { 'reservationFor.id': { $eq: params.eventId } }
382
+ },
383
+ {
384
+ $match: {
385
+ 'reservations.subReservation.identifier': {
386
+ $exists: true,
387
+ $eq: subReservation.identifier
388
+ }
389
+ }
390
+ }
391
+ ];
392
+ const aggregate = this.holdReservationModel.aggregate([
393
+ { $unwind: '$reservations' },
394
+ { $unwind: '$reservations.subReservation' },
395
+ ...matchStages,
396
+ {
397
+ $project: {
398
+ _id: 0,
399
+ reservationNumber: '$reservations.reservationNumber',
400
+ identifier: '$reservations.subReservation.identifier'
401
+ }
402
+ }
403
+ ]);
404
+ const subReservations = yield aggregate.exec();
405
+ debug('getHolder found subReservations.', subReservation.identifier, subReservations);
406
+ if (subReservations.length > 0) {
407
+ return String((_a = subReservations.shift()) === null || _a === void 0 ? void 0 : _a.reservationNumber);
408
+ }
409
+ else {
410
+ return;
411
+ }
412
+ }
413
+ else {
414
+ const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
415
+ const field = StockHolderRepository.offer2field(params.offer, params.hasTicketedSeat);
416
+ return this.redisClient.hGet(key, field);
417
+ }
174
418
  });
175
419
  }
176
420
  /**
@@ -207,17 +451,60 @@ class StockHolderRepository {
207
451
  // }
208
452
  searchHolders(params) {
209
453
  return __awaiter(this, void 0, void 0, function* () {
210
- const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
211
- const fields = params.offers.map((o) => {
212
- return StockHolderRepository.offer2field(o);
213
- });
214
- // Array reply: list of values associated with the given fields, in the same order as they are requested.
215
- const result = yield this.redisClient.hmGet(key, fields);
216
- if (!Array.isArray(result)) {
217
- throw new factory.errors.ServiceUnavailable(`searchAvailability got non-array: ${typeof result}`);
454
+ if (StockHolderRepository.useMongoose({
455
+ eventId: params.eventId,
456
+ startDate: params.startDate,
457
+ hasTicketedSeat: params.hasTicketedSeat
458
+ })) {
459
+ // [id]あるいは[seatNumber+seatSection]のリストでreservationNumberのリストを返す
460
+ const subReservations = params.offers.map((offer) => StockHolderRepository.offer2subReservation(offer, params.hasTicketedSeat));
461
+ const matchStages = [
462
+ {
463
+ $match: { 'reservationFor.id': { $eq: params.eventId } }
464
+ },
465
+ {
466
+ $match: {
467
+ 'reservations.subReservation.identifier': {
468
+ $exists: true,
469
+ $in: subReservations.map((r) => r.identifier)
470
+ }
471
+ }
472
+ }
473
+ ];
474
+ const aggregate = this.holdReservationModel.aggregate([
475
+ { $unwind: '$reservations' },
476
+ { $unwind: '$reservations.subReservation' },
477
+ ...matchStages,
478
+ {
479
+ $project: {
480
+ _id: 0,
481
+ reservationNumber: '$reservations.reservationNumber',
482
+ identifier: '$reservations.subReservation.identifier'
483
+ }
484
+ }
485
+ ]);
486
+ const subReservationsByDoc = yield aggregate.exec();
487
+ return subReservations.map((subReservation) => {
488
+ const subReservationByDoc = subReservationsByDoc.find((r) => r.identifier === subReservation.identifier);
489
+ return (subReservationByDoc !== undefined)
490
+ ? String(subReservationByDoc.reservationNumber)
491
+ // tslint:disable-next-line:no-null-keyword
492
+ : null;
493
+ });
494
+ }
495
+ else {
496
+ const key = StockHolderRepository.createKey({ eventId: params.eventId, startDate: params.startDate });
497
+ const fields = params.offers.map((o) => {
498
+ return StockHolderRepository.offer2field(o, params.hasTicketedSeat);
499
+ });
500
+ // Array reply: list of values associated with the given fields, in the same order as they are requested.
501
+ const result = yield this.redisClient.hmGet(key, fields);
502
+ if (!Array.isArray(result)) {
503
+ throw new factory.errors.ServiceUnavailable(`searchAvailability got non-array: ${typeof result}`);
504
+ }
505
+ // そのまま返却(2023-04-17~)
506
+ return result;
218
507
  }
219
- // そのまま返却(2023-04-17~)
220
- return result;
221
508
  });
222
509
  }
223
510
  // public async migrateKey(params: {
@@ -244,26 +531,257 @@ class StockHolderRepository {
244
531
  // }
245
532
  // }
246
533
  // }
534
+ /**
535
+ * 汎用的な検索(mongooseのみ対応)
536
+ */
537
+ // tslint:disable-next-line:max-func-body-length
538
+ search(params) {
539
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
540
+ return __awaiter(this, void 0, void 0, function* () {
541
+ const matchStages = [];
542
+ const projectIdEq = (_b = (_a = params.project) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.$eq;
543
+ if (typeof projectIdEq === 'string') {
544
+ matchStages.push({ $match: { 'project.id': { $eq: projectIdEq } } });
545
+ }
546
+ const reservationForIdEq = (_d = (_c = params.reservationFor) === null || _c === void 0 ? void 0 : _c.id) === null || _d === void 0 ? void 0 : _d.$eq;
547
+ if (typeof reservationForIdEq === 'string') {
548
+ matchStages.push({ $match: { 'reservationFor.id': { $eq: reservationForIdEq } } });
549
+ }
550
+ const reservationNumberEq = (_e = params.reservationNumber) === null || _e === void 0 ? void 0 : _e.$eq;
551
+ if (typeof reservationNumberEq === 'string') {
552
+ matchStages.push({ $match: { 'reservations.reservationNumber': { $exists: true, $eq: reservationNumberEq } } });
553
+ }
554
+ const identifierEq = (_f = params.identifier) === null || _f === void 0 ? void 0 : _f.$eq;
555
+ if (typeof identifierEq === 'string') {
556
+ matchStages.push({ $match: { 'reservations.subReservation.identifier': { $exists: true, $eq: identifierEq } } });
557
+ }
558
+ const idEq = (_g = params.id) === null || _g === void 0 ? void 0 : _g.$eq;
559
+ if (typeof idEq === 'string') {
560
+ matchStages.push({
561
+ $match: { 'reservations.subReservation.id': { $exists: true, $eq: idEq } }
562
+ });
563
+ }
564
+ const seatNumberEq = (_k = (_j = (_h = params.reservedTicket) === null || _h === void 0 ? void 0 : _h.ticketedSeat) === null || _j === void 0 ? void 0 : _j.seatNumber) === null || _k === void 0 ? void 0 : _k.$eq;
565
+ if (typeof seatNumberEq === 'string') {
566
+ matchStages.push({
567
+ $match: {
568
+ 'reservations.subReservation.reservedTicket.ticketedSeat.seatNumber': {
569
+ $exists: true,
570
+ $eq: seatNumberEq
571
+ }
572
+ }
573
+ });
574
+ }
575
+ const seatSectionEq = (_o = (_m = (_l = params.reservedTicket) === null || _l === void 0 ? void 0 : _l.ticketedSeat) === null || _m === void 0 ? void 0 : _m.seatSection) === null || _o === void 0 ? void 0 : _o.$eq;
576
+ if (typeof seatSectionEq === 'string') {
577
+ matchStages.push({
578
+ $match: {
579
+ 'reservations.subReservation.reservedTicket.ticketedSeat.seatSection': {
580
+ $exists: true,
581
+ $eq: seatSectionEq
582
+ }
583
+ }
584
+ });
585
+ }
586
+ const aggregate = this.holdReservationModel.aggregate([
587
+ ...(typeof ((_p = params.sort) === null || _p === void 0 ? void 0 : _p['reservationFor.startDate']) === 'number')
588
+ ? [{ $sort: { 'reservationFor.startDate': params.sort['reservationFor.startDate'] } }]
589
+ : [],
590
+ {
591
+ $unwind: {
592
+ path: '$reservations',
593
+ includeArrayIndex: 'reservationPackageIndex'
594
+ }
595
+ },
596
+ {
597
+ $unwind: {
598
+ path: '$reservations.subReservation',
599
+ includeArrayIndex: 'subReservationIndex'
600
+ }
601
+ },
602
+ ...matchStages,
603
+ {
604
+ $project: {
605
+ _id: 0,
606
+ id: '$reservations.subReservation.id',
607
+ identifier: '$reservations.subReservation.identifier',
608
+ reservationFor: {
609
+ id: '$reservationFor.id',
610
+ startDate: '$reservationFor.startDate',
611
+ aggregateReservation: {
612
+ reservationCount: '$reservationCount'
613
+ }
614
+ },
615
+ reservationNumber: '$reservations.reservationNumber',
616
+ reservedTicket: '$reservations.subReservation.reservedTicket',
617
+ objectSize: { $bsonSize: '$$ROOT' },
618
+ reservationPackageIndex: '$reservationPackageIndex',
619
+ subReservationIndex: '$subReservationIndex'
620
+ }
621
+ }
622
+ ]);
623
+ if (typeof params.limit === 'number' && params.limit > 0) {
624
+ const page = (typeof params.page === 'number' && params.page > 0) ? params.page : 1;
625
+ aggregate.limit(params.limit * page)
626
+ .skip(params.limit * (page - 1));
627
+ }
628
+ return aggregate.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
629
+ .exec();
630
+ });
631
+ }
632
+ /**
633
+ * 汎用的な集計(mongooseのみ対応)
634
+ */
635
+ aggregateByReservationFor(params) {
636
+ var _a, _b, _c, _d;
637
+ return __awaiter(this, void 0, void 0, function* () {
638
+ const matchStages = [];
639
+ const projectIdEq = (_b = (_a = params.project) === null || _a === void 0 ? void 0 : _a.id) === null || _b === void 0 ? void 0 : _b.$eq;
640
+ if (typeof projectIdEq === 'string') {
641
+ matchStages.push({ $match: { 'project.id': { $eq: projectIdEq } } });
642
+ }
643
+ const reservationForIdEq = (_d = (_c = params.reservationFor) === null || _c === void 0 ? void 0 : _c.id) === null || _d === void 0 ? void 0 : _d.$eq;
644
+ if (typeof reservationForIdEq === 'string') {
645
+ matchStages.push({ $match: { 'reservationFor.id': { $eq: reservationForIdEq } } });
646
+ }
647
+ const aggregate = this.holdReservationModel.aggregate([
648
+ ...matchStages,
649
+ {
650
+ $project: {
651
+ _id: 0,
652
+ typeOf: '$typeOf',
653
+ reservationCount: '$reservationCount',
654
+ reservationFor: '$reservationFor',
655
+ reservationPackageCount: { $size: '$reservations' },
656
+ objectSize: { $bsonSize: '$$ROOT' }
657
+ }
658
+ }
659
+ ]);
660
+ if (typeof params.limit === 'number' && params.limit > 0) {
661
+ const page = (typeof params.page === 'number' && params.page > 0) ? params.page : 1;
662
+ aggregate.limit(params.limit * page)
663
+ .skip(params.limit * (page - 1));
664
+ }
665
+ return aggregate.option({ maxTimeMS: settings_1.MONGO_MAX_TIME_MS })
666
+ .exec();
667
+ });
668
+ }
247
669
  checkIfConflicted(params) {
248
670
  return __awaiter(this, void 0, void 0, function* () {
249
671
  // 旧キーと新キーの両方存在検証(念のため)
250
672
  const oldKey = StockHolderRepository.createKey({ eventId: params.eventId, startDate: new Date('2020-01-01T00:00:00Z') });
251
673
  const newKey = StockHolderRepository.createKey({ eventId: params.eventId, startDate: new Date('2030-01-01T00:00:00Z') });
252
- if (params.key !== oldKey) {
674
+ if (params.useMongoose) {
675
+ // newもoldも存在するはずがない
253
676
  // newの場合oldが存在するはずがない
254
677
  const existingOldKeyCount = yield this.redisClient.exists(oldKey);
255
678
  debug('existingOldKeyCount:', existingOldKeyCount);
256
679
  if (existingOldKeyCount > 0) {
257
- throw new factory.errors.ServiceUnavailable('stockHolder keys conflicted');
680
+ throw new factory.errors.ServiceUnavailable(`stockHolder storage conflicted. eventId:${params.eventId}`);
258
681
  }
259
- }
260
- if (params.key !== newKey) {
261
682
  // oldの場合newが存在するはずがない
262
683
  const existingNewKeyCount = yield this.redisClient.exists(newKey);
263
684
  debug('existingNewKeyCount:', existingNewKeyCount);
264
685
  if (existingNewKeyCount > 0) {
265
- throw new factory.errors.ServiceUnavailable('stockHolder keys conflicted');
686
+ throw new factory.errors.ServiceUnavailable(`stockHolder storage conflicted. eventId:${params.eventId}`);
687
+ }
688
+ }
689
+ else {
690
+ // collectionにdocumentが存在するはずがない
691
+ const existingHoldReservationCount = yield this.holdReservationModel.count({ 'reservationFor.id': { $eq: params.eventId } })
692
+ .exec();
693
+ debug('existingHoldReservationCount:', existingHoldReservationCount);
694
+ if (existingHoldReservationCount > 0) {
695
+ throw new factory.errors.ServiceUnavailable(`stockHolder storage conflicted. eventId:${params.eventId}`);
696
+ }
697
+ if (params.key !== oldKey) {
698
+ // newの場合oldが存在するはずがない
699
+ const existingOldKeyCount = yield this.redisClient.exists(oldKey);
700
+ debug('existingOldKeyCount:', existingOldKeyCount);
701
+ if (existingOldKeyCount > 0) {
702
+ throw new factory.errors.ServiceUnavailable(`stockHolder keys conflicted. eventId:${params.eventId}`);
703
+ }
704
+ }
705
+ if (params.key !== newKey) {
706
+ // oldの場合newが存在するはずがない
707
+ const existingNewKeyCount = yield this.redisClient.exists(newKey);
708
+ debug('existingNewKeyCount:', existingNewKeyCount);
709
+ if (existingNewKeyCount > 0) {
710
+ throw new factory.errors.ServiceUnavailable(`stockHolder keys conflicted. eventId:${params.eventId}`);
711
+ }
712
+ }
713
+ }
714
+ });
715
+ }
716
+ initializeHoldReservation(params) {
717
+ return __awaiter(this, void 0, void 0, function* () {
718
+ if (!(params.startDate instanceof Date)) {
719
+ throw new factory.errors.Argument('startDate', 'must be Date');
720
+ }
721
+ const aggregateReservation = {
722
+ project: { id: params.project.id, typeOf: factory.organizationType.Project },
723
+ typeOf: 'AggregateReservation',
724
+ reservationCount: 0,
725
+ reservationFor: {
726
+ id: params.eventId,
727
+ startDate: params.startDate
728
+ },
729
+ reservations: []
730
+ };
731
+ const initializedResult = yield this.holdReservationModel.findOneAndUpdate({ 'reservationFor.id': { $eq: params.eventId } }, {
732
+ $setOnInsert: aggregateReservation
733
+ }, { new: true, upsert: true })
734
+ .select({ _id: 1 })
735
+ .exec();
736
+ debug('holdReservation initialized', initializedResult, params.eventId);
737
+ return { id: initializedResult.id };
738
+ });
739
+ }
740
+ /**
741
+ * 仮で追加したholdReservationsが重複していないかどうか検証する
742
+ */
743
+ checkIfAlreadyInUse(params) {
744
+ return __awaiter(this, void 0, void 0, function* () {
745
+ const objectId = new mongoose_1.Types.ObjectId(params.id);
746
+ const matchStages = [
747
+ { $match: { _id: { $eq: objectId } } },
748
+ {
749
+ $match: {
750
+ 'reservations.subReservation.identifier': {
751
+ $exists: true,
752
+ $in: params.reservationPackage.subReservation.map((r) => r.identifier)
753
+ }
754
+ }
755
+ }
756
+ ];
757
+ const aggregate = this.holdReservationModel.aggregate([
758
+ { $unwind: '$reservations' },
759
+ { $unwind: '$reservations.subReservation' },
760
+ ...matchStages,
761
+ {
762
+ $project: {
763
+ _id: 0,
764
+ reservationNumber: '$reservations.reservationNumber'
765
+ }
766
+ },
767
+ {
768
+ $group: {
769
+ _id: '$reservationNumber'
770
+ }
266
771
  }
772
+ ]);
773
+ const subReservationsByDoc = yield aggregate.exec();
774
+ debug('checkIfAlreadyInUse:subReservationsByDoc:', subReservationsByDoc);
775
+ if (subReservationsByDoc.length > 1) {
776
+ // 仮追加したsubReservationsを削除
777
+ debug('canceling hold reservationPackage...reservationNumber:', params.reservationPackage.reservationNumber);
778
+ const updateOneResult = yield this.holdReservationModel.updateOne({ _id: { $eq: objectId } }, {
779
+ $inc: { reservationCount: -params.reservationPackage.subReservation.length },
780
+ $pull: { reservations: { reservationNumber: { $eq: params.reservationPackage.reservationNumber } } }
781
+ })
782
+ .exec();
783
+ debug('hold reservationPackage canceled. reservationNumber:', params.reservationPackage.reservationNumber, updateOneResult);
784
+ throw new factory.errors.AlreadyInUse(factory.reservationType.EventReservation, ['ticketedSeat'], 'Already hold');
267
785
  }
268
786
  });
269
787
  }