Andrew Wallo пре 6 месеци
родитељ
комит
a4b17994b8

+ 4
- 3
app/Filament/Company/Resources/Accounting/BudgetResource.php Прегледај датотеку

@@ -27,6 +27,10 @@ class BudgetResource extends Resource
27 27
 
28 28
     protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
29 29
 
30
+    protected static ?string $recordTitleAttribute = 'name';
31
+
32
+    protected static bool $isGloballySearchable = false;
33
+
30 34
     public static function form(Form $form): Form
31 35
     {
32 36
         return $form
@@ -238,7 +242,6 @@ class BudgetResource extends Resource
238 242
             ->actions([
239 243
                 Tables\Actions\ActionGroup::make([
240 244
                     Tables\Actions\ViewAction::make(),
241
-                    Tables\Actions\EditAction::make(),
242 245
                     Tables\Actions\EditAction::make('editAllocations')
243 246
                         ->name('editAllocations')
244 247
                         ->url(null)
@@ -534,8 +537,6 @@ class BudgetResource extends Resource
534 537
             'index' => Pages\ListBudgets::route('/'),
535 538
             'create' => Pages\CreateBudget::route('/create'),
536 539
             'view' => Pages\ViewBudget::route('/{record}'),
537
-            'edit' => Pages\EditBudget::route('/{record}/edit'),
538
-            'allocations' => Pages\ManageAllocations::route('/{record}/allocations'),
539 540
         ];
540 541
     }
541 542
 }

+ 0
- 182
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/EditBudget.php Прегледај датотеку

@@ -1,182 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4
-
5
-use App\Enums\Accounting\BudgetIntervalType;
6
-use App\Filament\Company\Resources\Accounting\BudgetResource;
7
-use App\Filament\Forms\Components\CustomTableRepeater;
8
-use App\Models\Accounting\Budget;
9
-use App\Models\Accounting\BudgetAllocation;
10
-use App\Models\Accounting\BudgetItem;
11
-use Awcodes\TableRepeater\Header;
12
-use Filament\Actions;
13
-use Filament\Forms;
14
-use Filament\Forms\Form;
15
-use Filament\Resources\Pages\EditRecord;
16
-use Filament\Support\Enums\Alignment;
17
-use Filament\Support\Enums\MaxWidth;
18
-use Filament\Support\RawJs;
19
-use Illuminate\Database\Eloquent\Model;
20
-use Illuminate\Support\Carbon;
21
-
22
-class EditBudget extends EditRecord
23
-{
24
-    protected static string $resource = BudgetResource::class;
25
-
26
-    protected function getHeaderActions(): array
27
-    {
28
-        return [
29
-            Actions\ViewAction::make(),
30
-            Actions\DeleteAction::make(),
31
-        ];
32
-    }
33
-
34
-    public function getMaxContentWidth(): MaxWidth | string | null
35
-    {
36
-        return 'max-w-8xl';
37
-    }
38
-
39
-    public function form(Form $form): Form
40
-    {
41
-        /** @var Budget $budget */
42
-        $budget = $this->record;
43
-        $periods = $budget->getPeriods();
44
-
45
-        $headers = [
46
-            Header::make('Account')
47
-                ->label('Account')
48
-                ->width('200px'),
49
-        ];
50
-
51
-        foreach ($periods as $period) {
52
-            $headers[] = Header::make($period)
53
-                ->label($period)
54
-                ->width('120px')
55
-                ->align(Alignment::Center);
56
-        }
57
-
58
-        return $form->schema([
59
-            Forms\Components\Section::make('Budget Details')
60
-                ->schema([
61
-                    Forms\Components\TextInput::make('name')
62
-                        ->required(),
63
-                    Forms\Components\Select::make('interval_type')
64
-                        ->disabled(), // Can't change interval type after creation
65
-                    Forms\Components\DatePicker::make('start_date')
66
-                        ->disabled(),
67
-                    Forms\Components\DatePicker::make('end_date')
68
-                        ->disabled(),
69
-                    Forms\Components\Textarea::make('notes'),
70
-                ]),
71
-
72
-            Forms\Components\Section::make('Budget Allocations')
73
-                ->schema([
74
-                    CustomTableRepeater::make('budgetItems')
75
-                        ->relationship()
76
-                        ->headers($headers)
77
-                        ->schema([
78
-                            Forms\Components\Placeholder::make('account')
79
-                                ->hiddenLabel()
80
-                                ->content(fn ($record) => $record->account->name ?? ''),
81
-
82
-                            // Create a field for each period
83
-                            ...collect($periods)->map(function ($period) {
84
-                                return Forms\Components\TextInput::make("allocations.{$period}")
85
-                                    ->mask(RawJs::make('$money($input)'))
86
-                                    ->stripCharacters(',')
87
-                                    ->numeric()
88
-                                    ->afterStateHydrated(function ($component, $state, $record) use ($period) {
89
-                                        // Find the allocation for this period
90
-                                        $allocation = $record->allocations->firstWhere('period', $period);
91
-                                        $component->state($allocation ? $allocation->amount : 0);
92
-                                    })
93
-                                    ->dehydrated(false); // We'll handle saving manually
94
-                            })->toArray(),
95
-                        ])
96
-                        ->spreadsheet()
97
-                        ->itemLabel(fn ($record) => $record->account->name ?? 'Budget Item')
98
-                        ->deletable(false)
99
-                        ->reorderable(false)
100
-                        ->addable(false) // Don't allow adding new budget items
101
-                        ->columnSpanFull(),
102
-                ]),
103
-        ]);
104
-    }
105
-
106
-    //    protected function mutateFormDataBeforeFill(array $data): array
107
-    //    {
108
-    //        /** @var Budget $budget */
109
-    //        $budget = $this->record;
110
-    //
111
-    //        $data['budgetItems'] = $budget->budgetItems->map(function (BudgetItem $budgetItem) {
112
-    //            return [
113
-    //                'id' => $budgetItem->id,
114
-    //                'account_id' => $budgetItem->account_id,
115
-    //                'total_amount' => $budgetItem->allocations->sum('amount'), // Calculate total dynamically
116
-    //                'amounts' => $budgetItem->allocations->mapWithKeys(static function (BudgetAllocation $allocation) {
117
-    //                    return [$allocation->period => $allocation->amount]; // Use the correct period label
118
-    //                })->toArray(),
119
-    //            ];
120
-    //        })->toArray();
121
-    //
122
-    //        return $data;
123
-    //    }
124
-
125
-    protected function handleRecordUpdate(Model $record, array $data): Model
126
-    {
127
-        /** @var Budget $budget */
128
-        $budget = $record;
129
-
130
-        $budget->update([
131
-            'name' => $data['name'],
132
-            'interval_type' => $data['interval_type'],
133
-            'start_date' => $data['start_date'],
134
-            'end_date' => $data['end_date'],
135
-            'notes' => $data['notes'] ?? null,
136
-        ]);
137
-
138
-        $budgetItemIds = [];
139
-
140
-        foreach ($data['budgetItems'] as $itemData) {
141
-            /** @var BudgetItem $budgetItem */
142
-            $budgetItem = $budget->budgetItems()->updateOrCreate(
143
-                ['id' => $itemData['id'] ?? null],
144
-                ['account_id' => $itemData['account_id']]
145
-            );
146
-
147
-            $budgetItemIds[] = $budgetItem->id;
148
-
149
-            $budgetItem->allocations()->delete();
150
-
151
-            $allocationStart = Carbon::parse($data['start_date']);
152
-
153
-            foreach ($itemData['amounts'] as $periodLabel => $amount) {
154
-                $allocationEnd = self::calculateEndDate($allocationStart, BudgetIntervalType::parse($data['interval_type']));
155
-
156
-                // Recreate allocations
157
-                $budgetItem->allocations()->create([
158
-                    'period' => $periodLabel,
159
-                    'interval_type' => $data['interval_type'],
160
-                    'start_date' => $allocationStart->toDateString(),
161
-                    'end_date' => $allocationEnd->toDateString(),
162
-                    'amount' => $amount,
163
-                ]);
164
-
165
-                $allocationStart = $allocationEnd->addDay();
166
-            }
167
-        }
168
-
169
-        $budget->budgetItems()->whereNotIn('id', $budgetItemIds)->delete();
170
-
171
-        return $budget;
172
-    }
173
-
174
-    private static function calculateEndDate(Carbon $startDate, BudgetIntervalType $intervalType): Carbon
175
-    {
176
-        return match ($intervalType) {
177
-            BudgetIntervalType::Month => $startDate->copy()->endOfMonth(),
178
-            BudgetIntervalType::Quarter => $startDate->copy()->endOfQuarter(),
179
-            BudgetIntervalType::Year => $startDate->copy()->endOfYear(),
180
-        };
181
-    }
182
-}

+ 0
- 73
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/ManageAllocations.php Прегледај датотеку

@@ -1,73 +0,0 @@
1
-<?php
2
-
3
-namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4
-
5
-use App\Filament\Company\Resources\Accounting\BudgetResource;
6
-use Filament\Forms;
7
-use Filament\Forms\Form;
8
-use Filament\Resources\Pages\ManageRelatedRecords;
9
-use Filament\Tables;
10
-use Filament\Tables\Table;
11
-
12
-class ManageAllocations extends ManageRelatedRecords
13
-{
14
-    protected static string $resource = BudgetResource::class;
15
-
16
-    protected static string $relationship = 'allocations';
17
-
18
-    protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
19
-
20
-    public static function getNavigationLabel(): string
21
-    {
22
-        return 'Allocations';
23
-    }
24
-
25
-    public function form(Form $form): Form
26
-    {
27
-        return $form
28
-            ->schema([
29
-                Forms\Components\TextInput::make('account_id')
30
-                    ->required()
31
-                    ->maxLength(255),
32
-            ]);
33
-    }
34
-
35
-    public function table(Table $table): Table
36
-    {
37
-        return $table
38
-            ->recordTitleAttribute('account_id')
39
-            ->columns([
40
-                Tables\Columns\TextColumn::make('budgetItem.account.name')
41
-                    ->label('Account')
42
-                    ->sortable()
43
-                    ->searchable(),
44
-
45
-                Tables\Columns\TextColumn::make('period')
46
-                    ->label('Period')
47
-                    ->sortable(),
48
-
49
-                Tables\Columns\TextColumn::make('amount')
50
-                    ->label('Amount')
51
-                    ->money()
52
-                    ->sortable(),
53
-            ])
54
-            ->filters([
55
-                //
56
-            ])
57
-            ->headerActions([
58
-                Tables\Actions\CreateAction::make(),
59
-                Tables\Actions\AssociateAction::make(),
60
-            ])
61
-            ->actions([
62
-                Tables\Actions\EditAction::make(),
63
-                Tables\Actions\DissociateAction::make(),
64
-                Tables\Actions\DeleteAction::make(),
65
-            ])
66
-            ->bulkActions([
67
-                Tables\Actions\BulkActionGroup::make([
68
-                    Tables\Actions\DissociateBulkAction::make(),
69
-                    Tables\Actions\DeleteBulkAction::make(),
70
-                ]),
71
-            ]);
72
-    }
73
-}

+ 1
- 2
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/ViewBudget.php Прегледај датотеку

@@ -3,7 +3,6 @@
3 3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4 4
 
5 5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6
-use Filament\Actions;
7 6
 use Filament\Forms\Form;
8 7
 use Filament\Infolists\Infolist;
9 8
 use Filament\Resources\Pages\ViewRecord;
@@ -21,7 +20,7 @@ class ViewBudget extends ViewRecord
21 20
     protected function getHeaderActions(): array
22 21
     {
23 22
         return [
24
-            Actions\EditAction::make(),
23
+            //
25 24
         ];
26 25
     }
27 26
 

+ 74
- 98
app/Filament/Company/Resources/Accounting/BudgetResource/RelationManagers/BudgetItemsRelationManager.php Прегледај датотеку

@@ -3,6 +3,7 @@
3 3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\RelationManagers;
4 4
 
5 5
 use App\Filament\Tables\Columns\DeferredTextInputColumn;
6
+use App\Models\Accounting\Budget;
6 7
 use App\Models\Accounting\BudgetAllocation;
7 8
 use App\Models\Accounting\BudgetItem;
8 9
 use App\Utilities\Currency\CurrencyConverter;
@@ -18,7 +19,6 @@ use Filament\Tables\Grouping\Group;
18 19
 use Filament\Tables\Table;
19 20
 use Illuminate\Database\Eloquent\Builder;
20 21
 use Illuminate\Database\Eloquent\Collection;
21
-use Livewire\Attributes\On;
22 22
 
23 23
 class BudgetItemsRelationManager extends RelationManager
24 24
 {
@@ -26,39 +26,31 @@ class BudgetItemsRelationManager extends RelationManager
26 26
 
27 27
     protected static bool $isLazy = false;
28 28
 
29
-    // Store changes that are pending
30 29
     public array $batchChanges = [];
31 30
 
32
-    #[On('batch-column-changed')]
33 31
     public function handleBatchColumnChanged($data): void
34 32
     {
35
-        // Store the changed value
36 33
         $key = "{$data['recordKey']}.{$data['name']}";
37 34
         $this->batchChanges[$key] = $data['value'];
38 35
     }
39 36
 
40
-    #[On('save-batch-changes')]
41 37
     public function saveBatchChanges(): void
42 38
     {
43 39
         foreach ($this->batchChanges as $key => $value) {
44
-            // Parse the composite key
45 40
             [$recordKey, $column] = explode('.', $key, 2);
46 41
 
47
-            // Extract period from the column name (e.g., "allocations_by_period.2023-Q1")
48
-            preg_match('/allocations_by_period\.(.+)/', $column, $matches);
49
-            $period = $matches[1] ?? null;
42
+            preg_match('/amount_(.+)/', $column, $matches);
43
+            $period = str_replace('_', ' ', $matches[1] ?? '');
50 44
 
51 45
             if (! $period) {
52 46
                 continue;
53 47
             }
54 48
 
55
-            // Find the record
56 49
             $record = BudgetItem::find($recordKey);
57 50
             if (! $record) {
58 51
                 continue;
59 52
             }
60 53
 
61
-            // Update the allocation
62 54
             $allocation = $record->allocations->firstWhere('period', $period);
63 55
             if ($allocation) {
64 56
                 $allocation->update(['amount' => $value]);
@@ -66,15 +58,12 @@ class BudgetItemsRelationManager extends RelationManager
66 58
                 $record->allocations()->create([
67 59
                     'period' => $period,
68 60
                     'amount' => $value,
69
-                    // Add other required fields
70 61
                 ]);
71 62
             }
72 63
         }
73 64
 
74
-        // Clear the batch changes
75 65
         $this->batchChanges = [];
76 66
 
77
-        // Notify the user
78 67
         Notification::make()
79 68
             ->title('Budget allocations updated')
80 69
             ->success()
@@ -83,7 +72,6 @@ class BudgetItemsRelationManager extends RelationManager
83 72
 
84 73
     protected function calculateTotalSum(array $budgetItemIds): int
85 74
     {
86
-        // Get all applicable periods
87 75
         $periods = BudgetAllocation::whereIn('budget_item_id', $budgetItemIds)
88 76
             ->pluck('period')
89 77
             ->unique()
@@ -91,8 +79,6 @@ class BudgetItemsRelationManager extends RelationManager
91 79
             ->toArray();
92 80
 
93 81
         $total = 0;
94
-
95
-        // Sum up each period
96 82
         foreach ($periods as $period) {
97 83
             $total += $this->calculatePeriodSum($budgetItemIds, $period);
98 84
         }
@@ -102,26 +88,20 @@ class BudgetItemsRelationManager extends RelationManager
102 88
 
103 89
     protected function calculatePeriodSum(array $budgetItemIds, string $period): int
104 90
     {
105
-        // First get database values
106 91
         $dbTotal = BudgetAllocation::whereIn('budget_item_id', $budgetItemIds)
107 92
             ->where('period', $period)
108 93
             ->sum('amount');
109 94
 
110
-        // Now add any batch changes
111 95
         $batchTotal = 0;
112 96
         foreach ($budgetItemIds as $itemId) {
113
-            $key = "{$itemId}.allocations_by_period.{$period}";
97
+            $key = "{$itemId}.amount_" . str_replace(['-', '.', ' '], '_', $period);
114 98
             if (isset($this->batchChanges[$key])) {
115
-                // Get the current value from batch changes
116 99
                 $batchValue = CurrencyConverter::convertToCents($this->batchChanges[$key]);
117
-
118
-                // Find if there's a current allocation in DB
119 100
                 $existingAmount = BudgetAllocation::where('budget_item_id', $itemId)
120 101
                     ->where('period', $period)
121 102
                     ->first()
122
-                    ->getRawOriginal('amount');
103
+                    ?->getRawOriginal('amount') ?? 0;
123 104
 
124
-                // Add the difference to our running total
125 105
                 $batchTotal += ($batchValue - $existingAmount);
126 106
             }
127 107
         }
@@ -131,59 +111,44 @@ class BudgetItemsRelationManager extends RelationManager
131 111
 
132 112
     public function table(Table $table): Table
133 113
     {
114
+        /** @var Budget $budget */
134 115
         $budget = $this->getOwnerRecord();
135
-
136
-        // Get distinct periods for this budget
137
-        $periods = BudgetAllocation::query()
138
-            ->join('budget_items', 'budget_allocations.budget_item_id', '=', 'budget_items.id')
139
-            ->where('budget_items.budget_id', $budget->id)
140
-            ->orderBy('start_date')
141
-            ->pluck('period')
142
-            ->unique()
143
-            ->values()
144
-            ->toArray();
116
+        $periods = $budget->getPeriods();
145 117
 
146 118
         return $table
147 119
             ->recordTitleAttribute('account_id')
148 120
             ->paginated(false)
149
-            ->modifyQueryUsing(
150
-                fn (Builder $query) => $query->with(['account', 'allocations'])
151
-            )
152
-            ->headerActions([
153
-                Action::make('saveBatchChanges')
154
-                    ->label('Save All Changes')
155
-                    ->action('saveBatchChanges')
156
-                    ->color('primary')
157
-                    ->icon('heroicon-o-check-circle'),
158
-            ])
121
+            ->heading(null)
122
+            ->modifyQueryUsing(function (Builder $query) use ($periods) {
123
+                $query->select('budget_items.*')
124
+                    ->leftJoin('budget_allocations', 'budget_allocations.budget_item_id', '=', 'budget_items.id');
125
+
126
+                foreach ($periods as $period) {
127
+                    $alias = 'amount_' . str_replace(['-', '.', ' '], '_', $period);
128
+                    $query->selectRaw(
129
+                        "SUM(CASE WHEN budget_allocations.period = ? THEN budget_allocations.amount ELSE 0 END) as {$alias}",
130
+                        [$period]
131
+                    );
132
+                }
133
+
134
+                return $query->groupBy('budget_items.id');
135
+            })
159 136
             ->groups([
160 137
                 Group::make('account.category')
161 138
                     ->titlePrefixedWithLabel(false)
162 139
                     ->collapsible(),
163 140
             ])
141
+            ->recordClasses(['budget-items-relation-manager'])
164 142
             ->defaultGroup('account.category')
165
-            ->bulkActions([
166
-                BulkAction::make('clearAllocations')
167
-                    ->label('Clear Allocations')
168
-                    ->icon('heroicon-o-trash')
169
-                    ->color('danger')
170
-                    ->requiresConfirmation()
171
-                    ->deselectRecordsAfterCompletion()
172
-                    ->action(function (Collection $records) use ($periods) {
173
-                        foreach ($records as $record) {
174
-                            foreach ($periods as $period) {
175
-                                $periodKey = "{$record->getKey()}.allocations_by_period.{$period}";
176
-                                $this->batchChanges[$periodKey] = CurrencyConverter::convertCentsToFormatSimple(0);
177
-                            }
178
-
179
-                            $totalKey = "{$record->getKey()}.total";
180
-                            $this->batchChanges[$totalKey] = CurrencyConverter::convertCentsToFormatSimple(0);
181
-                        }
182
-                    }),
143
+            ->headerActions([
144
+                Action::make('saveBatchChanges')
145
+                    ->label('Save all changes')
146
+                    ->action('saveBatchChanges')
147
+                    ->color('primary'),
183 148
             ])
184
-            ->columns(array_merge([
149
+            ->columns([
185 150
                 TextColumn::make('account.name')
186
-                    ->label('Accounts')
151
+                    ->label('Account')
187 152
                     ->limit(30)
188 153
                     ->searchable(),
189 154
                 DeferredTextInputColumn::make('total')
@@ -195,9 +160,9 @@ class BudgetItemsRelationManager extends RelationManager
195 160
                             return $this->batchChanges["{$record->getKey()}.{$column->getName()}"];
196 161
                         }
197 162
 
198
-                        $total = $record->allocations->sum(function (BudgetAllocation $budgetAllocation) {
199
-                            return $budgetAllocation->getRawOriginal('amount');
200
-                        });
163
+                        $total = $record->allocations->sum(
164
+                            fn (BudgetAllocation $allocation) => $allocation->getRawOriginal('amount')
165
+                        );
201 166
 
202 167
                         return CurrencyConverter::convertCentsToFormatSimple($total);
203 168
                     })
@@ -219,9 +184,6 @@ class BudgetItemsRelationManager extends RelationManager
219 184
                     ->action(
220 185
                         Action::make('disperse')
221 186
                             ->label('Disperse')
222
-                            ->icon('heroicon-m-chevron-double-right')
223
-                            ->color('primary')
224
-                            ->iconButton()
225 187
                             ->action(function (BudgetItem $record) use ($periods) {
226 188
                                 if (empty($periods)) {
227 189
                                     return;
@@ -246,7 +208,7 @@ class BudgetItemsRelationManager extends RelationManager
246 208
 
247 209
                                 if ($totalCents <= 0) {
248 210
                                     foreach ($periods as $period) {
249
-                                        $periodKey = "{$record->getKey()}.allocations_by_period.{$period}";
211
+                                        $periodKey = "{$record->getKey()}.amount_" . str_replace(['-', '.', ' '], '_', $period);
250 212
                                         $this->batchChanges[$periodKey] = CurrencyConverter::convertCentsToFormatSimple(0);
251 213
                                     }
252 214
 
@@ -260,36 +222,50 @@ class BudgetItemsRelationManager extends RelationManager
260 222
                                     $amount = $baseAmount + ($index === 0 ? $remainder : 0);
261 223
                                     $formattedAmount = CurrencyConverter::convertCentsToFormatSimple($amount);
262 224
 
263
-                                    $periodKey = "{$record->getKey()}.allocations_by_period.{$period}";
225
+                                    $periodKey = "{$record->getKey()}." . 'amount_' . str_replace(['-', '.', ' '], '_', $period);
264 226
                                     $this->batchChanges[$periodKey] = $formattedAmount;
265 227
                                 }
266 228
                             }),
267 229
                     ),
268
-            ], collect($periods)->map(
269
-                fn ($period) => DeferredTextInputColumn::make("allocations_by_period.{$period}")
270
-                    ->label($period)
271
-                    ->alignRight()
272
-                    ->mask(RawJs::make('$money($input)'))
273
-                    ->summarize(
274
-                        Summarizer::make()
275
-                            ->using(function (\Illuminate\Database\Query\Builder $query) use ($period) {
276
-                                $budgetItemIds = $query->pluck('id')->toArray();
277
-                                $total = $this->calculatePeriodSum($budgetItemIds, $period);
278
-
279
-                                return CurrencyConverter::convertCentsToFormatSimple($total);
280
-                            })
281
-                    )
282
-                    ->getStateUsing(function ($record, DeferredTextInputColumn $column) use ($period) {
283
-                        $key = "{$record->getKey()}.{$column->getName()}";
284
-
285
-                        // Check if batch change exists
286
-                        if (isset($this->batchChanges[$key])) {
287
-                            return $this->batchChanges[$key];
230
+                ...array_map(function (string $period) {
231
+                    $alias = 'amount_' . str_replace(['-', '.', ' '], '_', $period); // Also replace space
232
+
233
+                    return DeferredTextInputColumn::make($alias)
234
+                        ->label($period)
235
+                        ->alignRight()
236
+                        ->batchMode()
237
+                        ->mask(RawJs::make('$money($input)'))
238
+                        ->getStateUsing(function ($record) use ($alias) {
239
+                            $key = "{$record->getKey()}.{$alias}";
240
+
241
+                            return $this->batchChanges[$key] ?? CurrencyConverter::convertCentsToFormatSimple($record->{$alias} ?? 0);
242
+                        })
243
+                        ->summarize(
244
+                            Summarizer::make()
245
+                                ->using(function (\Illuminate\Database\Query\Builder $query) use ($period) {
246
+                                    $budgetItemIds = $query->pluck('id')->toArray();
247
+                                    $total = $this->calculatePeriodSum($budgetItemIds, $period);
248
+
249
+                                    return CurrencyConverter::convertCentsToFormatSimple($total);
250
+                                })
251
+                        );
252
+                }, $periods),
253
+            ])
254
+            ->bulkActions([
255
+                BulkAction::make('clearAllocations')
256
+                    ->label('Clear Allocations')
257
+                    ->icon('heroicon-o-trash')
258
+                    ->color('danger')
259
+                    ->requiresConfirmation()
260
+                    ->deselectRecordsAfterCompletion()
261
+                    ->action(function (Collection $records) use ($periods) {
262
+                        foreach ($records as $record) {
263
+                            foreach ($periods as $period) {
264
+                                $periodKey = "{$record->getKey()}.amount_" . str_replace(['-', '.', ' '], '_', $period);
265
+                                $this->batchChanges[$periodKey] = CurrencyConverter::convertCentsToFormatSimple(0);
266
+                            }
288 267
                         }
289
-
290
-                        return $record->allocations->firstWhere('period', $period)?->amount;
291
-                    })
292
-                    ->batchMode(),
293
-            )->all()));
268
+                    }),
269
+            ]);
294 270
     }
295 271
 }

+ 4
- 10
app/Models/Accounting/Budget.php Прегледај датотеку

@@ -81,19 +81,13 @@ class Budget extends Model
81 81
         return $this->hasManyThrough(BudgetAllocation::class, BudgetItem::class);
82 82
     }
83 83
 
84
-    /**
85
-     * Get all periods for this budget in chronological order.
86
-     */
87 84
     public function getPeriods(): array
88 85
     {
89
-        return $this->budgetItems()
90
-            ->with('allocations')
91
-            ->get()
92
-            ->flatMap(fn ($item) => $item->allocations)
93
-            ->sortBy('start_date')
86
+        return $this->allocations()
87
+            ->select('period')
88
+            ->distinct()
89
+            ->orderBy('period')
94 90
             ->pluck('period')
95
-            ->unique()
96
-            ->values()
97 91
             ->toArray();
98 92
     }
99 93
 

+ 0
- 7
app/Models/Accounting/BudgetItem.php Прегледај датотеку

@@ -23,13 +23,6 @@ class BudgetItem extends Model
23 23
         'updated_by',
24 24
     ];
25 25
 
26
-    protected $appends = ['allocations_by_period'];
27
-
28
-    public function getAllocationsByPeriodAttribute(): array
29
-    {
30
-        return $this->allocations->pluck('amount', 'period')->toArray();
31
-    }
32
-
33 26
     public function budget(): BelongsTo
34 27
     {
35 28
         return $this->belongsTo(Budget::class);

+ 49
- 16
resources/css/filament/company/form-fields.css Прегледај датотеку

@@ -69,18 +69,63 @@
69 69
     width: max-content;
70 70
 }
71 71
 
72
+.fi-ta-table:has(.budget-items-relation-manager) {
73
+    .fi-ta-row {
74
+        @apply divide-x divide-gray-200 dark:divide-gray-700;
75
+    }
76
+
77
+    .fi-ta-summary-row-heading {
78
+        @apply px-3 py-3 !important;
79
+    }
80
+
81
+    .fi-ta-text-summary {
82
+        @apply px-3 py-3 !important;
83
+
84
+        > span {
85
+            @apply font-medium text-gray-950 dark:text-white;
86
+        }
87
+    }
88
+
89
+    .fi-ta-text {
90
+        @apply px-3 py-0 !important;
91
+    }
92
+
93
+    .fi-ta-text-input {
94
+        @apply px-0 py-0 !important;
95
+    }
96
+
97
+    .fi-ta-icon {
98
+        @apply px-3 py-0 !important;
99
+    }
100
+
101
+    .fi-ta-text-input {
102
+        .fi-input-wrp,
103
+        .fi-input {
104
+            @apply ring-0 bg-transparent shadow-none rounded-none !important;
105
+        }
106
+    }
107
+
108
+    .fi-ta-cell:focus-within {
109
+        outline: 2px solid #2563eb;
110
+        outline-offset: -2px;
111
+        z-index: 1;
112
+    }
113
+
114
+    .fi-ta-col-wrp button:focus {
115
+        outline: none !important;
116
+        box-shadow: none !important;
117
+    }
118
+}
119
+
120
+
72 121
 /* Excel/Spreadsheet styling */
73 122
 .is-spreadsheet {
74
-    /* Container styles */
75
-
76 123
     .table-repeater-container {
77 124
         border: 1px solid #e5e7eb !important;
78 125
         border-radius: 0 !important;
79 126
         @apply ring-0 !important;
80 127
     }
81 128
 
82
-    /* Header styles */
83
-
84 129
     .table-repeater-header {
85 130
         background-color: #f8f9fa !important;
86 131
     }
@@ -92,21 +137,15 @@
92 137
         padding: 8px 12px !important;
93 138
     }
94 139
 
95
-    /* Cell styles */
96
-
97 140
     .table-repeater-column {
98 141
         border: 1px solid #e5e7eb !important;
99 142
         padding: 8px 12px !important;
100 143
     }
101 144
 
102
-    /* Input styles */
103
-
104 145
     .table-repeater-column input {
105 146
         text-align: right !important;
106 147
     }
107 148
 
108
-    /* Flatten inputs */
109
-
110 149
     .fi-input-wrapper,
111 150
     .fi-input {
112 151
         padding: 0 !important;
@@ -121,8 +160,6 @@
121 160
         @apply bg-transparent !important;
122 161
     }
123 162
 
124
-    /* Focus states */
125
-
126 163
     .table-repeater-column:focus-within {
127 164
         outline: 2px solid #2563eb !important;
128 165
         outline-offset: -2px !important;
@@ -136,14 +173,10 @@
136 173
         outline: none !important;
137 174
     }
138 175
 
139
-    /* Row styling */
140
-
141 176
     .table-repeater-row:nth-child(even) {
142 177
         background-color: #f9fafb;
143 178
     }
144 179
 
145
-    /* Form control spacing */
146
-
147 180
     .fi-fo-field-wrp:has(.fi-fo-checkbox-list),
148 181
     .fi-fo-field-wrp:has(.fi-checkbox-input),
149 182
     .fi-fo-field-wrp:has(.fi-fo-radio) {

+ 5
- 7
resources/views/filament/tables/columns/deferred-text-input-column.blade.php Прегледај датотеку

@@ -106,12 +106,10 @@
106 106
                     $getExtraInputAttributeBag()
107 107
                         ->merge([
108 108
                             'x-on:change' . ($type === 'number' ? '.debounce.1s' : null) => $batchMode ? '
109
-                                $wire.dispatch(\'batch-column-changed\', {
110
-                                    data: {
111
-                                        name: name,
112
-                                        recordKey: recordKey,
113
-                                        value: $event.target.value
114
-                                    }
109
+                                $wire.handleBatchColumnChanged({
110
+                                    name: name,
111
+                                    recordKey: recordKey,
112
+                                    value: $event.target.value
115 113
                                 })
116 114
                             ' : '
117 115
                                 isLoading = true
@@ -130,7 +128,7 @@
130 128
 
131 129
                                 isLoading = false
132 130
                             ',
133
-                            'x-on:keydown.enter' => $batchMode ? '$wire.dispatch(\'save-batch-changes\')' : '',
131
+                            'x-on:keydown.enter' => $batchMode ? '$wire.saveBatchChanges()' : null,
134 132
                             'x-mask' . ($mask instanceof \Filament\Support\RawJs ? ':dynamic' : '') => filled($mask) ? $mask : null,
135 133
                         ])
136 134
                         ->class([

Loading…
Откажи
Сачувај