瀏覽代碼

Merge pull request #199 from andrewdwallo/development-3.x

Development 3.x
3.x
Andrew Wallo 2 月之前
父節點
當前提交
1c3e02e06c
沒有連結到貢獻者的電子郵件帳戶。
共有 26 個檔案被更改,包括 148 行新增145 行删除
  1. 0
    1
      app/Concerns/HasTransactionAction.php
  2. 0
    3
      app/DTO/ReportDTO.php
  3. 13
    13
      app/Factories/ReportDateFactory.php
  4. 1
    6
      app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php
  5. 3
    33
      app/Filament/Company/Pages/Concerns/HasDeferredFiltersForm.php
  6. 4
    6
      app/Filament/Company/Pages/Reports/BaseReportPage.php
  7. 1
    4
      app/Filament/Forms/Components/CreateAdjustmentSelect.php
  8. 4
    4
      app/Filament/Forms/Components/DateRangeSelect.php
  9. 0
    2
      app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php
  10. 0
    3
      app/Models/Accounting/RecurringInvoice.php
  11. 1
    1
      app/Models/Setting/Localization.php
  12. 3
    4
      app/Providers/Filament/CompanyPanelProvider.php
  13. 16
    4
      app/Providers/MacroServiceProvider.php
  14. 3
    3
      app/Scopes/CurrentCompanyScope.php
  15. 13
    0
      app/Services/CompanySettingsService.php
  16. 10
    7
      app/Services/DateRangeService.php
  17. 7
    8
      app/Services/ReportService.php
  18. 13
    3
      resources/views/components/report-summary-section.blade.php
  19. 6
    4
      resources/views/filament/company/pages/reports/balance-sheet.blade.php
  20. 11
    7
      resources/views/filament/company/pages/reports/cash-flow-statement.blade.php
  21. 11
    7
      resources/views/filament/company/pages/reports/detailed-report.blade.php
  22. 11
    7
      resources/views/filament/company/pages/reports/income-statement.blade.php
  23. 6
    4
      resources/views/filament/company/pages/reports/trial-balance.blade.php
  24. 2
    2
      tests/Feature/Accounting/TransactionTest.php
  25. 6
    6
      tests/Feature/Reports/AccountBalancesReportTest.php
  26. 3
    3
      tests/Feature/Reports/TrialBalanceReportTest.php

+ 0
- 1
app/Concerns/HasTransactionAction.php 查看文件

@@ -83,7 +83,6 @@ trait HasTransactionAction
83 83
             ->schema([
84 84
                 Forms\Components\DatePicker::make('posted_at')
85 85
                     ->label('Date')
86
-                    ->native()
87 86
                     ->required(),
88 87
                 Forms\Components\TextInput::make('description')
89 88
                     ->label('Description'),

+ 0
- 3
app/DTO/ReportDTO.php 查看文件

@@ -7,9 +7,6 @@ use Illuminate\Support\Carbon;
7 7
 class ReportDTO
8 8
 {
9 9
     public function __construct(
10
-        /**
11
-         * @var AccountCategoryDTO[]
12
-         */
13 10
         public array $categories,
14 11
         public ?AccountBalanceDTO $overallTotal = null,
15 12
         public ?AgingBucketDTO $agingSummary = null,

+ 13
- 13
app/Factories/ReportDateFactory.php 查看文件

@@ -29,24 +29,24 @@ class ReportDateFactory
29 29
 
30 30
     protected function buildReportDates(): void
31 31
     {
32
-        $fiscalYearStartDate = Carbon::parse($this->company->locale->fiscalYearStartDate())->startOfDay();
33
-        $fiscalYearEndDate = Carbon::parse($this->company->locale->fiscalYearEndDate())->endOfDay();
34
-        $defaultDateRange = 'FY-' . now()->year;
35
-        $defaultStartDate = $fiscalYearStartDate->startOfDay();
36
-        $defaultEndDate = $fiscalYearEndDate->isFuture() ? now()->endOfDay() : $fiscalYearEndDate->endOfDay();
32
+        $companyFyStartDate = Carbon::parse($this->company->locale->fiscalYearStartDate());
33
+        $companyFyEndDate = Carbon::parse($this->company->locale->fiscalYearEndDate())->endOfDay();
34
+        $dateRange = 'FY-' . company_today()->year;
35
+        $startDate = $companyFyStartDate->startOfDay();
36
+        $endDate = $companyFyEndDate->isFuture() ? company_today()->endOfDay() : $companyFyEndDate->endOfDay();
37 37
 
38 38
         // Calculate the earliest transaction date based on the company's transactions
39
-        $earliestTransactionDate = $this->company->transactions()->min('posted_at')
39
+        $earliestDate = $this->company->transactions()->min('posted_at')
40 40
             ? Carbon::parse($this->company->transactions()->min('posted_at'))->startOfDay()
41
-            : $defaultStartDate;
41
+            : $startDate;
42 42
 
43 43
         // Assign values to properties
44
-        $this->fiscalYearStartDate = $fiscalYearStartDate;
45
-        $this->fiscalYearEndDate = $fiscalYearEndDate;
46
-        $this->defaultDateRange = $defaultDateRange;
47
-        $this->defaultStartDate = $defaultStartDate;
48
-        $this->defaultEndDate = $defaultEndDate;
49
-        $this->earliestTransactionDate = $earliestTransactionDate;
44
+        $this->fiscalYearStartDate = $companyFyStartDate;
45
+        $this->fiscalYearEndDate = $companyFyEndDate;
46
+        $this->defaultDateRange = $dateRange;
47
+        $this->defaultStartDate = $startDate;
48
+        $this->defaultEndDate = $endDate;
49
+        $this->earliestTransactionDate = $earliestDate;
50 50
     }
51 51
 
52 52
     public function refresh(): self

+ 1
- 6
app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php 查看文件

@@ -10,7 +10,6 @@ use App\Enums\Accounting\AdjustmentType;
10 10
 use App\Filament\Company\Clusters\Settings;
11 11
 use App\Filament\Company\Clusters\Settings\Resources\AdjustmentResource\Pages;
12 12
 use App\Models\Accounting\Adjustment;
13
-use App\Services\CompanySettingsService;
14 13
 use Filament\Forms;
15 14
 use Filament\Forms\Form;
16 15
 use Filament\Notifications\Notification;
@@ -81,10 +80,8 @@ class AdjustmentResource extends Resource
81 80
                     ->columns(),
82 81
                 Forms\Components\Section::make('Dates')
83 82
                     ->schema([
84
-                        Forms\Components\DateTimePicker::make('start_date')
85
-                            ->timezone(CompanySettingsService::getDefaultTimezone()),
83
+                        Forms\Components\DateTimePicker::make('start_date'),
86 84
                         Forms\Components\DateTimePicker::make('end_date')
87
-                            ->timezone(CompanySettingsService::getDefaultTimezone())
88 85
                             ->after('start_date'),
89 86
                     ])
90 87
                     ->columns()
@@ -195,7 +192,6 @@ class AdjustmentResource extends Resource
195 192
                         ->form([
196 193
                             Forms\Components\DateTimePicker::make('paused_until')
197 194
                                 ->label('Auto-resume date')
198
-                                ->timezone(CompanySettingsService::getDefaultTimezone())
199 195
                                 ->helperText('When should this adjustment automatically resume? Leave empty to keep paused indefinitely.')
200 196
                                 ->after('now'),
201 197
                             Forms\Components\Textarea::make('status_reason')
@@ -255,7 +251,6 @@ class AdjustmentResource extends Resource
255 251
                         ->form([
256 252
                             Forms\Components\DateTimePicker::make('paused_until')
257 253
                                 ->label('Auto-resume date')
258
-                                ->timezone(CompanySettingsService::getDefaultTimezone())
259 254
                                 ->helperText('When should these adjustments automatically resume? Leave empty to keep paused indefinitely.')
260 255
                                 ->after('now'),
261 256
                             Forms\Components\Textarea::make('status_reason')

+ 3
- 33
app/Filament/Company/Pages/Concerns/HasDeferredFiltersForm.php 查看文件

@@ -4,7 +4,6 @@ namespace App\Filament\Company\Pages\Concerns;
4 4
 
5 5
 use Filament\Actions\Action;
6 6
 use Filament\Forms\Components\DatePicker;
7
-use Filament\Forms\Components\DateTimePicker;
8 7
 use Filament\Forms\Form;
9 8
 use Illuminate\Support\Arr;
10 9
 use Illuminate\Support\Carbon;
@@ -106,19 +105,6 @@ trait HasDeferredFiltersForm
106 105
         Arr::set($this->deferredFilters, $name, $value);
107 106
     }
108 107
 
109
-    protected function convertDatesToDateTimeString(array $filters): array
110
-    {
111
-        if (isset($filters['startDate'])) {
112
-            $filters['startDate'] = Carbon::parse($filters['startDate'])->startOfDay()->toDateTimeString();
113
-        }
114
-
115
-        if (isset($filters['endDate'])) {
116
-            $filters['endDate'] = Carbon::parse($filters['endDate'])->endOfDay()->toDateTimeString();
117
-        }
118
-
119
-        return $filters;
120
-    }
121
-
122 108
     protected function queryStringHasDeferredFiltersForm(): array
123 109
     {
124 110
         // Get the filter keys dynamically from the filters form
@@ -171,25 +157,9 @@ trait HasDeferredFiltersForm
171 157
         $flatFields = $this->getFiltersForm()->getFlatFields();
172 158
 
173 159
         foreach ($this->filters as $key => $value) {
174
-            if (! isset($flatFields[$key]) || blank($value)) {
175
-                continue;
176
-            }
177
-
178
-            $field = $flatFields[$key];
179
-
180
-            // Reproduce underlying conversion to UTC for DateTimePicker and DatePicker
181
-            if ($field instanceof DateTimePicker && $field->getTimezone() !== config('app.timezone')) {
182
-                try {
183
-                    $carbonValue = Carbon::parse($value, $field->getTimezone());
184
-
185
-                    // Shift back to UTC and format according to field type
186
-                    $this->filters[$key] = $carbonValue
187
-                        ->setTimezone(config('app.timezone'))
188
-                        ->format($field->getFormat());
189
-
190
-                } catch (\Exception $e) {
191
-                    continue;
192
-                }
160
+            if (isset($flatFields[$key]) && $flatFields[$key] instanceof DatePicker) {
161
+                // TODO: Submit a PR to Filament to address DatePicker being dehydrated as a datetime string in filters
162
+                $this->filters[$key] = Carbon::parse($value)->toDateString();
193 163
             }
194 164
         }
195 165
     }

+ 4
- 6
app/Filament/Company/Pages/Reports/BaseReportPage.php 查看文件

@@ -9,7 +9,6 @@ use App\Filament\Company\Pages\Concerns\HasTableColumnToggleForm;
9 9
 use App\Filament\Company\Pages\Reports;
10 10
 use App\Filament\Forms\Components\DateRangeSelect;
11 11
 use App\Models\Company;
12
-use App\Services\CompanySettingsService;
13 12
 use App\Services\DateRangeService;
14 13
 use App\Support\Column;
15 14
 use Filament\Actions\Action;
@@ -92,6 +91,7 @@ abstract class BaseReportPage extends Page
92 91
     {
93 92
         $flatFields = $this->getFiltersForm()->getFlatFields();
94 93
 
94
+        /** @var DateRangeSelect|null $dateRangeField */
95 95
         $dateRangeField = Arr::first($flatFields, static fn ($field) => $field instanceof DateRangeSelect);
96 96
 
97 97
         if (! $dateRangeField) {
@@ -122,7 +122,7 @@ abstract class BaseReportPage extends Page
122 122
         if ($endDateField && ! $startDateField) {
123 123
             $this->setFilterState('dateRange', $this->getDefaultDateRange());
124 124
             $defaultEndDate = Carbon::parse($this->fiscalYearEndDate);
125
-            $this->setFilterState($endDateField, $defaultEndDate->isFuture() ? now()->endOfDay()->toDateTimeString() : $defaultEndDate->endOfDay()->toDateTimeString());
125
+            $this->setFilterState($endDateField, $defaultEndDate->isFuture() ? company_today()->toDateString() : $defaultEndDate->toDateString());
126 126
 
127 127
             return;
128 128
         }
@@ -176,8 +176,8 @@ abstract class BaseReportPage extends Page
176 176
 
177 177
     public function setDateRange(Carbon $start, Carbon $end): void
178 178
     {
179
-        $this->setFilterState('startDate', $start->startOfDay()->toDateTimeString());
180
-        $this->setFilterState('endDate', $end->isFuture() ? now()->endOfDay()->toDateTimeString() : $end->endOfDay()->toDateTimeString());
179
+        $this->setFilterState('startDate', $start->toDateString());
180
+        $this->setFilterState('endDate', $end->isFuture() ? company_today()->toDateString() : $end->toDateString());
181 181
     }
182 182
 
183 183
     public function getFormattedStartDate(): string
@@ -254,7 +254,6 @@ abstract class BaseReportPage extends Page
254 254
         return DatePicker::make('startDate')
255 255
             ->label('Start date')
256 256
             ->live()
257
-            ->timezone(CompanySettingsService::getDefaultTimezone())
258 257
             ->afterStateUpdated(static function ($state, Set $set) {
259 258
                 $set('dateRange', 'Custom');
260 259
             });
@@ -265,7 +264,6 @@ abstract class BaseReportPage extends Page
265 264
         return DatePicker::make('endDate')
266 265
             ->label('End date')
267 266
             ->live()
268
-            ->timezone(CompanySettingsService::getDefaultTimezone())
269 267
             ->afterStateUpdated(static function (Set $set) {
270 268
                 $set('dateRange', 'Custom');
271 269
             });

+ 1
- 4
app/Filament/Forms/Components/CreateAdjustmentSelect.php 查看文件

@@ -8,7 +8,6 @@ use App\Enums\Accounting\AdjustmentScope;
8 8
 use App\Enums\Accounting\AdjustmentStatus;
9 9
 use App\Enums\Accounting\AdjustmentType;
10 10
 use App\Models\Accounting\Adjustment;
11
-use App\Services\CompanySettingsService;
12 11
 use Filament\Forms\Components\Actions\Action;
13 12
 use Filament\Forms\Components\Checkbox;
14 13
 use Filament\Forms\Components\DateTimePicker;
@@ -196,10 +195,8 @@ class CreateAdjustmentSelect extends Select
196 195
 
197 196
             Group::make()
198 197
                 ->schema([
199
-                    DateTimePicker::make('start_date')
200
-                        ->timezone(CompanySettingsService::getDefaultTimezone()),
198
+                    DateTimePicker::make('start_date'),
201 199
                     DateTimePicker::make('end_date')
202
-                        ->timezone(CompanySettingsService::getDefaultTimezone())
203 200
                         ->after('start_date'),
204 201
                 ])
205 202
                 ->visible(function (Get $get) {

+ 4
- 4
app/Filament/Forms/Components/DateRangeSelect.php 查看文件

@@ -88,7 +88,7 @@ class DateRangeSelect extends Select
88 88
 
89 89
     public function processFiscalYear($year, Set $set): void
90 90
     {
91
-        $currentYear = company_now()->year;
91
+        $currentYear = company_today()->year;
92 92
         $diff = $currentYear - $year;
93 93
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
94 94
         $fiscalYearEnd = Carbon::parse($this->fiscalYearEndDate)->subYears($diff);
@@ -97,7 +97,7 @@ class DateRangeSelect extends Select
97 97
 
98 98
     public function processFiscalQuarter($quarter, $year, Set $set): void
99 99
     {
100
-        $currentYear = company_now()->year;
100
+        $currentYear = company_today()->year;
101 101
         $diff = $currentYear - $year;
102 102
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
103 103
         $quarterStart = $fiscalYearStart->copy()->addMonths(($quarter - 1) * 3);
@@ -130,11 +130,11 @@ class DateRangeSelect extends Select
130 130
     public function setDateRange(Carbon $start, Carbon $end, Set $set): void
131 131
     {
132 132
         if ($this->startDateField) {
133
-            $set($this->startDateField, $start->startOfDay()->toDateTimeString());
133
+            $set($this->startDateField, $start->toDateString());
134 134
         }
135 135
 
136 136
         if ($this->endDateField) {
137
-            $set($this->endDateField, $end->isFuture() ? company_now()->endOfDay()->toDateTimeString() : $end->endOfDay()->toDateTimeString());
137
+            $set($this->endDateField, $end->isFuture() ? company_today()->toDateString() : $end->toDateString());
138 138
         }
139 139
     }
140 140
 }

+ 0
- 2
app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php 查看文件

@@ -8,7 +8,6 @@ use App\Models\Banking\BankAccount;
8 8
 use App\Models\Banking\ConnectedBankAccount;
9 9
 use App\Models\Banking\Institution;
10 10
 use App\Models\User;
11
-use App\Services\CompanySettingsService;
12 11
 use App\Services\PlaidService;
13 12
 use Filament\Actions\Action;
14 13
 use Filament\Actions\Concerns\InteractsWithActions;
@@ -90,7 +89,6 @@ class ListInstitutions extends Component implements HasActions, HasForms
90 89
                 DatePicker::make('start_date')
91 90
                     ->label('Start date')
92 91
                     ->required()
93
-                    ->timezone(CompanySettingsService::getDefaultTimezone())
94 92
                     ->placeholder('Select a start date for importing transactions.')
95 93
                     ->minDate(company_today()->subDays(PlaidService::TRANSACTION_DAYS_REQUESTED)->toDateString())
96 94
                     ->maxDate(company_today()->toDateString()),

+ 0
- 3
app/Models/Accounting/RecurringInvoice.php 查看文件

@@ -22,7 +22,6 @@ use App\Filament\Forms\Components\CustomSection;
22 22
 use App\Models\Common\Client;
23 23
 use App\Models\Setting\CompanyProfile;
24 24
 use App\Observers\RecurringInvoiceObserver;
25
-use App\Services\CompanySettingsService;
26 25
 use App\Support\ScheduleHandler;
27 26
 use App\Utilities\Localization\Timezone;
28 27
 use Filament\Actions\Action;
@@ -508,7 +507,6 @@ class RecurringInvoice extends Document
508 507
                             ->softRequired()
509 508
                             ->live()
510 509
                             ->minDate(company_today())
511
-                            ->timezone(CompanySettingsService::getDefaultTimezone())
512 510
                             ->closeOnDateSelection()
513 511
                             ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
514 512
                                 $handler = new ScheduleHandler($set, $get);
@@ -541,7 +539,6 @@ class RecurringInvoice extends Document
541 539
 
542 540
                             if ($endType?->isOn()) {
543 541
                                 $components[] = Forms\Components\DatePicker::make('end_date')
544
-                                    ->timezone(CompanySettingsService::getDefaultTimezone())
545 542
                                     ->live();
546 543
                             }
547 544
 

+ 1
- 1
app/Models/Setting/Localization.php 查看文件

@@ -104,7 +104,7 @@ class Localization extends Model
104 104
 
105 105
     public function getDateTimeFormatAttribute(): string
106 106
     {
107
-        return $this->date_format . ' ' . $this->time_format;
107
+        return $this->date_format->value . ' ' . $this->time_format->value;
108 108
     }
109 109
 
110 110
     public static function getAllLanguages(): array

+ 3
- 4
app/Providers/Filament/CompanyPanelProvider.php 查看文件

@@ -280,12 +280,11 @@ class CompanyPanelProvider extends PanelProvider
280 280
         Tables\Actions\CreateAction::configureUsing(static fn (Tables\Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
281 281
         Tables\Actions\DeleteAction::configureUsing(static fn (Tables\Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));
282 282
         Tables\Actions\DeleteBulkAction::configureUsing(static fn (Tables\Actions\DeleteBulkAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));
283
-        Forms\Components\DateTimePicker::configureUsing(static function (Forms\Components\DateTimePicker $component) {
284
-            $component->native(false);
285
-        });
286 283
 
287 284
         Tables\Table::configureUsing(static function (Tables\Table $table): void {
288
-            $table::$defaultDateDisplayFormat = CompanySettingsService::getDefaultDateFormat(session('current_company_id') ?? auth()->user()->current_company_id);
285
+            $table::$defaultDateDisplayFormat = CompanySettingsService::getDefaultDateFormat();
286
+            $table::$defaultTimeDisplayFormat = CompanySettingsService::getDefaultTimeFormat();
287
+            $table::$defaultDateTimeDisplayFormat = CompanySettingsService::getDefaultDateTimeFormat();
289 288
 
290 289
             $table
291 290
                 ->paginationPageOptions([5, 10, 25, 50, 100])

+ 16
- 4
app/Providers/MacroServiceProvider.php 查看文件

@@ -355,7 +355,7 @@ class MacroServiceProvider extends ServiceProvider
355 355
                     return null;
356 356
                 }
357 357
 
358
-                $timezone ??= $column->getTimezone() ?? CompanySettingsService::getDefaultTimezone();
358
+                $timezone ??= CompanySettingsService::getDefaultTimezone();
359 359
 
360 360
                 // Use shiftTimezone to shift UTC calendar date to the specified timezone
361 361
                 // Using setTimezone would convert which is wrong for calendar dates
@@ -386,7 +386,7 @@ class MacroServiceProvider extends ServiceProvider
386 386
                     return null;
387 387
                 }
388 388
 
389
-                $timezone ??= $entry->getTimezone() ?? CompanySettingsService::getDefaultTimezone();
389
+                $timezone ??= CompanySettingsService::getDefaultTimezone();
390 390
 
391 391
                 // Use shiftTimezone to shift UTC calendar date to the specified timezone
392 392
                 // Using setTimezone would convert which is wrong for calendar dates
@@ -485,9 +485,21 @@ class MacroServiceProvider extends ServiceProvider
485 485
         Carbon::macro('toDefaultDateFormat', function () {
486 486
             $dateFormat = CompanySettingsService::getDefaultDateFormat();
487 487
 
488
-            $this->format($dateFormat);
488
+            return $this->format($dateFormat);
489
+        });
489 490
 
490
-            return $this;
491
+        Carbon::macro('toCompanyTimezone', function () {
492
+            $timezone = CompanySettingsService::getDefaultTimezone();
493
+
494
+            // This will convert the date to the company's timezone, possibly changing the date and time
495
+            return $this->setTimezone($timezone);
496
+        });
497
+
498
+        Carbon::macro('asCompanyTimezone', function () {
499
+            $timezone = CompanySettingsService::getDefaultTimezone();
500
+
501
+            // This will only change the timezone without converting the date and time
502
+            return $this->shiftTimezone($timezone);
491 503
         });
492 504
 
493 505
         ExportColumn::macro('money', function () {

+ 3
- 3
app/Scopes/CurrentCompanyScope.php 查看文件

@@ -16,12 +16,12 @@ class CurrentCompanyScope implements Scope
16 16
      */
17 17
     public function apply(Builder $builder, Model $model): void
18 18
     {
19
-        if (app()->runningInConsole()) {
19
+        $companyId = session('current_company_id');
20
+
21
+        if (! $companyId && app()->runningInConsole()) {
20 22
             return;
21 23
         }
22 24
 
23
-        $companyId = session('current_company_id');
24
-
25 25
         if (! $companyId && ($user = Auth::user()) && ($companyId = $user->current_company_id)) {
26 26
             session(['current_company_id' => $companyId]);
27 27
         }

+ 13
- 0
app/Services/CompanySettingsService.php 查看文件

@@ -3,6 +3,7 @@
3 3
 namespace App\Services;
4 4
 
5 5
 use App\Enums\Setting\DateFormat;
6
+use App\Enums\Setting\TimeFormat;
6 7
 use App\Enums\Setting\WeekStart;
7 8
 use App\Models\Company;
8 9
 use App\Models\Setting\Currency;
@@ -43,6 +44,7 @@ class CompanySettingsService
43 44
                 'default_timezone' => $company->locale->timezone ?? config('app.timezone'),
44 45
                 'default_currency' => $defaultCurrency,
45 46
                 'default_date_format' => $company->locale->date_format->value ?? DateFormat::DEFAULT,
47
+                'default_time_format' => $company->locale->time_format->value ?? TimeFormat::DEFAULT,
46 48
                 'default_week_start' => $company->locale->week_start->value ?? WeekStart::DEFAULT,
47 49
             ];
48 50
         });
@@ -68,6 +70,7 @@ class CompanySettingsService
68 70
             'default_timezone' => config('app.timezone'),
69 71
             'default_currency' => 'USD',
70 72
             'default_date_format' => DateFormat::DEFAULT,
73
+            'default_time_format' => TimeFormat::DEFAULT,
71 74
             'default_week_start' => WeekStart::DEFAULT,
72 75
         ];
73 76
     }
@@ -103,4 +106,14 @@ class CompanySettingsService
103 106
     {
104 107
         return self::getSpecificSetting($companyId, 'default_week_start', WeekStart::DEFAULT);
105 108
     }
109
+
110
+    public static function getDefaultTimeFormat(?int $companyId = null): string
111
+    {
112
+        return self::getSpecificSetting($companyId, 'default_time_format', TimeFormat::DEFAULT);
113
+    }
114
+
115
+    public static function getDefaultDateTimeFormat(?int $companyId = null): string
116
+    {
117
+        return self::getDefaultDateFormat($companyId) . ' ' . self::getDefaultTimeFormat($companyId);
118
+    }
106 119
 }

+ 10
- 7
app/Services/DateRangeService.php 查看文件

@@ -29,7 +29,8 @@ class DateRangeService
29 29
     private function generateDateRangeOptions(): array
30 30
     {
31 31
         $earliestDate = Carbon::parse(Accounting::getEarliestTransactionDate());
32
-        $currentDate = now();
32
+        $currentDate = company_today();
33
+        $currentYear = $currentDate->year;
33 34
         $fiscalYearStartCurrent = Carbon::parse($this->fiscalYearStartDate);
34 35
 
35 36
         $options = [
@@ -46,7 +47,7 @@ class DateRangeService
46 47
         foreach ($period as $date) {
47 48
             $options['Fiscal Year']['FY-' . $date->year] = $date->year;
48 49
 
49
-            $fiscalYearStart = $fiscalYearStartCurrent->copy()->subYears($currentDate->year - $date->year);
50
+            $fiscalYearStart = $fiscalYearStartCurrent->copy()->subYears($currentYear - $date->year);
50 51
 
51 52
             for ($i = 0; $i < 4; $i++) {
52 53
                 $quarterNumber = $i + 1;
@@ -86,9 +87,9 @@ class DateRangeService
86 87
                     continue;
87 88
                 }
88 89
 
89
-                $expectedEnd = $expectedEnd->isFuture() ? now()->startOfDay() : $expectedEnd;
90
+                $expectedEnd = $expectedEnd->isFuture() ? company_today() : $expectedEnd;
90 91
 
91
-                if ($startDate->eq($expectedStart) && $endDate->eq($expectedEnd)) {
92
+                if ($startDate->isSameDay($expectedStart) && $endDate->isSameDay($expectedEnd)) {
92 93
                     return $key; // Return the matching range key (e.g., "FY-2024")
93 94
                 }
94 95
             }
@@ -99,17 +100,19 @@ class DateRangeService
99 100
 
100 101
     private function getExpectedDateRange(string $type, string $key): array
101 102
     {
103
+        $currentYear = company_today()->year;
104
+
102 105
         switch ($type) {
103 106
             case 'Fiscal Year':
104 107
                 $year = (int) substr($key, 3);
105
-                $start = Carbon::parse($this->fiscalYearStartDate)->subYears(now()->year - $year)->startOfDay();
106
-                $end = Carbon::parse($this->fiscalYearEndDate)->subYears(now()->year - $year)->startOfDay();
108
+                $start = Carbon::parse($this->fiscalYearStartDate)->subYears($currentYear - $year)->startOfDay();
109
+                $end = Carbon::parse($this->fiscalYearEndDate)->subYears($currentYear - $year)->startOfDay();
107 110
 
108 111
                 break;
109 112
 
110 113
             case 'Fiscal Quarter':
111 114
                 [$quarter, $year] = explode('-', substr($key, 3));
112
-                $start = Carbon::parse($this->fiscalYearStartDate)->subYears(now()->year - $year)->addMonths(($quarter - 1) * 3)->startOfDay();
115
+                $start = Carbon::parse($this->fiscalYearStartDate)->subYears($currentYear - $year)->addMonths(($quarter - 1) * 3)->startOfDay();
113 116
                 $end = $start->copy()->addMonths(3)->subDay()->startOfDay();
114 117
 
115 118
                 break;

+ 7
- 8
app/Services/ReportService.php 查看文件

@@ -38,9 +38,12 @@ class ReportService
38 38
     ) {}
39 39
 
40 40
     /**
41
-     * @param  class-string<BalanceFormattable>|null  $dtoClass
41
+     * @template T of BalanceFormattable
42
+     *
43
+     * @param  class-string<T>|null  $dtoClass
44
+     * @return T
42 45
      */
43
-    public function formatBalances(array $balances, ?string $dtoClass = null, bool $formatZeros = true): BalanceFormattable | array
46
+    public function formatBalances(array $balances, ?string $dtoClass = null, bool $formatZeros = true)
44 47
     {
45 48
         $dtoClass ??= AccountBalanceDTO::class;
46 49
 
@@ -52,10 +55,6 @@ class ReportService
52 55
             return CurrencyConverter::formatCentsToMoney($balance);
53 56
         }, $balances);
54 57
 
55
-        if (! $dtoClass) {
56
-            return $formattedBalances;
57
-        }
58
-
59 58
         return $dtoClass::fromArray($formattedBalances);
60 59
     }
61 60
 
@@ -355,7 +354,7 @@ class ReportService
355 354
         return new ReportDTO(categories: $accountCategories, overallTotal: $formattedReportTotalBalances, fields: $columns, reportType: $trialBalanceType);
356 355
     }
357 356
 
358
-    public function getRetainedEarningsBalances(string $startDate, string $endDate): BalanceFormattable | array
357
+    public function getRetainedEarningsBalances(string $startDate, string $endDate): AccountBalanceDTO
359 358
     {
360 359
         $retainedEarningsAmount = $this->calculateRetainedEarnings($startDate, $endDate)->getAmount();
361 360
 
@@ -498,7 +497,7 @@ class ReportService
498 497
         );
499 498
     }
500 499
 
501
-    private function calculateTotalCashFlows(array $sections, string $startDate): BalanceFormattable | array
500
+    private function calculateTotalCashFlows(array $sections, string $startDate): AccountBalanceDTO
502 501
     {
503 502
         $totalInflow = 0;
504 503
         $totalOutflow = 0;

+ 13
- 3
resources/views/components/report-summary-section.blade.php 查看文件

@@ -4,17 +4,27 @@
4 4
     'targetLabel' => null,
5 5
 ])
6 6
 
7
+@php
8
+    use App\Utilities\Currency\CurrencyAccessor;
9
+@endphp
10
+
7 11
 <div>
8 12
     <x-filament::section>
9 13
         @if($reportLoaded)
10
-            <div class="flex flex-col md:flex-row items-center md:items-end text-center justify-center gap-4 md:gap-8">
14
+            <div @class([
15
+                'grid grid-cols-1 gap-1 place-content-center items-end text-center max-w-fit mx-auto',
16
+                'md:grid-cols-[repeat(1,minmax(0,1fr)_minmax(0,4rem))_minmax(0,1fr)]' => count($summaryData) === 2,
17
+                'md:grid-cols-[repeat(2,minmax(0,1fr)_minmax(0,4rem))_minmax(0,1fr)]' => count($summaryData) === 3,
18
+                'md:grid-cols-[repeat(3,minmax(0,1fr)_minmax(0,4rem))_minmax(0,1fr)]' => count($summaryData) === 4,
19
+                'md:grid-cols-[repeat(4,minmax(0,1fr)_minmax(0,4rem))_minmax(0,1fr)]' => count($summaryData) === 5,
20
+            ])>
11 21
                 @foreach($summaryData as $summary)
12 22
                     <div class="text-sm">
13 23
                         <div class="text-gray-600 dark:text-gray-200 font-medium mb-2">{{ $summary['label'] }}</div>
14 24
 
15 25
                         @php
16 26
                             $isTargetLabel = $summary['label'] === $targetLabel;
17
-                            $isPositive = money($summary['value'], \App\Utilities\Currency\CurrencyAccessor::getDefaultCurrency())->isPositive();
27
+                            $isPositive = money($summary['value'], CurrencyAccessor::getDefaultCurrency())->isPositive();
18 28
                         @endphp
19 29
 
20 30
                         <strong
@@ -29,7 +39,7 @@
29 39
                     </div>
30 40
 
31 41
                     @if(! $loop->last)
32
-                        <div class="flex items-center justify-center px-2">
42
+                        <div class="flex items-center justify-center">
33 43
                             <strong class="text-lg">
34 44
                                 {{ $loop->remaining === 1 ? '=' : '-' }}
35 45
                             </strong>

+ 6
- 4
resources/views/filament/company/pages/reports/balance-sheet.blade.php 查看文件

@@ -1,14 +1,16 @@
1 1
 <x-filament-panels::page>
2 2
     <x-filament::section>
3
-        <div class="flex flex-col lg:flex-row items-start lg:items-end justify-between gap-4">
3
+        <div class="flex flex-col lg:flex-row items-start lg:items-end gap-4">
4 4
             <!-- Form Container -->
5 5
             @if(method_exists($this, 'filtersForm'))
6
-                {{ $this->filtersForm }}
6
+                <div class="flex-1 min-w-0">
7
+                    {{ $this->filtersForm }}
8
+                </div>
7 9
             @endif
8 10
 
9 11
             <!-- Grouping Button and Column Toggle -->
10 12
             @if($this->hasToggleableColumns())
11
-                <div class="lg:mb-1">
13
+                <div class="flex-shrink-0 lg:mb-1 mr-4">
12 14
                     <x-filament-tables::column-toggle.dropdown
13 15
                         :form="$this->getTableColumnToggleForm()"
14 16
                         :trigger-action="$this->getToggleColumnsTriggerAction()"
@@ -16,7 +18,7 @@
16 18
                 </div>
17 19
             @endif
18 20
 
19
-            <div class="inline-flex items-center min-w-0 lg:min-w-[9.5rem] justify-end">
21
+            <div class="flex-shrink-0 w-[9.5rem] flex justify-end">
20 22
                 {{ $this->applyFiltersAction }}
21 23
             </div>
22 24
         </div>

+ 11
- 7
resources/views/filament/company/pages/reports/cash-flow-statement.blade.php 查看文件

@@ -1,20 +1,24 @@
1 1
 <x-filament-panels::page>
2 2
     <x-filament::section>
3
-        <div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
3
+        <div class="flex flex-col md:flex-row items-start md:items-center gap-4">
4 4
             <!-- Form Container -->
5 5
             @if(method_exists($this, 'filtersForm'))
6
-                {{ $this->filtersForm }}
6
+                <div class="flex-1 min-w-0">
7
+                    {{ $this->filtersForm }}
8
+                </div>
7 9
             @endif
8 10
 
9 11
             <!-- Grouping Button and Column Toggle -->
10 12
             @if($this->hasToggleableColumns())
11
-                <x-filament-tables::column-toggle.dropdown
12
-                    :form="$this->getTableColumnToggleForm()"
13
-                    :trigger-action="$this->getToggleColumnsTriggerAction()"
14
-                />
13
+                <div class="flex-shrink-0 mr-4">
14
+                    <x-filament-tables::column-toggle.dropdown
15
+                        :form="$this->getTableColumnToggleForm()"
16
+                        :trigger-action="$this->getToggleColumnsTriggerAction()"
17
+                    />
18
+                </div>
15 19
             @endif
16 20
 
17
-            <div class="inline-flex items-center min-w-0 md:min-w-[9.5rem] justify-end">
21
+            <div class="flex-shrink-0 w-[9.5rem] flex justify-end">
18 22
                 {{ $this->applyFiltersAction }}
19 23
             </div>
20 24
         </div>

+ 11
- 7
resources/views/filament/company/pages/reports/detailed-report.blade.php 查看文件

@@ -1,20 +1,24 @@
1 1
 <x-filament-panels::page>
2 2
     <x-filament::section>
3
-        <div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
3
+        <div class="flex flex-col md:flex-row items-start md:items-center gap-4">
4 4
             <!-- Form Container -->
5 5
             @if(method_exists($this, 'filtersForm'))
6
-                {{ $this->filtersForm }}
6
+                <div class="flex-1 min-w-0">
7
+                    {{ $this->filtersForm }}
8
+                </div>
7 9
             @endif
8 10
 
9 11
             <!-- Grouping Button and Column Toggle -->
10 12
             @if($this->hasToggleableColumns())
11
-                <x-filament-tables::column-toggle.dropdown
12
-                    :form="$this->getTableColumnToggleForm()"
13
-                    :trigger-action="$this->getToggleColumnsTriggerAction()"
14
-                />
13
+                <div class="flex-shrink-0 mr-4">
14
+                    <x-filament-tables::column-toggle.dropdown
15
+                        :form="$this->getTableColumnToggleForm()"
16
+                        :trigger-action="$this->getToggleColumnsTriggerAction()"
17
+                    />
18
+                </div>
15 19
             @endif
16 20
 
17
-            <div class="inline-flex items-center min-w-0 md:min-w-[9.5rem] justify-end">
21
+            <div class="flex-shrink-0 w-[9.5rem] flex justify-end">
18 22
                 {{ $this->applyFiltersAction }}
19 23
             </div>
20 24
         </div>

+ 11
- 7
resources/views/filament/company/pages/reports/income-statement.blade.php 查看文件

@@ -1,20 +1,24 @@
1 1
 <x-filament-panels::page>
2 2
     <x-filament::section>
3
-        <div class="flex flex-col md:flex-row items-start md:items-center justify-between gap-4">
3
+        <div class="flex flex-col md:flex-row items-start md:items-center gap-4">
4 4
             <!-- Form Container -->
5 5
             @if(method_exists($this, 'filtersForm'))
6
-                {{ $this->filtersForm }}
6
+                <div class="flex-1 min-w-0">
7
+                    {{ $this->filtersForm }}
8
+                </div>
7 9
             @endif
8 10
 
9 11
             <!-- Grouping Button and Column Toggle -->
10 12
             @if($this->hasToggleableColumns())
11
-                <x-filament-tables::column-toggle.dropdown
12
-                    :form="$this->getTableColumnToggleForm()"
13
-                    :trigger-action="$this->getToggleColumnsTriggerAction()"
14
-                />
13
+                <div class="flex-shrink-0 mr-4">
14
+                    <x-filament-tables::column-toggle.dropdown
15
+                        :form="$this->getTableColumnToggleForm()"
16
+                        :trigger-action="$this->getToggleColumnsTriggerAction()"
17
+                    />
18
+                </div>
15 19
             @endif
16 20
 
17
-            <div class="inline-flex items-center min-w-0 md:min-w-[9.5rem] justify-end">
21
+            <div class="flex-shrink-0 w-[9.5rem] flex justify-end">
18 22
                 {{ $this->applyFiltersAction }}
19 23
             </div>
20 24
         </div>

+ 6
- 4
resources/views/filament/company/pages/reports/trial-balance.blade.php 查看文件

@@ -1,14 +1,16 @@
1 1
 <x-filament-panels::page>
2 2
     <x-filament::section>
3
-        <div class="flex flex-col lg:flex-row items-start lg:items-end justify-between gap-4">
3
+        <div class="flex flex-col lg:flex-row items-start lg:items-end gap-4">
4 4
             <!-- Form Container -->
5 5
             @if(method_exists($this, 'filtersForm'))
6
-                {{ $this->filtersForm }}
6
+                <div class="flex-1 min-w-0">
7
+                    {{ $this->filtersForm }}
8
+                </div>
7 9
             @endif
8 10
 
9 11
             <!-- Grouping Button and Column Toggle -->
10 12
             @if($this->hasToggleableColumns())
11
-                <div class="lg:mb-1">
13
+                <div class="flex-shrink-0 lg:mb-1 mr-4">
12 14
                     <x-filament-tables::column-toggle.dropdown
13 15
                         :form="$this->getTableColumnToggleForm()"
14 16
                         :trigger-action="$this->getToggleColumnsTriggerAction()"
@@ -16,7 +18,7 @@
16 18
                 </div>
17 19
             @endif
18 20
 
19
-            <div class="inline-flex items-center min-w-0 lg:min-w-[9.5rem] justify-end">
21
+            <div class="flex-shrink-0 w-[9.5rem] flex justify-end">
20 22
                 {{ $this->applyFiltersAction }}
21 23
             </div>
22 24
         </div>

+ 2
- 2
tests/Feature/Accounting/TransactionTest.php 查看文件

@@ -267,7 +267,7 @@ it('can add a transfer transaction', function () {
267 267
     livewire(ListTransactions::class)
268 268
         ->mountAction('createTransfer')
269 269
         ->assertActionDataSet([
270
-            'posted_at' => today(),
270
+            'posted_at' => company_today()->toDateString(),
271 271
             'type' => TransactionType::Transfer,
272 272
             'bank_account_id' => $sourceBankAccount->id,
273 273
             'amount' => '0.00',
@@ -300,7 +300,7 @@ it('can add a journal transaction', function () {
300 300
     livewire(ListTransactions::class)
301 301
         ->mountAction('createJournalEntry')
302 302
         ->assertActionDataSet([
303
-            'posted_at' => today(),
303
+            'posted_at' => company_today()->toDateString(),
304 304
             'journalEntries' => [
305 305
                 ['type' => JournalEntryType::Debit, 'account_id' => $defaultDebitAccount->id, 'amount' => '0.00'],
306 306
                 ['type' => JournalEntryType::Credit, 'account_id' => $defaultCreditAccount->id, 'amount' => '0.00'],

+ 6
- 6
tests/Feature/Reports/AccountBalancesReportTest.php 查看文件

@@ -54,8 +54,8 @@ it('correctly builds an account balances report for the current fiscal year', fu
54 54
     livewire(AccountBalances::class)
55 55
         ->assertFormSet([
56 56
             'deferredFilters.dateRange' => $defaultDateRange,
57
-            'deferredFilters.startDate' => $defaultStartDate->toDateTimeString(),
58
-            'deferredFilters.endDate' => $defaultEndDate->toDateTimeString(),
57
+            'deferredFilters.startDate' => $defaultStartDate->toDateString(),
58
+            'deferredFilters.endDate' => $defaultEndDate->toDateString(),
59 59
         ])
60 60
         ->assertSet('filters', [
61 61
             'dateRange' => $defaultDateRange,
@@ -120,8 +120,8 @@ it('correctly builds an account balances report for the previous fiscal year', f
120 120
     livewire(AccountBalances::class)
121 121
         ->assertFormSet([
122 122
             'deferredFilters.dateRange' => $defaultDateRange,
123
-            'deferredFilters.startDate' => $defaultStartDate->toDateTimeString(),
124
-            'deferredFilters.endDate' => $defaultEndDate->toDateTimeString(),
123
+            'deferredFilters.startDate' => $defaultStartDate->toDateString(),
124
+            'deferredFilters.endDate' => $defaultEndDate->toDateString(),
125 125
         ])
126 126
         ->assertSet('filters', [
127 127
             'dateRange' => $defaultDateRange,
@@ -129,8 +129,8 @@ it('correctly builds an account balances report for the previous fiscal year', f
129 129
             'endDate' => $defaultEndDate->toDateString(),
130 130
         ])
131 131
         ->set('deferredFilters', [
132
-            'startDate' => $defaultStartDate->subYear()->startOfYear()->toDateTimeString(),
133
-            'endDate' => $defaultEndDate->subYear()->endOfYear()->toDateTimeString(),
132
+            'startDate' => $defaultStartDate->subYear()->startOfYear()->toDateString(),
133
+            'endDate' => $defaultEndDate->subYear()->endOfYear()->toDateString(),
134 134
         ])
135 135
         ->call('applyFilters')
136 136
         ->assertSeeTextInOrder([

+ 3
- 3
tests/Feature/Reports/TrialBalanceReportTest.php 查看文件

@@ -60,7 +60,7 @@ it('correctly builds a standard trial balance report', function () {
60 60
         ->assertFormSet([
61 61
             'deferredFilters.reportType' => $defaultReportType,
62 62
             'deferredFilters.dateRange' => $defaultDateRange,
63
-            'deferredFilters.asOfDate' => $defaultEndDate->toDateTimeString(),
63
+            'deferredFilters.asOfDate' => $defaultEndDate->toDateString(),
64 64
         ])
65 65
         ->assertSet('filters', [
66 66
             'reportType' => $defaultReportType,
@@ -125,7 +125,7 @@ it('correctly builds a post-closing trial balance report', function () {
125 125
 
126 126
     $formattedExpectedBalances = Reporting::formatBalances($calculatedTrialBalances);
127 127
 
128
-    $formattedRetainedEarningsBalances = Reporting::getRetainedEarningsBalances($earliestTransactionDate->toDateTimeString(), $defaultEndDate->toDateTimeString());
128
+    $formattedRetainedEarningsBalances = Reporting::getRetainedEarningsBalances($earliestTransactionDate->toDateString(), $defaultEndDate->toDateString());
129 129
 
130 130
     // Use Livewire to assert the report's filters and displayed data
131 131
     livewire(TrialBalance::class)
@@ -133,7 +133,7 @@ it('correctly builds a post-closing trial balance report', function () {
133 133
         ->assertFormSet([
134 134
             'deferredFilters.reportType' => $defaultReportType,
135 135
             'deferredFilters.dateRange' => $defaultDateRange,
136
-            'deferredFilters.asOfDate' => $defaultEndDate->toDateTimeString(),
136
+            'deferredFilters.asOfDate' => $defaultEndDate->toDateString(),
137 137
         ])
138 138
         ->call('applyFilters')
139 139
         ->assertSet('filters', [

Loading…
取消
儲存