Migrated members comped status to reflect subscriptions (#13285)

* Migrated members comped status to reflect subscriptions

refs https://github.com/TryGhost/Team/issues/995

Due to a bug in subscription handling, members with Complimentary stripe
subscriptions were incorrectly given a status of 'paid'.

The goal of this migration is to fix existing broken members, and it
will be accompanied by a fix which prevents the bug for future members.

Since we are updating the status properties for members, we must ensure
that we also update the relevant member_status_events entries too, so
that we do not have incompatible sums between events and statuses.

For example, if we were to use events to graph comped members over time,
we would want the current count to match the query on statuses:

`SELECT COUNT(*) FROM members WHERE status='comped';`
This commit is contained in:
Fabien 'egg' O'Carroll 2021-09-06 18:56:44 +02:00 committed by GitHub
parent 677dc1a59b
commit a0a35df13b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -0,0 +1,70 @@
const {chunk} = require('lodash');
const {createTransactionalMigration} = require('../../utils');
const logging = require('@tryghost/logging');
module.exports = createTransactionalMigration(async function up(knex) {
const compedMemberIds = (await knex('members')
.select('members.id')
.innerJoin(
'members_stripe_customers',
'members.id',
'members_stripe_customers.member_id'
).innerJoin(
'members_stripe_customers_subscriptions',
function () {
this.on(
'members_stripe_customers.customer_id',
'members_stripe_customers_subscriptions.customer_id'
).onIn(
'members_stripe_customers_subscriptions.status',
['active', 'trialing', 'past_due', 'unpaid']
);
}
).where(
'members_stripe_customers_subscriptions.plan_nickname',
'=',
'Complimentary'
).andWhere(
'members.status',
'!=',
'comped'
)).map(({id}) => id);
if (!compedMemberIds.length) {
logging.warn('No Complimentary members found with incorrect status');
return;
} else {
logging.info(`Found ${compedMemberIds.length} Complimentary members with the incorrect status`);
}
// Umm? Well... The current version of SQLite3 bundled with Ghost supports
// a maximum of 999 variables, we use one variable for the SET value
// and so we're left with 998 for our WHERE IN clause values
const chunkSize = 998;
const compedMemberIdChunks = chunk(compedMemberIds, chunkSize);
for (const compedMemberIdsChunk of compedMemberIdChunks) {
await knex('members')
.update('status', 'comped')
.whereIn('id', compedMemberIdsChunk);
}
for (const memberId of compedMemberIds) {
const mostRecentStatusEvent = await knex('members_status_events')
.select('*')
.where('member_id', memberId)
.orderBy('created_at', 'desc')
.limit(1)
.first();
if (!mostRecentStatusEvent) {
logging.warn(`Could not find a status event for member ${memberId} - skipping this member`);
} else if (mostRecentStatusEvent.to_status !== 'comped') {
logging.info(`Updating members_status_event ${mostRecentStatusEvent.id}`);
await knex('members_status_events')
.update('to_status', 'comped')
.where('id', mostRecentStatusEvent.id);
}
}
}, async function down() {});