utc(); return DB::transaction(function () use ($draw, $input, $tz, $nowUtc): Draw { /** @var Draw $locked */ $locked = Draw::query()->whereKey($draw->id)->lockForUpdate()->firstOrFail(); $this->assertEditable($locked); $drawLocal = Carbon::parse((string) $input['draw_time'], $tz); $startLocal = isset($input['start_time']) && $input['start_time'] !== null && $input['start_time'] !== '' ? Carbon::parse((string) $input['start_time'], $tz) : null; $closeLocal = isset($input['close_time']) && $input['close_time'] !== null && $input['close_time'] !== '' ? Carbon::parse((string) $input['close_time'], $tz) : null; if ($startLocal === null || $closeLocal === null) { $defaults = $this->timeline->windowsFromDrawLocal($drawLocal); $startLocal ??= $defaults['start_local']; $closeLocal ??= $defaults['close_local']; } if (! $startLocal->lt($closeLocal) || ! $closeLocal->lt($drawLocal)) { throw new \RuntimeException('draw_timeline_invalid'); } $businessDate = isset($input['business_date']) && $input['business_date'] !== '' ? (string) $input['business_date'] : $drawLocal->format('Y-m-d'); $sequenceNo = isset($input['sequence_no']) && $input['sequence_no'] !== null ? max(1, (int) $input['sequence_no']) : (int) $locked->sequence_no; $drawNo = isset($input['draw_no']) && trim((string) $input['draw_no']) !== '' ? trim((string) $input['draw_no']) : (string) $locked->draw_no; if ( Draw::query() ->where('draw_no', $drawNo) ->where('id', '!=', $locked->id) ->exists() ) { throw new \RuntimeException('draw_no_exists'); } $built = $this->timeline->buildFromLocals($startLocal, $closeLocal, $drawLocal, $nowUtc); $locked->forceFill([ 'draw_no' => $drawNo, 'business_date' => $businessDate, 'sequence_no' => $sequenceNo, 'status' => $built['status'], 'start_time' => $built['start_utc'], 'close_time' => $built['close_utc'], 'draw_time' => $built['draw_utc'], ])->save(); return $locked->refresh(); }); } private function assertEditable(Draw $draw): void { if ($draw->resultBatches()->exists()) { throw new \RuntimeException('draw_result_exists'); } if (! in_array($draw->status, [DrawStatus::Pending->value, DrawStatus::Open->value], true)) { throw new \RuntimeException('draw_not_editable'); } if ($draw->status === DrawStatus::Open->value) { $betTotal = (int) TicketOrder::query()->where('draw_id', $draw->id)->sum('total_actual_deduct'); if ($betTotal > 0) { throw new \RuntimeException('draw_has_bets'); } } } }