Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
| QuotationsCleanupZeroDates | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |
0.00% |
0 / 1 |
| handle | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
6 | |||
| 1 | <?php |
| 2 | |
| 3 | namespace App\Console\Commands; |
| 4 | |
| 5 | use App\Models\TblQuotations; |
| 6 | use Illuminate\Console\Command; |
| 7 | use Illuminate\Support\Facades\Log; |
| 8 | |
| 9 | /** |
| 10 | * FIRE-1025 follow-up to FIRE-1001, widened by FIRE-1024. |
| 11 | * |
| 12 | * Sweeps `tbl_quotations.acceptance_date` rows that hold any zero-date |
| 13 | * sentinel back to NULL once a day. Replaces the five in-line sweepers in |
| 14 | * PresupuestosService.php (lines 186, 710, 1083, 1391, 1980 pre-fix) that |
| 15 | * were running this UPDATE at the END of every G3W sync iteration. |
| 16 | * Concurrent G3W syncs caused the same UPDATE to fight for an exclusive |
| 17 | * lock on `idx_acceptance_date`, which surfaced as the deadlocks observed |
| 18 | * on 2026-04-27 (`SHOW ENGINE INNODB STATUS` had multiple "Searching rows |
| 19 | * for update" / "WE ROLL BACK TRANSACTION (1)" entries on this exact |
| 20 | * statement). |
| 21 | * |
| 22 | * Since the FIRE-1001 + FIRE-1024 fix in PresupuestosService normalises |
| 23 | * the G3W `aceptacion` field on every write (`null`, `''`, `'0000-00-00'`, |
| 24 | * `'0000/00/00'`, `'0000-00-00 00:00:00'`, whitespace → null before the |
| 25 | * row is INSERTed/UPDATEd), no NEW row can land in this state. This |
| 26 | * command exists only to clean up legacy rows. After a few clean runs it |
| 27 | * will become a no-op; keep it scheduled as a janitor against any future |
| 28 | * regression that might re-introduce zero-dates. |
| 29 | * |
| 30 | * FIRE-1024 widening: pre-fix the matcher was `where('acceptance_date', |
| 31 | * '0000-00-00 00:00:00')` — only the datetime form. Any leak that produced |
| 32 | * the date-only `'0000-00-00'` or the slash form `'0000/00/00'` survived |
| 33 | * the daily sweep. Now matches all three sentinels via `whereIn`, which |
| 34 | * still uses the `idx_acceptance_date` index. |
| 35 | */ |
| 36 | class QuotationsCleanupZeroDates extends Command |
| 37 | { |
| 38 | protected $signature = 'quotations:cleanup-zero-dates'; |
| 39 | |
| 40 | protected $description = 'Sweep legacy tbl_quotations.acceptance_date zero-date rows to NULL (FIRE-1025 / FIRE-1024)'; |
| 41 | |
| 42 | /** |
| 43 | * All zero-date sentinels we accept as "this means null." Datetime form |
| 44 | * is what the legacy in-line sweepers chased; date-only and slash forms |
| 45 | * surface from older G3W payloads that the FIRE-1001 normaliser missed. |
| 46 | */ |
| 47 | private const ZERO_DATE_SENTINELS = [ |
| 48 | '0000-00-00 00:00:00', |
| 49 | '0000-00-00', |
| 50 | '0000/00/00', |
| 51 | ]; |
| 52 | |
| 53 | public function handle(): int |
| 54 | { |
| 55 | $count = TblQuotations::whereIn('acceptance_date', self::ZERO_DATE_SENTINELS)->count(); |
| 56 | |
| 57 | if ($count === 0) { |
| 58 | $this->info('quotations:cleanup-zero-dates — no zero-date rows found.'); |
| 59 | Log::channel('g3w')->info('quotations:cleanup-zero-dates — 0 rows.'); |
| 60 | return Command::SUCCESS; |
| 61 | } |
| 62 | |
| 63 | $updated = TblQuotations::whereIn('acceptance_date', self::ZERO_DATE_SENTINELS) |
| 64 | ->update(['acceptance_date' => null]); |
| 65 | |
| 66 | $this->info("quotations:cleanup-zero-dates — set {$updated} rows to NULL (out of {$count} matched)."); |
| 67 | Log::channel('g3w')->info("quotations:cleanup-zero-dates — set {$updated} rows to NULL."); |
| 68 | |
| 69 | return Command::SUCCESS; |
| 70 | } |
| 71 | } |