@wepublish/api
Version:
API core for we.publish.
339 lines • 15.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SubscriptionFlowService = void 0;
const tslib_1 = require("tslib");
const common_1 = require("@nestjs/common");
const subscription_flow_type_1 = require("./subscription-flow.type");
const client_1 = require("@prisma/client");
const SUBSCRIPTION_EVENT_MAX_DAYS_BEFORE = -25;
const SUBSCRIPTION_EVENT_MAX_DAYS_AFTER = 90;
let SubscriptionFlowService = exports.SubscriptionFlowService = class SubscriptionFlowService {
constructor(prismaService) {
this.prismaService = prismaService;
}
getFlows(defaultFlowOnly, memberPlanId) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
let where = {};
if (defaultFlowOnly) {
where = {
default: true
};
}
else if (memberPlanId !== undefined) {
// do not pass undefined member plan id.
where = {
OR: [
{
memberPlanId
},
{
memberPlanId: null
}
]
};
}
return yield this.prismaService.subscriptionFlow.findMany({
where,
orderBy: {
default: 'desc'
},
include: {
memberPlan: true,
paymentMethods: true,
intervals: {
include: {
mailTemplate: true
}
}
}
});
});
}
createFlow(flow) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (!flow.periodicities.length || !flow.autoRenewal.length || !flow.paymentMethodIds.length) {
throw new common_1.BadRequestException('Its not allowed to create subscription flow with no periodicities OR autoRenewal OR paymentMethods');
}
if (yield this.filterHasOverlap(flow.memberPlanId, flow)) {
throw new common_1.BadRequestException("You can't create this flow because there is a filter overlap!");
}
yield this.prismaService.subscriptionFlow.create({
data: {
default: false,
memberPlan: {
connect: {
id: flow.memberPlanId
}
},
paymentMethods: {
connect: flow.paymentMethodIds.map(paymentMethodId => ({ id: paymentMethodId }))
},
periodicities: flow.periodicities,
autoRenewal: flow.autoRenewal,
intervals: {
create: [
{
daysAwayFromEnding: -14,
event: client_1.SubscriptionEvent.INVOICE_CREATION,
mailTemplate: undefined
},
{
daysAwayFromEnding: 5,
event: client_1.SubscriptionEvent.DEACTIVATION_UNPAID,
mailTemplate: undefined
}
]
}
}
});
return this.getFlows(false);
});
}
updateFlow(flow) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if ((flow.periodicities && !flow.periodicities.length) ||
(flow.autoRenewal && !flow.autoRenewal.length) ||
(flow.paymentMethodIds && !flow.paymentMethodIds.length)) {
throw new common_1.BadRequestException('Its not allowed to update subscription flow with no periodicities OR autoRenewal OR paymentMethods');
}
const originalFlow = yield this.prismaService.subscriptionFlow.findUnique({
where: {
id: flow.id
},
include: {
paymentMethods: true
}
});
if (!originalFlow) {
throw new common_1.NotFoundException(`The subscription flow '${flow.id}' could not be found.`);
}
if (yield this.filterHasOverlap(originalFlow.memberPlanId, flow)) {
throw new common_1.BadRequestException("You can't update this flow because there is a filter overlap!");
}
yield this.prismaService.$transaction([
this.prismaService.subscriptionFlow.update({
where: { id: flow.id },
data: {
paymentMethods: {
disconnect: originalFlow.paymentMethods.map(paymentMethod => ({ id: paymentMethod.id }))
}
}
}),
this.prismaService.subscriptionFlow.update({
where: { id: flow.id },
data: {
paymentMethods: flow.paymentMethodIds
? {
connect: flow.paymentMethodIds.map(paymentMethodId => ({ id: paymentMethodId }))
}
: undefined,
periodicities: flow.periodicities,
autoRenewal: flow.autoRenewal
}
})
]);
return this.getFlows(false);
});
}
deleteFlow(subscriptionFlowId) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const originalFlow = yield this.prismaService.subscriptionFlow.findUnique({
where: {
id: subscriptionFlowId
},
include: {
paymentMethods: true,
intervals: true
}
});
if (!originalFlow) {
throw new common_1.NotFoundException('The given filter is not found!');
}
if (originalFlow.default) {
throw new common_1.BadRequestException("It's not allowed to delete default flow!");
}
yield this.prismaService.$transaction([
this.prismaService.subscriptionFlow.delete({
where: {
id: subscriptionFlowId
}
}),
this.prismaService.subscriptionInterval.deleteMany({
where: {
id: {
in: originalFlow.intervals.map(additionalInterval => additionalInterval.id)
}
}
})
]);
return this.getFlows(false);
});
}
createInterval(interval) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
yield this.isIntervalValid(interval);
yield this.prismaService.subscriptionInterval.create({
data: {
daysAwayFromEnding: interval.daysAwayFromEnding,
subscriptionFlow: {
connect: {
id: interval.subscriptionFlowId
}
},
event: interval.event,
mailTemplate: interval.mailTemplateId
? {
connect: {
id: interval.mailTemplateId
}
}
: {}
}
});
return this.getFlows(false);
});
}
updateInterval(interval) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const eventToUpdate = yield this.prismaService.subscriptionInterval.findUnique({
where: {
id: interval.id
}
});
if (!eventToUpdate) {
throw new common_1.NotFoundException('The given interval not found!');
}
yield this.isIntervalValid(Object.assign({ event: eventToUpdate.event, subscriptionFlowId: eventToUpdate.subscriptionFlowId }, interval), false);
yield this.prismaService.$transaction([
this.prismaService.subscriptionInterval.update({
where: { id: interval.id },
data: { mailTemplate: { disconnect: true } }
}),
this.prismaService.subscriptionInterval.update({
where: {
id: interval.id
},
data: {
mailTemplate: interval.mailTemplateId
? {
connect: {
id: interval.mailTemplateId
}
}
: {},
daysAwayFromEnding: interval.daysAwayFromEnding
}
})
]);
return this.getFlows(false);
});
}
deleteInterval(id) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const intervalToDelete = yield this.prismaService.subscriptionInterval.findUnique({
where: {
id
}
});
if (!intervalToDelete) {
throw new common_1.NotFoundException('The given interval not found!');
}
if (subscription_flow_type_1.subscriptionFlowRequiredEvents.includes(intervalToDelete.event)) {
throw new common_1.BadRequestException(`Its not allowed to delete a required ${intervalToDelete.event} event! `);
}
yield this.prismaService.subscriptionInterval.delete({
where: {
id
}
});
return this.getFlows(false);
});
}
filterHasOverlap(memberPlanId, newFlow) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
const whereClause = memberPlanId ? { memberPlan: { id: memberPlanId } } : {};
const allFlows = yield this.prismaService.subscriptionFlow.findMany({
where: whereClause,
select: {
id: true,
paymentMethods: {
select: {
id: true
}
},
periodicities: true,
autoRenewal: true
}
});
for (const flow of allFlows) {
// skip itself
if (newFlow.id === flow.id) {
continue;
}
const existingPM = new Set(flow.paymentMethods.map(pm => pm.id));
const newPM = new Set((newFlow.paymentMethodIds || []).map(pm => pm));
const existingPe = new Set(flow.periodicities);
const newPe = new Set(newFlow.periodicities);
const existingAr = new Set(flow.autoRenewal);
const newAr = new Set(newFlow.autoRenewal);
// find filter values that are the same as the existing filter values
const pmIntersection = new Set([...newPM].filter(x => existingPM.has(x)));
const peIntersection = new Set([...newPe].filter(x => existingPe.has(x)));
const arIntersection = new Set([...newAr].filter(x => existingAr.has(x)));
// if any of the filter intersection are not empty, means that the filter combination exists already.
if (pmIntersection.size !== 0 && peIntersection.size !== 0 && arIntersection.size !== 0) {
return true;
}
}
return false;
});
}
isIntervalValid(interval, checkEventUniqueConstraint = true) {
return tslib_1.__awaiter(this, void 0, void 0, function* () {
if (interval.daysAwayFromEnding === null || interval.daysAwayFromEnding === undefined) {
if (!subscription_flow_type_1.subscriptionFlowDaysAwayFromEndingNeedToBeNull.includes(interval.event)) {
throw new common_1.BadRequestException(`For event ${interval.event} daysAwayFromEnding can not be null!`);
}
}
else if (subscription_flow_type_1.subscriptionFlowDaysAwayFromEndingNeedToBeNull.includes(interval.event)) {
throw new common_1.BadRequestException(`For event ${interval.event} daysAwayFromEnding needs to be null!`);
}
if (checkEventUniqueConstraint && !subscription_flow_type_1.subscriptionFlowNonUniqueEvents.includes(interval.event)) {
const dbIntervals = yield this.prismaService.subscriptionInterval.findMany({
where: {
subscriptionFlow: {
id: interval.subscriptionFlowId
}
}
});
for (const dbInterval of dbIntervals) {
if (dbInterval.event === interval.event) {
throw new common_1.BadRequestException(`For each subscription flow its not allowed to have more than one events of the type ${interval.event}`);
}
}
}
// Limit daysAwayFromEnding
if (!!interval.daysAwayFromEnding &&
(interval.daysAwayFromEnding < SUBSCRIPTION_EVENT_MAX_DAYS_BEFORE ||
interval.daysAwayFromEnding > SUBSCRIPTION_EVENT_MAX_DAYS_AFTER)) {
throw new common_1.BadRequestException(`daysAwayFromEnding is not in allowed range of ${SUBSCRIPTION_EVENT_MAX_DAYS_BEFORE} to ${SUBSCRIPTION_EVENT_MAX_DAYS_AFTER}: ${interval.daysAwayFromEnding}`);
}
// check for special daysAwayFromEnding event constraints
if (interval.event === client_1.SubscriptionEvent.INVOICE_CREATION) {
if (!!interval.daysAwayFromEnding && interval.daysAwayFromEnding > 0) {
throw new common_1.BadRequestException(`It's not possible to set event ${interval.event} to a date later as the subscription is renewed`);
}
}
if (interval.event === client_1.SubscriptionEvent.DEACTIVATION_UNPAID) {
if (!!interval.daysAwayFromEnding && interval.daysAwayFromEnding < 0) {
throw new common_1.BadRequestException(`It's not possible to set event ${interval.event} to a date before as the subscription is renewed`);
}
}
});
}
};
exports.SubscriptionFlowService = SubscriptionFlowService = tslib_1.__decorate([
(0, common_1.Injectable)(),
tslib_1.__metadata("design:paramtypes", [client_1.PrismaClient])
], SubscriptionFlowService);
//# sourceMappingURL=subscription-flow.service.js.map