where('id', $originalBillId)->first(); if ($original === null) { throw new \InvalidArgumentException('bill_not_found'); } if ($this->periodCompletion->isPeriodReadOnly((int) $original->settlement_period_id)) { throw ValidationException::withMessages([ 'period' => ['completed'], ]); } if (! in_array((string) $original->status, ['confirmed', 'partial_paid', 'settled', 'overdue'], true)) { throw ValidationException::withMessages([ 'bill' => ['not_locked'], ]); } if ($amount === 0) { throw ValidationException::withMessages([ 'amount' => ['zero'], ]); } $type = in_array($adjustmentType, ['adjustment', 'reversal'], true) ? $adjustmentType : 'adjustment'; return (int) DB::transaction(function () use ($original, $amount, $type, $reason, $adminUserId): int { $now = now(); $newBillId = (int) DB::table('settlement_bills')->insertGetId([ 'settlement_period_id' => (int) $original->settlement_period_id, 'bill_type' => $type, 'owner_type' => (string) $original->owner_type, 'owner_id' => (int) $original->owner_id, 'counterparty_type' => (string) $original->counterparty_type, 'counterparty_id' => (int) $original->counterparty_id, 'gross_win_loss' => 0, 'rebate_amount' => 0, 'adjustment_amount' => $amount, 'platform_rounding_adjustment' => 0, 'net_amount' => $amount, 'paid_amount' => 0, 'unpaid_amount' => abs($amount), 'status' => 'pending_confirm', 'reversed_bill_id' => (int) $original->id, 'meta_json' => json_encode([ 'original_bill_id' => (int) $original->id, 'original_net_amount' => (int) $original->net_amount, ]), 'created_at' => $now, 'updated_at' => $now, ]); DB::table('settlement_adjustments')->insert([ 'settlement_period_id' => (int) $original->settlement_period_id, 'original_bill_id' => (int) $original->id, 'adjustment_type' => $type, 'amount' => $amount, 'reason' => $reason, 'created_by' => $adminUserId > 0 ? $adminUserId : null, 'created_at' => $now, 'updated_at' => $now, ]); return $newBillId; }); } }