Andrew Wallo 6 个月前
父节点
当前提交
01ea270c67

+ 4
- 29
app/Filament/Company/Pages/Accounting/Transactions.php 查看文件

36
 use Filament\Forms\Set;
36
 use Filament\Forms\Set;
37
 use Filament\Pages\Page;
37
 use Filament\Pages\Page;
38
 use Filament\Support\Colors\Color;
38
 use Filament\Support\Colors\Color;
39
-use Filament\Support\Enums\Alignment;
40
 use Filament\Support\Enums\FontWeight;
39
 use Filament\Support\Enums\FontWeight;
41
 use Filament\Support\Enums\IconPosition;
40
 use Filament\Support\Enums\IconPosition;
42
 use Filament\Support\Enums\IconSize;
41
 use Filament\Support\Enums\IconSize;
111
                     ->label('Add journal transaction')
110
                     ->label('Add journal transaction')
112
                     ->fillForm(fn (): array => $this->getFormDefaultsForType(TransactionType::Journal))
111
                     ->fillForm(fn (): array => $this->getFormDefaultsForType(TransactionType::Journal))
113
                     ->modalWidth(MaxWidth::Screen)
112
                     ->modalWidth(MaxWidth::Screen)
113
+                    ->extraModalWindowAttributes([
114
+                        'class' => 'journal-transaction-modal',
115
+                    ])
114
                     ->model(static::getModel())
116
                     ->model(static::getModel())
115
                     ->form(fn (Form $form) => $this->journalTransactionForm($form))
117
                     ->form(fn (Form $form) => $this->journalTransactionForm($form))
116
                     ->modalSubmitAction(fn (Actions\StaticAction $action) => $action->disabled(! $this->isJournalEntryBalanced()))
118
                     ->modalSubmitAction(fn (Actions\StaticAction $action) => $action->disabled(! $this->isJournalEntryBalanced()))
343
                 $filters['posted_at'],
345
                 $filters['posted_at'],
344
                 $filters['updated_at'],
346
                 $filters['updated_at'],
345
             ])
347
             ])
346
-            ->deferFilters()
347
-            ->deferLoading()
348
             ->filtersFormWidth(MaxWidth::ThreeExtraLarge)
348
             ->filtersFormWidth(MaxWidth::ThreeExtraLarge)
349
-            ->filtersTriggerAction(
350
-                fn (Tables\Actions\Action $action) => $action
351
-                    ->slideOver()
352
-                    ->modalFooterActionsAlignment(Alignment::End)
353
-                    ->modalCancelAction(false)
354
-                    ->extraModalFooterActions(function (Table $table) use ($action) {
355
-                        return [
356
-                            $table->getFiltersApplyAction()
357
-                                ->close(),
358
-                            Actions\StaticAction::make('cancel')
359
-                                ->label($action->getModalCancelActionLabel())
360
-                                ->button()
361
-                                ->close()
362
-                                ->color('gray'),
363
-                            Tables\Actions\Action::make('resetFilters')
364
-                                ->label(__('Clear all'))
365
-                                ->color('primary')
366
-                                ->link()
367
-                                ->extraAttributes([
368
-                                    'class' => 'me-auto',
369
-                                ])
370
-                                ->action('resetTableFiltersForm'),
371
-                        ];
372
-                    })
373
-            )
374
             ->actions([
349
             ->actions([
375
                 Tables\Actions\Action::make('markAsReviewed')
350
                 Tables\Actions\Action::make('markAsReviewed')
376
                     ->label('Mark as reviewed')
351
                     ->label('Mark as reviewed')
623
                 ->label('Type')
598
                 ->label('Type')
624
                 ->options(JournalEntryType::class)
599
                 ->options(JournalEntryType::class)
625
                 ->live()
600
                 ->live()
626
-                ->afterStateUpdated(function (Get $get, Set $set, ?string $state, ?string $old) {
601
+                ->afterStateUpdated(function (Get $get, Set $set, $state, $old) {
627
                     $this->adjustJournalEntryAmountsForTypeChange(JournalEntryType::parse($state), JournalEntryType::parse($old), $get('amount'));
602
                     $this->adjustJournalEntryAmountsForTypeChange(JournalEntryType::parse($state), JournalEntryType::parse($old), $get('amount'));
628
                 })
603
                 })
629
                 ->softRequired(),
604
                 ->softRequired(),

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

5
 use App\Enums\Accounting\BudgetIntervalType;
5
 use App\Enums\Accounting\BudgetIntervalType;
6
 use App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
6
 use App\Filament\Company\Resources\Accounting\BudgetResource\Pages;
7
 use App\Filament\Forms\Components\CustomSection;
7
 use App\Filament\Forms\Components\CustomSection;
8
+use App\Filament\Forms\Components\CustomTableRepeater;
8
 use App\Models\Accounting\Account;
9
 use App\Models\Accounting\Account;
9
 use App\Models\Accounting\Budget;
10
 use App\Models\Accounting\Budget;
11
+use App\Models\Accounting\BudgetItem;
12
+use Awcodes\TableRepeater\Header;
10
 use Filament\Forms;
13
 use Filament\Forms;
11
 use Filament\Forms\Form;
14
 use Filament\Forms\Form;
12
 use Filament\Resources\Resource;
15
 use Filament\Resources\Resource;
16
+use Filament\Support\Enums\Alignment;
17
+use Filament\Support\Enums\MaxWidth;
18
+use Filament\Support\RawJs;
13
 use Filament\Tables;
19
 use Filament\Tables;
14
 use Filament\Tables\Table;
20
 use Filament\Tables\Table;
15
 use Illuminate\Support\Carbon;
21
 use Illuminate\Support\Carbon;
232
                 Tables\Actions\ActionGroup::make([
238
                 Tables\Actions\ActionGroup::make([
233
                     Tables\Actions\ViewAction::make(),
239
                     Tables\Actions\ViewAction::make(),
234
                     Tables\Actions\EditAction::make(),
240
                     Tables\Actions\EditAction::make(),
241
+                    Tables\Actions\EditAction::make('editAllocations')
242
+                        ->name('editAllocations')
243
+                        ->url(null)
244
+                        ->label('Edit Allocations')
245
+                        ->icon('heroicon-o-table-cells')
246
+                        ->modalWidth(MaxWidth::Screen)
247
+                        ->modalHeading('Edit Budget Allocations')
248
+                        ->modalDescription('Update the allocations for this budget')
249
+                        ->slideOver()
250
+                        ->form(function (Budget $record) {
251
+                            $periods = $record->getPeriods();
252
+
253
+                            $headers = [
254
+                                Header::make('Account')
255
+                                    ->label('Account')
256
+                                    ->width('200px'),
257
+                            ];
258
+
259
+                            foreach ($periods as $period) {
260
+                                $headers[] = Header::make($period)
261
+                                    ->label($period)
262
+                                    ->width('120px')
263
+                                    ->align(Alignment::Right);
264
+                            }
265
+
266
+                            return [
267
+                                CustomTableRepeater::make('budgetItems')
268
+                                    ->relationship()
269
+                                    ->hiddenLabel()
270
+                                    ->headers($headers)
271
+                                    ->schema([
272
+                                        Forms\Components\Placeholder::make('account')
273
+                                            ->hiddenLabel()
274
+                                            ->content(fn (BudgetItem $record) => $record->account->name ?? ''),
275
+
276
+                                        // Create a field for each period
277
+                                        ...collect($periods)->map(function ($period) {
278
+                                            return Forms\Components\TextInput::make("allocations.{$period}")
279
+                                                ->mask(RawJs::make('$money($input)'))
280
+                                                ->stripCharacters(',')
281
+                                                ->numeric()
282
+                                                ->extraInputAttributes(['class' => 'text-right'])
283
+                                                ->afterStateHydrated(function ($component, $state, BudgetItem $record) use ($period) {
284
+                                                    // Find the allocation for this period
285
+                                                    $allocation = $record->allocations->firstWhere('period', $period);
286
+                                                    $component->state($allocation ? $allocation->amount : 0);
287
+                                                })
288
+                                                ->dehydrated(false); // We'll handle saving manually
289
+                                        })->toArray(),
290
+                                    ])
291
+                                    ->spreadsheet()
292
+                                    ->itemLabel(fn (BudgetItem $record) => $record->account->name ?? 'Budget Item')
293
+                                    ->deletable(false)
294
+                                    ->reorderable(false)
295
+                                    ->addable(false) // Don't allow adding new budget items
296
+                                    ->columnSpanFull(),
297
+                            ];
298
+                        }),
235
                 ]),
299
                 ]),
236
             ])
300
             ])
237
             ->bulkActions([
301
             ->bulkActions([

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

327
             ]);
327
             ]);
328
 
328
 
329
             $allocationStart = Carbon::parse($data['start_date']);
329
             $allocationStart = Carbon::parse($data['start_date']);
330
+            $budgetEndDate = Carbon::parse($data['end_date']);
330
 
331
 
331
             // Determine amounts based on the prefill method
332
             // Determine amounts based on the prefill method
332
             $amounts = match ($data['prefill_method'] ?? null) {
333
             $amounts = match ($data['prefill_method'] ?? null) {
335
                 default => $this->generateZeroAmounts($data['start_date'], $data['end_date'], BudgetIntervalType::parse($data['interval_type'])),
336
                 default => $this->generateZeroAmounts($data['start_date'], $data['end_date'], BudgetIntervalType::parse($data['interval_type'])),
336
             };
337
             };
337
 
338
 
339
+            if (empty($amounts)) {
340
+                $amounts = $this->generateZeroAmounts(
341
+                    $data['start_date'],
342
+                    $data['end_date'],
343
+                    BudgetIntervalType::parse($data['interval_type'])
344
+                );
345
+            }
346
+
338
             foreach ($amounts as $periodLabel => $amount) {
347
             foreach ($amounts as $periodLabel => $amount) {
348
+                if ($allocationStart->gt($budgetEndDate)) {
349
+                    break;
350
+                }
351
+
339
                 $allocationEnd = self::calculateEndDate($allocationStart, BudgetIntervalType::parse($data['interval_type']));
352
                 $allocationEnd = self::calculateEndDate($allocationStart, BudgetIntervalType::parse($data['interval_type']));
340
 
353
 
354
+                if ($allocationEnd->gt($budgetEndDate)) {
355
+                    $allocationEnd = $budgetEndDate->copy();
356
+                }
357
+
341
                 $budgetItem->allocations()->create([
358
                 $budgetItem->allocations()->create([
342
                     'period' => $periodLabel,
359
                     'period' => $periodLabel,
343
                     'interval_type' => $data['interval_type'],
360
                     'interval_type' => $data['interval_type'],
355
 
372
 
356
     private function getAmountsFromActuals(Account $account, int $fiscalYear, BudgetIntervalType $intervalType): array
373
     private function getAmountsFromActuals(Account $account, int $fiscalYear, BudgetIntervalType $intervalType): array
357
     {
374
     {
358
-        // Determine the fiscal year start and end dates
359
-        $fiscalYearStart = Carbon::create($fiscalYear, 1, 1)->startOfYear();
360
-        $fiscalYearEnd = $fiscalYearStart->copy()->endOfYear();
375
+        $amounts = [];
376
+
377
+        // Get the start and end date of the budget being created
378
+        $budgetStartDate = Carbon::parse($this->data['start_date']);
379
+        $budgetEndDate = Carbon::parse($this->data['end_date']);
380
+
381
+        // Map to equivalent dates in the reference fiscal year
382
+        $referenceStartDate = Carbon::create($fiscalYear, $budgetStartDate->month, $budgetStartDate->day);
383
+        $referenceEndDate = Carbon::create($fiscalYear, $budgetEndDate->month, $budgetEndDate->day);
384
+
385
+        // Handle year boundary case (if budget crosses year boundary)
386
+        if ($budgetStartDate->month > $budgetEndDate->month ||
387
+            ($budgetStartDate->month === $budgetEndDate->month && $budgetStartDate->day > $budgetEndDate->day)) {
388
+            $referenceEndDate->year++;
389
+        }
390
+
391
+        if ($intervalType->isMonth()) {
392
+            // Process month by month within the reference period
393
+            $currentDate = $referenceStartDate->copy()->startOfMonth();
394
+            $lastMonth = $referenceEndDate->copy()->startOfMonth();
361
 
395
 
362
-        $netMovement = Accounting::getNetMovement($account, $fiscalYearStart->toDateString(), $fiscalYearEnd->toDateString());
396
+            while ($currentDate->lte($lastMonth)) {
397
+                $periodStart = $currentDate->copy()->startOfMonth();
398
+                $periodEnd = $currentDate->copy()->endOfMonth();
399
+                $periodLabel = $this->determinePeriod($periodStart, $intervalType);
400
+
401
+                $netMovement = Accounting::getNetMovement(
402
+                    $account,
403
+                    $periodStart->toDateString(),
404
+                    $periodEnd->toDateString()
405
+                );
406
+
407
+                $amounts[$periodLabel] = $netMovement->getAmount();
408
+
409
+                $currentDate->addMonth();
410
+            }
411
+        } elseif ($intervalType->isQuarter()) {
412
+            // Process quarter by quarter within the reference period
413
+            $currentDate = $referenceStartDate->copy()->startOfQuarter();
414
+            $lastQuarter = $referenceEndDate->copy()->startOfQuarter();
415
+
416
+            while ($currentDate->lte($lastQuarter)) {
417
+                $periodStart = $currentDate->copy()->startOfQuarter();
418
+                $periodEnd = $currentDate->copy()->endOfQuarter();
419
+                $periodLabel = $this->determinePeriod($periodStart, $intervalType);
420
+
421
+                $netMovement = Accounting::getNetMovement(
422
+                    $account,
423
+                    $periodStart->toDateString(),
424
+                    $periodEnd->toDateString()
425
+                );
426
+
427
+                $amounts[$periodLabel] = $netMovement->getAmount();
428
+
429
+                $currentDate->addQuarter();
430
+            }
431
+        } else {
432
+            // For yearly intervals
433
+            $periodStart = $referenceStartDate->copy()->startOfYear();
434
+            $periodEnd = $referenceEndDate->copy()->endOfYear();
435
+            $periodLabel = $this->determinePeriod($periodStart, $intervalType);
436
+
437
+            $netMovement = Accounting::getNetMovement(
438
+                $account,
439
+                $periodStart->toDateString(),
440
+                $periodEnd->toDateString()
441
+            );
442
+
443
+            $amounts[$periodLabel] = $netMovement->getAmount();
444
+        }
363
 
445
 
364
-        return $this->distributeAmountAcrossPeriods($netMovement->getAmount(), $fiscalYearStart, $fiscalYearEnd, $intervalType);
446
+        return $amounts;
365
     }
447
     }
366
 
448
 
367
     private function distributeAmountAcrossPeriods(int $totalAmountInCents, Carbon $startDate, Carbon $endDate, BudgetIntervalType $intervalType): array
449
     private function distributeAmountAcrossPeriods(int $totalAmountInCents, Carbon $startDate, Carbon $endDate, BudgetIntervalType $intervalType): array
397
     {
479
     {
398
         $amounts = [];
480
         $amounts = [];
399
 
481
 
482
+        // Get the budget being created start and end dates
483
+        $newBudgetStartDate = Carbon::parse($this->data['start_date']);
484
+        $newBudgetEndDate = Carbon::parse($this->data['end_date']);
485
+
486
+        // Get source budget's date information
487
+        $sourceBudget = Budget::findOrFail($sourceBudgetId);
488
+        $sourceBudgetType = $sourceBudget->interval_type;
489
+
490
+        // Retrieve all previous allocations for this account
400
         $previousAllocations = BudgetAllocation::query()
491
         $previousAllocations = BudgetAllocation::query()
401
-            ->whereHas('budgetItem', fn ($query) => $query->where('account_id', $account->id)->where('budget_id', $sourceBudgetId))
492
+            ->whereHas(
493
+                'budgetItem',
494
+                fn ($query) => $query->where('account_id', $account->id)
495
+                    ->where('budget_id', $sourceBudgetId)
496
+            )
497
+            ->orderBy('start_date')
402
             ->get();
498
             ->get();
403
 
499
 
404
-        foreach ($previousAllocations as $allocation) {
405
-            $amounts[$allocation->period] = $allocation->getRawOriginal('amount');
500
+        if ($previousAllocations->isEmpty()) {
501
+            return $this->generateZeroAmounts(
502
+                $this->data['start_date'],
503
+                $this->data['end_date'],
504
+                $intervalType
505
+            );
506
+        }
507
+
508
+        // Map previous budget periods to current budget periods
509
+        if ($intervalType === $sourceBudgetType) {
510
+            // Same interval type: direct mapping of equivalent periods
511
+            foreach ($previousAllocations as $allocation) {
512
+                $allocationDate = Carbon::parse($allocation->start_date);
513
+
514
+                // Create an equivalent date in the new budget's time range
515
+                $equivalentMonth = $allocationDate->month;
516
+                $equivalentDay = $allocationDate->day;
517
+                $equivalentYear = $newBudgetStartDate->year;
518
+
519
+                // Adjust year if the budget spans multiple years
520
+                if ($newBudgetStartDate->month > $newBudgetEndDate->month &&
521
+                    $equivalentMonth < $newBudgetStartDate->month) {
522
+                    $equivalentYear++;
523
+                }
524
+
525
+                $equivalentDate = Carbon::create($equivalentYear, $equivalentMonth, $equivalentDay);
526
+
527
+                // Only include if the date falls within our new budget period
528
+                if ($equivalentDate->between($newBudgetStartDate, $newBudgetEndDate)) {
529
+                    $periodLabel = $this->determinePeriod($equivalentDate, $intervalType);
530
+                    $amounts[$periodLabel] = $allocation->getRawOriginal('amount');
531
+                }
532
+            }
533
+        } else {
534
+            // Handle conversion between different interval types
535
+            $newBudgetPeriods = $this->generateZeroAmounts(
536
+                $this->data['start_date'],
537
+                $this->data['end_date'],
538
+                $intervalType
539
+            );
540
+
541
+            // Fill with zeros initially
542
+            $amounts = array_fill_keys(array_keys($newBudgetPeriods), 0);
543
+
544
+            // Group previous allocations by their date range
545
+            $allocationsByRange = [];
546
+            foreach ($previousAllocations as $allocation) {
547
+                $allocationsByRange[] = [
548
+                    'start' => Carbon::parse($allocation->start_date),
549
+                    'end' => Carbon::parse($allocation->end_date),
550
+                    'amount' => $allocation->getRawOriginal('amount'),
551
+                ];
552
+            }
553
+
554
+            // Create new allocations based on interval type
555
+            $currentDate = Carbon::parse($this->data['start_date']);
556
+            $endDate = Carbon::parse($this->data['end_date']);
557
+
558
+            while ($currentDate->lte($endDate)) {
559
+                $periodStart = $currentDate->copy();
560
+                $periodEnd = self::calculateEndDate($currentDate, $intervalType);
561
+
562
+                if ($periodEnd->gt($endDate)) {
563
+                    $periodEnd = $endDate->copy();
564
+                }
565
+
566
+                $periodLabel = $this->determinePeriod($periodStart, $intervalType);
567
+
568
+                // Calculate the proportional amount from the source budget
569
+                $weightedAmount = 0;
570
+
571
+                foreach ($allocationsByRange as $allocation) {
572
+                    // Find overlapping days between new period and source allocation
573
+                    $overlapStart = max($periodStart, $allocation['start']);
574
+                    $overlapEnd = min($periodEnd, $allocation['end']);
575
+
576
+                    if ($overlapStart <= $overlapEnd) {
577
+                        // Calculate overlapping days
578
+                        $overlapDays = $overlapStart->diffInDays($overlapEnd) + 1;
579
+                        $allocationTotalDays = $allocation['start']->diffInDays($allocation['end']) + 1;
580
+
581
+                        // Calculate proportional amount based on days
582
+                        $proportion = $overlapDays / $allocationTotalDays;
583
+                        $proportionalAmount = (int) ($allocation['amount'] * $proportion);
584
+
585
+                        $weightedAmount += $proportionalAmount;
586
+                    }
587
+                }
588
+
589
+                // Assign the calculated amount to the period
590
+                if (array_key_exists($periodLabel, $amounts)) {
591
+                    $amounts[$periodLabel] = $weightedAmount;
592
+                }
593
+
594
+                // Move to the next period
595
+                $currentDate = $periodEnd->copy()->addDay();
596
+            }
406
         }
597
         }
407
 
598
 
408
         return $amounts;
599
         return $amounts;

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

4
 
4
 
5
 use App\Enums\Accounting\BudgetIntervalType;
5
 use App\Enums\Accounting\BudgetIntervalType;
6
 use App\Filament\Company\Resources\Accounting\BudgetResource;
6
 use App\Filament\Company\Resources\Accounting\BudgetResource;
7
+use App\Filament\Forms\Components\CustomTableRepeater;
7
 use App\Models\Accounting\Budget;
8
 use App\Models\Accounting\Budget;
8
 use App\Models\Accounting\BudgetAllocation;
9
 use App\Models\Accounting\BudgetAllocation;
9
 use App\Models\Accounting\BudgetItem;
10
 use App\Models\Accounting\BudgetItem;
11
+use Awcodes\TableRepeater\Header;
10
 use Filament\Actions;
12
 use Filament\Actions;
13
+use Filament\Forms;
14
+use Filament\Forms\Form;
11
 use Filament\Resources\Pages\EditRecord;
15
 use Filament\Resources\Pages\EditRecord;
16
+use Filament\Support\Enums\Alignment;
17
+use Filament\Support\Enums\MaxWidth;
18
+use Filament\Support\RawJs;
12
 use Illuminate\Database\Eloquent\Model;
19
 use Illuminate\Database\Eloquent\Model;
13
 use Illuminate\Support\Carbon;
20
 use Illuminate\Support\Carbon;
14
 
21
 
24
         ];
31
         ];
25
     }
32
     }
26
 
33
 
27
-    protected function mutateFormDataBeforeFill(array $data): array
34
+    public function getMaxContentWidth(): MaxWidth | string | null
35
+    {
36
+        return 'max-w-8xl';
37
+    }
38
+
39
+    public function form(Form $form): Form
28
     {
40
     {
29
         /** @var Budget $budget */
41
         /** @var Budget $budget */
30
         $budget = $this->record;
42
         $budget = $this->record;
43
+        $periods = $budget->getPeriods();
44
+
45
+        $headers = [
46
+            Header::make('Account')
47
+                ->label('Account')
48
+                ->width('200px'),
49
+        ];
31
 
50
 
32
-        $data['budgetItems'] = $budget->budgetItems->map(function (BudgetItem $budgetItem) {
33
-            return [
34
-                'id' => $budgetItem->id,
35
-                'account_id' => $budgetItem->account_id,
36
-                'total_amount' => $budgetItem->allocations->sum('amount'), // Calculate total dynamically
37
-                'amounts' => $budgetItem->allocations->mapWithKeys(static function (BudgetAllocation $allocation) {
38
-                    return [$allocation->period => $allocation->amount]; // Use the correct period label
39
-                })->toArray(),
40
-            ];
41
-        })->toArray();
42
-
43
-        return $data;
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
+        ]);
44
     }
104
     }
45
 
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
+
46
     protected function handleRecordUpdate(Model $record, array $data): Model
125
     protected function handleRecordUpdate(Model $record, array $data): Model
47
     {
126
     {
48
         /** @var Budget $budget */
127
         /** @var Budget $budget */

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

2
 
2
 
3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\RelationManagers;
3
 namespace App\Filament\Company\Resources\Accounting\BudgetResource\RelationManagers;
4
 
4
 
5
+use App\Models\Accounting\BudgetAllocation;
6
+use Filament\Forms\Components\Grid;
7
+use Filament\Forms\Components\TextInput;
5
 use Filament\Resources\RelationManagers\RelationManager;
8
 use Filament\Resources\RelationManagers\RelationManager;
6
 use Filament\Tables;
9
 use Filament\Tables;
7
 use Filament\Tables\Table;
10
 use Filament\Tables\Table;
36
                 // Tables\Actions\CreateAction::make(),
39
                 // Tables\Actions\CreateAction::make(),
37
             ])
40
             ])
38
             ->actions([
41
             ->actions([
39
-                // Tables\Actions\EditAction::make(),
40
-                // Tables\Actions\DeleteAction::make(),
42
+                Tables\Actions\Action::make('editAllocations')
43
+                    ->label('Edit Allocations')
44
+                    ->icon('heroicon-o-pencil')
45
+                    ->modalHeading(fn ($record) => "Edit Allocations for {$record->account->name}")
46
+                    ->modalWidth('xl')
47
+                    ->form(function ($record) {
48
+                        $fields = [];
49
+
50
+                        // Get allocations ordered by date
51
+                        $allocations = $record->allocations()->orderBy('start_date')->get();
52
+
53
+                        foreach ($allocations as $allocation) {
54
+                            $fields[] = TextInput::make("allocations.{$allocation->id}")
55
+                                ->label($allocation->period)
56
+                                ->numeric()
57
+                                ->default(function () use ($allocation) {
58
+                                    return $allocation->amount;
59
+                                })
60
+                                ->prefix('$')
61
+                                ->live(debounce: 500)
62
+                                ->afterStateUpdated(function (TextInput $component, $state) {
63
+                                    // Format the value as needed
64
+                                    $component->state(number_format($state, 2, '.', ''));
65
+                                });
66
+                        }
67
+
68
+                        return [
69
+                            Grid::make()
70
+                                ->schema($fields)
71
+                                ->columns(3),
72
+                        ];
73
+                    })
74
+                    ->action(function (array $data, $record) {
75
+                        foreach ($data['allocations'] as $allocationId => $amount) {
76
+                            BudgetAllocation::find($allocationId)->update([
77
+                                'amount' => $amount,
78
+                            ]);
79
+                        }
80
+                    }),
41
             ])
81
             ])
42
             ->bulkActions([
82
             ->bulkActions([
43
                 //                Tables\Actions\BulkActionGroup::make([
83
                 //                Tables\Actions\BulkActionGroup::make([

+ 38
- 0
app/Filament/Forms/Components/CustomTableRepeater.php 查看文件

1
+<?php
2
+
3
+namespace App\Filament\Forms\Components;
4
+
5
+use Awcodes\TableRepeater\Components\TableRepeater;
6
+use Closure;
7
+
8
+class CustomTableRepeater extends TableRepeater
9
+{
10
+    protected bool | Closure | null $spreadsheet = null;
11
+
12
+    public function spreadsheet(bool | Closure $condition = true): static
13
+    {
14
+        $this->spreadsheet = $condition;
15
+
16
+        return $this;
17
+    }
18
+
19
+    public function isSpreadsheet(): bool
20
+    {
21
+        return $this->evaluate($this->spreadsheet) ?? false;
22
+    }
23
+
24
+    protected function setUp(): void
25
+    {
26
+        parent::setUp();
27
+
28
+        $this->extraAttributes(function (): array {
29
+            $attributes = [];
30
+
31
+            if ($this->isSpreadsheet()) {
32
+                $attributes['class'] = 'is-spreadsheet';
33
+            }
34
+
35
+            return $attributes;
36
+        });
37
+    }
38
+}

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

57
         return $this->hasManyThrough(BudgetAllocation::class, BudgetItem::class);
57
         return $this->hasManyThrough(BudgetAllocation::class, BudgetItem::class);
58
     }
58
     }
59
 
59
 
60
+    /**
61
+     * Get all periods for this budget in chronological order.
62
+     *
63
+     * @return array
64
+     */
65
+    public function getPeriods(): array
66
+    {
67
+        return $this->budgetItems()
68
+            ->with('allocations')
69
+            ->get()
70
+            ->flatMap(fn ($item) => $item->allocations)
71
+            ->sortBy('start_date')
72
+            ->pluck('period')
73
+            ->unique()
74
+            ->values()
75
+            ->toArray();
76
+    }
77
+
78
+
60
     public function isDraft(): bool
79
     public function isDraft(): bool
61
     {
80
     {
62
         return $this->status === BudgetStatus::Draft;
81
         return $this->status === BudgetStatus::Draft;

+ 1
- 2
composer.json 查看文件

62
             "@php artisan filament:upgrade"
62
             "@php artisan filament:upgrade"
63
         ],
63
         ],
64
         "post-update-cmd": [
64
         "post-update-cmd": [
65
-            "@php artisan vendor:publish --tag=laravel-assets --ansi --force",
66
-            "npm up && npm run build || echo \"Skipping npm update and build (npm not available)\""
65
+            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
67
         ],
66
         ],
68
         "post-root-package-install": [
67
         "post-root-package-install": [
69
             "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
68
             "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""

+ 82
- 81
composer.lock 查看文件

302
         },
302
         },
303
         {
303
         {
304
             "name": "anourvalar/eloquent-serialize",
304
             "name": "anourvalar/eloquent-serialize",
305
-            "version": "1.2.29",
305
+            "version": "1.3.0",
306
             "source": {
306
             "source": {
307
                 "type": "git",
307
                 "type": "git",
308
                 "url": "https://github.com/AnourValar/eloquent-serialize.git",
308
                 "url": "https://github.com/AnourValar/eloquent-serialize.git",
309
-                "reference": "0919c91e548d01261308fd54d27fc05a83c79d03"
309
+                "reference": "91188f82c5ec2842a5469fca6d7d64baa37ea593"
310
             },
310
             },
311
             "dist": {
311
             "dist": {
312
                 "type": "zip",
312
                 "type": "zip",
313
-                "url": "https://api.github.com/repos/AnourValar/eloquent-serialize/zipball/0919c91e548d01261308fd54d27fc05a83c79d03",
314
-                "reference": "0919c91e548d01261308fd54d27fc05a83c79d03",
313
+                "url": "https://api.github.com/repos/AnourValar/eloquent-serialize/zipball/91188f82c5ec2842a5469fca6d7d64baa37ea593",
314
+                "reference": "91188f82c5ec2842a5469fca6d7d64baa37ea593",
315
                 "shasum": ""
315
                 "shasum": ""
316
             },
316
             },
317
             "require": {
317
             "require": {
362
             ],
362
             ],
363
             "support": {
363
             "support": {
364
                 "issues": "https://github.com/AnourValar/eloquent-serialize/issues",
364
                 "issues": "https://github.com/AnourValar/eloquent-serialize/issues",
365
-                "source": "https://github.com/AnourValar/eloquent-serialize/tree/1.2.29"
365
+                "source": "https://github.com/AnourValar/eloquent-serialize/tree/1.3.0"
366
             },
366
             },
367
-            "time": "2025-02-25T05:18:46+00:00"
367
+            "time": "2025-03-22T08:49:12+00:00"
368
         },
368
         },
369
         {
369
         {
370
             "name": "awcodes/filament-table-repeater",
370
             "name": "awcodes/filament-table-repeater",
497
         },
497
         },
498
         {
498
         {
499
             "name": "aws/aws-sdk-php",
499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.342.8",
500
+            "version": "3.342.11",
501
             "source": {
501
             "source": {
502
                 "type": "git",
502
                 "type": "git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "d8279a481cf87482a1fb74784f76e4160efcce90"
504
+                "reference": "e0afed3d0e2c89362f6c9b6bf8f278b04a4858b6"
505
             },
505
             },
506
             "dist": {
506
             "dist": {
507
                 "type": "zip",
507
                 "type": "zip",
508
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/d8279a481cf87482a1fb74784f76e4160efcce90",
509
-                "reference": "d8279a481cf87482a1fb74784f76e4160efcce90",
508
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/e0afed3d0e2c89362f6c9b6bf8f278b04a4858b6",
509
+                "reference": "e0afed3d0e2c89362f6c9b6bf8f278b04a4858b6",
510
                 "shasum": ""
510
                 "shasum": ""
511
             },
511
             },
512
             "require": {
512
             "require": {
588
             "support": {
588
             "support": {
589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
591
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.342.8"
591
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.342.11"
592
             },
592
             },
593
-            "time": "2025-03-18T18:15:40+00:00"
593
+            "time": "2025-03-21T18:11:10+00:00"
594
         },
594
         },
595
         {
595
         {
596
             "name": "aws/aws-sdk-php-laravel",
596
             "name": "aws/aws-sdk-php-laravel",
1666
         },
1666
         },
1667
         {
1667
         {
1668
             "name": "egulias/email-validator",
1668
             "name": "egulias/email-validator",
1669
-            "version": "4.0.3",
1669
+            "version": "4.0.4",
1670
             "source": {
1670
             "source": {
1671
                 "type": "git",
1671
                 "type": "git",
1672
                 "url": "https://github.com/egulias/EmailValidator.git",
1672
                 "url": "https://github.com/egulias/EmailValidator.git",
1673
-                "reference": "b115554301161fa21467629f1e1391c1936de517"
1673
+                "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa"
1674
             },
1674
             },
1675
             "dist": {
1675
             "dist": {
1676
                 "type": "zip",
1676
                 "type": "zip",
1677
-                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/b115554301161fa21467629f1e1391c1936de517",
1678
-                "reference": "b115554301161fa21467629f1e1391c1936de517",
1677
+                "url": "https://api.github.com/repos/egulias/EmailValidator/zipball/d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
1678
+                "reference": "d42c8731f0624ad6bdc8d3e5e9a4524f68801cfa",
1679
                 "shasum": ""
1679
                 "shasum": ""
1680
             },
1680
             },
1681
             "require": {
1681
             "require": {
1721
             ],
1721
             ],
1722
             "support": {
1722
             "support": {
1723
                 "issues": "https://github.com/egulias/EmailValidator/issues",
1723
                 "issues": "https://github.com/egulias/EmailValidator/issues",
1724
-                "source": "https://github.com/egulias/EmailValidator/tree/4.0.3"
1724
+                "source": "https://github.com/egulias/EmailValidator/tree/4.0.4"
1725
             },
1725
             },
1726
             "funding": [
1726
             "funding": [
1727
                 {
1727
                 {
1729
                     "type": "github"
1729
                     "type": "github"
1730
                 }
1730
                 }
1731
             ],
1731
             ],
1732
-            "time": "2024-12-27T00:36:43+00:00"
1732
+            "time": "2025-03-06T22:45:56+00:00"
1733
         },
1733
         },
1734
         {
1734
         {
1735
             "name": "filament/actions",
1735
             "name": "filament/actions",
1736
-            "version": "v3.3.4",
1736
+            "version": "v3.3.5",
1737
             "source": {
1737
             "source": {
1738
                 "type": "git",
1738
                 "type": "git",
1739
                 "url": "https://github.com/filamentphp/actions.git",
1739
                 "url": "https://github.com/filamentphp/actions.git",
1740
-                "reference": "dd738da99c8032986e7628b09f185d8f0db7850a"
1740
+                "reference": "66fc3526f39ec09156928fcdf2cd4cb44e97efc4"
1741
             },
1741
             },
1742
             "dist": {
1742
             "dist": {
1743
                 "type": "zip",
1743
                 "type": "zip",
1744
-                "url": "https://api.github.com/repos/filamentphp/actions/zipball/dd738da99c8032986e7628b09f185d8f0db7850a",
1745
-                "reference": "dd738da99c8032986e7628b09f185d8f0db7850a",
1744
+                "url": "https://api.github.com/repos/filamentphp/actions/zipball/66fc3526f39ec09156928fcdf2cd4cb44e97efc4",
1745
+                "reference": "66fc3526f39ec09156928fcdf2cd4cb44e97efc4",
1746
                 "shasum": ""
1746
                 "shasum": ""
1747
             },
1747
             },
1748
             "require": {
1748
             "require": {
1782
                 "issues": "https://github.com/filamentphp/filament/issues",
1782
                 "issues": "https://github.com/filamentphp/filament/issues",
1783
                 "source": "https://github.com/filamentphp/filament"
1783
                 "source": "https://github.com/filamentphp/filament"
1784
             },
1784
             },
1785
-            "time": "2025-03-11T16:33:01+00:00"
1785
+            "time": "2025-03-20T09:28:45+00:00"
1786
         },
1786
         },
1787
         {
1787
         {
1788
             "name": "filament/filament",
1788
             "name": "filament/filament",
1789
-            "version": "v3.3.4",
1789
+            "version": "v3.3.5",
1790
             "source": {
1790
             "source": {
1791
                 "type": "git",
1791
                 "type": "git",
1792
                 "url": "https://github.com/filamentphp/panels.git",
1792
                 "url": "https://github.com/filamentphp/panels.git",
1793
-                "reference": "ae0288f28054c88a9f36f666f4752660ce72cb08"
1793
+                "reference": "173e4695ed4c7318e26b11dafb79294f56775d55"
1794
             },
1794
             },
1795
             "dist": {
1795
             "dist": {
1796
                 "type": "zip",
1796
                 "type": "zip",
1797
-                "url": "https://api.github.com/repos/filamentphp/panels/zipball/ae0288f28054c88a9f36f666f4752660ce72cb08",
1798
-                "reference": "ae0288f28054c88a9f36f666f4752660ce72cb08",
1797
+                "url": "https://api.github.com/repos/filamentphp/panels/zipball/173e4695ed4c7318e26b11dafb79294f56775d55",
1798
+                "reference": "173e4695ed4c7318e26b11dafb79294f56775d55",
1799
                 "shasum": ""
1799
                 "shasum": ""
1800
             },
1800
             },
1801
             "require": {
1801
             "require": {
1847
                 "issues": "https://github.com/filamentphp/filament/issues",
1847
                 "issues": "https://github.com/filamentphp/filament/issues",
1848
                 "source": "https://github.com/filamentphp/filament"
1848
                 "source": "https://github.com/filamentphp/filament"
1849
             },
1849
             },
1850
-            "time": "2025-03-11T16:33:11+00:00"
1850
+            "time": "2025-03-20T09:28:52+00:00"
1851
         },
1851
         },
1852
         {
1852
         {
1853
             "name": "filament/forms",
1853
             "name": "filament/forms",
1854
-            "version": "v3.3.4",
1854
+            "version": "v3.3.5",
1855
             "source": {
1855
             "source": {
1856
                 "type": "git",
1856
                 "type": "git",
1857
                 "url": "https://github.com/filamentphp/forms.git",
1857
                 "url": "https://github.com/filamentphp/forms.git",
1858
-                "reference": "647f03f3d7a15a9ce71e803926b2e89f88693e8e"
1858
+                "reference": "e98beabb94e290b0edd837ffa4e6f821df5fcc89"
1859
             },
1859
             },
1860
             "dist": {
1860
             "dist": {
1861
                 "type": "zip",
1861
                 "type": "zip",
1862
-                "url": "https://api.github.com/repos/filamentphp/forms/zipball/647f03f3d7a15a9ce71e803926b2e89f88693e8e",
1863
-                "reference": "647f03f3d7a15a9ce71e803926b2e89f88693e8e",
1862
+                "url": "https://api.github.com/repos/filamentphp/forms/zipball/e98beabb94e290b0edd837ffa4e6f821df5fcc89",
1863
+                "reference": "e98beabb94e290b0edd837ffa4e6f821df5fcc89",
1864
                 "shasum": ""
1864
                 "shasum": ""
1865
             },
1865
             },
1866
             "require": {
1866
             "require": {
1903
                 "issues": "https://github.com/filamentphp/filament/issues",
1903
                 "issues": "https://github.com/filamentphp/filament/issues",
1904
                 "source": "https://github.com/filamentphp/filament"
1904
                 "source": "https://github.com/filamentphp/filament"
1905
             },
1905
             },
1906
-            "time": "2025-03-11T16:33:00+00:00"
1906
+            "time": "2025-03-20T09:29:10+00:00"
1907
         },
1907
         },
1908
         {
1908
         {
1909
             "name": "filament/infolists",
1909
             "name": "filament/infolists",
1910
-            "version": "v3.3.4",
1910
+            "version": "v3.3.5",
1911
             "source": {
1911
             "source": {
1912
                 "type": "git",
1912
                 "type": "git",
1913
                 "url": "https://github.com/filamentphp/infolists.git",
1913
                 "url": "https://github.com/filamentphp/infolists.git",
1914
-                "reference": "a1355ace7341e4e44d31dc30cfc6129bd61c7d8f"
1914
+                "reference": "cdf80f01fd822cbd7830dbb5892a1d1245e237fa"
1915
             },
1915
             },
1916
             "dist": {
1916
             "dist": {
1917
                 "type": "zip",
1917
                 "type": "zip",
1918
-                "url": "https://api.github.com/repos/filamentphp/infolists/zipball/a1355ace7341e4e44d31dc30cfc6129bd61c7d8f",
1919
-                "reference": "a1355ace7341e4e44d31dc30cfc6129bd61c7d8f",
1918
+                "url": "https://api.github.com/repos/filamentphp/infolists/zipball/cdf80f01fd822cbd7830dbb5892a1d1245e237fa",
1919
+                "reference": "cdf80f01fd822cbd7830dbb5892a1d1245e237fa",
1920
                 "shasum": ""
1920
                 "shasum": ""
1921
             },
1921
             },
1922
             "require": {
1922
             "require": {
1954
                 "issues": "https://github.com/filamentphp/filament/issues",
1954
                 "issues": "https://github.com/filamentphp/filament/issues",
1955
                 "source": "https://github.com/filamentphp/filament"
1955
                 "source": "https://github.com/filamentphp/filament"
1956
             },
1956
             },
1957
-            "time": "2025-03-11T16:33:01+00:00"
1957
+            "time": "2025-03-20T09:28:28+00:00"
1958
         },
1958
         },
1959
         {
1959
         {
1960
             "name": "filament/notifications",
1960
             "name": "filament/notifications",
1961
-            "version": "v3.3.4",
1961
+            "version": "v3.3.5",
1962
             "source": {
1962
             "source": {
1963
                 "type": "git",
1963
                 "type": "git",
1964
                 "url": "https://github.com/filamentphp/notifications.git",
1964
                 "url": "https://github.com/filamentphp/notifications.git",
2010
         },
2010
         },
2011
         {
2011
         {
2012
             "name": "filament/support",
2012
             "name": "filament/support",
2013
-            "version": "v3.3.4",
2013
+            "version": "v3.3.5",
2014
             "source": {
2014
             "source": {
2015
                 "type": "git",
2015
                 "type": "git",
2016
                 "url": "https://github.com/filamentphp/support.git",
2016
                 "url": "https://github.com/filamentphp/support.git",
2017
-                "reference": "fb5ff99b8f7559815434c109d505c12c141510da"
2017
+                "reference": "cf3fa32f6e419ca768e88ac061dc3c47d01ed401"
2018
             },
2018
             },
2019
             "dist": {
2019
             "dist": {
2020
                 "type": "zip",
2020
                 "type": "zip",
2021
-                "url": "https://api.github.com/repos/filamentphp/support/zipball/fb5ff99b8f7559815434c109d505c12c141510da",
2022
-                "reference": "fb5ff99b8f7559815434c109d505c12c141510da",
2021
+                "url": "https://api.github.com/repos/filamentphp/support/zipball/cf3fa32f6e419ca768e88ac061dc3c47d01ed401",
2022
+                "reference": "cf3fa32f6e419ca768e88ac061dc3c47d01ed401",
2023
                 "shasum": ""
2023
                 "shasum": ""
2024
             },
2024
             },
2025
             "require": {
2025
             "require": {
2065
                 "issues": "https://github.com/filamentphp/filament/issues",
2065
                 "issues": "https://github.com/filamentphp/filament/issues",
2066
                 "source": "https://github.com/filamentphp/filament"
2066
                 "source": "https://github.com/filamentphp/filament"
2067
             },
2067
             },
2068
-            "time": "2025-03-05T09:26:25+00:00"
2068
+            "time": "2025-03-20T09:29:02+00:00"
2069
         },
2069
         },
2070
         {
2070
         {
2071
             "name": "filament/tables",
2071
             "name": "filament/tables",
2072
-            "version": "v3.3.4",
2072
+            "version": "v3.3.5",
2073
             "source": {
2073
             "source": {
2074
                 "type": "git",
2074
                 "type": "git",
2075
                 "url": "https://github.com/filamentphp/tables.git",
2075
                 "url": "https://github.com/filamentphp/tables.git",
2076
-                "reference": "8f9bb6449c7ff74e234848f336161af5a64561c6"
2076
+                "reference": "b153de29ffe0cd5ef77d5c09a871c45f4d04b667"
2077
             },
2077
             },
2078
             "dist": {
2078
             "dist": {
2079
                 "type": "zip",
2079
                 "type": "zip",
2080
-                "url": "https://api.github.com/repos/filamentphp/tables/zipball/8f9bb6449c7ff74e234848f336161af5a64561c6",
2081
-                "reference": "8f9bb6449c7ff74e234848f336161af5a64561c6",
2080
+                "url": "https://api.github.com/repos/filamentphp/tables/zipball/b153de29ffe0cd5ef77d5c09a871c45f4d04b667",
2081
+                "reference": "b153de29ffe0cd5ef77d5c09a871c45f4d04b667",
2082
                 "shasum": ""
2082
                 "shasum": ""
2083
             },
2083
             },
2084
             "require": {
2084
             "require": {
2117
                 "issues": "https://github.com/filamentphp/filament/issues",
2117
                 "issues": "https://github.com/filamentphp/filament/issues",
2118
                 "source": "https://github.com/filamentphp/filament"
2118
                 "source": "https://github.com/filamentphp/filament"
2119
             },
2119
             },
2120
-            "time": "2025-03-11T16:33:31+00:00"
2120
+            "time": "2025-03-20T09:28:46+00:00"
2121
         },
2121
         },
2122
         {
2122
         {
2123
             "name": "filament/widgets",
2123
             "name": "filament/widgets",
2124
-            "version": "v3.3.4",
2124
+            "version": "v3.3.5",
2125
             "source": {
2125
             "source": {
2126
                 "type": "git",
2126
                 "type": "git",
2127
                 "url": "https://github.com/filamentphp/widgets.git",
2127
                 "url": "https://github.com/filamentphp/widgets.git",
6120
         },
6120
         },
6121
         {
6121
         {
6122
             "name": "ramsey/collection",
6122
             "name": "ramsey/collection",
6123
-            "version": "2.1.0",
6123
+            "version": "2.1.1",
6124
             "source": {
6124
             "source": {
6125
                 "type": "git",
6125
                 "type": "git",
6126
                 "url": "https://github.com/ramsey/collection.git",
6126
                 "url": "https://github.com/ramsey/collection.git",
6127
-                "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109"
6127
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2"
6128
             },
6128
             },
6129
             "dist": {
6129
             "dist": {
6130
                 "type": "zip",
6130
                 "type": "zip",
6131
-                "url": "https://api.github.com/repos/ramsey/collection/zipball/3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
6132
-                "reference": "3c5990b8a5e0b79cd1cf11c2dc1229e58e93f109",
6131
+                "url": "https://api.github.com/repos/ramsey/collection/zipball/344572933ad0181accbf4ba763e85a0306a8c5e2",
6132
+                "reference": "344572933ad0181accbf4ba763e85a0306a8c5e2",
6133
                 "shasum": ""
6133
                 "shasum": ""
6134
             },
6134
             },
6135
             "require": {
6135
             "require": {
6190
             ],
6190
             ],
6191
             "support": {
6191
             "support": {
6192
                 "issues": "https://github.com/ramsey/collection/issues",
6192
                 "issues": "https://github.com/ramsey/collection/issues",
6193
-                "source": "https://github.com/ramsey/collection/tree/2.1.0"
6193
+                "source": "https://github.com/ramsey/collection/tree/2.1.1"
6194
             },
6194
             },
6195
-            "time": "2025-03-02T04:48:29+00:00"
6195
+            "time": "2025-03-22T05:38:12+00:00"
6196
         },
6196
         },
6197
         {
6197
         {
6198
             "name": "ramsey/uuid",
6198
             "name": "ramsey/uuid",
6484
         },
6484
         },
6485
         {
6485
         {
6486
             "name": "spatie/laravel-package-tools",
6486
             "name": "spatie/laravel-package-tools",
6487
-            "version": "1.19.0",
6487
+            "version": "1.91.1",
6488
             "source": {
6488
             "source": {
6489
                 "type": "git",
6489
                 "type": "git",
6490
                 "url": "https://github.com/spatie/laravel-package-tools.git",
6490
                 "url": "https://github.com/spatie/laravel-package-tools.git",
6491
-                "reference": "1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa"
6491
+                "reference": "b0b509b9b01d77caa431ce9af3a706bc678e09c9"
6492
             },
6492
             },
6493
             "dist": {
6493
             "dist": {
6494
                 "type": "zip",
6494
                 "type": "zip",
6495
-                "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa",
6496
-                "reference": "1c9c30ac6a6576b8d15c6c37b6cf23d748df2faa",
6495
+                "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/b0b509b9b01d77caa431ce9af3a706bc678e09c9",
6496
+                "reference": "b0b509b9b01d77caa431ce9af3a706bc678e09c9",
6497
                 "shasum": ""
6497
                 "shasum": ""
6498
             },
6498
             },
6499
             "require": {
6499
             "require": {
6532
             ],
6532
             ],
6533
             "support": {
6533
             "support": {
6534
                 "issues": "https://github.com/spatie/laravel-package-tools/issues",
6534
                 "issues": "https://github.com/spatie/laravel-package-tools/issues",
6535
-                "source": "https://github.com/spatie/laravel-package-tools/tree/1.19.0"
6535
+                "source": "https://github.com/spatie/laravel-package-tools/tree/1.91.1"
6536
             },
6536
             },
6537
             "funding": [
6537
             "funding": [
6538
                 {
6538
                 {
6540
                     "type": "github"
6540
                     "type": "github"
6541
                 }
6541
                 }
6542
             ],
6542
             ],
6543
-            "time": "2025-02-06T14:58:20+00:00"
6543
+            "time": "2025-03-21T09:50:49+00:00"
6544
         },
6544
         },
6545
         {
6545
         {
6546
             "name": "squirephp/model",
6546
             "name": "squirephp/model",
9645
         },
9645
         },
9646
         {
9646
         {
9647
             "name": "jean85/pretty-package-versions",
9647
             "name": "jean85/pretty-package-versions",
9648
-            "version": "2.1.0",
9648
+            "version": "2.1.1",
9649
             "source": {
9649
             "source": {
9650
                 "type": "git",
9650
                 "type": "git",
9651
                 "url": "https://github.com/Jean85/pretty-package-versions.git",
9651
                 "url": "https://github.com/Jean85/pretty-package-versions.git",
9652
-                "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10"
9652
+                "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a"
9653
             },
9653
             },
9654
             "dist": {
9654
             "dist": {
9655
                 "type": "zip",
9655
                 "type": "zip",
9656
-                "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
9657
-                "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10",
9656
+                "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a",
9657
+                "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a",
9658
                 "shasum": ""
9658
                 "shasum": ""
9659
             },
9659
             },
9660
             "require": {
9660
             "require": {
9664
             "require-dev": {
9664
             "require-dev": {
9665
                 "friendsofphp/php-cs-fixer": "^3.2",
9665
                 "friendsofphp/php-cs-fixer": "^3.2",
9666
                 "jean85/composer-provided-replaced-stub-package": "^1.0",
9666
                 "jean85/composer-provided-replaced-stub-package": "^1.0",
9667
-                "phpstan/phpstan": "^1.4",
9667
+                "phpstan/phpstan": "^2.0",
9668
                 "phpunit/phpunit": "^7.5|^8.5|^9.6",
9668
                 "phpunit/phpunit": "^7.5|^8.5|^9.6",
9669
+                "rector/rector": "^2.0",
9669
                 "vimeo/psalm": "^4.3 || ^5.0"
9670
                 "vimeo/psalm": "^4.3 || ^5.0"
9670
             },
9671
             },
9671
             "type": "library",
9672
             "type": "library",
9698
             ],
9699
             ],
9699
             "support": {
9700
             "support": {
9700
                 "issues": "https://github.com/Jean85/pretty-package-versions/issues",
9701
                 "issues": "https://github.com/Jean85/pretty-package-versions/issues",
9701
-                "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0"
9702
+                "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1"
9702
             },
9703
             },
9703
-            "time": "2024-11-18T16:19:46+00:00"
9704
+            "time": "2025-03-19T14:43:43+00:00"
9704
         },
9705
         },
9705
         {
9706
         {
9706
             "name": "laravel/pint",
9707
             "name": "laravel/pint",
11413
         },
11414
         },
11414
         {
11415
         {
11415
             "name": "sebastian/code-unit",
11416
             "name": "sebastian/code-unit",
11416
-            "version": "3.0.2",
11417
+            "version": "3.0.3",
11417
             "source": {
11418
             "source": {
11418
                 "type": "git",
11419
                 "type": "git",
11419
                 "url": "https://github.com/sebastianbergmann/code-unit.git",
11420
                 "url": "https://github.com/sebastianbergmann/code-unit.git",
11420
-                "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca"
11421
+                "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64"
11421
             },
11422
             },
11422
             "dist": {
11423
             "dist": {
11423
                 "type": "zip",
11424
                 "type": "zip",
11424
-                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca",
11425
-                "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca",
11425
+                "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64",
11426
+                "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64",
11426
                 "shasum": ""
11427
                 "shasum": ""
11427
             },
11428
             },
11428
             "require": {
11429
             "require": {
11458
             "support": {
11459
             "support": {
11459
                 "issues": "https://github.com/sebastianbergmann/code-unit/issues",
11460
                 "issues": "https://github.com/sebastianbergmann/code-unit/issues",
11460
                 "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
11461
                 "security": "https://github.com/sebastianbergmann/code-unit/security/policy",
11461
-                "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2"
11462
+                "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3"
11462
             },
11463
             },
11463
             "funding": [
11464
             "funding": [
11464
                 {
11465
                 {
11466
                     "type": "github"
11467
                     "type": "github"
11467
                 }
11468
                 }
11468
             ],
11469
             ],
11469
-            "time": "2024-12-12T09:59:06+00:00"
11470
+            "time": "2025-03-19T07:56:08+00:00"
11470
         },
11471
         },
11471
         {
11472
         {
11472
             "name": "sebastian/code-unit-reverse-lookup",
11473
             "name": "sebastian/code-unit-reverse-lookup",
12800
         },
12801
         },
12801
         {
12802
         {
12802
             "name": "spatie/ray",
12803
             "name": "spatie/ray",
12803
-            "version": "1.41.5",
12804
+            "version": "1.41.6",
12804
             "source": {
12805
             "source": {
12805
                 "type": "git",
12806
                 "type": "git",
12806
                 "url": "https://github.com/spatie/ray.git",
12807
                 "url": "https://github.com/spatie/ray.git",
12807
-                "reference": "9d078f04ffa32ad543a20716844ec343fdd7d856"
12808
+                "reference": "ae6e32a54a901544a3d70b12b865900bc240f71c"
12808
             },
12809
             },
12809
             "dist": {
12810
             "dist": {
12810
                 "type": "zip",
12811
                 "type": "zip",
12811
-                "url": "https://api.github.com/repos/spatie/ray/zipball/9d078f04ffa32ad543a20716844ec343fdd7d856",
12812
-                "reference": "9d078f04ffa32ad543a20716844ec343fdd7d856",
12812
+                "url": "https://api.github.com/repos/spatie/ray/zipball/ae6e32a54a901544a3d70b12b865900bc240f71c",
12813
+                "reference": "ae6e32a54a901544a3d70b12b865900bc240f71c",
12813
                 "shasum": ""
12814
                 "shasum": ""
12814
             },
12815
             },
12815
             "require": {
12816
             "require": {
12869
             ],
12870
             ],
12870
             "support": {
12871
             "support": {
12871
                 "issues": "https://github.com/spatie/ray/issues",
12872
                 "issues": "https://github.com/spatie/ray/issues",
12872
-                "source": "https://github.com/spatie/ray/tree/1.41.5"
12873
+                "source": "https://github.com/spatie/ray/tree/1.41.6"
12873
             },
12874
             },
12874
             "funding": [
12875
             "funding": [
12875
                 {
12876
                 {
12881
                     "type": "other"
12882
                     "type": "other"
12882
                 }
12883
                 }
12883
             ],
12884
             ],
12884
-            "time": "2025-02-14T12:51:43+00:00"
12885
+            "time": "2025-03-21T08:56:30+00:00"
12885
         },
12886
         },
12886
         {
12887
         {
12887
             "name": "staabm/side-effects-detector",
12888
             "name": "staabm/side-effects-detector",

+ 122
- 18
resources/css/filament/company/form-fields.css 查看文件

53
     cursor: pointer;
53
     cursor: pointer;
54
 }
54
 }
55
 
55
 
56
-/* Table Repeater Styles */
56
+/* Base horizontal scrolling for all TableRepeater components */
57
 .table-repeater-container {
57
 .table-repeater-container {
58
-    @apply rounded-none ring-0;
58
+    overflow-x: auto;
59
+    max-width: 100%;
60
+    -webkit-overflow-scrolling: touch;
59
 }
61
 }
60
 
62
 
61
-.table-repeater-component {
62
-    @apply space-y-10;
63
+.table-repeater-container:has(.choices.is-open) {
64
+    overflow: visible;
63
 }
65
 }
64
 
66
 
65
-.table-repeater-component ul {
66
-    @apply justify-start;
67
+.table-repeater-container table {
68
+    min-width: 100%;
69
+    width: max-content;
67
 }
70
 }
68
 
71
 
69
-.table-repeater-row {
70
-    @apply divide-x-0 !important;
72
+/* Excel/Spreadsheet styling */
73
+.is-spreadsheet .table-repeater-container {
74
+    border-radius: 0 !important;
75
+    border: 1px solid #e5e7eb !important;
76
+    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
77
+    @apply ring-0 !important;
71
 }
78
 }
72
 
79
 
73
-.table-repeater-column {
74
-    @apply py-2 !important;
80
+.is-spreadsheet .table-repeater-header {
81
+    background-color: #f8f9fa !important;
82
+    border-radius: 0 !important;
75
 }
83
 }
76
 
84
 
77
-.table-repeater-header {
78
-    @apply rounded-t-none !important;
85
+/* Fix for input alignment with headers */
86
+.is-spreadsheet .table-repeater-header-column,
87
+.is-spreadsheet .table-repeater-column {
88
+    padding: 6px 8px !important;
79
 }
89
 }
80
 
90
 
81
-.table-repeater-rows-wrapper {
82
-    @apply divide-gray-300 last:border-b last:border-gray-300 dark:divide-white/20 dark:last:border-white/20;
91
+/* Right-align all inputs */
92
+.is-spreadsheet .table-repeater-column input {
93
+    text-align: right !important;
94
+    width: 100% !important;
83
 }
95
 }
84
 
96
 
85
-.table-repeater-header tr {
86
-    @apply divide-x-0 text-base sm:text-sm sm:leading-6 !important;
97
+/* Remove any additional padding from input containers */
98
+.is-spreadsheet .fi-input-wrapper,
99
+.is-spreadsheet .fi-input {
100
+    padding: 0 !important;
101
+    width: 100% !important;
87
 }
102
 }
88
 
103
 
89
-.table-repeater-header-column {
90
-    @apply ps-3 pe-3 font-semibold bg-gray-200 dark:bg-gray-800 rounded-none !important;
104
+.is-spreadsheet .table-repeater-header-column {
105
+    border-radius: 0 !important;
106
+    border: 1px solid #e5e7eb !important;
107
+    background-color: #f8f9fa !important;
108
+    font-weight: 600 !important;
109
+    padding: 6px 8px !important;
110
+}
111
+
112
+.is-spreadsheet .table-repeater-column {
113
+    border: 1px solid #e5e7eb !important;
114
+    padding: 4px 8px !important;
115
+    position: relative !important;
116
+}
117
+
118
+/* Incorporate flat input styling from streamlined */
119
+.is-spreadsheet .fi-input-wrp,
120
+.is-spreadsheet .fi-fo-file-upload .filepond--root {
121
+    @apply ring-0 bg-transparent shadow-none !important;
122
+}
123
+
124
+.is-spreadsheet .fi-input-wrp input {
125
+    @apply bg-transparent border-0 shadow-none !important;
126
+}
127
+
128
+.is-spreadsheet .fi-select-trigger {
129
+    @apply ring-0 bg-transparent shadow-none !important;
130
+}
131
+
132
+/* Excel-like row styles */
133
+.is-spreadsheet .table-repeater-row:nth-child(even) {
134
+    background-color: #f9fafb;
135
+}
136
+
137
+/* Excel-like focus/selection styles */
138
+.is-spreadsheet .table-repeater-column:focus-within {
139
+    outline: 2px solid #2563eb !important;
140
+    outline-offset: -2px !important;
141
+    z-index: 1 !important;
142
+}
143
+
144
+.is-spreadsheet .fi-input-wrp:focus-within {
145
+    @apply ring-0 shadow-none !important;
146
+}
147
+
148
+.is-spreadsheet input:focus,
149
+.is-spreadsheet select:focus,
150
+.is-spreadsheet textarea:focus {
151
+    @apply ring-0 shadow-none !important;
152
+    outline: none !important;
153
+}
154
+
155
+/* Spacing for form controls */
156
+.is-spreadsheet .fi-fo-field-wrp:has(.fi-fo-checkbox-list),
157
+.is-spreadsheet .fi-fo-field-wrp:has(.fi-checkbox-input),
158
+.is-spreadsheet .fi-fo-field-wrp:has(.fi-fo-radio) {
159
+    @apply py-2 px-3 !important;
160
+}
161
+
162
+.is-spreadsheet .fi-fo-field-wrp:has(.fi-fo-toggle) {
163
+    @apply inline-block mt-1 !important;
164
+}
165
+
166
+/* Preserve responsive behavior */
167
+@media (max-width: theme('screens.sm')) {
168
+    .table-repeater-component.break-point-sm .table-repeater-container {
169
+        overflow-x: visible;
170
+    }
171
+}
172
+
173
+@media (max-width: theme('screens.md')) {
174
+    .table-repeater-component.break-point-md .table-repeater-container {
175
+        overflow-x: visible;
176
+    }
177
+}
178
+
179
+@media (max-width: theme('screens.lg')) {
180
+    .table-repeater-component.break-point-lg .table-repeater-container {
181
+        overflow-x: visible;
182
+    }
183
+}
184
+
185
+@media (max-width: theme('screens.xl')) {
186
+    .table-repeater-component.break-point-xl .table-repeater-container {
187
+        overflow-x: visible;
188
+    }
189
+}
190
+
191
+@media (max-width: theme('screens.2xl')) {
192
+    .table-repeater-component.break-point-2xl .table-repeater-container {
193
+        overflow-x: visible;
194
+    }
91
 }
195
 }
92
 
196
 

+ 12
- 10
resources/css/filament/company/modal.css 查看文件

1
 /* Journal Entry Modal Styles */
1
 /* Journal Entry Modal Styles */
2
 .fi-modal.fi-width-screen {
2
 .fi-modal.fi-width-screen {
3
-    .fi-modal-header {
4
-        @apply xl:px-80;
3
+    .fi-modal-window.journal-transaction-modal {
4
+        .fi-modal-header {
5
+            @apply xl:px-80;
5
 
6
 
6
-        .absolute.end-4.top-4 {
7
-            @apply xl:end-80;
7
+            .absolute.end-4.top-4 {
8
+                @apply xl:end-80;
9
+            }
8
         }
10
         }
9
-    }
10
 
11
 
11
-    .fi-modal-content {
12
-        @apply xl:px-80;
13
-    }
12
+        .fi-modal-content {
13
+            @apply xl:px-80;
14
+        }
14
 
15
 
15
-    .fi-modal-footer {
16
-        @apply xl:px-80;
16
+        .fi-modal-footer {
17
+            @apply xl:px-80;
18
+        }
17
     }
19
     }
18
 }
20
 }

正在加载...
取消
保存