瀏覽代碼

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

Development 3.x
3.x
Andrew Wallo 2 月之前
父節點
當前提交
c37cc9a0e5
沒有連結到貢獻者的電子郵件帳戶。
共有 57 個檔案被更改,包括 363 行新增166 行删除
  1. 11
    2
      app/Concerns/CompanyOwned.php
  2. 4
    4
      app/Concerns/HasTransactionAction.php
  3. 1
    1
      app/Enums/Setting/DateFormat.php
  4. 1
    1
      app/Enums/Setting/PaymentTerms.php
  5. 1
    1
      app/Enums/Setting/WeekStart.php
  6. 2
    2
      app/Filament/Company/Clusters/Settings/Pages/Localization.php
  7. 6
    1
      app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php
  8. 2
    1
      app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource.php
  9. 20
    3
      app/Filament/Company/Pages/Concerns/HasDeferredFiltersForm.php
  10. 3
    0
      app/Filament/Company/Pages/Reports/BaseReportPage.php
  11. 2
    2
      app/Filament/Company/Resources/Accounting/BudgetResource.php
  12. 2
    2
      app/Filament/Company/Resources/Accounting/BudgetResource/Pages/CreateBudget.php
  13. 8
    8
      app/Filament/Company/Resources/Purchases/BillResource.php
  14. 1
    1
      app/Filament/Company/Resources/Purchases/BillResource/Pages/PayBills.php
  15. 1
    1
      app/Filament/Company/Resources/Purchases/BillResource/RelationManagers/PaymentsRelationManager.php
  16. 3
    3
      app/Filament/Company/Resources/Purchases/BillResource/Widgets/BillOverview.php
  17. 3
    3
      app/Filament/Company/Resources/Purchases/VendorResource/Widgets/BillOverview.php
  18. 1
    1
      app/Filament/Company/Resources/Sales/ClientResource/Widgets/InvoiceOverview.php
  19. 7
    7
      app/Filament/Company/Resources/Sales/EstimateResource.php
  20. 7
    7
      app/Filament/Company/Resources/Sales/InvoiceResource.php
  21. 1
    1
      app/Filament/Company/Resources/Sales/InvoiceResource/Pages/RecordPayments.php
  22. 1
    1
      app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php
  23. 1
    1
      app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php
  24. 4
    1
      app/Filament/Forms/Components/CreateAdjustmentSelect.php
  25. 3
    3
      app/Filament/Forms/Components/DateRangeSelect.php
  26. 16
    0
      app/Helpers/helpers.php
  27. 2
    2
      app/Jobs/ProcessTransactionImport.php
  28. 2
    2
      app/Jobs/ProcessTransactionUpdate.php
  29. 1
    1
      app/Listeners/UpdateAccountBalances.php
  30. 4
    2
      app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php
  31. 5
    5
      app/Models/Accounting/Adjustment.php
  32. 3
    3
      app/Models/Accounting/Bill.php
  33. 5
    5
      app/Models/Accounting/Budget.php
  34. 1
    1
      app/Models/Accounting/BudgetAllocation.php
  35. 11
    11
      app/Models/Accounting/Estimate.php
  36. 6
    6
      app/Models/Accounting/Invoice.php
  37. 9
    6
      app/Models/Accounting/RecurringInvoice.php
  38. 11
    0
      app/Models/Export.php
  39. 11
    0
      app/Models/Import.php
  40. 11
    0
      app/Models/Notification.php
  41. 2
    2
      app/Models/Setting/Localization.php
  42. 5
    0
      app/Models/User.php
  43. 1
    1
      app/Observers/AdjustmentObserver.php
  44. 1
    1
      app/Observers/RecurringInvoiceObserver.php
  45. 13
    0
      app/Providers/AppServiceProvider.php
  46. 5
    0
      app/Providers/Filament/CompanyPanelProvider.php
  47. 33
    21
      app/Providers/MacroServiceProvider.php
  48. 4
    9
      app/Scopes/CurrentCompanyScope.php
  49. 3
    3
      app/Services/AccountService.php
  50. 2
    0
      app/Services/CompanySettingsService.php
  51. 2
    2
      app/Services/ExportService.php
  52. 1
    1
      app/Support/ScheduleHandler.php
  53. 17
    17
      composer.lock
  54. 29
    0
      database/migrations/2025_07_13_042000_add_company_id_to_notifications_table.php
  55. 42
    0
      database/migrations/2025_07_13_043000_add_company_id_to_imports_exports_tables.php
  56. 8
    6
      package-lock.json
  57. 1
    1
      tests/Feature/Accounting/TransactionTest.php

+ 11
- 2
app/Concerns/CompanyOwned.php 查看文件

@@ -2,6 +2,8 @@
2 2
 
3 3
 namespace App\Concerns;
4 4
 
5
+use App\Models\Notification;
6
+use App\Models\User;
5 7
 use App\Scopes\CurrentCompanyScope;
6 8
 use Illuminate\Database\Eloquent\ModelNotFoundException;
7 9
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
@@ -17,11 +19,18 @@ trait CompanyOwned
17 19
             if (empty($model->company_id)) {
18 20
                 $companyId = session('current_company_id');
19 21
 
20
-                if (! $companyId && Auth::check()) {
21
-                    $companyId = Auth::user()->currentCompany->id;
22
+                if (! $companyId && ($user = Auth::user()) && ($companyId = $user->current_company_id)) {
22 23
                     session(['current_company_id' => $companyId]);
23 24
                 }
24 25
 
26
+                // For notifications in job context, get company_id from the notifiable user
27
+                if (! $companyId && $model instanceof Notification && $model->notifiable_type === User::class) {
28
+                    $notifiable = $model->notifiable;
29
+                    if ($notifiable instanceof User) {
30
+                        $companyId = $notifiable->current_company_id;
31
+                    }
32
+                }
33
+
25 34
                 if ($companyId) {
26 35
                     $model->company_id = $companyId;
27 36
                 } else {

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

@@ -39,7 +39,7 @@ trait HasTransactionAction
39 39
     protected function getFormDefaultsForType(TransactionType $type): array
40 40
     {
41 41
         $commonDefaults = [
42
-            'posted_at' => today(),
42
+            'posted_at' => company_today()->toDateString(),
43 43
         ];
44 44
 
45 45
         return match ($type) {
@@ -83,6 +83,7 @@ trait HasTransactionAction
83 83
             ->schema([
84 84
                 Forms\Components\DatePicker::make('posted_at')
85 85
                     ->label('Date')
86
+                    ->native()
86 87
                     ->required(),
87 88
                 Forms\Components\TextInput::make('description')
88 89
                     ->label('Description'),
@@ -203,12 +204,11 @@ trait HasTransactionAction
203 204
 
204 205
     protected function getTransactionDetailsGrid(): Forms\Components\Grid
205 206
     {
206
-        return Forms\Components\Grid::make(8)
207
+        return Forms\Components\Grid::make(6)
207 208
             ->schema([
208 209
                 Forms\Components\DatePicker::make('posted_at')
209 210
                     ->label('Date')
210
-                    ->softRequired()
211
-                    ->displayFormat('Y-m-d'),
211
+                    ->softRequired(),
212 212
                 Forms\Components\TextInput::make('description')
213 213
                     ->label('Description')
214 214
                     ->columnSpan(2),

+ 1
- 1
app/Enums/Setting/DateFormat.php 查看文件

@@ -36,6 +36,6 @@ enum DateFormat: string implements HasLabel
36 36
 
37 37
     public function getLabel(): ?string
38 38
     {
39
-        return now()->translatedFormat($this->value);
39
+        return company_today()->translatedFormat($this->value);
40 40
     }
41 41
 }

+ 1
- 1
app/Enums/Setting/PaymentTerms.php 查看文件

@@ -43,6 +43,6 @@ enum PaymentTerms: string implements HasLabel
43 43
     {
44 44
         $days = $this->getDays() ?? 0;
45 45
 
46
-        return now()->addDays($days)->translatedFormat($format);
46
+        return company_today()->addDays($days)->translatedFormat($format);
47 47
     }
48 48
 }

+ 1
- 1
app/Enums/Setting/WeekStart.php 查看文件

@@ -18,6 +18,6 @@ enum WeekStart: int implements HasLabel
18 18
 
19 19
     public function getLabel(): ?string
20 20
     {
21
-        return today()->isoWeekday($this->value)->dayName;
21
+        return company_today()->isoWeekday($this->value)->dayName;
22 22
     }
23 23
 }

+ 2
- 2
app/Filament/Company/Clusters/Settings/Pages/Localization.php 查看文件

@@ -177,7 +177,7 @@ class Localization extends Page
177 177
                         Cluster::make([
178 178
                             Select::make('fiscal_year_end_month')
179 179
                                 ->softRequired()
180
-                                ->options(array_combine(range(1, 12), array_map(static fn ($month) => now()->month($month)->monthName, range(1, 12))))
180
+                                ->options(array_combine(range(1, 12), array_map(static fn ($month) => company_today()->month($month)->monthName, range(1, 12))))
181 181
                                 ->afterStateUpdated(static fn (Set $set) => $set('fiscal_year_end_day', null))
182 182
                                 ->columnSpan(2)
183 183
                                 ->live(),
@@ -188,7 +188,7 @@ class Localization extends Page
188 188
                                 ->options(function (Get $get) {
189 189
                                     $month = (int) $get('fiscal_year_end_month');
190 190
 
191
-                                    $daysInMonth = now()->month($month)->daysInMonth;
191
+                                    $daysInMonth = company_today()->month($month)->daysInMonth;
192 192
 
193 193
                                     return array_combine(range(1, $daysInMonth), range(1, $daysInMonth));
194 194
                                 })

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

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

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

@@ -100,7 +100,8 @@ class DocumentDefaultResource extends Resource
100 100
                             ->loadingIndicatorPosition('left')
101 101
                             ->removeUploadedFileButtonPosition('right'),
102 102
                         Forms\Components\Checkbox::make('show_logo')
103
-                            ->localizeLabel(),
103
+                            ->localizeLabel()
104
+                            ->hidden(is_demo_environment()),
104 105
                         Forms\Components\ColorPicker::make('accent_color')
105 106
                             ->localizeLabel(),
106 107
                         Forms\Components\Select::make('font')

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

@@ -4,6 +4,7 @@ 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;
7 8
 use Filament\Forms\Form;
8 9
 use Illuminate\Support\Arr;
9 10
 use Illuminate\Support\Carbon;
@@ -170,9 +171,25 @@ trait HasDeferredFiltersForm
170 171
         $flatFields = $this->getFiltersForm()->getFlatFields();
171 172
 
172 173
         foreach ($this->filters as $key => $value) {
173
-            if (isset($flatFields[$key]) && $flatFields[$key] instanceof DatePicker) {
174
-                // TODO: Submit a PR to Filament to address DatePicker being dehydrated as a datetime string in filters
175
-                $this->filters[$key] = Carbon::parse($value)->toDateString();
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
+                }
176 193
             }
177 194
         }
178 195
     }

+ 3
- 0
app/Filament/Company/Pages/Reports/BaseReportPage.php 查看文件

@@ -9,6 +9,7 @@ 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;
12 13
 use App\Services\DateRangeService;
13 14
 use App\Support\Column;
14 15
 use Filament\Actions\Action;
@@ -253,6 +254,7 @@ abstract class BaseReportPage extends Page
253 254
         return DatePicker::make('startDate')
254 255
             ->label('Start date')
255 256
             ->live()
257
+            ->timezone(CompanySettingsService::getDefaultTimezone())
256 258
             ->afterStateUpdated(static function ($state, Set $set) {
257 259
                 $set('dateRange', 'Custom');
258 260
             });
@@ -263,6 +265,7 @@ abstract class BaseReportPage extends Page
263 265
         return DatePicker::make('endDate')
264 266
             ->label('End date')
265 267
             ->live()
268
+            ->timezone(CompanySettingsService::getDefaultTimezone())
266 269
             ->afterStateUpdated(static function (Set $set) {
267 270
                 $set('dateRange', 'Custom');
268 271
             });

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

@@ -50,11 +50,11 @@ class BudgetResource extends Resource
50 50
                             ->live(),
51 51
                         Forms\Components\DatePicker::make('start_date')
52 52
                             ->required()
53
-                            ->default(now()->startOfYear())
53
+                            ->default(company_today()->startOfYear())
54 54
                             ->live(),
55 55
                         Forms\Components\DatePicker::make('end_date')
56 56
                             ->required()
57
-                            ->default(now()->endOfYear())
57
+                            ->default(company_today()->endOfYear())
58 58
                             ->live()
59 59
                             ->disabled(static fn (Forms\Get $get) => blank($get('start_date')))
60 60
                             ->minDate(fn (Forms\Get $get) => match (BudgetIntervalType::parse($get('interval_type'))) {

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

@@ -117,11 +117,11 @@ class CreateBudget extends CreateRecord
117 117
                         ->live(),
118 118
                     Forms\Components\DatePicker::make('start_date')
119 119
                         ->required()
120
-                        ->default(now()->startOfYear())
120
+                        ->default(company_today()->startOfYear())
121 121
                         ->live(),
122 122
                     Forms\Components\DatePicker::make('end_date')
123 123
                         ->required()
124
-                        ->default(now()->endOfYear())
124
+                        ->default(company_today()->endOfYear())
125 125
                         ->live()
126 126
                         ->disabled(static fn (Forms\Get $get) => blank($get('start_date')))
127 127
                         ->minDate(fn (Forms\Get $get) => match (BudgetIntervalType::parse($get('interval_type'))) {

+ 8
- 8
app/Filament/Company/Resources/Purchases/BillResource.php 查看文件

@@ -88,14 +88,14 @@ class BillResource extends Resource
88 88
                                     Forms\Components\DatePicker::make('date')
89 89
                                         ->label('Bill date')
90 90
                                         ->live()
91
-                                        ->default(now())
91
+                                        ->default(company_today()->toDateString())
92 92
                                         ->disabled(function (?Bill $record) {
93 93
                                             return $record?->hasPayments();
94 94
                                         })
95 95
                                         ->columnSpan(2)
96 96
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
97
-                                            $date = $state;
98
-                                            $dueDate = $get('due_date');
97
+                                            $date = Carbon::parse($state)->toDateString();
98
+                                            $dueDate = Carbon::parse($get('due_date'))->toDateString();
99 99
 
100 100
                                             if ($date && $dueDate && $date > $dueDate) {
101 101
                                                 $set('due_date', $date);
@@ -137,8 +137,8 @@ class BillResource extends Resource
137 137
                                     ->columns(3),
138 138
                                 Forms\Components\DatePicker::make('due_date')
139 139
                                     ->label('Due date')
140
-                                    ->default(function () use ($company) {
141
-                                        return now()->addDays($company->defaultBill->payment_terms->getDays());
140
+                                    ->default(function () use ($settings) {
141
+                                        return company_today()->addDays($settings->payment_terms->getDays())->toDateString();
142 142
                                     })
143 143
                                     ->required()
144 144
                                     ->live()
@@ -450,7 +450,7 @@ class BillResource extends Resource
450 450
                             })
451 451
                             ->mountUsing(function (Bill $record, Form $form) {
452 452
                                 $form->fill([
453
-                                    'posted_at' => now(),
453
+                                    'posted_at' => company_today()->toDateString(),
454 454
                                     'amount' => $record->amount_due,
455 455
                                 ]);
456 456
                             })
@@ -614,8 +614,8 @@ class BillResource extends Resource
614 614
                         ->beforeReplicaSaved(function (Bill $replica) {
615 615
                             $replica->status = BillStatus::Open;
616 616
                             $replica->bill_number = Bill::getNextDocumentNumber();
617
-                            $replica->date = now();
618
-                            $replica->due_date = now()->addDays($replica->company->defaultBill->payment_terms->getDays());
617
+                            $replica->date = company_today();
618
+                            $replica->due_date = company_today()->addDays($replica->company->defaultBill->payment_terms->getDays());
619 619
                         })
620 620
                         ->withReplicatedRelationships(['lineItems'])
621 621
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 1
- 1
app/Filament/Company/Resources/Purchases/BillResource/Pages/PayBills.php 查看文件

@@ -151,7 +151,7 @@ class PayBills extends ListRecords
151 151
                             ->softRequired(),
152 152
                         Forms\Components\DatePicker::make('posted_at')
153 153
                             ->label('Date')
154
-                            ->default(now())
154
+                            ->default(company_today()->toDateString())
155 155
                             ->softRequired(),
156 156
                         Forms\Components\Select::make('payment_method')
157 157
                             ->label('Payment method')

+ 1
- 1
app/Filament/Company/Resources/Purchases/BillResource/RelationManagers/PaymentsRelationManager.php 查看文件

@@ -224,7 +224,7 @@ class PaymentsRelationManager extends RelationManager
224 224
                     ->mountUsing(function (Form $form) {
225 225
                         $record = $this->getOwnerRecord();
226 226
                         $form->fill([
227
-                            'posted_at' => now(),
227
+                            'posted_at' => company_today()->toDateString(),
228 228
                             'amount' => $record->amount_due,
229 229
                         ]);
230 230
                     })

+ 3
- 3
app/Filament/Company/Resources/Purchases/BillResource/Widgets/BillOverview.php 查看文件

@@ -51,8 +51,8 @@ class BillOverview extends EnhancedStatsOverviewWidget
51 51
 
52 52
             $lastMonthPaid = $this->getPageTableQuery()
53 53
                 ->whereBetween('date', [
54
-                    today()->subMonth()->startOfMonth(),
55
-                    today()->subMonth()->endOfMonth(),
54
+                    company_today()->subMonth()->startOfMonth(),
55
+                    company_today()->subMonth()->endOfMonth(),
56 56
                 ])
57 57
                 ->get()
58 58
                 ->sumMoneyInDefaultCurrency('amount_paid');
@@ -85,7 +85,7 @@ class BillOverview extends EnhancedStatsOverviewWidget
85 85
 
86 86
         $amountDueWithin7Days = $unpaidBills
87 87
             ->clone()
88
-            ->whereBetween('due_date', [today(), today()->addWeek()])
88
+            ->whereBetween('due_date', [company_today(), company_today()->addWeek()])
89 89
             ->get()
90 90
             ->sumMoneyInDefaultCurrency('amount_due');
91 91
 

+ 3
- 3
app/Filament/Company/Resources/Purchases/VendorResource/Widgets/BillOverview.php 查看文件

@@ -29,7 +29,7 @@ class BillOverview extends EnhancedStatsOverviewWidget
29 29
 
30 30
         $amountDueWithin7Days = $unpaidBills
31 31
             ->clone()
32
-            ->whereBetween('due_date', [today(), today()->addWeek()])
32
+            ->whereBetween('due_date', [company_today(), company_today()->addWeek()])
33 33
             ->get()
34 34
             ->sumMoneyInDefaultCurrency('amount_due');
35 35
 
@@ -54,8 +54,8 @@ class BillOverview extends EnhancedStatsOverviewWidget
54 54
         $lastMonthTotal = $this->record->bills()
55 55
             ->where('status', BillStatus::Paid)
56 56
             ->whereBetween('date', [
57
-                today()->subMonth()->startOfMonth(),
58
-                today()->subMonth()->endOfMonth(),
57
+                company_today()->subMonth()->startOfMonth(),
58
+                company_today()->subMonth()->endOfMonth(),
59 59
             ])
60 60
             ->get()
61 61
             ->sumMoneyInDefaultCurrency('amount_paid');

+ 1
- 1
app/Filament/Company/Resources/Sales/ClientResource/Widgets/InvoiceOverview.php 查看文件

@@ -26,7 +26,7 @@ class InvoiceOverview extends EnhancedStatsOverviewWidget
26 26
             ->sumMoneyInDefaultCurrency('amount_due');
27 27
 
28 28
         $amountDueWithin30Days = $unpaidInvoices->clone()
29
-            ->whereBetween('due_date', [today(), today()->addMonth()])
29
+            ->whereBetween('due_date', [company_today(), company_today()->addMonth()])
30 30
             ->get()
31 31
             ->sumMoneyInDefaultCurrency('amount_due');
32 32
 

+ 7
- 7
app/Filament/Company/Resources/Sales/EstimateResource.php 查看文件

@@ -91,11 +91,11 @@ class EstimateResource extends Resource
91 91
                                     Forms\Components\DatePicker::make('date')
92 92
                                         ->label('Estimate date')
93 93
                                         ->live()
94
-                                        ->default(now())
94
+                                        ->default(company_today()->toDateString())
95 95
                                         ->columnSpan(2)
96 96
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
97
-                                            $date = $state;
98
-                                            $expirationDate = $get('expiration_date');
97
+                                            $date = Carbon::parse($state)->toDateString();
98
+                                            $expirationDate = Carbon::parse($get('expiration_date'))->toDateString();
99 99
 
100 100
                                             if ($date && $expirationDate && $date > $expirationDate) {
101 101
                                                 $set('expiration_date', $date);
@@ -137,10 +137,10 @@ class EstimateResource extends Resource
137 137
                                 Forms\Components\DatePicker::make('expiration_date')
138 138
                                     ->label('Expiration date')
139 139
                                     ->default(function () use ($settings) {
140
-                                        return now()->addDays($settings->payment_terms->getDays());
140
+                                        return company_today()->addDays($settings->payment_terms->getDays())->toDateString();
141 141
                                     })
142 142
                                     ->minDate(static function (Forms\Get $get) {
143
-                                        return $get('date') ?? now();
143
+                                        return Carbon::parse($get('date'))->toDateString() ?? company_today()->toDateString();
144 144
                                     })
145 145
                                     ->live()
146 146
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -470,8 +470,8 @@ class EstimateResource extends Resource
470 470
                         ->beforeReplicaSaved(function (Estimate $replica) {
471 471
                             $replica->status = EstimateStatus::Draft;
472 472
                             $replica->estimate_number = Estimate::getNextDocumentNumber();
473
-                            $replica->date = now();
474
-                            $replica->expiration_date = now()->addDays($replica->company->defaultInvoice->payment_terms->getDays());
473
+                            $replica->date = company_today();
474
+                            $replica->expiration_date = company_today()->addDays($replica->company->defaultInvoice->payment_terms->getDays());
475 475
                         })
476 476
                         ->withReplicatedRelationships(['lineItems'])
477 477
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 7
- 7
app/Filament/Company/Resources/Sales/InvoiceResource.php 查看文件

@@ -95,14 +95,14 @@ class InvoiceResource extends Resource
95 95
                                     Forms\Components\DatePicker::make('date')
96 96
                                         ->label('Invoice date')
97 97
                                         ->live()
98
-                                        ->default(now())
98
+                                        ->default(company_today()->toDateString())
99 99
                                         ->disabled(function (?Invoice $record) {
100 100
                                             return $record?->hasPayments();
101 101
                                         })
102 102
                                         ->columnSpan(2)
103 103
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
104
-                                            $date = $state;
105
-                                            $dueDate = $get('due_date');
104
+                                            $date = Carbon::parse($state)->toDateString();
105
+                                            $dueDate = Carbon::parse($get('due_date'))->toDateString();
106 106
 
107 107
                                             if ($date && $dueDate && $date > $dueDate) {
108 108
                                                 $set('due_date', $date);
@@ -145,10 +145,10 @@ class InvoiceResource extends Resource
145 145
                                 Forms\Components\DatePicker::make('due_date')
146 146
                                     ->label('Payment due')
147 147
                                     ->default(function () use ($settings) {
148
-                                        return now()->addDays($settings->payment_terms->getDays());
148
+                                        return company_today()->addDays($settings->payment_terms->getDays())->toDateString();
149 149
                                     })
150 150
                                     ->minDate(static function (Forms\Get $get) {
151
-                                        return $get('date') ?? now();
151
+                                        return Carbon::parse($get('date'))->toDateString() ?? company_today()->toDateString();
152 152
                                     })
153 153
                                     ->live()
154 154
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -538,8 +538,8 @@ class InvoiceResource extends Resource
538 538
                         ->beforeReplicaSaved(function (Invoice $replica) {
539 539
                             $replica->status = InvoiceStatus::Draft;
540 540
                             $replica->invoice_number = Invoice::getNextDocumentNumber();
541
-                            $replica->date = now();
542
-                            $replica->due_date = now()->addDays($replica->company->defaultInvoice->payment_terms->getDays());
541
+                            $replica->date = company_today();
542
+                            $replica->due_date = company_today()->addDays($replica->company->defaultInvoice->payment_terms->getDays());
543 543
                         })
544 544
                         ->withReplicatedRelationships(['lineItems'])
545 545
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/RecordPayments.php 查看文件

@@ -201,7 +201,7 @@ class RecordPayments extends ListRecords
201 201
                             ->softRequired(),
202 202
                         Forms\Components\DatePicker::make('posted_at')
203 203
                             ->label('Date')
204
-                            ->default(now())
204
+                            ->default(company_today()->toDateString())
205 205
                             ->softRequired(),
206 206
                         Forms\Components\Select::make('payment_method')
207 207
                             ->label('Payment method')

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php 查看文件

@@ -237,7 +237,7 @@ class PaymentsRelationManager extends RelationManager
237 237
                     ->mountUsing(function (Form $form) {
238 238
                         $record = $this->getOwnerRecord();
239 239
                         $form->fill([
240
-                            'posted_at' => now(),
240
+                            'posted_at' => company_today()->toDateString(),
241 241
                             'amount' => abs($record->amount_due),
242 242
                         ]);
243 243
                     })

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php 查看文件

@@ -54,7 +54,7 @@ class InvoiceOverview extends EnhancedStatsOverviewWidget
54 54
 
55 55
         $amountDueWithin30Days = $unpaidInvoices
56 56
             ->clone()
57
-            ->whereBetween('due_date', [today(), today()->addMonth()])
57
+            ->whereBetween('due_date', [company_today(), company_today()->addMonth()])
58 58
             ->get()
59 59
             ->sumMoneyInDefaultCurrency('amount_due');
60 60
 

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

@@ -8,6 +8,7 @@ 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;
11 12
 use Filament\Forms\Components\Actions\Action;
12 13
 use Filament\Forms\Components\Checkbox;
13 14
 use Filament\Forms\Components\DateTimePicker;
@@ -195,8 +196,10 @@ class CreateAdjustmentSelect extends Select
195 196
 
196 197
             Group::make()
197 198
                 ->schema([
198
-                    DateTimePicker::make('start_date'),
199
+                    DateTimePicker::make('start_date')
200
+                        ->timezone(CompanySettingsService::getDefaultTimezone()),
199 201
                     DateTimePicker::make('end_date')
202
+                        ->timezone(CompanySettingsService::getDefaultTimezone())
200 203
                         ->after('start_date'),
201 204
                 ])
202 205
                 ->visible(function (Get $get) {

+ 3
- 3
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 = now()->year;
91
+        $currentYear = company_now()->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 = now()->year;
100
+        $currentYear = company_now()->year;
101 101
         $diff = $currentYear - $year;
102 102
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
103 103
         $quarterStart = $fiscalYearStart->copy()->addMonths(($quarter - 1) * 3);
@@ -134,7 +134,7 @@ class DateRangeSelect extends Select
134 134
         }
135 135
 
136 136
         if ($this->endDateField) {
137
-            $set($this->endDateField, $end->isFuture() ? now()->endOfDay()->toDateTimeString() : $end->endOfDay()->toDateTimeString());
137
+            $set($this->endDateField, $end->isFuture() ? company_now()->endOfDay()->toDateTimeString() : $end->endOfDay()->toDateTimeString());
138 138
         }
139 139
     }
140 140
 }

+ 16
- 0
app/Helpers/helpers.php 查看文件

@@ -3,7 +3,9 @@
3 3
 use App\Enums\Accounting\AdjustmentComputation;
4 4
 use App\Enums\Setting\NumberFormat;
5 5
 use App\Models\Setting\Localization;
6
+use App\Services\CompanySettingsService;
6 7
 use Filament\Support\RawJs;
8
+use Illuminate\Support\Carbon;
7 9
 
8 10
 if (! function_exists('generateJsCode')) {
9 11
     function generateJsCode(string $precision, ?string $currency = null): string
@@ -129,3 +131,17 @@ if (! function_exists('is_demo_environment')) {
129 131
         return app()->environment('demo');
130 132
     }
131 133
 }
134
+
135
+if (! function_exists('company_today')) {
136
+    function company_today(): Carbon
137
+    {
138
+        return today(CompanySettingsService::getDefaultTimezone());
139
+    }
140
+}
141
+
142
+if (! function_exists('company_now')) {
143
+    function company_now(): Carbon
144
+    {
145
+        return now(CompanySettingsService::getDefaultTimezone());
146
+    }
147
+}

+ 2
- 2
app/Jobs/ProcessTransactionImport.php 查看文件

@@ -30,7 +30,7 @@ class ProcessTransactionImport implements ShouldQueue
30 30
     public function handle(PlaidService $plaid, TransactionService $transactionService): void
31 31
     {
32 32
         $accessToken = $this->connectedBankAccount->access_token;
33
-        $endDate = Carbon::now()->toDateString();
33
+        $endDate = company_now()->toDateString();
34 34
         $startDate = Carbon::parse($this->startDate)->toDateString();
35 35
         $allTransactions = [];
36 36
         $offset = 0;
@@ -64,7 +64,7 @@ class ProcessTransactionImport implements ShouldQueue
64 64
             $transactionService->storeTransactions($this->company, $this->bankAccount, $newTransactions);
65 65
 
66 66
             $this->connectedBankAccount->update([
67
-                'last_imported_at' => Carbon::now(),
67
+                'last_imported_at' => company_now(),
68 68
             ]);
69 69
         }
70 70
     }

+ 2
- 2
app/Jobs/ProcessTransactionUpdate.php 查看文件

@@ -39,7 +39,7 @@ class ProcessTransactionUpdate implements ShouldQueue
39 39
             $bufferDays = 15;
40 40
             $lastImportedAtDate = Carbon::parse($connectedBankAccount->last_imported_at);
41 41
             $startDate = $lastImportedAtDate->subDays($bufferDays)->toDateString();
42
-            $endDate = Carbon::now()->toDateString();
42
+            $endDate = company_today()->toDateString();
43 43
 
44 44
             $transactionsResponse = $plaidService->getTransactions($accessToken, $startDate, $endDate, [
45 45
                 'account_ids' => [$connectedBankAccount->external_account_id],
@@ -67,7 +67,7 @@ class ProcessTransactionUpdate implements ShouldQueue
67 67
                 $transactionService->storeTransactions($this->company, $bankAccount, $newTransactions);
68 68
 
69 69
                 $connectedBankAccount->update([
70
-                    'last_imported_at' => Carbon::now(),
70
+                    'last_imported_at' => company_now(),
71 71
                 ]);
72 72
             }
73 73
         }

+ 1
- 1
app/Listeners/UpdateAccountBalances.php 查看文件

@@ -58,7 +58,7 @@ class UpdateAccountBalances
58 58
                         'type' => $transactionType,
59 59
                         'amount' => $formattedSimpleDifference,
60 60
                         'payment_channel' => 'other',
61
-                        'posted_at' => today(),
61
+                        'posted_at' => company_today(),
62 62
                         'description' => $description,
63 63
                         'pending' => false,
64 64
                         'reviewed' => false,

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

@@ -8,6 +8,7 @@ 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;
11 12
 use App\Services\PlaidService;
12 13
 use Filament\Actions\Action;
13 14
 use Filament\Actions\Concerns\InteractsWithActions;
@@ -89,9 +90,10 @@ class ListInstitutions extends Component implements HasActions, HasForms
89 90
                 DatePicker::make('start_date')
90 91
                     ->label('Start date')
91 92
                     ->required()
93
+                    ->timezone(CompanySettingsService::getDefaultTimezone())
92 94
                     ->placeholder('Select a start date for importing transactions.')
93
-                    ->minDate(now()->subDays(PlaidService::TRANSACTION_DAYS_REQUESTED)->toDateString())
94
-                    ->maxDate(now()->toDateString()),
95
+                    ->minDate(company_today()->subDays(PlaidService::TRANSACTION_DAYS_REQUESTED)->toDateString())
96
+                    ->maxDate(company_today()->toDateString()),
95 97
             ])
96 98
             ->action(function (array $data, ConnectedBankAccount $connectedBankAccount) {
97 99
                 $selectedBankAccountId = $data['bank_account_id'] ?? $connectedBankAccount->bank_account_id;

+ 5
- 5
app/Models/Accounting/Adjustment.php 查看文件

@@ -126,11 +126,11 @@ class Adjustment extends Model
126 126
 
127 127
     public function calculateNaturalStatus(): AdjustmentStatus
128 128
     {
129
-        if ($this->start_date?->isFuture()) {
129
+        if ($this->start_date?->isAfter(company_now())) {
130 130
             return AdjustmentStatus::Upcoming;
131 131
         }
132 132
 
133
-        if ($this->end_date?->isPast()) {
133
+        if ($this->end_date?->isBefore(company_now())) {
134 134
             return AdjustmentStatus::Expired;
135 135
         }
136 136
 
@@ -144,7 +144,7 @@ class Adjustment extends Model
144 144
         }
145 145
 
146 146
         return $this->update([
147
-            'paused_at' => now(),
147
+            'paused_at' => company_now(),
148 148
             'paused_until' => $untilDate,
149 149
             'status' => AdjustmentStatus::Paused,
150 150
             'status_reason' => $reason,
@@ -172,7 +172,7 @@ class Adjustment extends Model
172 172
         }
173 173
 
174 174
         return $this->update([
175
-            'archived_at' => now(),
175
+            'archived_at' => company_now(),
176 176
             'status' => AdjustmentStatus::Archived,
177 177
             'status_reason' => $reason,
178 178
         ]);
@@ -182,7 +182,7 @@ class Adjustment extends Model
182 182
     {
183 183
         return $this->status === AdjustmentStatus::Paused &&
184 184
             $this->paused_until !== null &&
185
-            $this->paused_until->isPast();
185
+            $this->paused_until->isBefore(company_now());
186 186
     }
187 187
 
188 188
     public function refreshStatus(): bool

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

@@ -131,7 +131,7 @@ class Bill extends Document
131 131
 
132 132
     public function shouldBeOverdue(): bool
133 133
     {
134
-        return $this->due_date->isBefore(today()) && $this->canBeOverdue();
134
+        return $this->due_date->isBefore(company_today()) && $this->canBeOverdue();
135 135
     }
136 136
 
137 137
     public function wasInitialized(): bool
@@ -426,8 +426,8 @@ class Bill extends Document
426 426
             ->beforeReplicaSaved(function (self $original, self $replica) {
427 427
                 $replica->status = BillStatus::Open;
428 428
                 $replica->bill_number = self::getNextDocumentNumber();
429
-                $replica->date = now();
430
-                $replica->due_date = now()->addDays($original->company->defaultBill->payment_terms->getDays());
429
+                $replica->date = company_today();
430
+                $replica->due_date = company_today()->addDays($original->company->defaultBill->payment_terms->getDays());
431 431
             })
432 432
             ->databaseTransaction()
433 433
             ->after(function (self $original, self $replica) {

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

@@ -154,14 +154,14 @@ class Budget extends Model
154 154
     public function scopeCurrentlyActive(Builder $query): Builder
155 155
     {
156 156
         return $query->active()
157
-            ->where('start_date', '<=', now())
158
-            ->where('end_date', '>=', now());
157
+            ->where('start_date', '<=', company_now())
158
+            ->where('end_date', '>=', company_now());
159 159
     }
160 160
 
161 161
     protected function isCurrentlyInPeriod(): Attribute
162 162
     {
163 163
         return Attribute::get(function () {
164
-            return now()->between($this->start_date, $this->end_date);
164
+            return company_now()->between($this->start_date, $this->end_date);
165 165
         });
166 166
     }
167 167
 
@@ -174,7 +174,7 @@ class Budget extends Model
174 174
             throw new \RuntimeException('Budget cannot be approved.');
175 175
         }
176 176
 
177
-        $approvedAt ??= now();
177
+        $approvedAt ??= company_now();
178 178
 
179 179
         $this->update([
180 180
             'status' => BudgetStatus::Active,
@@ -191,7 +191,7 @@ class Budget extends Model
191 191
             throw new \RuntimeException('Budget cannot be closed.');
192 192
         }
193 193
 
194
-        $closedAt ??= now();
194
+        $closedAt ??= company_now();
195 195
 
196 196
         $this->update([
197 197
             'status' => BudgetStatus::Closed,

+ 1
- 1
app/Models/Accounting/BudgetAllocation.php 查看文件

@@ -38,6 +38,6 @@ class BudgetAllocation extends Model
38 38
 
39 39
     public function isCurrentPeriod(): bool
40 40
     {
41
-        return now()->between($this->start_date, $this->end_date);
41
+        return company_now()->between($this->start_date, $this->end_date);
42 42
     }
43 43
 }

+ 11
- 11
app/Models/Accounting/Estimate.php 查看文件

@@ -132,7 +132,7 @@ class Estimate extends Document
132 132
 
133 133
     public function shouldBeExpired(): bool
134 134
     {
135
-        return $this->expiration_date?->isBefore(today()) && $this->canBeExpired();
135
+        return $this->expiration_date?->isBefore(company_today()) && $this->canBeExpired();
136 136
     }
137 137
 
138 138
     public function isDraft(): bool
@@ -257,7 +257,7 @@ class Estimate extends Document
257 257
             throw new \RuntimeException('Estimate is not in draft status.');
258 258
         }
259 259
 
260
-        $approvedAt ??= now();
260
+        $approvedAt ??= company_now();
261 261
 
262 262
         $this->update([
263 263
             'approved_at' => $approvedAt,
@@ -316,7 +316,7 @@ class Estimate extends Document
316 316
 
317 317
     public function markAsSent(?Carbon $sentAt = null): void
318 318
     {
319
-        $sentAt ??= now();
319
+        $sentAt ??= company_now();
320 320
 
321 321
         $this->update([
322 322
             'status' => EstimateStatus::Sent,
@@ -326,7 +326,7 @@ class Estimate extends Document
326 326
 
327 327
     public function markAsViewed(?Carbon $viewedAt = null): void
328 328
     {
329
-        $viewedAt ??= now();
329
+        $viewedAt ??= company_now();
330 330
 
331 331
         $this->update([
332 332
             'status' => EstimateStatus::Viewed,
@@ -357,8 +357,8 @@ class Estimate extends Document
357 357
             ->beforeReplicaSaved(function (self $original, self $replica) {
358 358
                 $replica->status = EstimateStatus::Draft;
359 359
                 $replica->estimate_number = self::getNextDocumentNumber();
360
-                $replica->date = now();
361
-                $replica->expiration_date = now()->addDays($original->company->defaultInvoice->payment_terms->getDays());
360
+                $replica->date = company_today();
361
+                $replica->expiration_date = company_today()->addDays($original->company->defaultInvoice->payment_terms->getDays());
362 362
             })
363 363
             ->databaseTransaction()
364 364
             ->after(function (self $original, self $replica) {
@@ -388,7 +388,7 @@ class Estimate extends Document
388 388
 
389 389
     public function markAsAccepted(?Carbon $acceptedAt = null): void
390 390
     {
391
-        $acceptedAt ??= now();
391
+        $acceptedAt ??= company_now();
392 392
 
393 393
         $this->update([
394 394
             'status' => EstimateStatus::Accepted,
@@ -417,7 +417,7 @@ class Estimate extends Document
417 417
 
418 418
     public function markAsDeclined(?Carbon $declinedAt = null): void
419 419
     {
420
-        $declinedAt ??= now();
420
+        $declinedAt ??= company_now();
421 421
 
422 422
         $this->update([
423 423
             'status' => EstimateStatus::Declined,
@@ -458,8 +458,8 @@ class Estimate extends Document
458 458
             'header' => $this->company->defaultInvoice->header,
459 459
             'subheader' => $this->company->defaultInvoice->subheader,
460 460
             'invoice_number' => Invoice::getNextDocumentNumber($this->company),
461
-            'date' => now(),
462
-            'due_date' => now()->addDays($this->company->defaultInvoice->payment_terms->getDays()),
461
+            'date' => company_today(),
462
+            'due_date' => company_today()->addDays($this->company->defaultInvoice->payment_terms->getDays()),
463 463
             'status' => InvoiceStatus::Draft,
464 464
             'currency_code' => $this->currency_code,
465 465
             'discount_method' => $this->discount_method,
@@ -477,7 +477,7 @@ class Estimate extends Document
477 477
 
478 478
         $this->replicateLineItems($invoice);
479 479
 
480
-        $convertedAt ??= now();
480
+        $convertedAt ??= company_now();
481 481
 
482 482
         $this->update([
483 483
             'status' => EstimateStatus::Converted,

+ 6
- 6
app/Models/Accounting/Invoice.php 查看文件

@@ -206,7 +206,7 @@ class Invoice extends Document
206 206
 
207 207
     public function shouldBeOverdue(): bool
208 208
     {
209
-        return $this->due_date->isBefore(today()) && $this->canBeOverdue();
209
+        return $this->due_date->isBefore(company_today()) && $this->canBeOverdue();
210 210
     }
211 211
 
212 212
     public function isDraft(): bool
@@ -369,7 +369,7 @@ class Invoice extends Document
369 369
 
370 370
         $this->createApprovalTransaction();
371 371
 
372
-        $approvedAt ??= now();
372
+        $approvedAt ??= company_now();
373 373
 
374 374
         $this->update([
375 375
             'approved_at' => $approvedAt,
@@ -613,7 +613,7 @@ class Invoice extends Document
613 613
 
614 614
     public function markAsSent(?Carbon $sentAt = null): void
615 615
     {
616
-        $sentAt ??= now();
616
+        $sentAt ??= company_now();
617 617
 
618 618
         $this->update([
619 619
             'status' => InvoiceStatus::Sent,
@@ -623,7 +623,7 @@ class Invoice extends Document
623 623
 
624 624
     public function markAsViewed(?Carbon $viewedAt = null): void
625 625
     {
626
-        $viewedAt ??= now();
626
+        $viewedAt ??= company_now();
627 627
 
628 628
         $this->update([
629 629
             'status' => InvoiceStatus::Viewed,
@@ -654,8 +654,8 @@ class Invoice extends Document
654 654
             ->beforeReplicaSaved(function (self $original, self $replica) {
655 655
                 $replica->status = InvoiceStatus::Draft;
656 656
                 $replica->invoice_number = self::getNextDocumentNumber();
657
-                $replica->date = now();
658
-                $replica->due_date = now()->addDays($original->company->defaultInvoice->payment_terms->getDays());
657
+                $replica->date = company_today();
658
+                $replica->due_date = company_today()->addDays($original->company->defaultInvoice->payment_terms->getDays());
659 659
             })
660 660
             ->databaseTransaction()
661 661
             ->after(function (self $original, self $replica) {

+ 9
- 6
app/Models/Accounting/RecurringInvoice.php 查看文件

@@ -22,6 +22,7 @@ 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;
25 26
 use App\Support\ScheduleHandler;
26 27
 use App\Utilities\Localization\Timezone;
27 28
 use Filament\Actions\Action;
@@ -208,7 +209,7 @@ class RecurringInvoice extends Document
208 209
         }
209 210
 
210 211
         // For unapproved/draft invoices, start date must be today or in the future
211
-        return $this->start_date?->gte(today()) ?? false;
212
+        return $this->start_date?->gte(company_today()) ?? false;
212 213
     }
213 214
 
214 215
     public function getScheduleDescription(): ?string
@@ -401,7 +402,7 @@ class RecurringInvoice extends Document
401 402
                 $data = $record->attributesToArray();
402 403
 
403 404
                 $data['day_of_month'] ??= DayOfMonth::First;
404
-                $data['start_date'] ??= now()->addMonth()->startOfMonth();
405
+                $data['start_date'] ??= company_today()->addMonth()->startOfMonth();
405 406
 
406 407
                 $form->fill($data);
407 408
             })
@@ -506,7 +507,8 @@ class RecurringInvoice extends Document
506 507
                             ->label('First invoice date')
507 508
                             ->softRequired()
508 509
                             ->live()
509
-                            ->minDate(today())
510
+                            ->minDate(company_today())
511
+                            ->timezone(CompanySettingsService::getDefaultTimezone())
510 512
                             ->closeOnDateSelection()
511 513
                             ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
512 514
                                 $handler = new ScheduleHandler($set, $get);
@@ -525,7 +527,7 @@ class RecurringInvoice extends Document
525 527
                                     $endType = EndType::parse($state);
526 528
 
527 529
                                     $set('max_occurrences', $endType?->isAfter() ? 1 : null);
528
-                                    $set('end_date', $endType?->isOn() ? now()->addMonth()->startOfMonth() : null);
530
+                                    $set('end_date', $endType?->isOn() ? company_today()->addMonth()->startOfMonth() : null);
529 531
                                 });
530 532
 
531 533
                             $endType = EndType::parse($get('end_type'));
@@ -539,6 +541,7 @@ class RecurringInvoice extends Document
539 541
 
540 542
                             if ($endType?->isOn()) {
541 543
                                 $components[] = Forms\Components\DatePicker::make('end_date')
544
+                                    ->timezone(CompanySettingsService::getDefaultTimezone())
542 545
                                     ->live();
543 546
                             }
544 547
 
@@ -603,7 +606,7 @@ class RecurringInvoice extends Document
603 606
             throw new \RuntimeException('Invoice is not in draft status.');
604 607
         }
605 608
 
606
-        $approvedAt ??= now();
609
+        $approvedAt ??= company_now();
607 610
 
608 611
         $this->update([
609 612
             'approved_at' => $approvedAt,
@@ -690,7 +693,7 @@ class RecurringInvoice extends Document
690 693
 
691 694
         $nextDate = $this->calculateNextDate();
692 695
 
693
-        if (! $nextDate || $nextDate->startOfDay()->isFuture()) {
696
+        if (! $nextDate || $nextDate->startOfDay()->isAfter(company_now())) {
694 697
             return false;
695 698
         }
696 699
 

+ 11
- 0
app/Models/Export.php 查看文件

@@ -0,0 +1,11 @@
1
+<?php
2
+
3
+namespace App\Models;
4
+
5
+use App\Concerns\CompanyOwned;
6
+use Filament\Actions\Exports\Models\Export as BaseExport;
7
+
8
+class Export extends BaseExport
9
+{
10
+    use CompanyOwned;
11
+}

+ 11
- 0
app/Models/Import.php 查看文件

@@ -0,0 +1,11 @@
1
+<?php
2
+
3
+namespace App\Models;
4
+
5
+use App\Concerns\CompanyOwned;
6
+use Filament\Actions\Imports\Models\Import as BaseImport;
7
+
8
+class Import extends BaseImport
9
+{
10
+    use CompanyOwned;
11
+}

+ 11
- 0
app/Models/Notification.php 查看文件

@@ -0,0 +1,11 @@
1
+<?php
2
+
3
+namespace App\Models;
4
+
5
+use App\Concerns\CompanyOwned;
6
+use Illuminate\Notifications\DatabaseNotification;
7
+
8
+class Notification extends DatabaseNotification
9
+{
10
+    use CompanyOwned;
11
+}

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

@@ -63,7 +63,7 @@ class Localization extends Model
63 63
     public static function getWeekStart(string $locale): int
64 64
     {
65 65
         /** @var Carbon $date */
66
-        $date = now()->locale($locale);
66
+        $date = company_now()->locale($locale);
67 67
 
68 68
         $firstDay = $date->startOfWeek()->dayOfWeekIso;
69 69
 
@@ -91,7 +91,7 @@ class Localization extends Model
91 91
     public function fiscalYearEndDate(): string
92 92
     {
93 93
         return once(function () {
94
-            $today = now();
94
+            $today = company_now();
95 95
             $fiscalYearEndThisYear = Carbon::createFromDate($today->year, $this->fiscal_year_end_month, $this->fiscal_year_end_day);
96 96
 
97 97
             if ($today->gt($fiscalYearEndThisYear)) {

+ 5
- 0
app/Models/User.php 查看文件

@@ -124,4 +124,9 @@ class User extends Authenticatable implements FilamentUser, HasAvatar, HasDefaul
124 124
 
125 125
         return true;
126 126
     }
127
+
128
+    public function notifications(): MorphMany
129
+    {
130
+        return $this->morphMany(Notification::class, 'notifiable')->latest();
131
+    }
127 132
 }

+ 1
- 1
app/Observers/AdjustmentObserver.php 查看文件

@@ -78,7 +78,7 @@ class AdjustmentObserver
78 78
 
79 79
         // Ensure consistency between paused status and paused_at field
80 80
         if ($adjustment->status === AdjustmentStatus::Paused && ! $adjustment->paused_at) {
81
-            $adjustment->paused_at = now();
81
+            $adjustment->paused_at = company_now();
82 82
         }
83 83
     }
84 84
 }

+ 1
- 1
app/Observers/RecurringInvoiceObserver.php 查看文件

@@ -20,7 +20,7 @@ class RecurringInvoiceObserver
20 20
 
21 21
         if ($recurringInvoice->end_type?->isAfter() && $recurringInvoice->occurrences_count >= $recurringInvoice->max_occurrences) {
22 22
             $recurringInvoice->status = RecurringInvoiceStatus::Ended;
23
-            $recurringInvoice->ended_at = now();
23
+            $recurringInvoice->ended_at = company_now();
24 24
         }
25 25
     }
26 26
 

+ 13
- 0
app/Providers/AppServiceProvider.php 查看文件

@@ -3,12 +3,18 @@
3 3
 namespace App\Providers;
4 4
 
5 5
 use App\Http\Responses\LoginRedirectResponse;
6
+use App\Models\Export;
7
+use App\Models\Import;
8
+use App\Models\Notification;
6 9
 use App\Services\DateRangeService;
10
+use Filament\Actions\Exports\Models\Export as BaseExport;
11
+use Filament\Actions\Imports\Models\Import as BaseImport;
7 12
 use Filament\Http\Responses\Auth\Contracts\LoginResponse;
8 13
 use Filament\Notifications\Livewire\Notifications;
9 14
 use Filament\Support\Assets\Js;
10 15
 use Filament\Support\Enums\Alignment;
11 16
 use Filament\Support\Facades\FilamentAsset;
17
+use Illuminate\Notifications\DatabaseNotification;
12 18
 use Illuminate\Support\ServiceProvider;
13 19
 
14 20
 class AppServiceProvider extends ServiceProvider
@@ -27,6 +33,13 @@ class AppServiceProvider extends ServiceProvider
27 33
      */
28 34
     public function boot(): void
29 35
     {
36
+        // Bind custom Import and Export models
37
+        $this->app->bind(BaseImport::class, Import::class);
38
+        $this->app->bind(BaseExport::class, Export::class);
39
+
40
+        // Bind custom Notification model
41
+        $this->app->bind(DatabaseNotification::class, Notification::class);
42
+
30 43
         Notifications::alignment(Alignment::Center);
31 44
 
32 45
         FilamentAsset::register([

+ 5
- 0
app/Providers/Filament/CompanyPanelProvider.php 查看文件

@@ -268,6 +268,11 @@ class CompanyPanelProvider extends PanelProvider
268 268
     {
269 269
         $this->configureSelect();
270 270
 
271
+        Forms\Components\FileUpload::configureUsing(function (Forms\Components\FileUpload $component): void {
272
+            $component
273
+                ->hidden(is_demo_environment());
274
+        });
275
+
271 276
         Actions\CreateAction::configureUsing(static fn (Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
272 277
         Actions\EditAction::configureUsing(static fn (Actions\EditAction $action) => FilamentComponentConfigurator::configureActionModals($action));
273 278
         Actions\DeleteAction::configureUsing(static fn (Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));

+ 33
- 21
app/Providers/MacroServiceProvider.php 查看文件

@@ -5,9 +5,7 @@ namespace App\Providers;
5 5
 use Akaunting\Money\Currency;
6 6
 use Akaunting\Money\Money;
7 7
 use App\Enums\Accounting\AdjustmentComputation;
8
-use App\Enums\Setting\DateFormat;
9 8
 use App\Models\Accounting\AccountSubtype;
10
-use App\Models\Setting\Localization;
11 9
 use App\Services\CompanySettingsService;
12 10
 use App\Utilities\Accounting\AccountCode;
13 11
 use App\Utilities\Currency\CurrencyAccessor;
@@ -173,24 +171,17 @@ class MacroServiceProvider extends ServiceProvider
173 171
         });
174 172
 
175 173
         TextColumn::macro('defaultDateFormat', function (): static {
176
-            $localization = Localization::firstOrFail();
174
+            $dateFormat = CompanySettingsService::getDefaultDateFormat();
177 175
 
178
-            $dateFormat = $localization->date_format->value ?? DateFormat::DEFAULT;
179
-            $timezone = $localization->timezone ?? Carbon::now()->timezoneName;
180
-
181
-            $this->date($dateFormat, $timezone);
176
+            $this->date($dateFormat);
182 177
 
183 178
             return $this;
184 179
         });
185 180
 
186 181
         DatePicker::macro('defaultDateFormat', function (): static {
187
-            $localization = Localization::firstOrFail();
188
-
189
-            $dateFormat = $localization->date_format->value ?? DateFormat::DEFAULT;
190
-            $timezone = $localization->timezone ?? Carbon::now()->timezoneName;
182
+            $dateFormat = CompanySettingsService::getDefaultDateFormat();
191 183
 
192
-            $this->displayFormat($dateFormat)
193
-                ->timezone($timezone);
184
+            $this->displayFormat($dateFormat);
194 185
 
195 186
             return $this;
196 187
         });
@@ -357,21 +348,32 @@ class MacroServiceProvider extends ServiceProvider
357 348
             return $this;
358 349
         });
359 350
 
351
+        // In your macro - simpler logic
360 352
         TextColumn::macro('asRelativeDay', function (?string $timezone = null): static {
361 353
             $this->formatStateUsing(function (TextColumn $column, mixed $state) use ($timezone) {
362 354
                 if (blank($state)) {
363 355
                     return null;
364 356
                 }
365 357
 
366
-                $date = Carbon::parse($state)
367
-                    ->setTimezone($timezone ?? $column->getTimezone());
358
+                $timezone ??= $column->getTimezone() ?? CompanySettingsService::getDefaultTimezone();
359
+
360
+                // Use shiftTimezone to shift UTC calendar date to the specified timezone
361
+                // Using setTimezone would convert which is wrong for calendar dates
362
+                $date = Carbon::parse($state)->shiftTimezone($timezone);
368 363
 
369 364
                 if ($date->isToday()) {
370 365
                     return 'Today';
366
+                } elseif ($date->isTomorrow()) {
367
+                    return 'Tomorrow';
368
+                } elseif ($date->isYesterday()) {
369
+                    return 'Yesterday';
371 370
                 }
372 371
 
373 372
                 return $date->diffForHumans([
374 373
                     'options' => CarbonInterface::ONE_DAY_WORDS,
374
+                    'skip' => ['month', 'week'], // Skip larger units, force days and years only
375
+                    'parts' => 2,
376
+                    'join' => ', ',
375 377
                 ]);
376 378
             });
377 379
 
@@ -384,15 +386,25 @@ class MacroServiceProvider extends ServiceProvider
384 386
                     return null;
385 387
                 }
386 388
 
387
-                $date = Carbon::parse($state)
388
-                    ->setTimezone($timezone ?? $entry->getTimezone());
389
+                $timezone ??= $entry->getTimezone() ?? CompanySettingsService::getDefaultTimezone();
390
+
391
+                // Use shiftTimezone to shift UTC calendar date to the specified timezone
392
+                // Using setTimezone would convert which is wrong for calendar dates
393
+                $date = Carbon::parse($state)->shiftTimezone($timezone);
389 394
 
390 395
                 if ($date->isToday()) {
391 396
                     return 'Today';
397
+                } elseif ($date->isTomorrow()) {
398
+                    return 'Tomorrow';
399
+                } elseif ($date->isYesterday()) {
400
+                    return 'Yesterday';
392 401
                 }
393 402
 
394 403
                 return $date->diffForHumans([
395 404
                     'options' => CarbonInterface::ONE_DAY_WORDS,
405
+                    'skip' => ['month', 'week'], // Skip larger units, force days and years only
406
+                    'parts' => 2,
407
+                    'join' => ', ',
396 408
                 ]);
397 409
             });
398 410
 
@@ -471,11 +483,11 @@ class MacroServiceProvider extends ServiceProvider
471 483
         });
472 484
 
473 485
         Carbon::macro('toDefaultDateFormat', function () {
474
-            $companyId = auth()->user()?->current_company_id;
475
-            $dateFormat = CompanySettingsService::getDefaultDateFormat($companyId);
476
-            $timezone = CompanySettingsService::getDefaultTimezone($companyId);
486
+            $dateFormat = CompanySettingsService::getDefaultDateFormat();
487
+
488
+            $this->format($dateFormat);
477 489
 
478
-            return $this->setTimezone($timezone)->format($dateFormat);
490
+            return $this;
479 491
         });
480 492
 
481 493
         ExportColumn::macro('money', function () {

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

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

+ 3
- 3
app/Services/AccountService.php 查看文件

@@ -372,12 +372,12 @@ class AccountService
372 372
     {
373 373
         $earliestDate = Transaction::min('posted_at');
374 374
 
375
-        return $earliestDate ?? today()->toDateTimeString();
375
+        return $earliestDate ?? company_today()->toDateTimeString();
376 376
     }
377 377
 
378 378
     public function getUnpaidClientInvoices(?string $asOfDate = null): Builder
379 379
     {
380
-        $asOfDate = $asOfDate ?? now()->toDateString();
380
+        $asOfDate = $asOfDate ?? company_today()->toDateString();
381 381
         $driver = DB::getDriverName();
382 382
 
383 383
         $datediff = $driver === 'pgsql'
@@ -400,7 +400,7 @@ class AccountService
400 400
 
401 401
     public function getUnpaidVendorBills(?string $asOfDate = null): Builder
402 402
     {
403
-        $asOfDate = $asOfDate ?? now()->toDateString();
403
+        $asOfDate = $asOfDate ?? company_today()->toDateString();
404 404
         $driver = DB::getDriverName();
405 405
 
406 406
         $datediff = $driver === 'pgsql'

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

@@ -14,6 +14,8 @@ class CompanySettingsService
14 14
 
15 15
     public static function getSettings(?int $companyId = null): array
16 16
     {
17
+        $companyId ??= session('current_company_id');
18
+
17 19
         if (! $companyId) {
18 20
             return self::getDefaultSettings();
19 21
         }

+ 2
- 2
app/Services/ExportService.php 查看文件

@@ -28,7 +28,7 @@ class ExportService
28 28
             $dateLabel = $formattedAsOfDate;
29 29
         }
30 30
 
31
-        $timestamp = Carbon::now()->format('Y-m-d_H-i-s');
31
+        $timestamp = company_now()->format('Y-m-d_H-i-s');
32 32
 
33 33
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.csv';
34 34
 
@@ -269,7 +269,7 @@ class ExportService
269 269
             $dateLabel = $formattedAsOfDate;
270 270
         }
271 271
 
272
-        $timestamp = Carbon::now()->format('Y-m-d_H-i-s');
272
+        $timestamp = company_now()->format('Y-m-d_H-i-s');
273 273
 
274 274
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.pdf';
275 275
 

+ 1
- 1
app/Support/ScheduleHandler.php 查看文件

@@ -22,7 +22,7 @@ class ScheduleHandler
22 22
 
23 23
     public function __construct(Set $set, ?Get $get = null)
24 24
     {
25
-        $this->today = today()->toImmutable();
25
+        $this->today = company_today()->toImmutable();
26 26
         $this->set = $set;
27 27
         $this->get = $get;
28 28
     }

+ 17
- 17
composer.lock 查看文件

@@ -497,16 +497,16 @@
497 497
         },
498 498
         {
499 499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.351.4",
500
+            "version": "3.351.7",
501 501
             "source": {
502 502
                 "type": "git",
503 503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "19dfd2db0f6ce35e1947e8bff4d956614b4aaaaf"
504
+                "reference": "9506d7fdb3cb84f8d7b175c594db9993264814be"
505 505
             },
506 506
             "dist": {
507 507
                 "type": "zip",
508
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/19dfd2db0f6ce35e1947e8bff4d956614b4aaaaf",
509
-                "reference": "19dfd2db0f6ce35e1947e8bff4d956614b4aaaaf",
508
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/9506d7fdb3cb84f8d7b175c594db9993264814be",
509
+                "reference": "9506d7fdb3cb84f8d7b175c594db9993264814be",
510 510
                 "shasum": ""
511 511
             },
512 512
             "require": {
@@ -588,9 +588,9 @@
588 588
             "support": {
589 589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
590 590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
591
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.351.4"
591
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.351.7"
592 592
             },
593
-            "time": "2025-07-22T18:12:04+00:00"
593
+            "time": "2025-07-25T18:06:34+00:00"
594 594
         },
595 595
         {
596 596
             "name": "aws/aws-sdk-php-laravel",
@@ -3516,16 +3516,16 @@
3516 3516
         },
3517 3517
         {
3518 3518
             "name": "laravel/socialite",
3519
-            "version": "v5.22.0",
3519
+            "version": "v5.23.0",
3520 3520
             "source": {
3521 3521
                 "type": "git",
3522 3522
                 "url": "https://github.com/laravel/socialite.git",
3523
-                "reference": "99d0fe750a7c68e5b60d8b1850de2554f3ea4072"
3523
+                "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5"
3524 3524
             },
3525 3525
             "dist": {
3526 3526
                 "type": "zip",
3527
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/99d0fe750a7c68e5b60d8b1850de2554f3ea4072",
3528
-                "reference": "99d0fe750a7c68e5b60d8b1850de2554f3ea4072",
3527
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5",
3528
+                "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5",
3529 3529
                 "shasum": ""
3530 3530
             },
3531 3531
             "require": {
@@ -3584,7 +3584,7 @@
3584 3584
                 "issues": "https://github.com/laravel/socialite/issues",
3585 3585
                 "source": "https://github.com/laravel/socialite"
3586 3586
             },
3587
-            "time": "2025-07-08T22:07:57+00:00"
3587
+            "time": "2025-07-23T14:16:08+00:00"
3588 3588
         },
3589 3589
         {
3590 3590
             "name": "laravel/tinker",
@@ -4448,16 +4448,16 @@
4448 4448
         },
4449 4449
         {
4450 4450
             "name": "masterminds/html5",
4451
-            "version": "2.9.0",
4451
+            "version": "2.10.0",
4452 4452
             "source": {
4453 4453
                 "type": "git",
4454 4454
                 "url": "https://github.com/Masterminds/html5-php.git",
4455
-                "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
4455
+                "reference": "fcf91eb64359852f00d921887b219479b4f21251"
4456 4456
             },
4457 4457
             "dist": {
4458 4458
                 "type": "zip",
4459
-                "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
4460
-                "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6",
4459
+                "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/fcf91eb64359852f00d921887b219479b4f21251",
4460
+                "reference": "fcf91eb64359852f00d921887b219479b4f21251",
4461 4461
                 "shasum": ""
4462 4462
             },
4463 4463
             "require": {
@@ -4509,9 +4509,9 @@
4509 4509
             ],
4510 4510
             "support": {
4511 4511
                 "issues": "https://github.com/Masterminds/html5-php/issues",
4512
-                "source": "https://github.com/Masterminds/html5-php/tree/2.9.0"
4512
+                "source": "https://github.com/Masterminds/html5-php/tree/2.10.0"
4513 4513
             },
4514
-            "time": "2024-03-31T07:05:07+00:00"
4514
+            "time": "2025-07-25T09:04:22+00:00"
4515 4515
         },
4516 4516
         {
4517 4517
             "name": "matomo/device-detector",

+ 29
- 0
database/migrations/2025_07_13_042000_add_company_id_to_notifications_table.php 查看文件

@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\DB;
6
+use Illuminate\Support\Facades\Schema;
7
+
8
+return new class extends Migration
9
+{
10
+    /**
11
+     * Run the migrations.
12
+     */
13
+    public function up(): void
14
+    {
15
+        // Clear existing notifications since we're adding company scoping
16
+        DB::table('notifications')->truncate();
17
+
18
+        Schema::table('notifications', function (Blueprint $table) {
19
+            $table->foreignId('company_id')
20
+                ->after('notifiable_id')
21
+                ->nullable()
22
+                ->constrained()
23
+                ->cascadeOnDelete();
24
+
25
+            $table->index(['company_id', 'created_at']);
26
+            $table->index(['company_id', 'notifiable_type', 'notifiable_id']);
27
+        });
28
+    }
29
+};

+ 42
- 0
database/migrations/2025_07_13_043000_add_company_id_to_imports_exports_tables.php 查看文件

@@ -0,0 +1,42 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\DB;
6
+use Illuminate\Support\Facades\Schema;
7
+
8
+return new class extends Migration
9
+{
10
+    /**
11
+     * Run the migrations.
12
+     */
13
+    public function up(): void
14
+    {
15
+        // Disable foreign key checks and clear existing data
16
+        DB::statement('SET FOREIGN_KEY_CHECKS=0;');
17
+        DB::table('failed_import_rows')->truncate();
18
+        DB::table('exports')->truncate();
19
+        DB::table('imports')->truncate();
20
+        DB::statement('SET FOREIGN_KEY_CHECKS=1;');
21
+
22
+        Schema::table('imports', function (Blueprint $table) {
23
+            $table->foreignId('company_id')
24
+                ->after('id')
25
+                ->nullable()
26
+                ->constrained()
27
+                ->cascadeOnDelete();
28
+
29
+            $table->index(['company_id', 'created_at']);
30
+        });
31
+
32
+        Schema::table('exports', function (Blueprint $table) {
33
+            $table->foreignId('company_id')
34
+                ->after('id')
35
+                ->nullable()
36
+                ->constrained()
37
+                ->cascadeOnDelete();
38
+
39
+            $table->index(['company_id', 'created_at']);
40
+        });
41
+    }
42
+};

+ 8
- 6
package-lock.json 查看文件

@@ -4,6 +4,7 @@
4 4
     "requires": true,
5 5
     "packages": {
6 6
         "": {
7
+            "name": "erpsaas",
7 8
             "devDependencies": {
8 9
                 "@tailwindcss/forms": "^0.5.9",
9 10
                 "@tailwindcss/typography": "^0.5.15",
@@ -1022,9 +1023,9 @@
1022 1023
             }
1023 1024
         },
1024 1025
         "node_modules/brace-expansion": {
1025
-            "version": "2.0.1",
1026
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
1027
-            "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
1026
+            "version": "2.0.2",
1027
+            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
1028
+            "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
1028 1029
             "dev": true,
1029 1030
             "license": "MIT",
1030 1031
             "dependencies": {
@@ -1483,15 +1484,16 @@
1483 1484
             }
1484 1485
         },
1485 1486
         "node_modules/form-data": {
1486
-            "version": "4.0.2",
1487
-            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
1488
-            "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
1487
+            "version": "4.0.4",
1488
+            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz",
1489
+            "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==",
1489 1490
             "dev": true,
1490 1491
             "license": "MIT",
1491 1492
             "dependencies": {
1492 1493
                 "asynckit": "^0.4.0",
1493 1494
                 "combined-stream": "^1.0.8",
1494 1495
                 "es-set-tostringtag": "^2.1.0",
1496
+                "hasown": "^2.0.2",
1495 1497
                 "mime-types": "^2.1.12"
1496 1498
             },
1497 1499
             "engines": {

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

@@ -233,7 +233,7 @@ it('can add an income or expense transaction', function (TransactionType $transa
233 233
     livewire(ListTransactions::class)
234 234
         ->mountAction($actionName)
235 235
         ->assertActionDataSet([
236
-            'posted_at' => today(),
236
+            'posted_at' => company_today()->toDateString(),
237 237
             'type' => $transactionType,
238 238
             'bank_account_id' => $defaultBankAccount->id,
239 239
             'amount' => '0.00',

Loading…
取消
儲存