|
| 1 | +/** |
| 2 | + * @typedef {import('typeorm').MigrationInterface} MigrationInterface |
| 3 | + * @typedef {import('typeorm').QueryRunner} QueryRunner |
| 4 | + */ |
| 5 | + |
| 6 | +/** |
| 7 | + * Sync transaction AML data for 114 CryptoCrypto BuyCrypto records. |
| 8 | + * |
| 9 | + * Same root cause as migration 1768900000000-SyncTransactionAmlData: |
| 10 | + * BuyCrypto was completed (isComplete=true) before postProcessing could |
| 11 | + * set the transaction-level fields. All 115 records have bc.amlCheck='Pass' |
| 12 | + * but t.amlCheck/eventDate/amountInChf/assets are NULL. |
| 13 | + * |
| 14 | + * TX 278332 excluded: stuck PaymentLink transaction (isConfirmed=false, status=Created). |
| 15 | + * |
| 16 | + * All affected transactions: |
| 17 | + * - Account: userDataId 259962 (KycFile 3767) |
| 18 | + * - Created: 2025-12-30, batch-completed: 2025-12-31 |
| 19 | + * - Type: CryptoCrypto (all output to BTC) |
| 20 | + * - Total volume: ~1,536 CHF (excl. TX 278332 which is stuck in Created status) |
| 21 | + * |
| 22 | + * @class |
| 23 | + * @implements {MigrationInterface} |
| 24 | + */ |
| 25 | +module.exports = class SyncCryptoCryptoTransactionAmlData1770500000000 { |
| 26 | + name = 'SyncCryptoCryptoTransactionAmlData1770500000000'; |
| 27 | + |
| 28 | + /** |
| 29 | + * @param {QueryRunner} queryRunner |
| 30 | + */ |
| 31 | + async up(queryRunner) { |
| 32 | + // Hardcoded transaction data from production DB query (2026-02-06) |
| 33 | + // Format: [txId, amountInChf (null if unknown), assets, eventDate] |
| 34 | + const transactions = [ |
| 35 | + [261949, 0.86, 'BTC-BTC', '2025-12-30T08:41:15.583Z'], |
| 36 | + [261950, 1.72, 'BTC-BTC', '2025-12-30T14:08:50.550Z'], |
| 37 | + [262020, 2.16, 'XMR-BTC', '2025-12-30T14:08:30.266Z'], |
| 38 | + [262028, 7.18, 'XMR-BTC', '2025-12-30T14:08:33.106Z'], |
| 39 | + [262143, 2.05, 'XMR-BTC', '2025-12-30T14:08:32.363Z'], |
| 40 | + [262335, 50.55, 'BTC-BTC', '2025-12-30T14:08:31.646Z'], |
| 41 | + [262746, 5.77, 'BTC-BTC', '2025-12-30T14:08:30.973Z'], |
| 42 | + [263544, 42.8, 'BTC-BTC', '2025-12-30T14:08:43.990Z'], |
| 43 | + [263545, 18.87, 'BTC-BTC', '2025-12-30T14:08:38.496Z'], |
| 44 | + [264117, 18.27, 'BTC-BTC', '2025-12-30T14:08:52.546Z'], |
| 45 | + [264418, 15.73, 'BTC-BTC', '2025-12-30T14:09:10.426Z'], |
| 46 | + [264422, 41.4, 'BTC-BTC', '2025-12-30T14:09:03.110Z'], |
| 47 | + [267046, 22.71, 'BTC-BTC', '2025-12-30T14:08:34.233Z'], |
| 48 | + [267066, 11.65, 'BTC-BTC', '2025-12-30T14:08:58.240Z'], |
| 49 | + [267161, 14.94, 'BTC-BTC', '2025-12-30T14:09:00.486Z'], |
| 50 | + [267162, 10.32, 'BTC-BTC', '2025-12-30T14:08:35.756Z'], |
| 51 | + [267170, 26.01, 'BTC-BTC', '2025-12-30T14:08:55.476Z'], |
| 52 | + [267911, 3.79, 'BTC-BTC', '2025-12-30T14:09:23.390Z'], |
| 53 | + [268141, 10.42, 'BTC-BTC', '2025-12-30T14:09:16.463Z'], |
| 54 | + [268507, 7.14, 'BTC-BTC', '2025-12-30T14:09:17.243Z'], |
| 55 | + [268887, 7.39, 'BTC-BTC', '2025-12-30T14:09:08.140Z'], |
| 56 | + [269574, 1.73, 'USDT-BTC', '2025-12-30T14:09:12.926Z'], |
| 57 | + [271252, 2.1, 'BTC-BTC', '2025-12-30T14:09:18.140Z'], |
| 58 | + [271665, 36.98, 'BTC-BTC', '2025-12-30T14:09:24.270Z'], |
| 59 | + [271670, 1.65, 'USDT-BTC', '2025-12-30T14:09:18.883Z'], |
| 60 | + [272117, 15.04, 'USDT-BTC', '2025-12-30T14:09:19.140Z'], |
| 61 | + [273008, 2.51, 'XMR-BTC', '2025-12-30T14:09:15.356Z'], |
| 62 | + [273105, 8.43, 'BTC-BTC', '2025-12-30T14:09:13.386Z'], |
| 63 | + [273395, 23.57, 'BTC-BTC', '2025-12-30T14:08:52.906Z'], |
| 64 | + [273417, 5.68, 'XMR-BTC', '2025-12-30T14:08:58.633Z'], |
| 65 | + [273464, 40.26, 'BTC-BTC', '2025-12-30T14:09:21.756Z'], |
| 66 | + [273724, 8.27, 'BTC-BTC', '2025-12-30T14:09:11.770Z'], |
| 67 | + [273793, 1.06, 'USDT-BTC', '2025-12-30T14:09:09.263Z'], |
| 68 | + [274027, 19.04, 'BTC-BTC', '2025-12-30T14:09:02.880Z'], |
| 69 | + [274062, 2.64, 'BTC-BTC', '2025-12-30T14:08:59.423Z'], |
| 70 | + [274075, 22.13, 'BTC-BTC', '2025-12-30T14:09:09.583Z'], |
| 71 | + [274303, 3.57, 'BTC-BTC', '2025-12-30T14:08:53.306Z'], |
| 72 | + [274360, 10.75, 'USDT-BTC', '2025-12-30T14:08:58.990Z'], |
| 73 | + [274625, 5.41, 'USDT-BTC', '2025-12-30T14:09:22.010Z'], |
| 74 | + [274626, 13.63, 'USDT-BTC', '2025-12-30T14:09:12.246Z'], |
| 75 | + [274657, 3.45, 'BTC-BTC', '2025-12-30T14:09:10.126Z'], |
| 76 | + [274964, 9.74, 'BTC-BTC', '2025-12-30T14:08:29.593Z'], |
| 77 | + [275078, 15.74, 'BTC-BTC', '2025-12-30T14:08:44.456Z'], |
| 78 | + [275079, 19.24, 'BTC-BTC', '2025-12-30T14:08:50.960Z'], |
| 79 | + [275274, 10.74, 'BTC-BTC', '2025-12-30T14:08:30.643Z'], |
| 80 | + [275295, 3.1, 'BTC-BTC', '2025-12-30T14:08:33.460Z'], |
| 81 | + [275367, 15.56, 'USDT-BTC', '2025-12-30T14:08:32.766Z'], |
| 82 | + [275385, 1.55, 'BTC-BTC', '2025-12-30T14:08:32.040Z'], |
| 83 | + [275386, 15.47, 'USDT-BTC', '2025-12-30T14:08:31.316Z'], |
| 84 | + [275388, 9.43, 'BTC-BTC', '2025-12-30T14:08:29.896Z'], |
| 85 | + [275555, 1.55, 'BTC-BTC', '2025-12-30T14:08:49.826Z'], |
| 86 | + [275622, 8.63, 'BTC-BTC', '2025-12-30T14:09:00.223Z'], |
| 87 | + [275650, 1.53, 'BTC-BTC', '2025-12-30T14:08:34.576Z'], |
| 88 | + [275669, 9.32, 'USDT-BTC', '2025-12-30T14:08:54.396Z'], |
| 89 | + [275692, 1.51, 'BTC-BTC', '2025-12-30T14:08:53.680Z'], |
| 90 | + [275838, 31.86, 'BTC-BTC', '2025-12-30T14:08:38.976Z'], |
| 91 | + [275872, 3.05, 'BTC-BTC', '2025-12-30T14:08:37.966Z'], |
| 92 | + [275932, 8.13, 'BTC-BTC', '2025-12-30T14:08:33.813Z'], |
| 93 | + [275943, 11.4, 'BTC-BTC', '2025-12-30T14:08:57.580Z'], |
| 94 | + [276247, 13.92, 'BTC-BTC', '2025-12-30T14:09:19.453Z'], |
| 95 | + [276249, 6.8, 'BTC-BTC', '2025-12-30T14:09:01.566Z'], |
| 96 | + [276257, 21.46, 'BTC-BTC', '2025-12-30T14:09:03.703Z'], |
| 97 | + [276464, 3.16, 'BTC-BTC', '2025-12-30T14:09:22.546Z'], |
| 98 | + [276726, 11.66, 'BTC-BTC', '2025-12-30T14:09:16.210Z'], |
| 99 | + [276963, 11.99, 'BTC-BTC', '2025-12-30T14:09:15.980Z'], |
| 100 | + [277199, 1.61, 'BTC-BTC', '2025-12-30T14:09:07.830Z'], |
| 101 | + [277200, 9.44, 'BTC-BTC', '2025-12-30T14:09:02.636Z'], |
| 102 | + [277244, 9.64, 'BTC-BTC', '2025-12-30T14:09:19.723Z'], |
| 103 | + [277563, 13.62, 'BTC-BTC', '2025-12-30T14:09:04.803Z'], |
| 104 | + [277694, 6.74, 'BTC-BTC', '2025-12-30T14:09:19.960Z'], |
| 105 | + [277784, 3.61, 'BTC-BTC', '2025-12-30T14:09:01.870Z'], |
| 106 | + [277785, 12.78, 'BTC-BTC', '2025-12-30T14:09:14.280Z'], |
| 107 | + [277786, 28.41, 'BTC-BTC', '2025-12-30T14:09:22.850Z'], |
| 108 | + [277993, 9.03, 'BTC-BTC', '2025-12-30T14:08:56.210Z'], |
| 109 | + [278009, 3.02, 'BTC-BTC', '2025-12-30T14:08:51.383Z'], |
| 110 | + [278078, 2.46, 'BTC-BTC', '2025-12-30T14:09:00.743Z'], |
| 111 | + [278165, 3.35, 'BTC-BTC', '2025-12-30T14:09:05.253Z'], |
| 112 | + [278346, 9.04, 'BTC-BTC', '2025-12-30T14:09:02.110Z'], |
| 113 | + [278381, 5.02, 'BTC-BTC', '2025-12-30T14:09:14.563Z'], |
| 114 | + [278392, 6.24, 'BTC-BTC', '2025-12-30T14:09:23.136Z'], |
| 115 | + [278719, 1.43, 'BTC-BTC', '2025-12-30T14:08:56.830Z'], |
| 116 | + [279171, 10.11, 'USDT-BTC', '2025-12-30T14:08:51.740Z'], |
| 117 | + [279375, 3.11, 'BTC-BTC', '2025-12-30T14:09:00.990Z'], |
| 118 | + [280273, 20.76, 'BTC-BTC', '2025-12-30T14:08:54.060Z'], |
| 119 | + [280332, 21.15, 'BTC-BTC', '2025-12-30T14:09:10.706Z'], |
| 120 | + [281625, 6.97, 'BTC-BTC', '2025-12-30T14:09:08.863Z'], |
| 121 | + [281751, 8.82, 'BTC-BTC', '2025-12-30T14:08:52.146Z'], |
| 122 | + [282365, 8.5, 'BTC-BTC', '2025-12-30T14:08:59.890Z'], |
| 123 | + [282608, 24.19, 'BTC-BTC', '2025-12-30T14:09:09.870Z'], |
| 124 | + [283945, 28.6, 'BTC-BTC', '2025-12-30T14:09:01.290Z'], |
| 125 | + [283977, 0.9, 'BTC-BTC', '2025-12-30T14:09:03.360Z'], |
| 126 | + [284056, 10.75, 'BTC-BTC', '2025-12-30T14:09:22.283Z'], |
| 127 | + [284208, 20.36, 'BTC-BTC', '2025-12-30T14:09:12.606Z'], |
| 128 | + [284209, 14.45, 'BTC-BTC', '2025-12-30T14:09:15.733Z'], |
| 129 | + [284453, 9.48, 'USDT-BTC', '2025-12-30T14:09:07.546Z'], |
| 130 | + [284460, 10.23, 'USDT-BTC', '2025-12-30T14:09:02.366Z'], |
| 131 | + [284791, 22.94, 'BTC-BTC', '2025-12-30T14:09:06.803Z'], |
| 132 | + [284874, 7.49, 'BTC-BTC', '2025-12-30T14:09:20.566Z'], |
| 133 | + [284876, 10.36, 'BTC-BTC', '2025-12-30T14:09:13.753Z'], |
| 134 | + [285012, 27.13, 'BTC-BTC', '2025-12-30T14:09:16.726Z'], |
| 135 | + [285085, 6.57, 'BTC-BTC', '2025-12-30T14:09:23.676Z'], |
| 136 | + [285169, 37.02, 'BTC-BTC', '2025-12-30T14:09:18.640Z'], |
| 137 | + [285420, 31.06, 'BTC-BTC', '2025-12-30T14:09:17.516Z'], |
| 138 | + [285461, 6.81, 'BTC-BTC', '2025-12-30T14:09:08.390Z'], |
| 139 | + [285583, 31.95, 'BTC-BTC', '2025-12-30T14:09:07.103Z'], |
| 140 | + [285667, 6.45, 'BTC-BTC', '2025-12-30T14:09:20.973Z'], |
| 141 | + [286099, 30.99, 'BTC-BTC', '2025-12-30T14:09:13.986Z'], |
| 142 | + [286517, 16.24, 'BTC-BTC', '2025-12-30T14:09:16.980Z'], |
| 143 | + [286708, 31.16, 'BTC-BTC', '2025-12-30T14:09:23.963Z'], |
| 144 | + [287341, 22.64, 'BTC-BTC', '2025-12-30T14:09:21.233Z'], |
| 145 | + [288951, 28.7, 'BTC-BTC', '2025-12-30T14:09:14.820Z'], |
| 146 | + [289695, 23.74, 'BTC-BTC', '2025-12-30T14:09:17.770Z'], |
| 147 | + [290951, 45.64, 'BTC-BTC', '2025-12-30T14:09:21.493Z'], |
| 148 | + [292062, 29.3, 'BTC-BTC', '2025-12-30T14:09:15.080Z'], |
| 149 | + ]; |
| 150 | + |
| 151 | + console.log('=== Sync CryptoCrypto Transaction AML Data ===\n'); |
| 152 | + console.log(`Transactions to fix: ${transactions.length}`); |
| 153 | + |
| 154 | + // Verify current state - all should have NULL amlCheck |
| 155 | + const currentState = await queryRunner.query(` |
| 156 | + SELECT COUNT(*) as count |
| 157 | + FROM dbo.[transaction] |
| 158 | + WHERE id IN (${transactions.map((t) => t[0]).join(',')}) |
| 159 | + AND amlCheck IS NULL |
| 160 | + `); |
| 161 | + |
| 162 | + const nullCount = currentState[0].count; |
| 163 | + console.log(`Verified: ${nullCount}/${transactions.length} have amlCheck=NULL\n`); |
| 164 | + |
| 165 | + if (nullCount !== transactions.length) { |
| 166 | + const alreadyFixed = await queryRunner.query(` |
| 167 | + SELECT id, amlCheck |
| 168 | + FROM dbo.[transaction] |
| 169 | + WHERE id IN (${transactions.map((t) => t[0]).join(',')}) |
| 170 | + AND amlCheck IS NOT NULL |
| 171 | + `); |
| 172 | + console.log(`WARNING: ${alreadyFixed.length} transactions already have amlCheck set:`); |
| 173 | + for (const tx of alreadyFixed) { |
| 174 | + console.log(` - Transaction ${tx.id}: amlCheck=${tx.amlCheck}`); |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + // Update each transaction with hardcoded values |
| 179 | + let updated = 0; |
| 180 | + |
| 181 | + for (const [txId, amountInChf, assets, eventDate] of transactions) { |
| 182 | + const result = await queryRunner.query( |
| 183 | + ` |
| 184 | + UPDATE dbo.[transaction] |
| 185 | + SET |
| 186 | + amlCheck = 'Pass', |
| 187 | + assets = '${assets}', |
| 188 | + amountInChf = ${amountInChf === null ? 'NULL' : amountInChf}, |
| 189 | + highRisk = 0, |
| 190 | + eventDate = '${eventDate}', |
| 191 | + amlType = 'CryptoCrypto', |
| 192 | + updated = GETDATE() |
| 193 | + WHERE id = ${txId} |
| 194 | + AND amlCheck IS NULL |
| 195 | + `, |
| 196 | + ); |
| 197 | + |
| 198 | + if (result?.rowsAffected?.[0] > 0) updated++; |
| 199 | + } |
| 200 | + |
| 201 | + console.log(`Updated ${updated}/${transactions.length} transactions\n`); |
| 202 | + |
| 203 | + // Verify final state |
| 204 | + const finalState = await queryRunner.query(` |
| 205 | + SELECT COUNT(*) as count, SUM(amountInChf) as totalChf |
| 206 | + FROM dbo.[transaction] |
| 207 | + WHERE id IN (${transactions.map((t) => t[0]).join(',')}) |
| 208 | + AND amlCheck = 'Pass' |
| 209 | + `); |
| 210 | + |
| 211 | + console.log(`=== Verification ===`); |
| 212 | + console.log(` Transactions with amlCheck=Pass: ${finalState[0].count}`); |
| 213 | + console.log(` Total volume: ${finalState[0].totalChf} CHF`); |
| 214 | + } |
| 215 | + |
| 216 | + /** |
| 217 | + * @param {QueryRunner} queryRunner |
| 218 | + */ |
| 219 | + async down(queryRunner) { |
| 220 | + const txIds = [ |
| 221 | + 261949, 261950, 262020, 262028, 262143, 262335, 262746, 263544, 263545, 264117, 264418, 264422, 267046, 267066, |
| 222 | + 267161, 267162, 267170, 267911, 268141, 268507, 268887, 269574, 271252, 271665, 271670, 272117, 273008, 273105, |
| 223 | + 273395, 273417, 273464, 273724, 273793, 274027, 274062, 274075, 274303, 274360, 274625, 274626, 274657, 274964, |
| 224 | + 275078, 275079, 275274, 275295, 275367, 275385, 275386, 275388, 275555, 275622, 275650, 275669, 275692, 275838, |
| 225 | + 275872, 275932, 275943, 276247, 276249, 276257, 276464, 276726, 276963, 277199, 277200, 277244, 277563, 277694, |
| 226 | + 277784, 277785, 277786, 277993, 278009, 278078, 278165, 278346, 278381, 278392, 278719, 279171, 279375, 280273, |
| 227 | + 280332, 281625, 281751, 282365, 282608, 283945, 283977, 284056, 284208, 284209, 284453, 284460, 284791, 284874, |
| 228 | + 284876, 285012, 285085, 285169, 285420, 285461, 285583, 285667, 286099, 286517, 286708, 287341, 288951, 289695, |
| 229 | + 290951, 292062, |
| 230 | + ]; |
| 231 | + |
| 232 | + await queryRunner.query(` |
| 233 | + UPDATE dbo.[transaction] |
| 234 | + SET |
| 235 | + amlCheck = NULL, |
| 236 | + assets = NULL, |
| 237 | + amountInChf = NULL, |
| 238 | + highRisk = NULL, |
| 239 | + eventDate = NULL, |
| 240 | + amlType = NULL, |
| 241 | + updated = GETDATE() |
| 242 | + WHERE id IN (${txIds.join(',')}) |
| 243 | + `); |
| 244 | + } |
| 245 | +}; |
0 commit comments