@vrplatform/hyperline-client 1.0.9 → 1.0.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/build/main/_types.generated.d.ts +202 -80
  2. package/build/main/_types.generated.js.map +1 -1
  3. package/build/main/client.d.ts +7 -15
  4. package/build/main/client.js +2 -2
  5. package/build/main/client.js.map +1 -1
  6. package/build/main/const.d.ts +4 -0
  7. package/build/main/const.js +4 -0
  8. package/build/main/const.js.map +1 -1
  9. package/build/main/index.d.ts +1 -0
  10. package/build/main/index.js +1 -0
  11. package/build/main/index.js.map +1 -1
  12. package/build/main/tsconfig.main.tsbuildinfo +1 -1
  13. package/build/main/utils/getMrrDiffReasons.d.ts +20 -0
  14. package/build/main/utils/getMrrDiffReasons.js +214 -0
  15. package/build/main/utils/getMrrDiffReasons.js.map +1 -0
  16. package/build/main/utils/index.d.ts +1 -0
  17. package/build/main/utils/index.js +2 -0
  18. package/build/main/utils/index.js.map +1 -0
  19. package/build/module/_types.generated.d.ts +202 -80
  20. package/build/module/_types.generated.js.map +1 -1
  21. package/build/module/client.d.ts +7 -15
  22. package/build/module/client.js +2 -2
  23. package/build/module/client.js.map +1 -1
  24. package/build/module/const.d.ts +4 -0
  25. package/build/module/const.js +4 -0
  26. package/build/module/const.js.map +1 -1
  27. package/build/module/index.d.ts +1 -0
  28. package/build/module/index.js +1 -0
  29. package/build/module/index.js.map +1 -1
  30. package/build/module/tsconfig.esm.tsbuildinfo +1 -1
  31. package/build/module/utils/getMrrDiffReasons.d.ts +20 -0
  32. package/build/module/utils/getMrrDiffReasons.js +214 -0
  33. package/build/module/utils/getMrrDiffReasons.js.map +1 -0
  34. package/build/module/utils/index.d.ts +1 -0
  35. package/build/module/utils/index.js +2 -0
  36. package/build/module/utils/index.js.map +1 -0
  37. package/package.json +4 -1
  38. package/src/_types.generated.ts +202 -80
  39. package/src/client.ts +4 -2
  40. package/src/const.ts +5 -0
  41. package/src/index.ts +1 -0
  42. package/src/utils/getMrrDiffReasons.ts +305 -0
  43. package/src/utils/index.ts +1 -0
package/src/client.ts CHANGED
@@ -187,7 +187,7 @@ export class HyperlineAPI {
187
187
 
188
188
  // SUBSCRIPTION
189
189
 
190
- async getSubscription(id: string) {
190
+ async getSubscription(id: string, liveBilling = false) {
191
191
  if (!id) {
192
192
  this.logger.error('Missing subscription ID', { id });
193
193
  throw new Error('Missing subscription ID');
@@ -195,7 +195,9 @@ export class HyperlineAPI {
195
195
 
196
196
  return await this.fetch<
197
197
  paths['/v2/subscriptions/{id}']['get']['responses'][200]['content']['application/json']
198
- >(`/v2/subscriptions/${id}?include_live_billing=true`);
198
+ >(
199
+ `/v2/subscriptions/${id}?include_live_billing=${liveBilling ? 'true' : 'false'}`
200
+ );
199
201
  }
200
202
 
201
203
  async getSubscriptions(
package/src/const.ts CHANGED
@@ -19,6 +19,11 @@ export const XIMPLIFI_DEV_COUPON_ID = 'cou_MGtxHbT8IR1gK9';
19
19
  export const ACCOUNTING_PARTNER_COUPON_ID = 'cou_PJQ328MBAD9kxY';
20
20
  export const ACCOUNTING_PARTNER_DEV_COUPON_ID = 'cou_y6UfaI2X1m34Qg';
21
21
 
22
+ export const VRTRUST_HALF_PRICE_COUPON_ID = 'cou_CTOQqs0SOeSc-e'; // 50% off
23
+ export const VRTRUST_HALF_PRICE_DEV_COUPON_ID = 'cou_tHLiRdwQ6xGqKA'; // 50% off
24
+ export const VRTRUST_FREE_PRICE_COUPON_ID = 'cou_JGiLite8_nFtnX'; // 100% off
25
+ export const VRTRUST_FREE_PRICE_DEV_COUPON_ID = 'cou_N4GNqSSLEFscto'; // 100% off
26
+
22
27
  // Plans
23
28
  export const HYPERLINE_DEFAULT_PLAN_ID = 'plan_Dyb5P0ICZ6HsIT';
24
29
  export const HYPERLINE_DEFAULT_DEV_PLAN_ID = 'plan_UTCVXFQnKD0oMS';
package/src/index.ts CHANGED
@@ -2,3 +2,4 @@ export * from './const';
2
2
  export * from './types';
3
3
  export * from './client';
4
4
  export * from './hooks';
5
+ export * from './utils';
@@ -0,0 +1,305 @@
1
+ import { useHasuraClient } from '@finalytic/graphql';
2
+ import { groupBy, utc } from '@finalytic/utils';
3
+ import {
4
+ VRP_TENANT_ID,
5
+ XIMPLIFI_TENANT_ID,
6
+ } from '@vrplatform/hyperline-client';
7
+
8
+ function printFinancialNumber(number: number, showPlusMinus = true) {
9
+ return `${showPlusMinus ? (number >= 0 ? '+' : '-') : ''}$${Math.abs(number)
10
+ .toFixed(2)
11
+ .toString()
12
+ .replace(/\B(?=(\d{3})+(?!\d))/g, ',')}`;
13
+ }
14
+ function printPlusMinus(number: number) {
15
+ return `${number >= 0 ? '+' : '-'}${Math.abs(number)}`;
16
+ }
17
+
18
+ const hasura = useHasuraClient({
19
+ secret: process.env.HASURA_GRAPHQL_ADMIN_SECRET,
20
+ });
21
+
22
+ export async function getMrrDiffReasons(
23
+ firstDate: string,
24
+ lastDate: string,
25
+ client = hasura
26
+ ) {
27
+ const messages: string[] = [];
28
+ messages.push(
29
+ `MRR Diff Reasons from ${utc(firstDate).yyyymmdd()} to ${utc(lastDate).yyyymmdd()}`
30
+ );
31
+ messages.push('--------------------------------');
32
+
33
+ const { mrrEvents, listingsEvents } = await client.query((q) => ({
34
+ mrrEvents: q
35
+ .tracking_event({
36
+ where: {
37
+ name: { _eq: 'mrr_reported' },
38
+ _or: [
39
+ {
40
+ createdAt: {
41
+ _gte: utc(firstDate).yyyymmdd(),
42
+ _lte: utc(firstDate).add(1, 'day').yyyymmdd(),
43
+ },
44
+ },
45
+ {
46
+ createdAt: {
47
+ _gte: utc(lastDate).yyyymmdd(),
48
+ _lte: utc(lastDate).add(1, 'day').yyyymmdd(),
49
+ },
50
+ },
51
+ ],
52
+ },
53
+ })
54
+ .map((e) => ({
55
+ tenantId: e.tenant?.id!,
56
+ tenantName: e.tenant?.name,
57
+ tenantCreatedAt: e.tenant?.createdAt,
58
+ tenantPlan: e.tenant?.billingPlan,
59
+ partnerTeamsCreatedFirstDate: e.tenant
60
+ ?.clients({
61
+ where: {
62
+ createdAt: {
63
+ _lte: utc(firstDate).yyyymmdd(),
64
+ },
65
+ },
66
+ })
67
+ .map((c) => c.id).length,
68
+ partnerTeamsCreatedLastDate: e.tenant
69
+ ?.clients({
70
+ where: {
71
+ createdAt: {
72
+ _lte: utc(lastDate).yyyymmdd(),
73
+ },
74
+ },
75
+ })
76
+ .map((c) => c.id).length,
77
+ partnerTeamsCancelledFirstDate: e.tenant
78
+ ?.clients({
79
+ where: {
80
+ cancelledAt: {
81
+ _lte: utc(firstDate).yyyymmdd(),
82
+ },
83
+ },
84
+ })
85
+ .map((c) => c.id).length,
86
+ partnerTeamsCancelledLastDate: e.tenant
87
+ ?.clients({
88
+ where: {
89
+ cancelledAt: {
90
+ _lte: utc(lastDate).yyyymmdd(),
91
+ },
92
+ },
93
+ })
94
+ .map((c) => c.id).length,
95
+ mrr: e.props()?.mrr,
96
+ createdAt: utc(e.createdAt).yyyymmdd(),
97
+ })),
98
+ listingsEvents: q
99
+ .tracking_event({
100
+ where: {
101
+ name: { _eq: 'listings_reported' },
102
+ _or: [
103
+ {
104
+ createdAt: {
105
+ _gte: utc(firstDate).yyyymmdd(),
106
+ _lte: utc(firstDate).add(1, 'day').yyyymmdd(),
107
+ },
108
+ },
109
+ {
110
+ createdAt: {
111
+ _gte: utc(lastDate).yyyymmdd(),
112
+ _lte: utc(lastDate).add(1, 'day').yyyymmdd(),
113
+ },
114
+ },
115
+ ],
116
+ },
117
+ })
118
+ .map((e) => ({
119
+ tenantId: e.tenant?.id!,
120
+ billingPartnerId: e.tenant?.calculatedBillingPartnerId!,
121
+ listings: e.props()?.activeListings,
122
+ createdAt: utc(e.createdAt).yyyymmdd(),
123
+ })),
124
+ }));
125
+ messages.push(`Found ${mrrEvents.length} mrr events`);
126
+
127
+ // join mrr and listings events
128
+ const trackingEvents = mrrEvents.map((e) => {
129
+ const filteredEvents = listingsEvents.filter((l) => {
130
+ const tenantId = [VRP_TENANT_ID, XIMPLIFI_TENANT_ID].includes(
131
+ l.billingPartnerId!
132
+ )
133
+ ? l.tenantId!
134
+ : l.billingPartnerId!;
135
+
136
+ return tenantId === e.tenantId && l.createdAt === e.createdAt;
137
+ });
138
+
139
+ return {
140
+ ...e,
141
+ listings: filteredEvents.reduce((acc, l) => acc + (l.listings ?? 0), 0),
142
+ };
143
+ });
144
+
145
+ // get list of tenants
146
+ const tenants: Record<
147
+ string,
148
+ {
149
+ id: string;
150
+ name?: string;
151
+ plan?: string;
152
+ activePartnerTeamsDiff: number;
153
+ mrrFirstDate?: number;
154
+ mrrLastDate?: number;
155
+ mrrDiff: number;
156
+ listingsFirstDate?: number;
157
+ listingsLastDate?: number;
158
+ listingsDiff: number;
159
+ createdAt: string;
160
+ isNew: boolean;
161
+ hasCancelled?: boolean;
162
+ }
163
+ > = {};
164
+ for (const event of trackingEvents) {
165
+ if (!tenants[event.tenantId]) {
166
+ const partnerTeamsCreatedDiff =
167
+ (event.partnerTeamsCreatedLastDate || 0) -
168
+ (event.partnerTeamsCreatedFirstDate || 0);
169
+ const partnerTeamsCancelledDiff =
170
+ (event.partnerTeamsCancelledLastDate || 0) -
171
+ (event.partnerTeamsCancelledFirstDate || 0);
172
+
173
+ tenants[event.tenantId] = {
174
+ id: event.tenantId,
175
+ name: event.tenantName,
176
+ plan: event.tenantPlan,
177
+ activePartnerTeamsDiff:
178
+ partnerTeamsCreatedDiff - partnerTeamsCancelledDiff,
179
+ mrrFirstDate: undefined,
180
+ mrrLastDate: undefined,
181
+ mrrDiff: 0,
182
+ listingsFirstDate: undefined,
183
+ listingsLastDate: undefined,
184
+ listingsDiff: 0,
185
+ createdAt: utc(event.tenantCreatedAt).yyyymmdd(),
186
+ isNew: utc(event.tenantCreatedAt).isBetween(
187
+ utc(firstDate),
188
+ utc(lastDate)
189
+ ),
190
+ };
191
+ }
192
+
193
+ if (event.createdAt === firstDate) {
194
+ tenants[event.tenantId].mrrFirstDate = event.mrr;
195
+ tenants[event.tenantId].listingsFirstDate = event.listings;
196
+ }
197
+ if (event.createdAt === lastDate) {
198
+ tenants[event.tenantId].mrrLastDate = event.mrr;
199
+ tenants[event.tenantId].listingsLastDate = event.listings;
200
+ }
201
+ }
202
+
203
+ const result = Object.values(tenants)
204
+ .map(
205
+ ({
206
+ mrrFirstDate,
207
+ mrrLastDate,
208
+ listingsFirstDate,
209
+ listingsLastDate,
210
+ ...t
211
+ }) => ({
212
+ ...t,
213
+ mrrDiff: (mrrLastDate ?? 0) - (mrrFirstDate ?? 0),
214
+ listingsDiff: (listingsLastDate ?? 0) - (listingsFirstDate ?? 0),
215
+ hasCancelled: mrrFirstDate !== undefined && mrrLastDate === undefined,
216
+ })
217
+ )
218
+ .filter((t) => t.mrrDiff)
219
+ .sort((a, b) => b.mrrDiff - a.mrrDiff)
220
+ .reverse();
221
+
222
+ // calculate summ of mrr per day
223
+ const groupedEvents = groupBy(trackingEvents, (e) => e.createdAt);
224
+ const summedMrrPerDay = Object.entries(groupedEvents)
225
+ .map(([date, events]) => {
226
+ const mrr = events.reduce((acc, e) => acc + (e.mrr ?? 0), 0);
227
+ const listings = events.reduce((acc, e) => acc + (e.listings ?? 0), 0);
228
+
229
+ return {
230
+ date,
231
+ mrr,
232
+ count: events.length,
233
+ listings,
234
+ };
235
+ })
236
+ .sort((a, b) => utc(b.date).diff(utc(a.date)));
237
+ messages.push(
238
+ `MRR sum changed from ${printFinancialNumber(summedMrrPerDay[1].mrr, false)} to ${printFinancialNumber(summedMrrPerDay[0].mrr, false)} (${printFinancialNumber(summedMrrPerDay[0].mrr - summedMrrPerDay[1].mrr)})`
239
+ );
240
+ messages.push(
241
+ `MRR Reports changed from ${summedMrrPerDay[1].count.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} to ${summedMrrPerDay[0].count.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} (${printPlusMinus(summedMrrPerDay[0].count - summedMrrPerDay[1].count)})`
242
+ );
243
+ messages.push(
244
+ `Active Listings changed from ${summedMrrPerDay[1].listings.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} to ${summedMrrPerDay[0].listings.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',')} (${printPlusMinus(summedMrrPerDay[0].listings - summedMrrPerDay[1].listings)})`
245
+ );
246
+ messages.push(
247
+ `Active Partner Teams changed by ${printPlusMinus(
248
+ result.reduce((acc, t) => acc + t.activePartnerTeamsDiff, 0)
249
+ )}`
250
+ );
251
+ messages.push();
252
+
253
+ const cancelledTenants = result.filter((t) => t.hasCancelled);
254
+ const legacyTenants = result.filter(
255
+ (t) =>
256
+ t.plan?.includes('Legacy') &&
257
+ !cancelledTenants.map((t) => t.id).includes(t.id)
258
+ );
259
+ const newTenants = result.filter(
260
+ (t) =>
261
+ t.isNew &&
262
+ !cancelledTenants.map((t) => t.id).includes(t.id) &&
263
+ !legacyTenants.map((t) => t.id).includes(t.id)
264
+ );
265
+ const activeListings = result.filter(
266
+ (t) =>
267
+ t.listingsDiff &&
268
+ !newTenants.map((t) => t.id).includes(t.id) &&
269
+ !cancelledTenants.map((t) => t.id).includes(t.id) &&
270
+ !legacyTenants.map((t) => t.id).includes(t.id)
271
+ );
272
+ const otherTenants = result.filter(
273
+ (t) =>
274
+ !newTenants.map((t) => t.id).includes(t.id) &&
275
+ !cancelledTenants.map((t) => t.id).includes(t.id) &&
276
+ !legacyTenants.map((t) => t.id).includes(t.id) &&
277
+ !activeListings.map((t) => t.id).includes(t.id)
278
+ );
279
+ const total =
280
+ newTenants.reduce((acc, t) => acc + t.mrrDiff, 0) +
281
+ cancelledTenants.reduce((acc, t) => acc + t.mrrDiff, 0) +
282
+ legacyTenants.reduce((acc, t) => acc + t.mrrDiff, 0) +
283
+ activeListings.reduce((acc, t) => acc + t.mrrDiff, 0) +
284
+ otherTenants.reduce((acc, t) => acc + t.mrrDiff, 0);
285
+ messages.push(
286
+ `MRR from ${newTenants.length} new tenants: ${printFinancialNumber(newTenants.reduce((acc, t) => acc + t.mrrDiff, 0))}`
287
+ );
288
+ messages.push(
289
+ `MRR from ${cancelledTenants.length} cancelled tenants: ${printFinancialNumber(cancelledTenants.reduce((acc, t) => acc + t.mrrDiff, 0))}`
290
+ );
291
+ messages.push(
292
+ `MRR from ${legacyTenants.length} tenants with Legacy plans: ${printFinancialNumber(legacyTenants.reduce((acc, t) => acc + t.mrrDiff, 0))}`
293
+ );
294
+ messages.push(
295
+ `MRR from ${activeListings.length} tenants with active listings changes: ${printFinancialNumber(activeListings.reduce((acc, t) => acc + t.mrrDiff, 0))}`
296
+ );
297
+ messages.push(
298
+ `MRR from ${otherTenants.length} other tenants: ${printFinancialNumber(
299
+ otherTenants.reduce((acc, t) => acc + t.mrrDiff, 0)
300
+ )} (${otherTenants.map((t) => `${t.name} (${printFinancialNumber(t.mrrDiff)})`).join(', ')})`
301
+ );
302
+ messages.push(`= ${printFinancialNumber(total)}`);
303
+
304
+ return { messages, result };
305
+ }
@@ -0,0 +1 @@
1
+ export * from './getMrrDiffReasons';