Compare commits

...

8 Commits

Author SHA1 Message Date
standardci
e15d1e52bd chore(release): publish new version
- @standardnotes/analytics@2.9.0
2022-11-10 14:19:41 +00:00
Karol Sójko
ce3e259bde feat(analytics): add mrr for annual, monthly, pro and plus subscription plans 2022-11-10 15:17:35 +01:00
standardci
87361f90b1 chore(release): publish new version
- @standardnotes/analytics@2.8.3
2022-11-10 11:27:40 +00:00
Karol Sójko
81be06598c fix(analytics): add subscription id to error logs 2022-11-10 12:25:46 +01:00
standardci
9492da6789 chore(release): publish new version
- @standardnotes/analytics@2.8.2
2022-11-10 10:54:18 +00:00
Karol Sójko
fce47a0a37 fix(analytics): add monthly mrr to the report 2022-11-10 11:52:24 +01:00
standardci
92ba682198 chore(release): publish new version
- @standardnotes/analytics@2.8.1
2022-11-10 10:43:40 +00:00
Karol Sójko
8df0482eb4 fix(analytics): add persisting mrr for this month and this year as well 2022-11-10 11:41:24 +01:00
14 changed files with 133 additions and 18 deletions

View File

@@ -3,6 +3,30 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
# [2.9.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.8.3...@standardnotes/analytics@2.9.0) (2022-11-10)
### Features
* **analytics:** add mrr for annual, monthly, pro and plus subscription plans ([ce3e259](https://github.com/standardnotes/server/commit/ce3e259bdedd10796fb4469f0eabd64bc326a115))
## [2.8.3](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.8.2...@standardnotes/analytics@2.8.3) (2022-11-10)
### Bug Fixes
* **analytics:** add subscription id to error logs ([81be065](https://github.com/standardnotes/server/commit/81be06598c918279f98a8ba6b59ea1b3803c949c))
## [2.8.2](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.8.1...@standardnotes/analytics@2.8.2) (2022-11-10)
### Bug Fixes
* **analytics:** add monthly mrr to the report ([fce47a0](https://github.com/standardnotes/server/commit/fce47a0a37a67b3edf3ea0b6ccda43c54dbd9870))
## [2.8.1](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.8.0...@standardnotes/analytics@2.8.1) (2022-11-10)
### Bug Fixes
* **analytics:** add persisting mrr for this month and this year as well ([8df0482](https://github.com/standardnotes/server/commit/8df0482eb4bfd63b9639fd786c9b6952ad7f801d))
# [2.8.0](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.7.3...@standardnotes/analytics@2.8.0) (2022-11-10)
### Features

View File

@@ -100,8 +100,6 @@ const requestReport = async (
})
}
const thirtyDaysStatisticsNames = [StatisticsMeasure.MRR]
const statisticsOverTime: Array<{
name: string
period: number
@@ -110,6 +108,14 @@ const requestReport = async (
totalCount: number
}>
}> = []
const thirtyDaysStatisticsNames = [
StatisticsMeasure.MRR,
StatisticsMeasure.AnnualPlansMRR,
StatisticsMeasure.MonthlyPlansMRR,
StatisticsMeasure.PlusPlansMRR,
StatisticsMeasure.ProPlansMRR,
]
for (const statisticName of thirtyDaysStatisticsNames) {
statisticsOverTime.push({
name: statisticName,
@@ -118,6 +124,15 @@ const requestReport = async (
})
}
const monthlyStatisticsNames = [StatisticsMeasure.MRR]
for (const statisticName of monthlyStatisticsNames) {
statisticsOverTime.push({
name: statisticName,
period: Period.ThisYear,
counts: await statisticsStore.calculateTotalCountOverPeriod(statisticName, Period.ThisYear),
})
}
const statisticMeasureNames = [
StatisticsMeasure.Income,
StatisticsMeasure.PlusSubscriptionInitialAnnualPaymentsIncome,

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/analytics",
"version": "2.8.0",
"version": "2.9.0",
"engines": {
"node": ">=14.0.0 <17.0.0"
},

View File

@@ -46,7 +46,9 @@ export class SubscriptionCancelledEventHandler implements DomainEventHandlerInte
})
if (result.isFailed()) {
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
this.logger.error(
`[${event.type}][${event.payload.subscriptionId}] Could not save revenue modification: ${result.getError()}`,
)
}
}

View File

@@ -50,7 +50,9 @@ export class SubscriptionExpiredEventHandler implements DomainEventHandlerInterf
})
if (result.isFailed()) {
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
this.logger.error(
`[${event.type}][${event.payload.subscriptionId}] Could not save revenue modification: ${result.getError()}`,
)
}
}
}

View File

@@ -74,7 +74,9 @@ export class SubscriptionPurchasedEventHandler implements DomainEventHandlerInte
})
if (result.isFailed()) {
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
this.logger.error(
`[${event.type}][${event.payload.subscriptionId}] Could not save revenue modification: ${result.getError()}`,
)
}
}
}

View File

@@ -46,7 +46,9 @@ export class SubscriptionRefundedEventHandler implements DomainEventHandlerInter
})
if (result.isFailed()) {
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
this.logger.error(
`[${event.type}][${event.payload.subscriptionId}] Could not save revenue modification: ${result.getError()}`,
)
}
}

View File

@@ -46,7 +46,9 @@ export class SubscriptionRenewedEventHandler implements DomainEventHandlerInterf
})
if (result.isFailed()) {
this.logger.error(`[${event.type}] Could not save revenue modification: ${result.getError()}`)
this.logger.error(
`[${event.type}][${event.payload.subscriptionId}] Could not save revenue modification: ${result.getError()}`,
)
}
}
}

View File

@@ -3,6 +3,6 @@ import { RevenueModification } from './RevenueModification'
export interface RevenueModificationRepositoryInterface {
findLastByUserUuid(userUuid: Uuid): Promise<RevenueModification | null>
sumMRRDiff(): Promise<number>
sumMRRDiff(dto: { planName?: string; billingFrequency?: number }): Promise<number>
save(revenueModification: RevenueModification): Promise<RevenueModification>
}

View File

@@ -16,4 +16,8 @@ export enum StatisticsMeasure {
NewCustomers = 'new-customers',
TotalCustomers = 'total-customers',
MRR = 'mrr',
MonthlyPlansMRR = 'monthly-plans-mrr',
AnnualPlansMRR = 'annual-plans-mrr',
ProPlansMRR = 'pro-plans-mrr',
PlusPlansMRR = 'plus-plans-mrr',
}

View File

@@ -24,6 +24,10 @@ describe('CalculateMonthlyRecurringRevenue', () => {
it('should calculate the MRR diff and persist it as a statistic', async () => {
await createUseCase().execute({})
expect(statisticsStore.setMeasure).toHaveBeenCalledWith(StatisticsMeasure.MRR, 123.45, [Period.Today])
expect(statisticsStore.setMeasure).toHaveBeenCalledWith(StatisticsMeasure.MRR, 123.45, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
})
})

View File

@@ -1,3 +1,4 @@
import { SubscriptionBillingFrequency, SubscriptionName } from '@standardnotes/common'
import { inject, injectable } from 'inversify'
import TYPES from '../../../Bootstrap/Types'
import { Result } from '../../Core/Result'
@@ -18,9 +19,53 @@ export class CalculateMonthlyRecurringRevenue implements DomainUseCaseInterface<
) {}
async execute(_dto: CalculateMonthlyRecurringRevenueDTO): Promise<Result<MonthlyRevenue>> {
const mrrDiff = await this.revenueModificationRepository.sumMRRDiff()
const mrrDiff = await this.revenueModificationRepository.sumMRRDiff({})
await this.statisticsStore.setMeasure(StatisticsMeasure.MRR, mrrDiff, [Period.Today])
await this.statisticsStore.setMeasure(StatisticsMeasure.MRR, mrrDiff, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
const monthlyPlansMrrDiff = await this.revenueModificationRepository.sumMRRDiff({
billingFrequency: SubscriptionBillingFrequency.Monthly,
})
await this.statisticsStore.setMeasure(StatisticsMeasure.MonthlyPlansMRR, monthlyPlansMrrDiff, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
const annualPlansMrrDiff = await this.revenueModificationRepository.sumMRRDiff({
billingFrequency: SubscriptionBillingFrequency.Annual,
})
await this.statisticsStore.setMeasure(StatisticsMeasure.AnnualPlansMRR, annualPlansMrrDiff, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
const proPlansMrrDiff = await this.revenueModificationRepository.sumMRRDiff({
planName: SubscriptionName.ProPlan,
})
await this.statisticsStore.setMeasure(StatisticsMeasure.ProPlansMRR, proPlansMrrDiff, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
const plusPlansMrrDiff = await this.revenueModificationRepository.sumMRRDiff({
planName: SubscriptionName.PlusPlan,
})
await this.statisticsStore.setMeasure(StatisticsMeasure.PlusPlansMRR, plusPlansMrrDiff, [
Period.Today,
Period.ThisMonth,
Period.ThisYear,
])
return MonthlyRevenue.create(mrrDiff)
}

View File

@@ -17,11 +17,17 @@ export class MySQLRevenueModificationRepository implements RevenueModificationRe
private revenueModificationMap: MapInterface<RevenueModification, TypeORMRevenueModification>,
) {}
async sumMRRDiff(): Promise<number> {
const result = await this.ormRepository
.createQueryBuilder()
.select('sum(new_mrr - previous_mrr)', 'mrrDiff')
.getRawOne()
async sumMRRDiff(dto: { planName?: string; billingFrequency?: number }): Promise<number> {
const query = this.ormRepository.createQueryBuilder().select('sum(new_mrr - previous_mrr)', 'mrrDiff')
if (dto.planName !== undefined) {
query.where('subscription_plan = :planName', { planName: dto.planName })
}
if (dto.billingFrequency !== undefined) {
query.where('billing_frequency = :billingFrequency', { billingFrequency: dto.billingFrequency })
}
const result = await query.getRawOne()
if (result === undefined) {
return 0

View File

@@ -14,7 +14,14 @@ export class RedisStatisticsStore implements StatisticsStoreInterface {
period: Period,
): Promise<{ periodKey: string; totalCount: number }[]> {
if (
![Period.Last30Days, Period.Q1ThisYear, Period.Q2ThisYear, Period.Q3ThisYear, Period.Q4ThisYear].includes(period)
![
Period.Last30Days,
Period.ThisYear,
Period.Q1ThisYear,
Period.Q2ThisYear,
Period.Q3ThisYear,
Period.Q4ThisYear,
].includes(period)
) {
throw new Error(`Unsuporrted period: ${period}`)
}