Explorar el Código

wip budgets

3.x
Andrew Wallo hace 7 meses
padre
commit
74daa80425

+ 30
- 52
app/Filament/Company/Resources/Accounting/BudgetResource.php Ver fichero

6
 use App\Filament\Forms\Components\CustomSection;
6
 use App\Filament\Forms\Components\CustomSection;
7
 use App\Models\Accounting\Account;
7
 use App\Models\Accounting\Account;
8
 use App\Models\Accounting\Budget;
8
 use App\Models\Accounting\Budget;
9
-use App\Models\Accounting\BudgetItem;
10
 use Filament\Forms;
9
 use Filament\Forms;
11
 use Filament\Forms\Form;
10
 use Filament\Forms\Form;
12
 use Filament\Resources\Resource;
11
 use Filament\Resources\Resource;
56
                 Forms\Components\Section::make('Budget Items')
55
                 Forms\Components\Section::make('Budget Items')
57
                     ->schema([
56
                     ->schema([
58
                         Forms\Components\Repeater::make('budgetItems')
57
                         Forms\Components\Repeater::make('budgetItems')
59
-                            ->relationship()
60
                             ->columns(4)
58
                             ->columns(4)
61
                             ->hiddenLabel()
59
                             ->hiddenLabel()
62
                             ->schema([
60
                             ->schema([
95
     {
93
     {
96
         return $table
94
         return $table
97
             ->columns([
95
             ->columns([
98
-                //
96
+                Tables\Columns\TextColumn::make('name')
97
+                    ->sortable()
98
+                    ->searchable(),
99
+
100
+                Tables\Columns\TextColumn::make('interval_type')
101
+                    ->label('Interval')
102
+                    ->sortable()
103
+                    ->badge()
104
+                    ->formatStateUsing(fn (string $state) => ucfirst($state)),
105
+
106
+                Tables\Columns\TextColumn::make('start_date')
107
+                    ->label('Start Date')
108
+                    ->date()
109
+                    ->sortable(),
110
+
111
+                Tables\Columns\TextColumn::make('end_date')
112
+                    ->label('End Date')
113
+                    ->date()
114
+                    ->sortable(),
115
+
116
+                Tables\Columns\TextColumn::make('total_budgeted_amount')
117
+                    ->label('Total Budgeted')
118
+                    ->money()
119
+                    ->sortable()
120
+                    ->alignEnd()
121
+                    ->formatStateUsing(fn (Budget $record) => $record->budgetItems->sum(fn ($item) => $item->allocations->sum('amount'))),
99
             ])
122
             ])
100
             ->filters([
123
             ->filters([
101
                 //
124
                 //
102
             ])
125
             ])
103
             ->actions([
126
             ->actions([
104
-                Tables\Actions\ViewAction::make(),
105
-                Tables\Actions\EditAction::make(),
127
+                Tables\Actions\ActionGroup::make([
128
+                    Tables\Actions\ViewAction::make(),
129
+                    Tables\Actions\EditAction::make(),
130
+                ]),
106
             ])
131
             ])
107
             ->bulkActions([
132
             ->bulkActions([
108
                 Tables\Actions\BulkActionGroup::make([
133
                 Tables\Actions\BulkActionGroup::make([
206
         return $fields;
231
         return $fields;
207
     }
232
     }
208
 
233
 
209
-    /**
210
-     * Generates an array of interval labels (e.g., Jan 2024, Q1 2024, etc.).
211
-     */
212
-    private static function generateIntervals(string $startDate, string $endDate, string $intervalType): array
213
-    {
214
-        $start = Carbon::parse($startDate);
215
-        $end = Carbon::parse($endDate);
216
-        $intervals = [];
217
-
218
-        while ($start->lte($end)) {
219
-            if ($intervalType === 'month') {
220
-                $intervals[] = $start->format('M Y'); // Example: Jan 2024
221
-                $start->addMonth();
222
-            } elseif ($intervalType === 'quarter') {
223
-                $intervals[] = 'Q' . $start->quarter . ' ' . $start->year; // Example: Q1 2024
224
-                $start->addQuarter();
225
-            } elseif ($intervalType === 'year') {
226
-                $intervals[] = $start->year; // Example: 2024
227
-                $start->addYear();
228
-            }
229
-        }
230
-
231
-        return $intervals;
232
-    }
233
-
234
-    /**
235
-     * Saves budget allocations correctly in `budget_allocations` table.
236
-     */
237
-    public static function saveBudgetAllocations(BudgetItem $record, array $data): void
238
-    {
239
-        $record->update($data);
240
-
241
-        $intervals = self::generateIntervals($data['start_date'], $data['end_date'], $data['interval_type']);
242
-
243
-        foreach ($intervals as $interval) {
244
-            $record->allocations()->updateOrCreate(
245
-                ['period' => $interval],
246
-                [
247
-                    'interval_type' => $data['interval_type'],
248
-                    'start_date' => Carbon::parse($interval)->startOfMonth(),
249
-                    'end_date' => Carbon::parse($interval)->endOfMonth(),
250
-                    'amount' => $data['allocations'][$interval] ?? 0,
251
-                ]
252
-            );
253
-        }
254
-    }
255
-
256
     public static function getRelations(): array
234
     public static function getRelations(): array
257
     {
235
     {
258
         return [
236
         return [

+ 44
- 2
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/CreateBudget.php Ver fichero

3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4
 
4
 
5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6
+use App\Models\Accounting\Budget;
7
+use App\Models\Accounting\BudgetItem;
6
 use Filament\Resources\Pages\CreateRecord;
8
 use Filament\Resources\Pages\CreateRecord;
7
 use Illuminate\Database\Eloquent\Model;
9
 use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Support\Carbon;
8
 
11
 
9
 class CreateBudget extends CreateRecord
12
 class CreateBudget extends CreateRecord
10
 {
13
 {
12
 
15
 
13
     protected function handleRecordCreation(array $data): Model
16
     protected function handleRecordCreation(array $data): Model
14
     {
17
     {
15
-        ray($data);
18
+        /** @var Budget $budget */
19
+        $budget = Budget::create([
20
+            'name' => $data['name'],
21
+            'interval_type' => $data['interval_type'],
22
+            'start_date' => $data['start_date'],
23
+            'end_date' => $data['end_date'],
24
+            'notes' => $data['notes'] ?? null,
25
+        ]);
16
 
26
 
17
-        return parent::handleRecordCreation($data); // TODO: Change the autogenerated stub
27
+        foreach ($data['budgetItems'] as $itemData) {
28
+            /** @var BudgetItem $budgetItem */
29
+            $budgetItem = $budget->budgetItems()->create([
30
+                'account_id' => $itemData['account_id'],
31
+            ]);
32
+
33
+            $allocationStart = Carbon::parse($data['start_date']);
34
+
35
+            foreach ($itemData['amounts'] as $periodLabel => $amount) {
36
+                $allocationEnd = self::calculateEndDate($allocationStart, $data['interval_type']);
37
+
38
+                $budgetItem->allocations()->create([
39
+                    'period' => $periodLabel,
40
+                    'interval_type' => $data['interval_type'],
41
+                    'start_date' => $allocationStart->toDateString(),
42
+                    'end_date' => $allocationEnd->toDateString(),
43
+                    'amount' => $amount,
44
+                ]);
45
+
46
+                $allocationStart = $allocationEnd->addDay();
47
+            }
48
+        }
49
+
50
+        return $budget;
51
+    }
52
+
53
+    private static function calculateEndDate(Carbon $startDate, string $intervalType): Carbon
54
+    {
55
+        return match ($intervalType) {
56
+            'quarter' => $startDate->copy()->addMonths(2)->endOfMonth(),
57
+            'year' => $startDate->copy()->endOfYear(),
58
+            default => $startDate->copy()->endOfMonth(),
59
+        };
18
     }
60
     }
19
 }
61
 }

+ 81
- 0
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/EditBudget.php Ver fichero

3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4
 
4
 
5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6
+use App\Models\Accounting\Budget;
7
+use App\Models\Accounting\BudgetItem;
6
 use Filament\Actions;
8
 use Filament\Actions;
7
 use Filament\Resources\Pages\EditRecord;
9
 use Filament\Resources\Pages\EditRecord;
10
+use Illuminate\Database\Eloquent\Model;
11
+use Illuminate\Support\Carbon;
8
 
12
 
9
 class EditBudget extends EditRecord
13
 class EditBudget extends EditRecord
10
 {
14
 {
17
             Actions\DeleteAction::make(),
21
             Actions\DeleteAction::make(),
18
         ];
22
         ];
19
     }
23
     }
24
+
25
+    protected function mutateFormDataBeforeFill(array $data): array
26
+    {
27
+        /** @var Budget $budget */
28
+        $budget = $this->record;
29
+
30
+        $data['budgetItems'] = $budget->budgetItems->map(function ($budgetItem) {
31
+            return [
32
+                'id' => $budgetItem->id,
33
+                'account_id' => $budgetItem->account_id,
34
+                'total_amount' => $budgetItem->allocations->sum('amount'), // Calculate total dynamically
35
+                'amounts' => $budgetItem->allocations->mapWithKeys(function ($allocation) {
36
+                    return [$allocation->period => $allocation->amount]; // Use the correct period label
37
+                })->toArray(),
38
+            ];
39
+        })->toArray();
40
+
41
+        return $data;
42
+    }
43
+
44
+    protected function handleRecordUpdate(Model $record, array $data): Model
45
+    {
46
+        /** @var Budget $budget */
47
+        $budget = $record;
48
+
49
+        $budget->update([
50
+            'name' => $data['name'],
51
+            'interval_type' => $data['interval_type'],
52
+            'start_date' => $data['start_date'],
53
+            'end_date' => $data['end_date'],
54
+            'notes' => $data['notes'] ?? null,
55
+        ]);
56
+
57
+        $budgetItemIds = [];
58
+
59
+        foreach ($data['budgetItems'] as $itemData) {
60
+            /** @var BudgetItem $budgetItem */
61
+            $budgetItem = $budget->budgetItems()->updateOrCreate(
62
+                ['id' => $itemData['id'] ?? null],
63
+                ['account_id' => $itemData['account_id']]
64
+            );
65
+
66
+            $budgetItemIds[] = $budgetItem->id;
67
+
68
+            $budgetItem->allocations()->delete();
69
+
70
+            $allocationStart = Carbon::parse($data['start_date']);
71
+
72
+            foreach ($itemData['amounts'] as $periodLabel => $amount) {
73
+                $allocationEnd = self::calculateEndDate($allocationStart, $data['interval_type']);
74
+
75
+                // Recreate allocations
76
+                $budgetItem->allocations()->create([
77
+                    'period' => $periodLabel,
78
+                    'interval_type' => $data['interval_type'],
79
+                    'start_date' => $allocationStart->toDateString(),
80
+                    'end_date' => $allocationEnd->toDateString(),
81
+                    'amount' => $amount,
82
+                ]);
83
+
84
+                $allocationStart = $allocationEnd->addDay();
85
+            }
86
+        }
87
+
88
+        $budget->budgetItems()->whereNotIn('id', $budgetItemIds)->delete();
89
+
90
+        return $budget;
91
+    }
92
+
93
+    private static function calculateEndDate(Carbon $startDate, string $intervalType): Carbon
94
+    {
95
+        return match ($intervalType) {
96
+            'quarter' => $startDate->copy()->addMonths(2)->endOfMonth(),
97
+            'year' => $startDate->copy()->endOfYear(),
98
+            default => $startDate->copy()->endOfMonth(),
99
+        };
100
+    }
20
 }
101
 }

+ 2
- 2
app/Models/Accounting/BudgetAllocation.php Ver fichero

2
 
2
 
3
 namespace App\Models\Accounting;
3
 namespace App\Models\Accounting;
4
 
4
 
5
-use App\Casts\MoneyCast;
5
+use App\Casts\DocumentMoneyCast;
6
 use App\Concerns\CompanyOwned;
6
 use App\Concerns\CompanyOwned;
7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
8
 use Illuminate\Database\Eloquent\Model;
8
 use Illuminate\Database\Eloquent\Model;
26
     protected $casts = [
26
     protected $casts = [
27
         'start_date' => 'date',
27
         'start_date' => 'date',
28
         'end_date' => 'date',
28
         'end_date' => 'date',
29
-        'amount' => MoneyCast::class,
29
+        'amount' => DocumentMoneyCast::class,
30
     ];
30
     ];
31
 
31
 
32
     public function budgetItem(): BelongsTo
32
     public function budgetItem(): BelongsTo

Loading…
Cancelar
Guardar