UNPKG

@chevre/domain

Version:

Chevre Domain Library for Node.js

351 lines (350 loc) 22.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.authorize = authorize; const factory = require("../../../factory"); const publishOrderNumberIfNotExist_1 = require("../../transaction/placeOrder/publishOrderNumberIfNotExist"); const any_1 = require("../any"); const defaultOffer_1 = require("./authorize/defaultOffer"); const factory_1 = require("./authorize/factory"); const processStartReserve4chevre_1 = require("./authorize/processStartReserve4chevre"); const searchOffersByIds_1 = require("./searchOffersByIds"); /** * 興行オファー承認 */ function authorize(params, options) { return (repos, settings // credentials: { // jwt: JWTCredentials; // } ) => __awaiter(this, void 0, void 0, function* () { var _a; const { noOfferSpecified } = options; const { transaction, event } = yield validateCreateRequest(params)(repos); const { acceptedOffers, ticketOffers, unitPriceOffers } = yield validateAcceptedOffers({ object: params.object, event, seller: { typeOf: transaction.seller.typeOf, id: transaction.seller.id }, transaction, store: params.store, noOfferSpecified })(repos); let acceptedOffers4result = []; const now = new Date(); const orderNumber = yield (0, publishOrderNumberIfNotExist_1.publishOrderNumberIfNotExist)({ project: { id: params.project.id }, id: transaction.id, object: { orderDate: now } })(repos); // まず取引番号発行 const { transactionNumber } = yield repos.transactionNumber.publishByTimestamp({ startDate: now }); const actionAttributes = (0, factory_1.createAuthorizeSeatReservationActionAttributes)({ event, transaction, instrument: { transactionNumber } }); const action = yield repos.action.start(actionAttributes); try { const processStartReserveResult = yield (0, processStartReserve4chevre_1.processStartReserve4chevre)(Object.assign({ acceptedOffers, event, transactionNumber, transaction, availableAtOrFrom: { id: params.store.id }, ticketOffers, unitPriceOffers, validateEvent: params.validateEvent === true, validateEventOfferPeriod: params.validateEventOfferPeriod === true, orderNumber }, (typeof ((_a = params.object.broker) === null || _a === void 0 ? void 0 : _a.typeOf) === 'string') ? { broker: params.object.broker } : undefined), options)(repos, settings); acceptedOffers4result = processStartReserveResult.acceptedOffers4result; // add orderInTransaction(2024-01-15~) if (!noOfferSpecified) { // if (params.options.useCreateOrderOnOfferAccepted) { yield (0, any_1.acceptOffer)({ orderNumber, project: transaction.project, acceptedOffers: acceptedOffers4result })(repos); // } } } catch (error) { try { yield repos.action.giveUp({ typeOf: action.typeOf, id: action.id, error }); } catch (__) { // no op } throw error; } const result = (0, factory_1.acceptedOffers2authorizeResult)({ acceptedOffers, acceptedOffers4result, noOfferSpecified, ticketOffers }); yield repos.action.completeWithVoid({ typeOf: action.typeOf, id: action.id, result: result }); return { id: action.id, result }; }); } function validateCreateRequest(params) { return (repos) => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const transaction = yield repos.transaction.projectFieldsInProgressById({ typeOf: factory.transactionType.PlaceOrder, id: params.transaction.id }, ['agent', 'expires', 'project', 'seller', 'typeOf', 'instrument']); if (transaction.agent.id !== params.agent.id) { throw new factory.errors.Forbidden('Transaction not yours'); } // クライアント検証(2024-02-07~) const instrumentId = (_a = transaction.instrument) === null || _a === void 0 ? void 0 : _a.id; if (typeof instrumentId === 'string' && instrumentId !== params.store.id) { throw new factory.errors.Forbidden('client not match that of the transaction'); } if (typeof ((_b = params.object.reservationFor) === null || _b === void 0 ? void 0 : _b.id) !== 'string' || params.object.reservationFor.id.length === 0) { throw new factory.errors.ArgumentNull('object.reservationFor.id'); } // support validForMemberTier(2025-05-14~) const validForMemberTierToken = (_c = params.object.reservationFor.offers) === null || _c === void 0 ? void 0 : _c.validForMemberTier; const event = Object.assign({ id: params.object.reservationFor.id, typeOf: factory.eventType.ScreeningEvent }, (typeof validForMemberTierToken === 'string') ? { offers: { validForMemberTier: validForMemberTierToken } } : undefined); return { transaction, event }; }); } /** * 受け入れらたオファーの内容を検証 */ // tslint:disable-next-line:max-func-body-length function validateAcceptedOffers(params) { // tslint:disable-next-line:max-func-body-length return (repos) => __awaiter(this, void 0, void 0, function* () { let ticketOffers; let unitPriceOffers; let acceptedOffersWithoutDetail = params.object.acceptedOffer; if (params.noOfferSpecified === true) { if (Array.isArray(acceptedOffersWithoutDetail)) { acceptedOffersWithoutDetail = acceptedOffersWithoutDetail.map((offerWithoutDetail) => { return Object.assign(Object.assign({}, offerWithoutDetail), { id: defaultOffer_1.DEFAULT_OFFER_ID }); }); } } const offerIds = (Array.isArray(acceptedOffersWithoutDetail)) ? [...new Set(acceptedOffersWithoutDetail.map((o) => o.id))] : []; /** * 明示的なサブカタログ指定も考慮する(2024-10-01~) */ let includedInDataCatalogIdsSpecified; if (Array.isArray(acceptedOffersWithoutDetail)) { acceptedOffersWithoutDetail.forEach(({ includedInDataCatalog }) => { if (typeof (includedInDataCatalog === null || includedInDataCatalog === void 0 ? void 0 : includedInDataCatalog.id) === 'string') { if (!Array.isArray(includedInDataCatalogIdsSpecified)) { includedInDataCatalogIdsSpecified = []; } includedInDataCatalogIdsSpecified.push(includedInDataCatalog.id); } }); } if (params.noOfferSpecified === true) { if (Array.isArray(acceptedOffersWithoutDetail)) { acceptedOffersWithoutDetail = acceptedOffersWithoutDetail.map((offerWithoutDetail) => { return Object.assign(Object.assign({}, offerWithoutDetail), { id: defaultOffer_1.DEFAULT_OFFER_ID }); }); } ticketOffers = [defaultOffer_1.DEFAULT_TICKET_OFFER]; unitPriceOffers = [defaultOffer_1.DEFAULT_AVAILABLE_OFFER]; } else { // 利用可能なチケットオファーを検索 const searchEventTicketOffersResult = yield (0, searchOffersByIds_1.searchOffersByIds)({ // 受け入れたオファーIDだけ取得する(2023-01-26~) ids: offerIds, event: { id: params.event.id, typeOf: params.event.typeOf }, store: params.store, options: Object.assign({}, (Array.isArray(includedInDataCatalogIdsSpecified)) ? { includedInDataCatalog: { id: includedInDataCatalogIdsSpecified } } : undefined) // priceSpecification: {}, // 絞る必要なし // includedInDataCatalog: { id: '' }, // 承認時はカタログ指定の必要なし // onlyValid: true, // addSortIndex: false, // validateOfferRateLimit: true // useIncludeInDataCatalog: false })(repos); ticketOffers = searchEventTicketOffersResult.ticketOffers; unitPriceOffers = searchEventTicketOffersResult.unitPriceOffers; } // 利用可能なチケットオファーであれば受け入れる const acceptedOffers = (Array.isArray(acceptedOffersWithoutDetail)) ? yield Promise.all(acceptedOffersWithoutDetail.map((offerWithoutDetail) => __awaiter(this, void 0, void 0, function* () { return acceptedOfferWithoutDetail2acceptedOffer({ availableTicketOffers: ticketOffers, offerWithoutDetail, // event: params.event, transaction: params.transaction, seller: params.seller })(); }))) : []; // reserveTransaction.startへ処理を移行(2024-07-03~) // オファーIDごとにオファー適用条件を確認 // offerIds.forEach((offerId) => { // const acceptedOffersByOfferId = acceptedOffers.filter((o) => o.id === offerId); // const numAcceptedOffersByOfferId = acceptedOffersByOfferId.length; // let referenceQuantityValue = 1; // // 単価仕様を取り出す // const unitPriceSpec = acceptedOffersByOfferId[0].priceSpecification.priceComponent.find( // (spec) => spec.typeOf === factory.priceSpecificationType.UnitPriceSpecification && (!Array.isArray(spec.appliesToAddOn)) // ); // // 単価仕様は必ず存在するはず // if (unitPriceSpec?.typeOf !== factory.priceSpecificationType.UnitPriceSpecification) { // throw new factory.errors.Internal(`unitPriceSpec of the offer '${offerId}' not found`); // } // const unitPriceSpecReferenceQuantityValue = unitPriceSpec.referenceQuantity?.value; // if (typeof unitPriceSpecReferenceQuantityValue === 'number') { // referenceQuantityValue = unitPriceSpecReferenceQuantityValue; // } // // アイテム数が適用単位要件を満たしていなければエラー // if (numAcceptedOffersByOfferId % referenceQuantityValue !== 0) { // throw new factory.errors.Argument( // 'acceptedOffers', // `Offer ${offerId} requires reference quantity value ${referenceQuantityValue}` // ); // } // // 適用数量要件を満たしていなければエラー // const maxValue = unitPriceSpec.eligibleQuantity?.maxValue; // if (typeof maxValue === 'number') { // if (numAcceptedOffersByOfferId > maxValue) { // throw new factory.errors.Argument( // 'acceptedOffers', // `Number of offer:${offerId} must be less than or equal to ${maxValue}` // ); // } // } // const minValue = unitPriceSpec.eligibleQuantity?.minValue; // if (typeof minValue === 'number') { // if (numAcceptedOffersByOfferId < minValue) { // throw new factory.errors.Argument( // 'acceptedOffers', // `Number of offer:${offerId} must be more than or equal to ${minValue}` // ); // } // } // }); return { acceptedOffers, ticketOffers, unitPriceOffers }; }); } function acceptedOfferWithoutDetail2acceptedOffer(params) { // tslint:disable-next-line:cyclomatic-complexity max-func-body-length return () => __awaiter(this, void 0, void 0, function* () { var _a, _b, _c; const availableTicketOffers = params.availableTicketOffers; const offerWithoutDetail = params.offerWithoutDetail; const offer = availableTicketOffers.find((o) => o.id === offerWithoutDetail.id); if (offer === undefined) { throw new factory.errors.NotFound('Ticket Offer', `Ticket Offer ${offerWithoutDetail.id} not found`); } // ポイント特典入金先の指定があれば入金識別子を発行 const pointAward = createPointAwardByOfferWithoutDetail({ offerWithoutDetail, transaction: params.transaction }); const acceptedAppliesToMovieTicket = (_a = offerWithoutDetail.priceSpecification) === null || _a === void 0 ? void 0 : _a.appliesToMovieTicket; // 承認アクションオブジェクトのacceptedOfferにappliesToMovieTicketを連携する(2022-08-02~) // const priceSpecification: IAcceptedOfferPriceSpecification = { // // 必要な属性のみに最適化(2024-04-09~) // // ...offer.priceSpecification, // typeOf: offer.priceSpecification.typeOf, // priceCurrency: offer.priceSpecification.priceCurrency, // // valueAddedTaxIncluded: offer.priceSpecification.valueAddedTaxIncluded, // priceComponent: offer.priceSpecification.priceComponent.map(({ // name, accounting, id, priceCurrency, valueAddedTaxIncluded, ...necessaryComponentFields // }) => necessaryComponentFields), // // appliesToMovieTicket.identifierを連携する // ...(Array.isArray(acceptedAppliesToMovieTicket) || typeof acceptedAppliesToMovieTicket?.identifier === 'string') // ? { appliesToMovieTicket: acceptedAppliesToMovieTicket } // : undefined // }; const priceSpecification = Object.assign({}, (Array.isArray(acceptedAppliesToMovieTicket) || typeof (acceptedAppliesToMovieTicket === null || acceptedAppliesToMovieTicket === void 0 ? void 0 : acceptedAppliesToMovieTicket.identifier) === 'string') ? { appliesToMovieTicket: acceptedAppliesToMovieTicket } : undefined); const acceptedAddOns = (Array.isArray(offerWithoutDetail.addOn)) ? offerWithoutDetail.addOn.map((acceptedAddOnOffer) => { var _a, _b; if (typeof acceptedAddOnOffer.id !== 'string' || acceptedAddOnOffer.id.length === 0) { throw new factory.errors.ArgumentNull('object.acceptedOffer.addOn.id'); } const addOnOfferReferenceQuantityValue = (_b = (_a = acceptedAddOnOffer.priceSpecification) === null || _a === void 0 ? void 0 : _a.referenceQuantity) === null || _b === void 0 ? void 0 : _b.value; return Object.assign({ id: acceptedAddOnOffer.id }, (typeof addOnOfferReferenceQuantityValue === 'number') ? { priceSpecification: { referenceQuantity: { value: addOnOfferReferenceQuantityValue } } } : undefined); }) : []; const serviceOutputByOfferWithoutDetail = (_b = offerWithoutDetail.itemOffered) === null || _b === void 0 ? void 0 : _b.serviceOutput; // itemOfferedServiceOutputを最適化(2024-04-08~) let itemOfferedServiceOutput; if (typeof (serviceOutputByOfferWithoutDetail === null || serviceOutputByOfferWithoutDetail === void 0 ? void 0 : serviceOutputByOfferWithoutDetail.typeOf) === 'string') { const { typeOf, additionalProperty, additionalTicketText, programMembershipUsed, reservedTicket, subReservation } = serviceOutputByOfferWithoutDetail; let ticketedSeat; if (typeof ((_c = reservedTicket === null || reservedTicket === void 0 ? void 0 : reservedTicket.ticketedSeat) === null || _c === void 0 ? void 0 : _c.typeOf) === 'string') { ticketedSeat = Object.assign({ typeOf: factory.placeType.Seat, seatNumber: reservedTicket.ticketedSeat.seatNumber, seatRow: reservedTicket.ticketedSeat.seatRow, seatSection: reservedTicket.ticketedSeat.seatSection }, (Array.isArray(reservedTicket.ticketedSeat.seatingType)) ? { seatingType: reservedTicket.ticketedSeat.seatingType } : undefined); } switch (typeOf) { case factory.reservationType.BusReservation: const serviceOutputAsBusReservation = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ typeOf }, (Array.isArray(additionalProperty)) ? { additionalProperty } : undefined), (typeof additionalTicketText === 'string') ? { additionalTicketText } : undefined), (programMembershipUsed !== undefined) ? { programMembershipUsed } : undefined), (Array.isArray(subReservation)) ? { subReservation } : undefined), (reservedTicket !== undefined) ? { reservedTicket: Object.assign(Object.assign({ typeOf: 'Ticket' }, (typeof reservedTicket.identifier === 'string') ? { identifier: reservedTicket.identifier } : undefined), (typeof (ticketedSeat === null || ticketedSeat === void 0 ? void 0 : ticketedSeat.typeOf) === 'string') ? { ticketedSeat } : undefined) } : undefined); itemOfferedServiceOutput = serviceOutputAsBusReservation; break; case factory.reservationType.EventReservation: const serviceOutputAsEventReservation = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({ typeOf }, (Array.isArray(additionalProperty)) ? { additionalProperty } : undefined), (typeof additionalTicketText === 'string') ? { additionalTicketText } : undefined), (programMembershipUsed !== undefined) ? { programMembershipUsed } : undefined), (Array.isArray(subReservation)) ? { subReservation } : undefined), (reservedTicket !== undefined) ? { reservedTicket: Object.assign(Object.assign({ typeOf: 'Ticket' }, (typeof reservedTicket.identifier === 'string') ? { identifier: reservedTicket.identifier } : undefined), (typeof (ticketedSeat === null || ticketedSeat === void 0 ? void 0 : ticketedSeat.typeOf) === 'string') ? { ticketedSeat } : undefined) } : undefined); itemOfferedServiceOutput = serviceOutputAsEventReservation; break; default: // no op } } return { // オファーの中身を最適化する(必要最低限の情報のみに) id: String(offer.id), // identifier: offer.identifier, priceSpecification, // typeOf: offer.typeOf, // priceCurrency: offer.priceCurrency, itemOffered: Object.assign(Object.assign({}, (itemOfferedServiceOutput !== undefined) ? { serviceOutput: itemOfferedServiceOutput } : undefined), (pointAward !== undefined) ? { pointAward } : undefined), addOn: acceptedAddOns, // 追加属性をマージ additionalProperty: [ ...(Array.isArray(offerWithoutDetail.additionalProperty)) ? offerWithoutDetail.additionalProperty : [], ...(Array.isArray(offer.additionalProperty)) ? offer.additionalProperty : [] ] // ...(Array.isArray(offer.acceptedPaymentMethod)) // discontinue(2024-07-03~) // ? { acceptedPaymentMethod: offer.acceptedPaymentMethod } // : undefined }; }); } function createPointAwardByOfferWithoutDetail(params) { var _a, _b, _c, _d, _e, _f; const offerWithoutDetail = params.offerWithoutDetail; // ポイント特典入金先の指定があれば入金識別子を発行 let pointAward; const pointAwardToLocationIdentifier = (_c = (_b = (_a = offerWithoutDetail.itemOffered) === null || _a === void 0 ? void 0 : _a.pointAward) === null || _b === void 0 ? void 0 : _b.toLocation) === null || _c === void 0 ? void 0 : _c.identifier; const pointAwardToLocationIssuedThroughId = (_f = (_e = (_d = offerWithoutDetail.itemOffered) === null || _d === void 0 ? void 0 : _d.pointAward) === null || _e === void 0 ? void 0 : _e.toLocation) === null || _f === void 0 ? void 0 : _f.issuedThrough.id; if (typeof pointAwardToLocationIdentifier === 'string' && pointAwardToLocationIdentifier.length > 0 && typeof pointAwardToLocationIssuedThroughId === 'string' && pointAwardToLocationIssuedThroughId.length > 0) { pointAward = { toLocation: { identifier: pointAwardToLocationIdentifier, issuedThrough: { id: pointAwardToLocationIssuedThroughId } }, typeOf: factory.actionType.MoneyTransfer, // purpose.identifierはChevre側で自動指定 // purpose: { identifier: pointAwardPurposeIdentifier }, // ひとまずrecipientは強制的に指定(指定しないと入金できない) recipient: { id: params.transaction.agent.id, name: `${params.transaction.agent.name}`, typeOf: params.transaction.agent.typeOf } }; } return pointAward; }