Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

BudgetResource.php 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. <?php
  2. namespace App\Filament\Company\Resources\Accounting;
  3. use App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
  4. use App\Filament\Forms\Components\CustomSection;
  5. use App\Models\Accounting\Account;
  6. use App\Models\Accounting\Budget;
  7. use App\Models\Accounting\BudgetItem;
  8. use Filament\Forms;
  9. use Filament\Forms\Form;
  10. use Filament\Resources\Resource;
  11. use Filament\Tables;
  12. use Filament\Tables\Table;
  13. use Illuminate\Support\Carbon;
  14. class BudgetResource extends Resource
  15. {
  16. protected static ?string $model = Budget::class;
  17. protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
  18. public static function form(Form $form): Form
  19. {
  20. return $form
  21. ->schema([
  22. Forms\Components\Section::make('Budget Details')
  23. ->columns()
  24. ->schema([
  25. Forms\Components\TextInput::make('name')
  26. ->required()
  27. ->maxLength(255),
  28. Forms\Components\Select::make('interval_type')
  29. ->label('Budget Interval')
  30. ->options([
  31. 'day' => 'Daily',
  32. 'week' => 'Weekly',
  33. 'month' => 'Monthly',
  34. 'quarter' => 'Quarterly',
  35. 'year' => 'Yearly',
  36. ])
  37. ->default('month')
  38. ->required()
  39. ->live(),
  40. Forms\Components\DatePicker::make('start_date')
  41. ->required()
  42. ->default(now()->startOfYear())
  43. ->live(),
  44. Forms\Components\DatePicker::make('end_date')
  45. ->required()
  46. ->default(now()->endOfYear())
  47. ->live(),
  48. Forms\Components\Textarea::make('notes')->columnSpanFull(),
  49. ]),
  50. Forms\Components\Section::make('Budget Items')
  51. ->schema([
  52. Forms\Components\Repeater::make('budgetItems')
  53. ->relationship()
  54. ->columns(4)
  55. ->hiddenLabel()
  56. ->schema([
  57. Forms\Components\Select::make('account_id')
  58. ->label('Account')
  59. ->options(Account::query()->pluck('name', 'id'))
  60. ->searchable()
  61. ->columnSpan(1)
  62. ->required(),
  63. CustomSection::make('Budget Allocations')
  64. ->contained(false)
  65. ->columns(4)
  66. ->schema(static fn (Forms\Get $get) => self::getAllocationFields($get('../../start_date'), $get('../../end_date'), $get('../../interval_type'))),
  67. ])
  68. ->defaultItems(1)
  69. ->addActionLabel('Add Budget Item'),
  70. ]),
  71. ]);
  72. }
  73. public static function table(Table $table): Table
  74. {
  75. return $table
  76. ->columns([
  77. //
  78. ])
  79. ->filters([
  80. //
  81. ])
  82. ->actions([
  83. Tables\Actions\ViewAction::make(),
  84. Tables\Actions\EditAction::make(),
  85. ])
  86. ->bulkActions([
  87. Tables\Actions\BulkActionGroup::make([
  88. Tables\Actions\DeleteBulkAction::make(),
  89. ]),
  90. ]);
  91. }
  92. private static function getAllocationFields(?string $startDate, ?string $endDate, ?string $intervalType): array
  93. {
  94. if (! $startDate || ! $endDate || ! $intervalType) {
  95. return [];
  96. }
  97. $start = Carbon::parse($startDate);
  98. $end = Carbon::parse($endDate);
  99. $fields = [];
  100. while ($start->lte($end)) {
  101. $label = match ($intervalType) {
  102. 'month' => $start->format('M'), // Example: Jan, Feb, Mar
  103. 'quarter' => 'Q' . $start->quarter, // Example: Q1, Q2, Q3
  104. 'year' => (string) $start->year, // Example: 2024, 2025
  105. default => '',
  106. };
  107. $fields[] = Forms\Components\TextInput::make("amounts.{$label}")
  108. ->label($label)
  109. ->numeric()
  110. ->required();
  111. // Move to the next period
  112. match ($intervalType) {
  113. 'month' => $start->addMonth(),
  114. 'quarter' => $start->addQuarter(),
  115. 'year' => $start->addYear(),
  116. default => null,
  117. };
  118. }
  119. return $fields;
  120. }
  121. /**
  122. * Generates an array of interval labels (e.g., Jan 2024, Q1 2024, etc.).
  123. */
  124. private static function generateIntervals(string $startDate, string $endDate, string $intervalType): array
  125. {
  126. $start = Carbon::parse($startDate);
  127. $end = Carbon::parse($endDate);
  128. $intervals = [];
  129. while ($start->lte($end)) {
  130. if ($intervalType === 'month') {
  131. $intervals[] = $start->format('M Y'); // Example: Jan 2024
  132. $start->addMonth();
  133. } elseif ($intervalType === 'quarter') {
  134. $intervals[] = 'Q' . $start->quarter . ' ' . $start->year; // Example: Q1 2024
  135. $start->addQuarter();
  136. } elseif ($intervalType === 'year') {
  137. $intervals[] = $start->year; // Example: 2024
  138. $start->addYear();
  139. }
  140. }
  141. return $intervals;
  142. }
  143. /**
  144. * Saves budget allocations correctly in `budget_allocations` table.
  145. */
  146. public static function saveBudgetAllocations(BudgetItem $record, array $data): void
  147. {
  148. $record->update($data);
  149. $intervals = self::generateIntervals($data['start_date'], $data['end_date'], $data['interval_type']);
  150. foreach ($intervals as $interval) {
  151. $record->allocations()->updateOrCreate(
  152. ['period' => $interval],
  153. [
  154. 'interval_type' => $data['interval_type'],
  155. 'start_date' => Carbon::parse($interval)->startOfMonth(),
  156. 'end_date' => Carbon::parse($interval)->endOfMonth(),
  157. 'amount' => $data['allocations'][$interval] ?? 0,
  158. ]
  159. );
  160. }
  161. }
  162. public static function getRelations(): array
  163. {
  164. return [
  165. //
  166. ];
  167. }
  168. public static function getPages(): array
  169. {
  170. return [
  171. 'index' => Pages\ListBudgets::route('/'),
  172. 'create' => Pages\CreateBudget::route('/create'),
  173. 'view' => Pages\ViewBudget::route('/{record}'),
  174. 'edit' => Pages\EditBudget::route('/{record}/edit'),
  175. ];
  176. }
  177. }