Files
hqland-app/app/Filament/Resources/Contracts/Schemas/ContractForm.php

244 lines
14 KiB
PHP

<?php
namespace App\Filament\Resources\Contracts\Schemas;
use App\Models\Product;
use App\Models\PaymentTemplate;
use App\Models\SalesPhase;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\DatePicker;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Grid;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Placeholder;
use Filament\Schemas\Schema;
use Filament\Schemas\Components\Utilities\Get;
use Filament\Schemas\Components\Utilities\Set;
use Illuminate\Support\HtmlString;
class ContractForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
Grid::make(3)
->schema([
Section::make('Thông tin định danh')
->columnSpan(2)
->columns(2)
->schema([
Select::make('product_id')
->label('Sản phẩm (Lô đất)')
->relationship('product', 'code')
->searchable()
->preload()
->required()
->live()
->afterStateUpdated(function (Set $set, $state) {
if ($state) {
$product = Product::find($state);
if ($product) {
$set('total_value', $product->total_price);
$set('land_value', $product->qsdd_value);
$set('foundation_value', $product->foundation_temp_value);
$set('sales_phase_id', null);
}
}
}),
Select::make('sales_phase_id')
->label('Đợt mở bán')
->options(function (Get $get) {
$productId = $get('product_id');
if (! $productId) return [];
$product = Product::find($productId);
if (! $product) return [];
return SalesPhase::where('project_id', $product->project_id)
->whereIn('status', ['Chuẩn bị', 'Đang mở bán'])
->pluck('name', 'id');
})
->searchable()
->preload()
->live()
->helperText('Chọn đợt mở bán để áp dụng giá và chính sách của đợt')
->afterStateUpdated(function (Set $set, Get $get, $state) {
$productId = $get('product_id');
if ($state && $productId) {
$phaseProduct = \App\Models\SalesPhaseProduct::where('sales_phase_id', $state)
->where('product_id', $productId)
->first();
if ($phaseProduct) {
$set('land_value', $phaseProduct->land_value ?? $get('land_value'));
$set('foundation_value', $phaseProduct->foundation_value ?? $get('foundation_value'));
$total = (float) ($phaseProduct->land_value ?? $get('land_value')) + (float) ($phaseProduct->foundation_value ?? $get('foundation_value'));
if ($phaseProduct->sale_price > 0) {
$total = (float) $phaseProduct->sale_price;
}
$set('total_value', $total);
if ($phaseProduct->discount_details) {
$set('discount_details', $phaseProduct->discount_details);
}
}
}
}),
TextInput::make('contract_number')
->label('Số HĐMB')
->required()
->unique(ignoreRecord: true),
Select::make('contract_type')
->label('Loại hợp đồng')
->options([
'HĐMB' => 'Hợp đồng mua bán',
'HĐGV' => 'Hợp đồng góp vốn',
'HĐDC' => 'Hợp đồng đặt cọc',
])
->default('HĐMB')
->required(),
TextInput::make('transfer_order')
->label('Thứ tự chuyển nhượng')
->numeric()
->default(0)
->helperText('0 là chủ hiện tại, 1 là F0, 2 là F1...'),
]),
Section::make('Trạng thái')
->columnSpan(1)
->schema([
Select::make('status')
->label('Trạng thái pháp lý')
->options([
'Đang hiệu lực' => 'Đang hiệu lực',
'Đã hoàn thành' => 'Đã hoàn thành',
'Đã hủy' => 'Đã hủy',
])
->default('Đang hiệu lực')
->required(),
DatePicker::make('signing_date')
->label('Ngày ký HĐ')
->required(),
DatePicker::make('sale_date')
->label('Ngày bán thực tế'),
]),
]),
Section::make('Chi tiết Tài chính & Chiết khấu')
->columns(3)
->schema([
TextInput::make('land_value')
->label('Giá trị QSDĐ')
->numeric()
->prefix('VND')
->live(onBlur: true)
->afterStateUpdated(fn ($state, $get, $set) => $set('total_value', (float)$state + (float)$get('foundation_value'))),
TextInput::make('foundation_value')
->label('Giá trị Móng')
->numeric()
->prefix('VND')
->live(onBlur: true)
->afterStateUpdated(fn ($state, $get, $set) => $set('total_value', (float)$get('land_value') + (float)$state)),
TextInput::make('total_value')
->label('Tổng giá trị niêm yết')
->numeric()
->prefix('VND')
->readOnly(),
Placeholder::make('discount_overview')
->label('Tổng quan chiết khấu (Dữ liệu từ Excel)')
->columnSpanFull()
->content(function ($record) {
if (!$record || !$record->discount_details) return 'Không có chiết khấu';
$details = $record->discount_details;
$html = '<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 15px; background: #f9fafb; padding: 15px; border-radius: 8px; border: 1px solid #e5e7eb;">';
foreach ($details as $key => $val) {
if (empty($val)) continue;
$label = match($key) {
'open_sale' => 'Mở bán',
'multi_lot' => 'Số nhiều',
'wholesale' => 'Mua sỉ',
'ctv' => 'Cộng tác viên',
'full_payment' => 'Trả 1 lần',
'total_amount' => 'Tổng tiền CK',
'total_percentage' => 'Tổng % CK',
default => $key
};
$style = str_contains($key, 'total') ? 'font-weight: bold; color: #16a34a;' : 'color: #4b5563;';
$html .= "<div>
<div style='font-size: 0.7rem; color: #9ca3af; text-transform: uppercase; margin-bottom: 4px;'>{$label}</div>
<div style='{$style} font-size: 0.9rem;'>{$val}</div>
</div>";
}
$html .= '</div>';
return new HtmlString($html);
}),
KeyValue::make('discount_details')
->label('Bảng chi tiết chiết khấu (Dạng Key-Value)')
->columnSpanFull(),
Placeholder::make('price_sheet')
->label('Phiếu tính giá')
->columnSpanFull()
->content(function ($record) {
if (! $record || ! $record->calculation_log) {
return new HtmlString("<div style='font-size: 0.9rem; color: #9ca3af;'>Chưa có dữ liệu tính toán. Vui lòng lưu hợp đồng để tạo phiếu tính giá.</div>");
}
$steps = $record->calculation_log['price_sheet'] ?? [];
if (empty($steps)) {
return '-';
}
$html = '<div style="background: #f9fafb; border: 1px solid #e5e7eb; border-radius: 8px; padding: 16px;">';
$html .= '<table style="width: 100%; border-collapse: collapse; font-size: 0.9rem;">';
$html .= '<thead><tr style="border-bottom: 2px solid #e5e7eb;"><th style="text-align: left; padding: 8px;">Diễn giải</th><th style="text-align: right; padding: 8px;">Giá trị (VNĐ)</th></tr></thead><tbody>';
foreach ($steps as $step) {
$desc = $step['description'];
$value = number_format($step['value']);
$isOverride = $step['is_overridden'] ? ' <span style="color: #f59e0b; font-size: 0.75rem;">(ghi đè)</span>' : '';
$style = str_contains(strtolower($desc), 'tổng') ? 'font-weight: bold; border-top: 1px solid #e5e7eb;' : '';
$html .= "<tr style='{$style}'><td style='padding: 8px;'>{$desc}{$isOverride}</td><td style='text-align: right; padding: 8px;'>{$value}</td></tr>";
}
$html .= '</tbody></table></div>';
return new HtmlString($html);
}),
]),
Section::make('Thông tin quản lý & Khách hàng')
->columns(2)
->schema([
Select::make('customers')
->label('Khách hàng đứng tên')
->multiple()
->relationship('customers', 'full_name')
->preload()
->required()
->columnSpanFull(),
TextInput::make('brokerage_name')
->label('Đơn vị môi giới'),
DatePicker::make('hql_confirmation_date')
->label('Ngày HQL xác nhận'),
TextInput::make('stored_contract_count')
->label('Số lượng HĐ lưu')
->numeric()
->default(0),
TextInput::make('filing_note')
->label('Ghi chú hồ sơ')
->columnSpanFull(),
Select::make('payment_template_id')
->label('Áp dụng mẫu thanh toán')
->placeholder('Chọn mẫu để tự động tạo lịch trình...')
->options(PaymentTemplate::pluck('name', 'id'))
->searchable()
->hiddenOn('edit')
->helperText('Hệ thống sẽ tự động tạo lịch thanh toán sau khi lưu hợp đồng.'),
]),
]);
}
}