migrate_water_fulfillment.php 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
  1. <?php
  2. /** Additive fulfillment migration. Existing quantities, receipts and task states are preserved. */
  3. require dirname(__DIR__).'/vendor/autoload.php';
  4. \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load();
  5. \support\App::loadAllConfig(['route']);
  6. use plugin\saiadmin\app\model\water\WaterSku;
  7. use plugin\saiadmin\app\model\water\{WaterModel,WaterDelivery,WaterDriver,WaterRoute,WaterDeliveryEvent};
  8. $db=(new WaterSku())->getConnection();
  9. if (!in_array('--apply',$argv,true)) { echo "Plan: add fulfillment counters, route settlement and immutable delivery events.\n"; exit; }
  10. $columns=[
  11. 'sa_water_delivery'=>[
  12. 'delivered_qty'=>'INT UNSIGNED NOT NULL DEFAULT 0','return_good_qty'=>'INT UNSIGNED NOT NULL DEFAULT 0',
  13. 'return_damaged_qty'=>'INT UNSIGNED NOT NULL DEFAULT 0','empty_in_vehicle'=>'INT UNSIGNED NOT NULL DEFAULT 0',
  14. 'receiver'=>"VARCHAR(60) NOT NULL DEFAULT ''",'exception_reason'=>"VARCHAR(255) NOT NULL DEFAULT ''",
  15. 'received_at'=>'DATETIME NULL','returned_at'=>'DATETIME NULL','redelivery_of'=>'BIGINT UNSIGNED NULL',
  16. ],
  17. 'sa_water_route'=>['settled_at'=>'DATETIME NULL','settled_by'=>'BIGINT UNSIGNED NULL','settlement_note'=>"VARCHAR(255) NOT NULL DEFAULT ''"],
  18. 'sa_water_sku'=>['damaged_qty'=>'INT UNSIGNED NOT NULL DEFAULT 0','empty_lost_qty'=>'INT UNSIGNED NOT NULL DEFAULT 0'],
  19. ];
  20. foreach ($columns as $table=>$fields) foreach ($fields as $name=>$definition) {
  21. if (!$db->select("SHOW COLUMNS FROM `$table` LIKE '$name'")) $db->statement("ALTER TABLE `$table` ADD COLUMN `$name` $definition");
  22. }
  23. if (!$db->select("SHOW INDEX FROM sa_water_delivery WHERE Key_name='delivery_redelivery'")) $db->statement('ALTER TABLE sa_water_delivery ADD UNIQUE KEY delivery_redelivery (redelivery_of)');
  24. $db->statement("CREATE TABLE IF NOT EXISTS sa_water_delivery_event (
  25. id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, delivery_id BIGINT UNSIGNED NULL, route_id BIGINT UNSIGNED NULL,
  26. event_key VARCHAR(100) NOT NULL, action VARCHAR(30) NOT NULL, payload_hash CHAR(64) NOT NULL,
  27. payload LONGTEXT NOT NULL, snapshot LONGTEXT NOT NULL, actor_id BIGINT UNSIGNED NOT NULL, created_at DATETIME NOT NULL,
  28. UNIQUE KEY fulfillment_event(event_key), KEY delivery_events(delivery_id,id), KEY route_events(route_id,id)
  29. ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4");
  30. $linked=WaterModel::transaction(function () {
  31. $ids=WaterDelivery::query()->whereNull('route_id')->whereIn('status',['assigned','transit'])->orderBy('id')->pluck('id')->all();
  32. $count=0;
  33. foreach ($ids as $id) {
  34. $preview=WaterDelivery::query()->find($id);
  35. WaterSku::query()->where('id',$preview->sku_id)->lockForUpdate()->first();
  36. $driver=WaterDriver::query()->where('id',$preview->driver_id)->lockForUpdate()->first();
  37. $task=WaterDelivery::query()->where('id',$id)->lockForUpdate()->first();
  38. if ($task->route_id || !in_array($task->status,['assigned','transit'],true)) continue;
  39. if (!$driver) throw new RuntimeException('Legacy delivery #'.$id.' requires a registered driver before migration');
  40. $routeId=WaterRoute::query()->insertGetId(['code'=>'LEGACY-FULFILL-'.$id,'driver_id'=>$driver->id,
  41. 'work_date'=>$task->scheduled_date?:substr($task->created_at,0,10),'region'=>$task->region,'capacity'=>max((int)$driver->capacity,(int)$task->quantity),
  42. 'request_key'=>'fulfillment-legacy-'.$id,'request_hash'=>hash('sha256','fulfillment-legacy-'.$id),'created_by'=>$task->created_by,'created_at'=>date('Y-m-d H:i:s')]);
  43. WaterDelivery::query()->where('id',$id)->update(['route_id'=>$routeId,'stop_order'=>1]);
  44. WaterDeliveryEvent::query()->insert(['delivery_id'=>$id,'route_id'=>$routeId,'event_key'=>'legacy_route:'.$id,'action'=>'legacy_route',
  45. 'payload_hash'=>hash('sha256','{}'),'payload'=>'{}','snapshot'=>json_encode(['before'=>$task->toArray(),'route_id'=>$routeId]),'actor_id'=>0,'created_at'=>date('Y-m-d H:i:s')]);
  46. $count++;
  47. }
  48. return $count;
  49. });
  50. echo "Applied additive fulfillment migration; linked $linked active legacy tasks to routes; historical balances and statuses preserved.\n";