import { Model, mixin } from 'objection'; import SystemModel from '@/system/models/SystemModel'; import moment from 'moment'; import SubscriptionPeriod from '@/services/Subscription/SubscriptionPeriod'; import { SubscriptionPaymentStatus } from '@/interfaces'; export default class PlanSubscription extends mixin(SystemModel) { public lemonSubscriptionId: number; public endsAt: Date; public startsAt: Date; public canceledAt: Date; public trialEndsAt: Date; public paymentStatus: SubscriptionPaymentStatus; /** * Table name. */ static get tableName() { return 'subscription_plan_subscriptions'; } /** * Timestamps columns. */ get timestamps() { return ['createdAt', 'updatedAt']; } /** * Defined virtual attributes. */ static get virtualAttributes() { return [ 'active', 'inactive', 'ended', 'canceled', 'onTrial', 'status', 'isPaymentFailed', 'isPaymentSucceed', ]; } /** * Modifiers queries. */ static get modifiers() { return { activeSubscriptions(builder) { const dateFormat = 'YYYY-MM-DD HH:mm:ss'; const now = moment().format(dateFormat); builder.where('ends_at', '>', now); builder.where('trial_ends_at', '>', now); }, inactiveSubscriptions(builder) { builder.modify('endedTrial'); builder.modify('endedPeriod'); }, subscriptionBySlug(builder, subscriptionSlug) { builder.where('slug', subscriptionSlug); }, endedTrial(builder) { const dateFormat = 'YYYY-MM-DD HH:mm:ss'; const endDate = moment().format(dateFormat); builder.where('ends_at', '<=', endDate); }, endedPeriod(builder) { const dateFormat = 'YYYY-MM-DD HH:mm:ss'; const endDate = moment().format(dateFormat); builder.where('trial_ends_at', '<=', endDate); }, /** * Filter the failed payment. * @param builder */ failedPayment(builder) { builder.where('payment_status', SubscriptionPaymentStatus.Failed); }, /** * Filter the succeed payment. * @param builder */ succeedPayment(builder) { builder.where('payment_status', SubscriptionPaymentStatus.Succeed); }, }; } /** * Relations mappings. */ static get relationMappings() { const Tenant = require('system/models/Tenant'); const Plan = require('system/models/Subscriptions/Plan'); return { /** * Plan subscription belongs to tenant. */ tenant: { relation: Model.BelongsToOneRelation, modelClass: Tenant.default, join: { from: 'subscription_plan_subscriptions.tenantId', to: 'tenants.id', }, }, /** * Plan description belongs to plan. */ plan: { relation: Model.BelongsToOneRelation, modelClass: Plan.default, join: { from: 'subscription_plan_subscriptions.planId', to: 'subscription_plans.id', }, }, }; } /** * Check if the subscription is active. * Crtiria should be active: * - During the trial period should NOT be canceled. * - Out of trial period should NOT be ended. * @return {Boolean} */ public active() { return this.onTrial() ? !this.canceled() : !this.ended(); } /** * Check if the subscription is inactive. * @return {Boolean} */ public inactive() { return !this.active(); } /** * Check if paid subscription period has ended. * @return {Boolean} */ public ended() { return this.endsAt ? moment().isAfter(this.endsAt) : false; } /** * Check if the paid subscription has started. * @returns {Boolean} */ public started() { return this.startsAt ? moment().isAfter(this.startsAt) : false; } /** * Check if subscription is currently on trial. * @return {Boolean} */ public onTrial() { return this.trialEndsAt ? moment().isBefore(this.trialEndsAt) : false; } /** * Check if the subscription is canceled. * @returns {boolean} */ public canceled() { return !!this.canceledAt; } /** * Retrieves the subscription status. * @returns {string} */ public status() { return this.canceled() ? 'canceled' : this.onTrial() ? 'on_trial' : this.active() ? 'active' : 'inactive'; } /** * Set new period from the given details. * @param {string} invoiceInterval * @param {number} invoicePeriod * @param {string} start * * @return {Object} */ static setNewPeriod(invoiceInterval, invoicePeriod, start) { const period = new SubscriptionPeriod( invoiceInterval, invoicePeriod, start ); const startsAt = period.getStartDate(); const endsAt = period.getEndDate(); return { startsAt, endsAt }; } /** * Renews subscription period. * @Promise */ renew(invoiceInterval, invoicePeriod) { const { startsAt, endsAt } = PlanSubscription.setNewPeriod( invoiceInterval, invoicePeriod ); return this.$query().update({ startsAt, endsAt }); } /** * Detarmines the subscription payment whether is failed. * @returns {boolean} */ public isPaymentFailed() { return this.paymentStatus === SubscriptionPaymentStatus.Failed; } /** * Detarmines the subscription payment whether is succeed. * @returns {boolean} */ public isPaymentSucceed() { return this.paymentStatus === SubscriptionPaymentStatus.Succeed; } }