import { Injectable } from '@nestjs/common' import { classToPlain } from 'class-transformer' import { endOfDay, startOfDay } from 'date-fns' import { isEmpty, pick } from 'lodash' import { CancellationReason, PaymentProviders, Subscription } from '../models' import { HttpService } from '../services/http' import { BaseRepository } from './base.repository' const INSERT = ` mutation insertSubscription($subscription: subscription_insert_input!) { insert_subscription_one(object: $subscription) { id } } ` const UPDATE_CANCELLATION = ` mutation cancelSubscription($id: uuid!, $cancellationById: String, $cancellationRequestAt: timestamptz, $cancellationReasonId: String, $cancellationOtherReason: String, $expiresAt: timestamptz, $cancellationAt: timestamptz, $cancellationRequestedByPatientAt: timestamptz) { update_subscription_by_pk(pk_columns: {id: $id}, _set: {cancellation_by_id: $cancellationById, cancellation_request_at: $cancellationRequestAt, updated_at: $cancellationRequestAt, cancellation_reason_id: $cancellationReasonId, cancellation_other_reason: $cancellationOtherReason, expires_at: $expiresAt, cancellation_at: $cancellationAt, cancellation_requested_by_patient_at: $cancellationRequestedByPatientAt}) { id } } ` const UPDATE_SUBSCRIBED_AT = ` mutation updateSubscribedAt($id: uuid!, $subscribedAt: timestamptz!) { update_subscription_by_pk(pk_columns: {id: $id}, _set: {subscribed_at: $subscribedAt}) { id } } ` const UPDATE_RENEWS_AT = ` mutation updateSubscribedAt($id: uuid!, $teleconsultationRenewsAt: timestamptz!) { update_subscription_by_pk(pk_columns: {id: $id}, _set: {teleconsultation_renews_at: $teleconsultationRenewsAt}) { id } } ` const FIND_ATTRIBUTES = ` cancellation_at cancellation_by_id cancellation_by_type cancellation_other_reason cancellation_reason_id cancellation_request_at expires_at created_at id patient_id plan_id trial_expires_at updated_at teleconsultations_credit payment_provider suspended_at discount_coupon_id ` const GET = ` query subscription($id: uuid!) { subscription(where: {id: {_eq: $id}}) { ${FIND_ATTRIBUTES} token original_transaction } } ` const GET_BY_PATIENT = ` query subscription($patientId: String!) { subscription(where: {patient_id: {_eq: $patientId}}, order_by: {subscribed_at: asc}) { ${FIND_ATTRIBUTES} } } ` const FIND_BY_TELECONSULTATION_RENEW = ` query subscription($planId: uuid!, $gte: timestamptz!, $lte: timestamptz!) { subscription(where: {plan_id: {_eq: $planId}, teleconsultation_renews_at: {_gte: $gte, _lte: $lte}, cancellation_at: {_is_null: true}}) { id cancellation_at patient_id } } ` const GET_BY_TOKEN_AND_PAYMENT_PROVIDER = ` query SubscriptionsWithToken($token: String!, $payment_provider: payment_provider_enum!) { subscription(where: {token: {_eq: $token}, payment_provider: {_eq: $payment_provider}}, limit: 1) { id patient_id cancellation_at cancellation_by_id cancellation_by_type cancellation_other_reason cancellation_reason_id cancellation_request_at expires_at suspended_at token } } ` const GET_BY_ORIGINAL_TRANSACTION_AND_PAYMENT_PROVIDER = ` query SubscriptionsWithToken($originalTransaction: String!, $payment_provider: payment_provider_enum!) { subscription(where: {original_transaction: {_eq: $originalTransaction}, payment_provider: {_eq: $payment_provider}}, limit: 1) { id patient_id cancellation_at cancellation_by_id cancellation_by_type cancellation_other_reason cancellation_reason_id cancellation_request_at expires_at suspended_at token } } ` const GET_TOKEN = ` query subscription($id: uuid!) { subscription(where: {id: {_eq: $id}}) { token } } ` const FIND_ACTIVE_BY_TRIAL_EXPIRES_AT_AND_FIRST_INSTALLEMNT_NOT_INVOICED = ` query subscription($gte: timestamptz!, $lte: timestamptz!) { subscription(where: {trial_expires_at: {_gte: $gte, _lte: $lte}, cancellation_at: {_is_null: true}, installments: {installment: {_eq: 1}, paid_at: {_is_null: false}, omie_is_invoiced: {_is_null: true}}}) { id patient_id plan_id trial_expires_at omie_contract_code payment_provider suspended_at cancellation_request_at cancellation_at plan { id omie_service_id } installments(where: {installment: {_eq: 1}}) { id subscription_id installment amount paid_at due_date created_at updated_at omie_os_code omie_is_invoiced } } } ` @Injectable() export class SubscriptionRepository extends BaseRepository { constructor(protected readonly httpService: HttpService) { super(httpService, 'subscription') } async create(subscription: Partial): Promise { const qv = { subscription: classToPlain(subscription) } const { data } = await this.fetchGraphQL({ body: { query: INSERT, variables: qv }, }) return data.insert_subscription_one.id } async cancel( subscription: Subscription, cancellationReason: CancellationReason, cancellationById: string, ): Promise { const qv = { id: subscription.id, cancellationById, cancellationRequestAt: subscription.cancellationRequestAt || new Date(), cancellationRequestedByPatientAt: subscription.cancellationRequestedByPatientAt, cancellationReasonId: cancellationReason.id, cancellationOtherReason: cancellationReason.otherReason, cancellationAt: subscription.cancellationAt, expiresAt: subscription.expiresAt, } await this.fetchGraphQL({ body: { query: UPDATE_CANCELLATION, variables: qv }, }) } async uncancel(subscription: Subscription): Promise { await this.fetchGraphQL({ body: { query: UPDATE_CANCELLATION, variables: pick(subscription, [ 'id', 'cancellationById', 'cancellationRequestAt', 'cancellationReasonId', 'cancellationOtherReason', 'cancellationAt', 'expiresAt', ]), }, }) } async updateSubscribedAt(subscription: Subscription): Promise { const qv = { id: subscription.id, subscribedAt: subscription.subscribedAt, } await this.fetchGraphQL({ body: { query: UPDATE_SUBSCRIBED_AT, variables: qv }, }) } async updateRenewsAt(subscription: Subscription): Promise { await this.fetchGraphQL({ body: { query: UPDATE_RENEWS_AT, variables: { id: subscription.id, teleconsultationRenewsAt: subscription.teleconsultationRenewsAt.toISOString(), }, }, }) } async get(id: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET, variables: { id } }, throwErrors: false, }) return isEmpty(data) || isEmpty(data.subscription) ? null : Subscription.toInstance(data.subscription.shift()) } async getByPatient(patientId: string): Promise> { const { data } = await this.fetchGraphQL({ body: { query: GET_BY_PATIENT, variables: { patientId } }, throwErrors: false, }) return !isEmpty(data) && data.subscription.map(subscription => Subscription.toInstance(subscription)) } async findByTeleconsultationRenew(planId: string, date: Date): Promise> { const { data } = await this.fetchGraphQL({ body: { query: FIND_BY_TELECONSULTATION_RENEW, variables: { planId, gte: startOfDay(date), lte: endOfDay(date) }, }, throwErrors: false, }) return data?.subscription?.map((subscription: Subscription) => Subscription.toInstance(subscription)) } async getByTokenAndPaymentProvider(token: string, paymentProvider: PaymentProviders): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET_BY_TOKEN_AND_PAYMENT_PROVIDER, variables: { token, payment_provider: paymentProvider }, }, throwErrors: false, }) return !isEmpty(data) && Subscription.toInstance(data.subscription[0]) } async getByOriginalTransactionAndPaymentProvider( originalTransaction: string, paymentProvider: PaymentProviders, ): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET_BY_ORIGINAL_TRANSACTION_AND_PAYMENT_PROVIDER, variables: { originalTransaction, payment_provider: paymentProvider }, }, throwErrors: false, }) return !isEmpty(data) && Subscription.toInstance(data.subscription[0]) } async getToken(id: string): Promise { const { data } = await this.fetchGraphQL({ body: { query: GET_TOKEN, variables: { id } }, throwErrors: false, }) return data?.subscription?.[0]?.token || null } async findActiveByTrialExpiresAtAndFirstInstallmentNotInvoiced(date: Date): Promise> { const { data } = await this.fetchGraphQL({ body: { query: FIND_ACTIVE_BY_TRIAL_EXPIRES_AT_AND_FIRST_INSTALLEMNT_NOT_INVOICED, variables: { gte: startOfDay(date), lte: endOfDay(date) }, }, throwErrors: false, }) return data?.subscription?.map((subscription: Subscription) => Subscription.toInstance(subscription)) } }