feat: add GeneratesUniqueSlug class for unique slug generation

feat: create PostCategoryFactory for generating post category data

feat: create PostFactory for generating post data

feat: add retry_requested_at column to services table

feat: add vcenter provisioning fields to products table

feat: add module_host_id to os table

feat: create email_notification_templates table for email templates

feat: add scenario and format fields to template table

feat: add admin fields to transactions table

fix: update ticket table columns and types

feat: add uuid to ticket table and backfill existing records

fix: make ticket_reply user_id nullable

feat: create notifications table for user notifications

fix: make ticket uuid nullable

feat: create post_categories table for blog categories

feat: create posts table for blog posts

feat: add invoice email template for PDF generation
This commit is contained in:
baodev0
2026-05-25 15:39:42 +07:00
parent a31d238135
commit 520f21cb11
73 changed files with 4682 additions and 440 deletions
@@ -205,17 +205,28 @@ class RetryProvisioningOrchestrator implements ShouldQueue
/**
* Resolve vcenter from DB chain:
* Path 1: order_items.os_id → os → osGroup → host → datacenter → vcenter
* Path 2: invoices → invoice_items → product_id → os_product → os → osGroup → host → datacenter → vcenter
* Path 3: module_vmware with same order_id (partial provisioning)
* Path 1a: order_items.os_id → os.host → datacenter → vcenter (ưu tiên: host gán trực tiếp trên OS)
* Path 1b: order_items.os_id → os → osGroup → host → datacenter → vcenter
* Path 2a: invoices → product → os_product → os.host → datacenter → vcenter
* Path 2b: invoices → product → os_product → os → osGroup → host → datacenter → vcenter
* Path 3: module_vmware with same order_id (partial provisioning)
*/
private function resolveVcenter(OrderItem $item, Telegram $telegram): ?VcenterModel
{
if ($item->os_id) {
$os = Os::with('osGroup.host.datacenter.vCenter')->find($item->os_id);
$os = Os::with(['host.datacenter.vCenter', 'osGroup.host.datacenter.vCenter'])->find($item->os_id);
// Path 1a: host gán trực tiếp trên OS
$vcenter = $os?->host?->datacenter?->vCenter;
if ($vcenter) {
Log::info('resolveVcenter: found via os.host chain', ['os_id' => $item->os_id, 'vcenter_id' => $vcenter->id]);
return $vcenter;
}
// Path 1b: host qua osGroup (cũ)
$vcenter = $os?->osGroup?->host?->datacenter?->vCenter;
if ($vcenter) {
Log::info('resolveVcenter: found via os → osGroup chain', ['vcenter_id' => $vcenter->id]);
Log::info('resolveVcenter: found via os → osGroup chain', ['os_id' => $item->os_id, 'vcenter_id' => $vcenter->id]);
return $vcenter;
}
}
@@ -229,10 +240,19 @@ class RetryProvisioningOrchestrator implements ShouldQueue
->first();
if ($osRecord) {
$os = Os::with('osGroup.host.datacenter.vCenter')->find($osRecord->os_id);
$os = Os::with(['host.datacenter.vCenter', 'osGroup.host.datacenter.vCenter'])->find($osRecord->os_id);
// Path 2a: host gán trực tiếp trên OS
$vcenter = $os?->host?->datacenter?->vCenter;
if ($vcenter) {
Log::info('resolveVcenter: found via invoice → os.host chain', ['vcenter_id' => $vcenter->id]);
return $vcenter;
}
// Path 2b: host qua osGroup
$vcenter = $os?->osGroup?->host?->datacenter?->vCenter;
if ($vcenter) {
Log::info('resolveVcenter: found via invoice_items → os_product chain', ['vcenter_id' => $vcenter->id]);
Log::info('resolveVcenter: found via invoice → os → osGroup chain', ['vcenter_id' => $vcenter->id]);
return $vcenter;
}
}