Hoan thien core finance v2 - Calculation Pipeline, Form Templates

This commit is contained in:
2026-04-28 03:57:18 +00:00
parent 002c9a8b99
commit 49aa20a634
24 changed files with 1043 additions and 875 deletions

View File

@@ -0,0 +1,42 @@
<?php
namespace App\Filament\Resources\FormTemplates;
use App\Filament\Resources\FormTemplates\Pages;
use App\Models\FormTemplate;
use App\Enums\NavigationGroup;
use Filament\Schemas\Schema;
use Filament\Resources\Resource;
use Filament\Tables\Table;
use App\Filament\Resources\FormTemplates\Schemas\FormTemplateForm;
use App\Filament\Resources\FormTemplates\Tables\FormTemplatesTable;
class FormTemplateResource extends Resource
{
protected static ?string $model = FormTemplate::class;
protected static string | \BackedEnum | null $navigationIcon = 'heroicon-o-document-duplicate';
protected static string | \UnitEnum | null $navigationGroup = NavigationGroup::SETTING->value;
protected static ?int $navigationSort = 10;
protected static ?string $modelLabel = 'Biểu mẫu';
protected static ?string $pluralModelLabel = 'Biểu mẫu in ấn';
public static function form(Schema $schema): Schema
{
return FormTemplateForm::configure($schema);
}
public static function table(Table $table): Table
{
return FormTemplatesTable::configure($table);
}
public static function getPages(): array
{
return [
'index' => Pages\ListFormTemplates::route('/'),
'create' => Pages\CreateFormTemplate::route('/create'),
'edit' => Pages\EditFormTemplate::route('/{record}/edit'),
];
}
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\FormTemplates\Pages;
use App\Filament\Resources\FormTemplates\FormTemplateResource;
use Filament\Resources\Pages\CreateRecord;
class CreateFormTemplate extends CreateRecord
{
protected static string $resource = FormTemplateResource::class;
}

View File

@@ -0,0 +1,11 @@
<?php
namespace App\Filament\Resources\FormTemplates\Pages;
use App\Filament\Resources\FormTemplates\FormTemplateResource;
use Filament\Resources\Pages\EditRecord;
class EditFormTemplate extends EditRecord
{
protected static string $resource = FormTemplateResource::class;
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Filament\Resources\FormTemplates\Pages;
use App\Filament\Resources\FormTemplates\FormTemplateResource;
use Filament\Actions;
use Filament\Resources\Pages\ListRecords;
class ListFormTemplates extends ListRecords
{
protected static string $resource = FormTemplateResource::class;
protected function getHeaderActions(): array
{
return [
Actions\CreateAction::make(),
];
}
}

View File

@@ -0,0 +1,148 @@
<?php
namespace App\Filament\Resources\FormTemplates\Schemas;
use Filament\Forms\Components\KeyValue;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\RichEditor;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Repeater;
use Filament\Schemas\Components\Section;
use Filament\Schemas\Components\Grid;
use Filament\Schemas\Schema;
class FormTemplateForm
{
public static function configure(Schema $schema): Schema
{
return $schema
->components([
// BLOCK 1: Thông tin cơ bản
Section::make('Thông tin biểu mẫu')
->schema([
Grid::make(3)
->schema([
TextInput::make('name')
->label('Tên biểu mẫu')
->required(),
TextInput::make('code')
->label('Mã biểu mẫu')
->required()
->unique(ignoreRecord: true),
Select::make('target_model')
->label('Áp dụng cho')
->options([
'App\Models\Contract' => 'Hợp đồng',
'App\Models\Product' => 'Sản phẩm',
'App\Models\Customer' => 'Khách hàng',
])
->required(),
]),
Grid::make(3)
->schema([
Select::make('paper_size')
->label('Khổ giấy')
->options([
'A4' => 'A4',
'A5' => 'A5',
'Letter' => 'Letter',
])
->default('A4')
->required(),
]),
]),
// BLOCK 2: Danh sách trường dữ liệu
Section::make('Danh sách trường dữ liệu (Merge Fields)')
->schema([
Repeater::make('fields')
->relationship('fields')
->schema([
Grid::make(3)
->schema([
TextInput::make('code')
->label('Mã trường')
->required()
->placeholder('ten_khach_hang'),
TextInput::make('label')
->label('Tên hiển thị')
->required()
->placeholder('Tên khách hàng'),
Select::make('source_type')
->label('Nguồn dữ liệu')
->options([
'db_column' => 'Cột trong DB',
'db_relation' => 'Quan hệ (relation)',
'formula' => 'Công thức tính toán',
'input' => 'Nhập tay khi in',
'static' => 'Giá trị cố định',
])
->required()
->live(),
]),
Grid::make(3)
->schema([
KeyValue::make('source_config')
->label('Cấu hình nguồn')
->keyLabel('Tham số')
->valueLabel('Giá trị')
->helperText(fn ($get) => match ($get('source_type')) {
'db_column' => 'Ví dụ: column => land_value',
'db_relation' => 'Ví dụ: relation => customers, column => full_name, index => 0',
'formula' => 'Ví dụ: expression => land_value + foundation_value',
'input' => 'Ví dụ: default => (giá trị mặc định)',
'static' => 'Ví dụ: value => Hà Nội',
default => 'Nhập cấu hình phù hợp với loại nguồn dữ liệu',
}),
Select::make('format')
->label('Định dạng hiển thị')
->options([
'text' => 'Văn bản',
'number' => 'Số',
'currency' => 'Tiền tệ (VNĐ)',
'date' => 'Ngày tháng',
'percent' => 'Phần trăm',
])
->default('text'),
TextInput::make('decimal_places')
->label('Số thập phân')
->numeric()
->default(0)
->visible(fn ($get) => in_array($get('format'), ['number', 'currency', 'percent'])),
]),
TextInput::make('display_order')
->label('Thứ tự')
->numeric()
->default(0)
->hidden(),
])
->addActionLabel('Thêm trường dữ liệu')
->reorderable()
->orderColumn('display_order')
->defaultItems(0)
->collapsible()
->columnSpanFull(),
]),
// BLOCK 3: Nội dung mẫu in - FULL WIDTH, TO RỘNG
Section::make('Nội dung mẫu in')
->schema([
RichEditor::make('html_template')
->label('')
->required()
->placeholder('Soạn thảo nội dung biểu mẫu...')
->helperText('Chèn trường dữ liệu bằng cú pháp {{ma_truong}}. Ví dụ: Tên khách hàng: {{ten_khach_hang}}')
->columnSpanFull(),
]),
]);
}
}

View File

@@ -0,0 +1,56 @@
<?php
namespace App\Filament\Resources\FormTemplates\Tables;
use Filament\Tables;
use Filament\Tables\Table;
class FormTemplatesTable
{
public static function configure(Table $table): Table
{
return $table
->columns([
Tables\Columns\TextColumn::make('name')
->label('Tên biểu mẫu')
->searchable()
->sortable(),
Tables\Columns\TextColumn::make('code')
->label('Mã')
->searchable()
->copyable(),
Tables\Columns\TextColumn::make('target_model')
->label('Áp dụng cho')
->formatStateUsing(fn ($state) => match ($state) {
'App\Models\Contract' => 'Hợp đồng',
'App\Models\Product' => 'Sản phẩm',
'App\Models\Customer' => 'Khách hàng',
default => $state,
}),
Tables\Columns\TextColumn::make('paper_size')
->label('Khổ giấy')
->badge(),
Tables\Columns\IconColumn::make('is_active')
->label('Hoạt động')
->boolean(),
Tables\Columns\TextColumn::make('fields_count')
->label('Số trường')
->counts('fields'),
])
->filters([
Tables\Filters\SelectFilter::make('target_model')
->label('Áp dụng cho')
->options([
'App\Models\Contract' => 'Hợp đồng',
'App\Models\Product' => 'Sản phẩm',
'App\Models\Customer' => 'Khách hàng',
]),
])
->defaultSort('name');
}
}