Fix 3 loi nghiem trong: eval() -> safe parser, Contract::saved() infinite loop, DB Transaction for schedule generation

This commit is contained in:
2026-04-28 08:04:30 +00:00
parent 49aa20a634
commit e229da5e8c
7 changed files with 361 additions and 78 deletions

View File

@@ -7,63 +7,67 @@ use App\Models\PaymentSchedule;
use App\Models\PaymentScheduleItem;
use App\Models\PaymentTemplate;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
class ContractScheduleService
{
/**
* Tạo lịch thanh toán cho hợp đồng dựa trên mẫu.
* Nếu đã tồn tại lịch , sẽ xóa tạo lại.
* Toàn bộ quá trình được bọc trong DB Transaction để đảm bảo tính toàn vẹn.
*/
public static function generateFromTemplate(Contract $contract, ?PaymentTemplate $template = null): PaymentSchedule
{
if (! $template) {
// Ưu tiên template của dự án
$template = $contract->product?->project?->paymentTemplate;
}
if (! $template) {
throw new \InvalidArgumentException('Không tìm thấy mẫu thanh toán cho hợp đồng này.');
}
// Xóa lịch cũ nếu có
if ($contract->paymentSchedule) {
$contract->paymentSchedule->items()->delete();
$contract->paymentSchedule->delete();
}
$schedule = PaymentSchedule::create([
'contract_id' => $contract->id,
'template_id' => $template->id,
]);
$items = $template->items()->orderBy('installment_no')->get();
$lastDueDate = Carbon::parse($contract->signing_date);
foreach ($items as $item) {
$dueDate = null;
if ($item->days_after_signing !== null) {
$dueDate = Carbon::parse($contract->signing_date)->addDays($item->days_after_signing);
} elseif ($item->days_after_previous !== null) {
$dueDate = $lastDueDate->copy()->addDays($item->days_after_previous);
} elseif ($item->due_date !== null) {
$dueDate = $item->due_date;
return DB::transaction(function () use ($contract, $template) {
if (! $template) {
// Ưu tiên template của dự án
$template = $contract->product?->project?->paymentTemplate;
}
PaymentScheduleItem::create([
'schedule_id' => $schedule->id,
'installment_no' => $item->installment_no,
'type' => $item->type,
'percentage' => $item->percentage,
'amount' => $contract->total_value * ($item->percentage / 100),
'due_date' => $dueDate,
if (! $template) {
throw new \InvalidArgumentException('Không tìm thấy mẫu thanh toán cho hợp đồng này.');
}
// Xóa lịch cũ nếu có
if ($contract->paymentSchedule) {
$contract->paymentSchedule->items()->delete();
$contract->paymentSchedule->delete();
}
$schedule = PaymentSchedule::create([
'contract_id' => $contract->id,
'template_id' => $template->id,
]);
if ($dueDate) {
$lastDueDate = $dueDate;
}
}
$items = $template->items()->orderBy('installment_no')->get();
$lastDueDate = Carbon::parse($contract->signing_date);
return $schedule;
foreach ($items as $item) {
$dueDate = null;
if ($item->days_after_signing !== null) {
$dueDate = Carbon::parse($contract->signing_date)->addDays($item->days_after_signing);
} elseif ($item->days_after_previous !== null) {
$dueDate = $lastDueDate->copy()->addDays($item->days_after_previous);
} elseif ($item->due_date !== null) {
$dueDate = $item->due_date;
}
PaymentScheduleItem::create([
'schedule_id' => $schedule->id,
'installment_no' => $item->installment_no,
'type' => $item->type,
'percentage' => $item->percentage,
'amount' => $contract->total_value * ($item->percentage / 100),
'due_date' => $dueDate,
]);
if ($dueDate) {
$lastDueDate = $dueDate;
}
}
return $schedule;
});
}
}