diff --git a/backend/run-member-number-migration.js b/backend/run-member-number-migration.js new file mode 100644 index 0000000..e6073a5 --- /dev/null +++ b/backend/run-member-number-migration.js @@ -0,0 +1,96 @@ +const { sequelize } = require('./src/config/database'); +const { QueryTypes } = require('sequelize'); + +async function runMigration() { + try { + console.log('šŸš€ Starting member_number migration...\n'); + + // Step 1: Add column (if not exists) + console.log('Step 1: Adding member_number column...'); + await sequelize.query(` + ALTER TABLE group_members + ADD COLUMN IF NOT EXISTS member_number INTEGER + `); + console.log('āœ“ Column added\n'); + + // Step 2: Populate for existing members + console.log('Step 2: Assigning numbers to existing members...'); + const [results] = await sequelize.query(` + WITH numbered_members AS ( + SELECT + id, + ROW_NUMBER() OVER ( + PARTITION BY group_id + ORDER BY joined_date ASC, created_at ASC + ) as assigned_number + FROM group_members + WHERE member_number IS NULL + ) + UPDATE group_members gm + SET member_number = nm.assigned_number + FROM numbered_members nm + WHERE gm.id = nm.id + RETURNING gm.member_number + `); + console.log(`āœ“ Assigned numbers to ${results.length} existing members\n`); + + // Step 3: Make NOT NULL + console.log('Step 3: Setting NOT NULL constraint...'); + await sequelize.query(` + ALTER TABLE group_members + ALTER COLUMN member_number SET NOT NULL + `); + console.log('āœ“ NOT NULL constraint set\n'); + + // Step 4: Add unique index + console.log('Step 4: Creating unique index...'); + await sequelize.query(` + CREATE UNIQUE INDEX IF NOT EXISTS idx_group_members_member_number + ON group_members(group_id, member_number) + `); + console.log('āœ“ Unique index created\n'); + + // Step 5: Verify + console.log('Step 5: Verifying migration...'); + const members = await sequelize.query( + `SELECT + gm.member_number, + u.full_name, + cg.name as group_name + FROM group_members gm + JOIN users u ON gm.user_id = u.id + JOIN chit_groups cg ON gm.group_id = cg.id + ORDER BY cg.name, gm.member_number + LIMIT 20`, + { type: QueryTypes.SELECT } + ); + + console.log('\nāœ… Migration completed successfully!\n'); + console.log('Sample members with assigned numbers:'); + console.log('─'.repeat(70)); + members.forEach(m => { + console.log(`#${m.member_number}\t${m.full_name}\t(${m.group_name})`); + }); + console.log('─'.repeat(70)); + console.log(`\nāœ“ Total members checked: ${members.length}`); + console.log('\nšŸŽ‰ Member numbers are now active!'); + console.log(' Restart your backend server to see them in action.'); + + await sequelize.close(); + process.exit(0); + } catch (error) { + console.error('\nāŒ Migration failed:', error.message); + console.error('\nFull error:', error); + await sequelize.close(); + process.exit(1); + } +} + +console.log('═'.repeat(70)); +console.log(' MEMBER NUMBER MIGRATION'); +console.log(' Adding readable serial numbers (1, 2, 3...) to members'); +console.log('═'.repeat(70)); +console.log(''); + +runMigration(); +