connection->executeStatement( 'ALTER TABLE wallet_transactions ADD recorded_entity_type VARCHAR(10) NULL, ADD recorded_entity_id INT NULL' ); // Gateway-backed rows: the payment already carries the environment. $this->connection->executeStatement( 'UPDATE wallet_transactions w JOIN payments p ON p.id = w.payment_id SET w.recorded_entity_type = p.entity_type, w.recorded_entity_id = p.entity_id WHERE w.payment_id IS NOT NULL' ); // Desk payments taken against a visit: reference is "session:{uuid}". $this->connection->executeStatement( "UPDATE wallet_transactions w JOIN patient_sessions s ON s.uuid = SUBSTRING(w.reference, 9) JOIN patient_records r ON r.id = s.record_id SET w.recorded_entity_type = r.entity_type, w.recorded_entity_id = r.entity_id WHERE w.recorded_entity_type IS NULL AND w.reference LIKE 'session:%'" ); $unattributed = (int) $this->connection->fetchOne( 'SELECT COUNT(*) FROM wallet_transactions WHERE recorded_entity_type IS NULL' ); $this->write(sprintf( ' wallet_transactions: %d row(s) could not be attributed to an environment and stay visible in all of them.', $unattributed, )); $this->connection->executeStatement( 'CREATE INDEX idx_wallet_user_recorded_entity ON wallet_transactions (user_id, recorded_entity_type, recorded_entity_id)' ); } public function down(Schema $schema): void { $this->addSql('DROP INDEX idx_wallet_user_recorded_entity ON wallet_transactions'); $this->addSql('ALTER TABLE wallet_transactions DROP recorded_entity_type, DROP recorded_entity_id'); } /** DDL on MariaDB commits implicitly; wrapping up() in a transaction would be a lie. */ public function isTransactional(): bool { return false; } }