浏览代码

wip budgets

3.x
Andrew Wallo 7 个月前
父节点
当前提交
1eb23d2e77

+ 46
- 0
app/Enums/Accounting/BudgetIntervalType.php 查看文件

@@ -0,0 +1,46 @@
1
+<?php
2
+
3
+namespace App\Enums\Accounting;
4
+
5
+use App\Enums\Concerns\ParsesEnum;
6
+use Filament\Support\Contracts\HasLabel;
7
+
8
+enum BudgetIntervalType: string implements HasLabel
9
+{
10
+    use ParsesEnum;
11
+
12
+    case Week = 'week';
13
+    case Month = 'month';
14
+    case Quarter = 'quarter';
15
+    case Year = 'year';
16
+
17
+    public function getLabel(): ?string
18
+    {
19
+        return match ($this) {
20
+            self::Week => 'Weekly',
21
+            self::Month => 'Monthly',
22
+            self::Quarter => 'Quarterly',
23
+            self::Year => 'Yearly',
24
+        };
25
+    }
26
+
27
+    public function isWeek(): bool
28
+    {
29
+        return $this === self::Week;
30
+    }
31
+
32
+    public function isMonth(): bool
33
+    {
34
+        return $this === self::Month;
35
+    }
36
+
37
+    public function isQuarter(): bool
38
+    {
39
+        return $this === self::Quarter;
40
+    }
41
+
42
+    public function isYear(): bool
43
+    {
44
+        return $this === self::Year;
45
+    }
46
+}

+ 28
- 28
app/Filament/Company/Resources/Accounting/BudgetResource.php 查看文件

@@ -2,6 +2,7 @@
2 2
 
3 3
 namespace App\Filament\Company\Resources\Accounting;
4 4
 
5
+use App\Enums\Accounting\BudgetIntervalType;
5 6
 use App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
6 7
 use App\Filament\Forms\Components\CustomSection;
7 8
 use App\Models\Accounting\Account;
@@ -31,14 +32,8 @@ class BudgetResource extends Resource
31 32
                             ->maxLength(255),
32 33
                         Forms\Components\Select::make('interval_type')
33 34
                             ->label('Budget Interval')
34
-                            ->options([
35
-                                'day' => 'Daily',
36
-                                'week' => 'Weekly',
37
-                                'month' => 'Monthly',
38
-                                'quarter' => 'Quarterly',
39
-                                'year' => 'Yearly',
40
-                            ])
41
-                            ->default('month')
35
+                            ->options(BudgetIntervalType::class)
36
+                            ->default(BudgetIntervalType::Month->value)
42 37
                             ->required()
43 38
                             ->live(),
44 39
                         Forms\Components\DatePicker::make('start_date')
@@ -75,7 +70,7 @@ class BudgetResource extends Resource
75 70
                                             ->label('Disperse')
76 71
                                             ->icon('heroicon-m-bars-arrow-down')
77 72
                                             ->color('primary')
78
-                                            ->action(fn (Forms\Set $set, Forms\Get $get, $state) => self::disperseTotalAmount($set, $get, $state))
73
+                                            ->action(static fn (Forms\Set $set, Forms\Get $get, $state) => self::disperseTotalAmount($set, $get, $state))
79 74
                                     ),
80 75
 
81 76
                                 CustomSection::make('Budget Allocations')
@@ -100,8 +95,7 @@ class BudgetResource extends Resource
100 95
                 Tables\Columns\TextColumn::make('interval_type')
101 96
                     ->label('Interval')
102 97
                     ->sortable()
103
-                    ->badge()
104
-                    ->formatStateUsing(fn (string $state) => ucfirst($state)),
98
+                    ->badge(),
105 99
 
106 100
                 Tables\Columns\TextColumn::make('start_date')
107 101
                     ->label('Start Date')
@@ -118,7 +112,7 @@ class BudgetResource extends Resource
118 112
                     ->money()
119 113
                     ->sortable()
120 114
                     ->alignEnd()
121
-                    ->formatStateUsing(fn (Budget $record) => $record->budgetItems->sum(fn ($item) => $item->allocations->sum('amount'))),
115
+                    ->getStateUsing(fn (Budget $record) => $record->budgetItems->sum(fn ($item) => $item->allocations->sum('amount'))),
122 116
             ])
123 117
             ->filters([
124 118
                 //
@@ -175,20 +169,23 @@ class BudgetResource extends Resource
175 169
     {
176 170
         $start = Carbon::parse($startDate);
177 171
         $end = Carbon::parse($endDate);
172
+        $intervalTypeEnum = BudgetIntervalType::parse($intervalType);
178 173
         $labels = [];
179 174
 
180 175
         while ($start->lte($end)) {
181
-            $labels[] = match ($intervalType) {
182
-                'month' => $start->format('M'), // Example: Jan, Feb, Mar
183
-                'quarter' => 'Q' . $start->quarter, // Example: Q1, Q2, Q3
184
-                'year' => (string) $start->year, // Example: 2024, 2025
176
+            $labels[] = match ($intervalTypeEnum) {
177
+                BudgetIntervalType::Week => 'W' . $start->weekOfYear . ' ' . $start->year, // Example: W10 2024
178
+                BudgetIntervalType::Month => $start->format('M'), // Example: Jan, Feb, Mar
179
+                BudgetIntervalType::Quarter => 'Q' . $start->quarter, // Example: Q1, Q2, Q3
180
+                BudgetIntervalType::Year => (string) $start->year, // Example: 2024, 2025
185 181
                 default => '',
186 182
             };
187 183
 
188
-            match ($intervalType) {
189
-                'month' => $start->addMonth(),
190
-                'quarter' => $start->addQuarter(),
191
-                'year' => $start->addYear(),
184
+            match ($intervalTypeEnum) {
185
+                BudgetIntervalType::Week => $start->addWeek(),
186
+                BudgetIntervalType::Month => $start->addMonth(),
187
+                BudgetIntervalType::Quarter => $start->addQuarter(),
188
+                BudgetIntervalType::Year => $start->addYear(),
192 189
                 default => null,
193 190
             };
194 191
         }
@@ -204,13 +201,15 @@ class BudgetResource extends Resource
204 201
 
205 202
         $start = Carbon::parse($startDate);
206 203
         $end = Carbon::parse($endDate);
204
+        $intervalTypeEnum = BudgetIntervalType::parse($intervalType);
207 205
         $fields = [];
208 206
 
209 207
         while ($start->lte($end)) {
210
-            $label = match ($intervalType) {
211
-                'month' => $start->format('M'), // Example: Jan, Feb, Mar
212
-                'quarter' => 'Q' . $start->quarter, // Example: Q1, Q2, Q3
213
-                'year' => (string) $start->year, // Example: 2024, 2025
208
+            $label = match ($intervalTypeEnum) {
209
+                BudgetIntervalType::Week => 'W' . $start->weekOfYear . ' ' . $start->year, // Example: W10 2024
210
+                BudgetIntervalType::Month => $start->format('M'), // Example: Jan, Feb, Mar
211
+                BudgetIntervalType::Quarter => 'Q' . $start->quarter, // Example: Q1, Q2, Q3
212
+                BudgetIntervalType::Year => (string) $start->year, // Example: 2024, 2025
214 213
                 default => '',
215 214
             };
216 215
 
@@ -220,10 +219,11 @@ class BudgetResource extends Resource
220 219
                 ->required();
221 220
 
222 221
             // Move to the next period
223
-            match ($intervalType) {
224
-                'month' => $start->addMonth(),
225
-                'quarter' => $start->addQuarter(),
226
-                'year' => $start->addYear(),
222
+            match ($intervalTypeEnum) {
223
+                BudgetIntervalType::Week => $start->addWeek(),
224
+                BudgetIntervalType::Month => $start->addMonth(),
225
+                BudgetIntervalType::Quarter => $start->addQuarter(),
226
+                BudgetIntervalType::Year => $start->addYear(),
227 227
                 default => null,
228 228
             };
229 229
         }

+ 7
- 5
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/CreateBudget.php 查看文件

@@ -2,6 +2,7 @@
2 2
 
3 3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4 4
 
5
+use App\Enums\Accounting\BudgetIntervalType;
5 6
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6 7
 use App\Models\Accounting\Budget;
7 8
 use App\Models\Accounting\BudgetItem;
@@ -33,7 +34,7 @@ class CreateBudget extends CreateRecord
33 34
             $allocationStart = Carbon::parse($data['start_date']);
34 35
 
35 36
             foreach ($itemData['amounts'] as $periodLabel => $amount) {
36
-                $allocationEnd = self::calculateEndDate($allocationStart, $data['interval_type']);
37
+                $allocationEnd = self::calculateEndDate($allocationStart, BudgetIntervalType::parse($data['interval_type']));
37 38
 
38 39
                 $budgetItem->allocations()->create([
39 40
                     'period' => $periodLabel,
@@ -50,12 +51,13 @@ class CreateBudget extends CreateRecord
50 51
         return $budget;
51 52
     }
52 53
 
53
-    private static function calculateEndDate(Carbon $startDate, string $intervalType): Carbon
54
+    private static function calculateEndDate(Carbon $startDate, BudgetIntervalType $intervalType): Carbon
54 55
     {
55 56
         return match ($intervalType) {
56
-            'quarter' => $startDate->copy()->addMonths(2)->endOfMonth(),
57
-            'year' => $startDate->copy()->endOfYear(),
58
-            default => $startDate->copy()->endOfMonth(),
57
+            BudgetIntervalType::Week => $startDate->copy()->endOfWeek(),
58
+            BudgetIntervalType::Month => $startDate->copy()->endOfMonth(),
59
+            BudgetIntervalType::Quarter => $startDate->copy()->endOfQuarter(),
60
+            BudgetIntervalType::Year => $startDate->copy()->endOfYear(),
59 61
         };
60 62
     }
61 63
 }

+ 10
- 7
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/EditBudget.php 查看文件

@@ -2,8 +2,10 @@
2 2
 
3 3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4 4
 
5
+use App\Enums\Accounting\BudgetIntervalType;
5 6
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6 7
 use App\Models\Accounting\Budget;
8
+use App\Models\Accounting\BudgetAllocation;
7 9
 use App\Models\Accounting\BudgetItem;
8 10
 use Filament\Actions;
9 11
 use Filament\Resources\Pages\EditRecord;
@@ -27,12 +29,12 @@ class EditBudget extends EditRecord
27 29
         /** @var Budget $budget */
28 30
         $budget = $this->record;
29 31
 
30
-        $data['budgetItems'] = $budget->budgetItems->map(function ($budgetItem) {
32
+        $data['budgetItems'] = $budget->budgetItems->map(function (BudgetItem $budgetItem) {
31 33
             return [
32 34
                 'id' => $budgetItem->id,
33 35
                 'account_id' => $budgetItem->account_id,
34 36
                 'total_amount' => $budgetItem->allocations->sum('amount'), // Calculate total dynamically
35
-                'amounts' => $budgetItem->allocations->mapWithKeys(function ($allocation) {
37
+                'amounts' => $budgetItem->allocations->mapWithKeys(static function (BudgetAllocation $allocation) {
36 38
                     return [$allocation->period => $allocation->amount]; // Use the correct period label
37 39
                 })->toArray(),
38 40
             ];
@@ -70,7 +72,7 @@ class EditBudget extends EditRecord
70 72
             $allocationStart = Carbon::parse($data['start_date']);
71 73
 
72 74
             foreach ($itemData['amounts'] as $periodLabel => $amount) {
73
-                $allocationEnd = self::calculateEndDate($allocationStart, $data['interval_type']);
75
+                $allocationEnd = self::calculateEndDate($allocationStart, BudgetIntervalType::parse($data['interval_type']));
74 76
 
75 77
                 // Recreate allocations
76 78
                 $budgetItem->allocations()->create([
@@ -90,12 +92,13 @@ class EditBudget extends EditRecord
90 92
         return $budget;
91 93
     }
92 94
 
93
-    private static function calculateEndDate(Carbon $startDate, string $intervalType): Carbon
95
+    private static function calculateEndDate(Carbon $startDate, BudgetIntervalType $intervalType): Carbon
94 96
     {
95 97
         return match ($intervalType) {
96
-            'quarter' => $startDate->copy()->addMonths(2)->endOfMonth(),
97
-            'year' => $startDate->copy()->endOfYear(),
98
-            default => $startDate->copy()->endOfMonth(),
98
+            BudgetIntervalType::Week => $startDate->copy()->endOfWeek(),
99
+            BudgetIntervalType::Month => $startDate->copy()->endOfMonth(),
100
+            BudgetIntervalType::Quarter => $startDate->copy()->endOfQuarter(),
101
+            BudgetIntervalType::Year => $startDate->copy()->endOfYear(),
99 102
         };
100 103
     }
101 104
 }

+ 19
- 0
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/ViewBudget.php 查看文件

@@ -4,6 +4,8 @@ namespace App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
4 4
 
5 5
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6 6
 use Filament\Actions;
7
+use Filament\Forms\Form;
8
+use Filament\Infolists\Infolist;
7 9
 use Filament\Resources\Pages\ViewRecord;
8 10
 
9 11
 class ViewBudget extends ViewRecord
@@ -16,4 +18,21 @@ class ViewBudget extends ViewRecord
16 18
             Actions\EditAction::make(),
17 19
         ];
18 20
     }
21
+
22
+    public function getRelationManagers(): array
23
+    {
24
+        return [
25
+            BudgetResource\RelationManagers\BudgetItemsRelationManager::class,
26
+        ];
27
+    }
28
+
29
+    public function form(Form $form): Form
30
+    {
31
+        return $form->schema([]);
32
+    }
33
+
34
+    public function infolist(Infolist $infolist): Infolist
35
+    {
36
+        return $infolist->schema([]);
37
+    }
19 38
 }

+ 48
- 0
app/Filament/Company/Resources/Accounting/BudgetResource/RelationManagers/BudgetItemsRelationManager.php 查看文件

@@ -0,0 +1,48 @@
1
+<?php
2
+
3
+namespace App\Filament\Company\Resources\Accounting\BudgetResource\RelationManagers;
4
+
5
+use Filament\Resources\RelationManagers\RelationManager;
6
+use Filament\Tables;
7
+use Filament\Tables\Table;
8
+
9
+class BudgetItemsRelationManager extends RelationManager
10
+{
11
+    protected static string $relationship = 'budgetItems';
12
+
13
+    protected static bool $isLazy = false;
14
+
15
+    public function table(Table $table): Table
16
+    {
17
+        return $table
18
+            ->recordTitleAttribute('account_id')
19
+            ->columns([
20
+                Tables\Columns\TextColumn::make('account.name')
21
+                    ->label('Account')
22
+                    ->sortable()
23
+                    ->searchable(),
24
+
25
+                Tables\Columns\TextColumn::make('allocations_sum_amount')
26
+                    ->label('Total Allocations')
27
+                    ->sortable()
28
+                    ->alignEnd()
29
+                    ->sum('allocations', 'amount')
30
+                    ->money(divideBy: 100),
31
+            ])
32
+            ->filters([
33
+                //
34
+            ])
35
+            ->headerActions([
36
+                // Tables\Actions\CreateAction::make(),
37
+            ])
38
+            ->actions([
39
+                // Tables\Actions\EditAction::make(),
40
+                // Tables\Actions\DeleteAction::make(),
41
+            ])
42
+            ->bulkActions([
43
+                //                Tables\Actions\BulkActionGroup::make([
44
+                //                    Tables\Actions\DeleteBulkAction::make(),
45
+                //                ]),
46
+            ]);
47
+    }
48
+}

+ 2
- 0
app/Models/Accounting/Budget.php 查看文件

@@ -4,6 +4,7 @@ namespace App\Models\Accounting;
4 4
 
5 5
 use App\Concerns\Blamable;
6 6
 use App\Concerns\CompanyOwned;
7
+use App\Enums\Accounting\BudgetIntervalType;
7 8
 use App\Enums\Accounting\BudgetStatus;
8 9
 use App\Filament\Company\Resources\Accounting\BudgetResource;
9 10
 use Filament\Actions\Action;
@@ -41,6 +42,7 @@ class Budget extends Model
41 42
         'start_date' => 'date',
42 43
         'end_date' => 'date',
43 44
         'status' => BudgetStatus::class,
45
+        'interval_type' => BudgetIntervalType::class,
44 46
         'approved_at' => 'datetime',
45 47
         'closed_at' => 'datetime',
46 48
     ];

正在加载...
取消
保存