|
@@ -59,13 +59,106 @@ class BudgetResource extends Resource
|
59
|
59
|
|
60
|
60
|
Forms\Components\Section::make('Budget Items')
|
61
|
61
|
->headerActions([
|
|
62
|
+ Forms\Components\Actions\Action::make('addAccounts')
|
|
63
|
+ ->label('Add Accounts')
|
|
64
|
+ ->icon('heroicon-m-plus')
|
|
65
|
+ ->outlined()
|
|
66
|
+ ->color('primary')
|
|
67
|
+ ->form(fn (Forms\Get $get) => [
|
|
68
|
+ Forms\Components\Select::make('selected_accounts')
|
|
69
|
+ ->label('Choose Accounts to Add')
|
|
70
|
+ ->options(function () use ($get) {
|
|
71
|
+ $existingAccounts = collect($get('budgetItems'))->pluck('account_id')->toArray();
|
|
72
|
+
|
|
73
|
+ return Account::query()
|
|
74
|
+ ->budgetable()
|
|
75
|
+ ->whereNotIn('id', $existingAccounts) // Prevent duplicate selections
|
|
76
|
+ ->pluck('name', 'id');
|
|
77
|
+ })
|
|
78
|
+ ->searchable()
|
|
79
|
+ ->multiple()
|
|
80
|
+ ->hint('Select the accounts you want to add to this budget'),
|
|
81
|
+ ])
|
|
82
|
+ ->action(static fn (Forms\Set $set, Forms\Get $get, array $data) => self::addSelectedAccounts($set, $get, $data)),
|
|
83
|
+
|
62
|
84
|
Forms\Components\Actions\Action::make('addAllAccounts')
|
63
|
85
|
->label('Add All Accounts')
|
64
|
|
- ->icon('heroicon-m-plus')
|
|
86
|
+ ->icon('heroicon-m-folder-plus')
|
65
|
87
|
->outlined()
|
66
|
88
|
->color('primary')
|
67
|
89
|
->action(static fn (Forms\Set $set, Forms\Get $get) => self::addAllAccounts($set, $get))
|
68
|
90
|
->hidden(static fn (Forms\Get $get) => filled($get('budgetItems'))),
|
|
91
|
+
|
|
92
|
+ Forms\Components\Actions\Action::make('increaseAllocations')
|
|
93
|
+ ->label('Increase Allocations')
|
|
94
|
+ ->icon('heroicon-m-arrow-up')
|
|
95
|
+ ->outlined()
|
|
96
|
+ ->color('success')
|
|
97
|
+ ->form(fn (Forms\Get $get) => [
|
|
98
|
+ Forms\Components\Select::make('increase_type')
|
|
99
|
+ ->label('Increase Type')
|
|
100
|
+ ->options([
|
|
101
|
+ 'percentage' => 'Percentage (%)',
|
|
102
|
+ 'fixed' => 'Fixed Amount',
|
|
103
|
+ ])
|
|
104
|
+ ->default('percentage')
|
|
105
|
+ ->live()
|
|
106
|
+ ->required(),
|
|
107
|
+
|
|
108
|
+ Forms\Components\TextInput::make('percentage')
|
|
109
|
+ ->label('Increase by %')
|
|
110
|
+ ->numeric()
|
|
111
|
+ ->suffix('%')
|
|
112
|
+ ->required()
|
|
113
|
+ ->hidden(fn (Forms\Get $get) => $get('increase_type') !== 'percentage'),
|
|
114
|
+
|
|
115
|
+ Forms\Components\TextInput::make('fixed_amount')
|
|
116
|
+ ->label('Increase by Fixed Amount')
|
|
117
|
+ ->numeric()
|
|
118
|
+ ->suffix('USD')
|
|
119
|
+ ->required()
|
|
120
|
+ ->hidden(fn (Forms\Get $get) => $get('increase_type') !== 'fixed'),
|
|
121
|
+
|
|
122
|
+ Forms\Components\Select::make('apply_to_accounts')
|
|
123
|
+ ->label('Apply to Accounts')
|
|
124
|
+ ->options(function () use ($get) {
|
|
125
|
+ $budgetItems = $get('budgetItems') ?? [];
|
|
126
|
+ $accountIds = collect($budgetItems)
|
|
127
|
+ ->pluck('account_id')
|
|
128
|
+ ->filter()
|
|
129
|
+ ->unique()
|
|
130
|
+ ->toArray();
|
|
131
|
+
|
|
132
|
+ return Account::query()
|
|
133
|
+ ->whereIn('id', $accountIds)
|
|
134
|
+ ->pluck('name', 'id')
|
|
135
|
+ ->toArray();
|
|
136
|
+ })
|
|
137
|
+ ->searchable()
|
|
138
|
+ ->multiple()
|
|
139
|
+ ->hint('Leave blank to apply to all accounts'),
|
|
140
|
+
|
|
141
|
+ Forms\Components\Select::make('apply_to_periods')
|
|
142
|
+ ->label('Apply to Periods')
|
|
143
|
+ ->options(static function () use ($get) {
|
|
144
|
+ $startDate = $get('start_date');
|
|
145
|
+ $endDate = $get('end_date');
|
|
146
|
+ $intervalType = $get('interval_type');
|
|
147
|
+
|
|
148
|
+ if (blank($startDate) || blank($endDate) || blank($intervalType)) {
|
|
149
|
+ return [];
|
|
150
|
+ }
|
|
151
|
+
|
|
152
|
+ $labels = self::generateFormattedLabels($startDate, $endDate, $intervalType);
|
|
153
|
+
|
|
154
|
+ return array_combine($labels, $labels);
|
|
155
|
+ })
|
|
156
|
+ ->searchable()
|
|
157
|
+ ->multiple()
|
|
158
|
+ ->hint('Leave blank to apply to all periods'),
|
|
159
|
+ ])
|
|
160
|
+ ->action(static fn (Forms\Set $set, Forms\Get $get, array $data) => self::increaseAllocations($set, $get, $data))
|
|
161
|
+ ->visible(static fn (Forms\Get $get) => filled($get('budgetItems'))),
|
69
|
162
|
])
|
70
|
163
|
->schema([
|
71
|
164
|
Forms\Components\Repeater::make('budgetItems')
|
|
@@ -74,7 +167,9 @@ class BudgetResource extends Resource
|
74
|
167
|
->schema([
|
75
|
168
|
Forms\Components\Select::make('account_id')
|
76
|
169
|
->label('Account')
|
77
|
|
- ->options(Account::query()->pluck('name', 'id'))
|
|
170
|
+ ->options(Account::query()
|
|
171
|
+ ->budgetable()
|
|
172
|
+ ->pluck('name', 'id'))
|
78
|
173
|
->searchable()
|
79
|
174
|
->disableOptionsWhenSelectedInSiblingRepeaterItems()
|
80
|
175
|
->columnSpan(1)
|
|
@@ -152,6 +247,7 @@ class BudgetResource extends Resource
|
152
|
247
|
private static function addAllAccounts(Forms\Set $set, Forms\Get $get): void
|
153
|
248
|
{
|
154
|
249
|
$accounts = Account::query()
|
|
250
|
+ ->budgetable()
|
155
|
251
|
->pluck('id');
|
156
|
252
|
|
157
|
253
|
$budgetItems = $accounts->map(static fn ($accountId) => [
|
|
@@ -163,6 +259,33 @@ class BudgetResource extends Resource
|
163
|
259
|
$set('budgetItems', $budgetItems);
|
164
|
260
|
}
|
165
|
261
|
|
|
262
|
+ private static function addSelectedAccounts(Forms\Set $set, Forms\Get $get, array $data): void
|
|
263
|
+ {
|
|
264
|
+ $selectedAccountIds = $data['selected_accounts'] ?? [];
|
|
265
|
+
|
|
266
|
+ if (empty($selectedAccountIds)) {
|
|
267
|
+ return; // No accounts selected, do nothing.
|
|
268
|
+ }
|
|
269
|
+
|
|
270
|
+ $existingAccountIds = collect($get('budgetItems'))
|
|
271
|
+ ->pluck('account_id')
|
|
272
|
+ ->unique()
|
|
273
|
+ ->filter()
|
|
274
|
+ ->toArray();
|
|
275
|
+
|
|
276
|
+ // Only add accounts that aren't already in the budget items
|
|
277
|
+ $newAccounts = array_diff($selectedAccountIds, $existingAccountIds);
|
|
278
|
+
|
|
279
|
+ $newBudgetItems = collect($newAccounts)->map(static fn ($accountId) => [
|
|
280
|
+ 'account_id' => $accountId,
|
|
281
|
+ 'total_amount' => 0,
|
|
282
|
+ 'amounts' => self::generateDefaultAllocations($get('start_date'), $get('end_date'), $get('interval_type')),
|
|
283
|
+ ])->toArray();
|
|
284
|
+
|
|
285
|
+ // Merge new budget items with existing ones
|
|
286
|
+ $set('budgetItems', array_merge($get('budgetItems') ?? [], $newBudgetItems));
|
|
287
|
+ }
|
|
288
|
+
|
166
|
289
|
private static function generateDefaultAllocations(?string $startDate, ?string $endDate, ?string $intervalType): array
|
167
|
290
|
{
|
168
|
291
|
if (! $startDate || ! $endDate || ! $intervalType) {
|
|
@@ -174,6 +297,47 @@ class BudgetResource extends Resource
|
174
|
297
|
return collect($labels)->mapWithKeys(static fn ($label) => [$label => 0])->toArray();
|
175
|
298
|
}
|
176
|
299
|
|
|
300
|
+ private static function increaseAllocations(Forms\Set $set, Forms\Get $get, array $data): void
|
|
301
|
+ {
|
|
302
|
+ $increaseType = $data['increase_type']; // 'percentage' or 'fixed'
|
|
303
|
+ $percentage = $data['percentage'] ?? 0;
|
|
304
|
+ $fixedAmount = $data['fixed_amount'] ?? 0;
|
|
305
|
+
|
|
306
|
+ $selectedAccounts = $data['apply_to_accounts'] ?? []; // Selected account IDs
|
|
307
|
+ $selectedPeriods = $data['apply_to_periods'] ?? []; // Selected period labels
|
|
308
|
+
|
|
309
|
+ $budgetItems = $get('budgetItems') ?? [];
|
|
310
|
+
|
|
311
|
+ foreach ($budgetItems as $index => $budgetItem) {
|
|
312
|
+ // Skip if this account isn't selected (unless all accounts are being updated)
|
|
313
|
+ if (! empty($selectedAccounts) && ! in_array($budgetItem['account_id'], $selectedAccounts)) {
|
|
314
|
+ continue;
|
|
315
|
+ }
|
|
316
|
+
|
|
317
|
+ if (empty($budgetItem['amounts'])) {
|
|
318
|
+ continue; // Skip if no allocations exist
|
|
319
|
+ }
|
|
320
|
+
|
|
321
|
+ $updatedAmounts = $budgetItem['amounts']; // Clone existing amounts
|
|
322
|
+ foreach ($updatedAmounts as $label => $amount) {
|
|
323
|
+ // Skip if this period isn't selected (unless all periods are being updated)
|
|
324
|
+ if (! empty($selectedPeriods) && ! in_array($label, $selectedPeriods)) {
|
|
325
|
+ continue;
|
|
326
|
+ }
|
|
327
|
+
|
|
328
|
+ // Apply increase based on selected type
|
|
329
|
+ $updatedAmounts[$label] = match ($increaseType) {
|
|
330
|
+ 'percentage' => round($amount * (1 + $percentage / 100), 2),
|
|
331
|
+ 'fixed' => round($amount + $fixedAmount, 2),
|
|
332
|
+ default => $amount,
|
|
333
|
+ };
|
|
334
|
+ }
|
|
335
|
+
|
|
336
|
+ $set("budgetItems.{$index}.amounts", $updatedAmounts);
|
|
337
|
+ $set("budgetItems.{$index}.total_amount", round(array_sum($updatedAmounts), 2));
|
|
338
|
+ }
|
|
339
|
+ }
|
|
340
|
+
|
177
|
341
|
private static function disperseTotalAmount(Forms\Set $set, Forms\Get $get, float $totalAmount): void
|
178
|
342
|
{
|
179
|
343
|
$startDate = $get('../../start_date');
|