Преглед изворни кода

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

Development 3.x
3.x
Andrew Wallo пре 2 месеци
родитељ
комит
c37cc9a0e5
No account linked to committer's email address
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
 
2
 
3
 namespace App\Concerns;
3
 namespace App\Concerns;
4
 
4
 
5
+use App\Models\Notification;
6
+use App\Models\User;
5
 use App\Scopes\CurrentCompanyScope;
7
 use App\Scopes\CurrentCompanyScope;
6
 use Illuminate\Database\Eloquent\ModelNotFoundException;
8
 use Illuminate\Database\Eloquent\ModelNotFoundException;
7
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
9
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
17
             if (empty($model->company_id)) {
19
             if (empty($model->company_id)) {
18
                 $companyId = session('current_company_id');
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
                     session(['current_company_id' => $companyId]);
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
                 if ($companyId) {
34
                 if ($companyId) {
26
                     $model->company_id = $companyId;
35
                     $model->company_id = $companyId;
27
                 } else {
36
                 } else {

+ 4
- 4
app/Concerns/HasTransactionAction.php Прегледај датотеку

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

+ 1
- 1
app/Enums/Setting/DateFormat.php Прегледај датотеку

36
 
36
 
37
     public function getLabel(): ?string
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
     {
43
     {
44
         $days = $this->getDays() ?? 0;
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
 
18
 
19
     public function getLabel(): ?string
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
                         Cluster::make([
177
                         Cluster::make([
178
                             Select::make('fiscal_year_end_month')
178
                             Select::make('fiscal_year_end_month')
179
                                 ->softRequired()
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
                                 ->afterStateUpdated(static fn (Set $set) => $set('fiscal_year_end_day', null))
181
                                 ->afterStateUpdated(static fn (Set $set) => $set('fiscal_year_end_day', null))
182
                                 ->columnSpan(2)
182
                                 ->columnSpan(2)
183
                                 ->live(),
183
                                 ->live(),
188
                                 ->options(function (Get $get) {
188
                                 ->options(function (Get $get) {
189
                                     $month = (int) $get('fiscal_year_end_month');
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
                                     return array_combine(range(1, $daysInMonth), range(1, $daysInMonth));
193
                                     return array_combine(range(1, $daysInMonth), range(1, $daysInMonth));
194
                                 })
194
                                 })

+ 6
- 1
app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php Прегледај датотеку

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

+ 2
- 1
app/Filament/Company/Clusters/Settings/Resources/DocumentDefaultResource.php Прегледај датотеку

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

+ 20
- 3
app/Filament/Company/Pages/Concerns/HasDeferredFiltersForm.php Прегледај датотеку

4
 
4
 
5
 use Filament\Actions\Action;
5
 use Filament\Actions\Action;
6
 use Filament\Forms\Components\DatePicker;
6
 use Filament\Forms\Components\DatePicker;
7
+use Filament\Forms\Components\DateTimePicker;
7
 use Filament\Forms\Form;
8
 use Filament\Forms\Form;
8
 use Illuminate\Support\Arr;
9
 use Illuminate\Support\Arr;
9
 use Illuminate\Support\Carbon;
10
 use Illuminate\Support\Carbon;
170
         $flatFields = $this->getFiltersForm()->getFlatFields();
171
         $flatFields = $this->getFiltersForm()->getFlatFields();
171
 
172
 
172
         foreach ($this->filters as $key => $value) {
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
 use App\Filament\Company\Pages\Reports;
9
 use App\Filament\Company\Pages\Reports;
10
 use App\Filament\Forms\Components\DateRangeSelect;
10
 use App\Filament\Forms\Components\DateRangeSelect;
11
 use App\Models\Company;
11
 use App\Models\Company;
12
+use App\Services\CompanySettingsService;
12
 use App\Services\DateRangeService;
13
 use App\Services\DateRangeService;
13
 use App\Support\Column;
14
 use App\Support\Column;
14
 use Filament\Actions\Action;
15
 use Filament\Actions\Action;
253
         return DatePicker::make('startDate')
254
         return DatePicker::make('startDate')
254
             ->label('Start date')
255
             ->label('Start date')
255
             ->live()
256
             ->live()
257
+            ->timezone(CompanySettingsService::getDefaultTimezone())
256
             ->afterStateUpdated(static function ($state, Set $set) {
258
             ->afterStateUpdated(static function ($state, Set $set) {
257
                 $set('dateRange', 'Custom');
259
                 $set('dateRange', 'Custom');
258
             });
260
             });
263
         return DatePicker::make('endDate')
265
         return DatePicker::make('endDate')
264
             ->label('End date')
266
             ->label('End date')
265
             ->live()
267
             ->live()
268
+            ->timezone(CompanySettingsService::getDefaultTimezone())
266
             ->afterStateUpdated(static function (Set $set) {
269
             ->afterStateUpdated(static function (Set $set) {
267
                 $set('dateRange', 'Custom');
270
                 $set('dateRange', 'Custom');
268
             });
271
             });

+ 2
- 2
app/Filament/Company/Resources/Accounting/BudgetResource.php Прегледај датотеку

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

+ 2
- 2
app/Filament/Company/Resources/Accounting/BudgetResource/Pages/CreateBudget.php Прегледај датотеку

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

+ 8
- 8
app/Filament/Company/Resources/Purchases/BillResource.php Прегледај датотеку

88
                                     Forms\Components\DatePicker::make('date')
88
                                     Forms\Components\DatePicker::make('date')
89
                                         ->label('Bill date')
89
                                         ->label('Bill date')
90
                                         ->live()
90
                                         ->live()
91
-                                        ->default(now())
91
+                                        ->default(company_today()->toDateString())
92
                                         ->disabled(function (?Bill $record) {
92
                                         ->disabled(function (?Bill $record) {
93
                                             return $record?->hasPayments();
93
                                             return $record?->hasPayments();
94
                                         })
94
                                         })
95
                                         ->columnSpan(2)
95
                                         ->columnSpan(2)
96
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
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
                                             if ($date && $dueDate && $date > $dueDate) {
100
                                             if ($date && $dueDate && $date > $dueDate) {
101
                                                 $set('due_date', $date);
101
                                                 $set('due_date', $date);
137
                                     ->columns(3),
137
                                     ->columns(3),
138
                                 Forms\Components\DatePicker::make('due_date')
138
                                 Forms\Components\DatePicker::make('due_date')
139
                                     ->label('Due date')
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
                                     ->required()
143
                                     ->required()
144
                                     ->live()
144
                                     ->live()
450
                             })
450
                             })
451
                             ->mountUsing(function (Bill $record, Form $form) {
451
                             ->mountUsing(function (Bill $record, Form $form) {
452
                                 $form->fill([
452
                                 $form->fill([
453
-                                    'posted_at' => now(),
453
+                                    'posted_at' => company_today()->toDateString(),
454
                                     'amount' => $record->amount_due,
454
                                     'amount' => $record->amount_due,
455
                                 ]);
455
                                 ]);
456
                             })
456
                             })
614
                         ->beforeReplicaSaved(function (Bill $replica) {
614
                         ->beforeReplicaSaved(function (Bill $replica) {
615
                             $replica->status = BillStatus::Open;
615
                             $replica->status = BillStatus::Open;
616
                             $replica->bill_number = Bill::getNextDocumentNumber();
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
                         ->withReplicatedRelationships(['lineItems'])
620
                         ->withReplicatedRelationships(['lineItems'])
621
                         ->withExcludedRelationshipAttributes('lineItems', [
621
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 1
- 1
app/Filament/Company/Resources/Purchases/BillResource/Pages/PayBills.php Прегледај датотеку

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

+ 1
- 1
app/Filament/Company/Resources/Purchases/BillResource/RelationManagers/PaymentsRelationManager.php Прегледај датотеку

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

+ 3
- 3
app/Filament/Company/Resources/Purchases/BillResource/Widgets/BillOverview.php Прегледај датотеку

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

+ 3
- 3
app/Filament/Company/Resources/Purchases/VendorResource/Widgets/BillOverview.php Прегледај датотеку

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

+ 1
- 1
app/Filament/Company/Resources/Sales/ClientResource/Widgets/InvoiceOverview.php Прегледај датотеку

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

+ 7
- 7
app/Filament/Company/Resources/Sales/EstimateResource.php Прегледај датотеку

91
                                     Forms\Components\DatePicker::make('date')
91
                                     Forms\Components\DatePicker::make('date')
92
                                         ->label('Estimate date')
92
                                         ->label('Estimate date')
93
                                         ->live()
93
                                         ->live()
94
-                                        ->default(now())
94
+                                        ->default(company_today()->toDateString())
95
                                         ->columnSpan(2)
95
                                         ->columnSpan(2)
96
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
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
                                             if ($date && $expirationDate && $date > $expirationDate) {
100
                                             if ($date && $expirationDate && $date > $expirationDate) {
101
                                                 $set('expiration_date', $date);
101
                                                 $set('expiration_date', $date);
137
                                 Forms\Components\DatePicker::make('expiration_date')
137
                                 Forms\Components\DatePicker::make('expiration_date')
138
                                     ->label('Expiration date')
138
                                     ->label('Expiration date')
139
                                     ->default(function () use ($settings) {
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
                                     ->minDate(static function (Forms\Get $get) {
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
                                     ->live()
145
                                     ->live()
146
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
146
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
470
                         ->beforeReplicaSaved(function (Estimate $replica) {
470
                         ->beforeReplicaSaved(function (Estimate $replica) {
471
                             $replica->status = EstimateStatus::Draft;
471
                             $replica->status = EstimateStatus::Draft;
472
                             $replica->estimate_number = Estimate::getNextDocumentNumber();
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
                         ->withReplicatedRelationships(['lineItems'])
476
                         ->withReplicatedRelationships(['lineItems'])
477
                         ->withExcludedRelationshipAttributes('lineItems', [
477
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 7
- 7
app/Filament/Company/Resources/Sales/InvoiceResource.php Прегледај датотеку

95
                                     Forms\Components\DatePicker::make('date')
95
                                     Forms\Components\DatePicker::make('date')
96
                                         ->label('Invoice date')
96
                                         ->label('Invoice date')
97
                                         ->live()
97
                                         ->live()
98
-                                        ->default(now())
98
+                                        ->default(company_today()->toDateString())
99
                                         ->disabled(function (?Invoice $record) {
99
                                         ->disabled(function (?Invoice $record) {
100
                                             return $record?->hasPayments();
100
                                             return $record?->hasPayments();
101
                                         })
101
                                         })
102
                                         ->columnSpan(2)
102
                                         ->columnSpan(2)
103
                                         ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
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
                                             if ($date && $dueDate && $date > $dueDate) {
107
                                             if ($date && $dueDate && $date > $dueDate) {
108
                                                 $set('due_date', $date);
108
                                                 $set('due_date', $date);
145
                                 Forms\Components\DatePicker::make('due_date')
145
                                 Forms\Components\DatePicker::make('due_date')
146
                                     ->label('Payment due')
146
                                     ->label('Payment due')
147
                                     ->default(function () use ($settings) {
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
                                     ->minDate(static function (Forms\Get $get) {
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
                                     ->live()
153
                                     ->live()
154
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
154
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
538
                         ->beforeReplicaSaved(function (Invoice $replica) {
538
                         ->beforeReplicaSaved(function (Invoice $replica) {
539
                             $replica->status = InvoiceStatus::Draft;
539
                             $replica->status = InvoiceStatus::Draft;
540
                             $replica->invoice_number = Invoice::getNextDocumentNumber();
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
                         ->withReplicatedRelationships(['lineItems'])
544
                         ->withReplicatedRelationships(['lineItems'])
545
                         ->withExcludedRelationshipAttributes('lineItems', [
545
                         ->withExcludedRelationshipAttributes('lineItems', [

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/Pages/RecordPayments.php Прегледај датотеку

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

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php Прегледај датотеку

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

+ 1
- 1
app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php Прегледај датотеку

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

+ 4
- 1
app/Filament/Forms/Components/CreateAdjustmentSelect.php Прегледај датотеку

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

+ 3
- 3
app/Filament/Forms/Components/DateRangeSelect.php Прегледај датотеку

88
 
88
 
89
     public function processFiscalYear($year, Set $set): void
89
     public function processFiscalYear($year, Set $set): void
90
     {
90
     {
91
-        $currentYear = now()->year;
91
+        $currentYear = company_now()->year;
92
         $diff = $currentYear - $year;
92
         $diff = $currentYear - $year;
93
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
93
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
94
         $fiscalYearEnd = Carbon::parse($this->fiscalYearEndDate)->subYears($diff);
94
         $fiscalYearEnd = Carbon::parse($this->fiscalYearEndDate)->subYears($diff);
97
 
97
 
98
     public function processFiscalQuarter($quarter, $year, Set $set): void
98
     public function processFiscalQuarter($quarter, $year, Set $set): void
99
     {
99
     {
100
-        $currentYear = now()->year;
100
+        $currentYear = company_now()->year;
101
         $diff = $currentYear - $year;
101
         $diff = $currentYear - $year;
102
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
102
         $fiscalYearStart = Carbon::parse($this->fiscalYearStartDate)->subYears($diff);
103
         $quarterStart = $fiscalYearStart->copy()->addMonths(($quarter - 1) * 3);
103
         $quarterStart = $fiscalYearStart->copy()->addMonths(($quarter - 1) * 3);
134
         }
134
         }
135
 
135
 
136
         if ($this->endDateField) {
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
 use App\Enums\Accounting\AdjustmentComputation;
3
 use App\Enums\Accounting\AdjustmentComputation;
4
 use App\Enums\Setting\NumberFormat;
4
 use App\Enums\Setting\NumberFormat;
5
 use App\Models\Setting\Localization;
5
 use App\Models\Setting\Localization;
6
+use App\Services\CompanySettingsService;
6
 use Filament\Support\RawJs;
7
 use Filament\Support\RawJs;
8
+use Illuminate\Support\Carbon;
7
 
9
 
8
 if (! function_exists('generateJsCode')) {
10
 if (! function_exists('generateJsCode')) {
9
     function generateJsCode(string $precision, ?string $currency = null): string
11
     function generateJsCode(string $precision, ?string $currency = null): string
129
         return app()->environment('demo');
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
     public function handle(PlaidService $plaid, TransactionService $transactionService): void
30
     public function handle(PlaidService $plaid, TransactionService $transactionService): void
31
     {
31
     {
32
         $accessToken = $this->connectedBankAccount->access_token;
32
         $accessToken = $this->connectedBankAccount->access_token;
33
-        $endDate = Carbon::now()->toDateString();
33
+        $endDate = company_now()->toDateString();
34
         $startDate = Carbon::parse($this->startDate)->toDateString();
34
         $startDate = Carbon::parse($this->startDate)->toDateString();
35
         $allTransactions = [];
35
         $allTransactions = [];
36
         $offset = 0;
36
         $offset = 0;
64
             $transactionService->storeTransactions($this->company, $this->bankAccount, $newTransactions);
64
             $transactionService->storeTransactions($this->company, $this->bankAccount, $newTransactions);
65
 
65
 
66
             $this->connectedBankAccount->update([
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
             $bufferDays = 15;
39
             $bufferDays = 15;
40
             $lastImportedAtDate = Carbon::parse($connectedBankAccount->last_imported_at);
40
             $lastImportedAtDate = Carbon::parse($connectedBankAccount->last_imported_at);
41
             $startDate = $lastImportedAtDate->subDays($bufferDays)->toDateString();
41
             $startDate = $lastImportedAtDate->subDays($bufferDays)->toDateString();
42
-            $endDate = Carbon::now()->toDateString();
42
+            $endDate = company_today()->toDateString();
43
 
43
 
44
             $transactionsResponse = $plaidService->getTransactions($accessToken, $startDate, $endDate, [
44
             $transactionsResponse = $plaidService->getTransactions($accessToken, $startDate, $endDate, [
45
                 'account_ids' => [$connectedBankAccount->external_account_id],
45
                 'account_ids' => [$connectedBankAccount->external_account_id],
67
                 $transactionService->storeTransactions($this->company, $bankAccount, $newTransactions);
67
                 $transactionService->storeTransactions($this->company, $bankAccount, $newTransactions);
68
 
68
 
69
                 $connectedBankAccount->update([
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
                         'type' => $transactionType,
58
                         'type' => $transactionType,
59
                         'amount' => $formattedSimpleDifference,
59
                         'amount' => $formattedSimpleDifference,
60
                         'payment_channel' => 'other',
60
                         'payment_channel' => 'other',
61
-                        'posted_at' => today(),
61
+                        'posted_at' => company_today(),
62
                         'description' => $description,
62
                         'description' => $description,
63
                         'pending' => false,
63
                         'pending' => false,
64
                         'reviewed' => false,
64
                         'reviewed' => false,

+ 4
- 2
app/Livewire/Company/Service/ConnectedAccount/ListInstitutions.php Прегледај датотеку

8
 use App\Models\Banking\ConnectedBankAccount;
8
 use App\Models\Banking\ConnectedBankAccount;
9
 use App\Models\Banking\Institution;
9
 use App\Models\Banking\Institution;
10
 use App\Models\User;
10
 use App\Models\User;
11
+use App\Services\CompanySettingsService;
11
 use App\Services\PlaidService;
12
 use App\Services\PlaidService;
12
 use Filament\Actions\Action;
13
 use Filament\Actions\Action;
13
 use Filament\Actions\Concerns\InteractsWithActions;
14
 use Filament\Actions\Concerns\InteractsWithActions;
89
                 DatePicker::make('start_date')
90
                 DatePicker::make('start_date')
90
                     ->label('Start date')
91
                     ->label('Start date')
91
                     ->required()
92
                     ->required()
93
+                    ->timezone(CompanySettingsService::getDefaultTimezone())
92
                     ->placeholder('Select a start date for importing transactions.')
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
             ->action(function (array $data, ConnectedBankAccount $connectedBankAccount) {
98
             ->action(function (array $data, ConnectedBankAccount $connectedBankAccount) {
97
                 $selectedBankAccountId = $data['bank_account_id'] ?? $connectedBankAccount->bank_account_id;
99
                 $selectedBankAccountId = $data['bank_account_id'] ?? $connectedBankAccount->bank_account_id;

+ 5
- 5
app/Models/Accounting/Adjustment.php Прегледај датотеку

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

+ 3
- 3
app/Models/Accounting/Bill.php Прегледај датотеку

131
 
131
 
132
     public function shouldBeOverdue(): bool
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
     public function wasInitialized(): bool
137
     public function wasInitialized(): bool
426
             ->beforeReplicaSaved(function (self $original, self $replica) {
426
             ->beforeReplicaSaved(function (self $original, self $replica) {
427
                 $replica->status = BillStatus::Open;
427
                 $replica->status = BillStatus::Open;
428
                 $replica->bill_number = self::getNextDocumentNumber();
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
             ->databaseTransaction()
432
             ->databaseTransaction()
433
             ->after(function (self $original, self $replica) {
433
             ->after(function (self $original, self $replica) {

+ 5
- 5
app/Models/Accounting/Budget.php Прегледај датотеку

154
     public function scopeCurrentlyActive(Builder $query): Builder
154
     public function scopeCurrentlyActive(Builder $query): Builder
155
     {
155
     {
156
         return $query->active()
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
     protected function isCurrentlyInPeriod(): Attribute
161
     protected function isCurrentlyInPeriod(): Attribute
162
     {
162
     {
163
         return Attribute::get(function () {
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
             throw new \RuntimeException('Budget cannot be approved.');
174
             throw new \RuntimeException('Budget cannot be approved.');
175
         }
175
         }
176
 
176
 
177
-        $approvedAt ??= now();
177
+        $approvedAt ??= company_now();
178
 
178
 
179
         $this->update([
179
         $this->update([
180
             'status' => BudgetStatus::Active,
180
             'status' => BudgetStatus::Active,
191
             throw new \RuntimeException('Budget cannot be closed.');
191
             throw new \RuntimeException('Budget cannot be closed.');
192
         }
192
         }
193
 
193
 
194
-        $closedAt ??= now();
194
+        $closedAt ??= company_now();
195
 
195
 
196
         $this->update([
196
         $this->update([
197
             'status' => BudgetStatus::Closed,
197
             'status' => BudgetStatus::Closed,

+ 1
- 1
app/Models/Accounting/BudgetAllocation.php Прегледај датотеку

38
 
38
 
39
     public function isCurrentPeriod(): bool
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
 
132
 
133
     public function shouldBeExpired(): bool
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
     public function isDraft(): bool
138
     public function isDraft(): bool
257
             throw new \RuntimeException('Estimate is not in draft status.');
257
             throw new \RuntimeException('Estimate is not in draft status.');
258
         }
258
         }
259
 
259
 
260
-        $approvedAt ??= now();
260
+        $approvedAt ??= company_now();
261
 
261
 
262
         $this->update([
262
         $this->update([
263
             'approved_at' => $approvedAt,
263
             'approved_at' => $approvedAt,
316
 
316
 
317
     public function markAsSent(?Carbon $sentAt = null): void
317
     public function markAsSent(?Carbon $sentAt = null): void
318
     {
318
     {
319
-        $sentAt ??= now();
319
+        $sentAt ??= company_now();
320
 
320
 
321
         $this->update([
321
         $this->update([
322
             'status' => EstimateStatus::Sent,
322
             'status' => EstimateStatus::Sent,
326
 
326
 
327
     public function markAsViewed(?Carbon $viewedAt = null): void
327
     public function markAsViewed(?Carbon $viewedAt = null): void
328
     {
328
     {
329
-        $viewedAt ??= now();
329
+        $viewedAt ??= company_now();
330
 
330
 
331
         $this->update([
331
         $this->update([
332
             'status' => EstimateStatus::Viewed,
332
             'status' => EstimateStatus::Viewed,
357
             ->beforeReplicaSaved(function (self $original, self $replica) {
357
             ->beforeReplicaSaved(function (self $original, self $replica) {
358
                 $replica->status = EstimateStatus::Draft;
358
                 $replica->status = EstimateStatus::Draft;
359
                 $replica->estimate_number = self::getNextDocumentNumber();
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
             ->databaseTransaction()
363
             ->databaseTransaction()
364
             ->after(function (self $original, self $replica) {
364
             ->after(function (self $original, self $replica) {
388
 
388
 
389
     public function markAsAccepted(?Carbon $acceptedAt = null): void
389
     public function markAsAccepted(?Carbon $acceptedAt = null): void
390
     {
390
     {
391
-        $acceptedAt ??= now();
391
+        $acceptedAt ??= company_now();
392
 
392
 
393
         $this->update([
393
         $this->update([
394
             'status' => EstimateStatus::Accepted,
394
             'status' => EstimateStatus::Accepted,
417
 
417
 
418
     public function markAsDeclined(?Carbon $declinedAt = null): void
418
     public function markAsDeclined(?Carbon $declinedAt = null): void
419
     {
419
     {
420
-        $declinedAt ??= now();
420
+        $declinedAt ??= company_now();
421
 
421
 
422
         $this->update([
422
         $this->update([
423
             'status' => EstimateStatus::Declined,
423
             'status' => EstimateStatus::Declined,
458
             'header' => $this->company->defaultInvoice->header,
458
             'header' => $this->company->defaultInvoice->header,
459
             'subheader' => $this->company->defaultInvoice->subheader,
459
             'subheader' => $this->company->defaultInvoice->subheader,
460
             'invoice_number' => Invoice::getNextDocumentNumber($this->company),
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
             'status' => InvoiceStatus::Draft,
463
             'status' => InvoiceStatus::Draft,
464
             'currency_code' => $this->currency_code,
464
             'currency_code' => $this->currency_code,
465
             'discount_method' => $this->discount_method,
465
             'discount_method' => $this->discount_method,
477
 
477
 
478
         $this->replicateLineItems($invoice);
478
         $this->replicateLineItems($invoice);
479
 
479
 
480
-        $convertedAt ??= now();
480
+        $convertedAt ??= company_now();
481
 
481
 
482
         $this->update([
482
         $this->update([
483
             'status' => EstimateStatus::Converted,
483
             'status' => EstimateStatus::Converted,

+ 6
- 6
app/Models/Accounting/Invoice.php Прегледај датотеку

206
 
206
 
207
     public function shouldBeOverdue(): bool
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
     public function isDraft(): bool
212
     public function isDraft(): bool
369
 
369
 
370
         $this->createApprovalTransaction();
370
         $this->createApprovalTransaction();
371
 
371
 
372
-        $approvedAt ??= now();
372
+        $approvedAt ??= company_now();
373
 
373
 
374
         $this->update([
374
         $this->update([
375
             'approved_at' => $approvedAt,
375
             'approved_at' => $approvedAt,
613
 
613
 
614
     public function markAsSent(?Carbon $sentAt = null): void
614
     public function markAsSent(?Carbon $sentAt = null): void
615
     {
615
     {
616
-        $sentAt ??= now();
616
+        $sentAt ??= company_now();
617
 
617
 
618
         $this->update([
618
         $this->update([
619
             'status' => InvoiceStatus::Sent,
619
             'status' => InvoiceStatus::Sent,
623
 
623
 
624
     public function markAsViewed(?Carbon $viewedAt = null): void
624
     public function markAsViewed(?Carbon $viewedAt = null): void
625
     {
625
     {
626
-        $viewedAt ??= now();
626
+        $viewedAt ??= company_now();
627
 
627
 
628
         $this->update([
628
         $this->update([
629
             'status' => InvoiceStatus::Viewed,
629
             'status' => InvoiceStatus::Viewed,
654
             ->beforeReplicaSaved(function (self $original, self $replica) {
654
             ->beforeReplicaSaved(function (self $original, self $replica) {
655
                 $replica->status = InvoiceStatus::Draft;
655
                 $replica->status = InvoiceStatus::Draft;
656
                 $replica->invoice_number = self::getNextDocumentNumber();
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
             ->databaseTransaction()
660
             ->databaseTransaction()
661
             ->after(function (self $original, self $replica) {
661
             ->after(function (self $original, self $replica) {

+ 9
- 6
app/Models/Accounting/RecurringInvoice.php Прегледај датотеку

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

+ 11
- 0
app/Models/Export.php Прегледај датотеку

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 Прегледај датотеку

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 Прегледај датотеку

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

+ 5
- 0
app/Models/User.php Прегледај датотеку

124
 
124
 
125
         return true;
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
 
78
 
79
         // Ensure consistency between paused status and paused_at field
79
         // Ensure consistency between paused status and paused_at field
80
         if ($adjustment->status === AdjustmentStatus::Paused && ! $adjustment->paused_at) {
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
 
20
 
21
         if ($recurringInvoice->end_type?->isAfter() && $recurringInvoice->occurrences_count >= $recurringInvoice->max_occurrences) {
21
         if ($recurringInvoice->end_type?->isAfter() && $recurringInvoice->occurrences_count >= $recurringInvoice->max_occurrences) {
22
             $recurringInvoice->status = RecurringInvoiceStatus::Ended;
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
 namespace App\Providers;
3
 namespace App\Providers;
4
 
4
 
5
 use App\Http\Responses\LoginRedirectResponse;
5
 use App\Http\Responses\LoginRedirectResponse;
6
+use App\Models\Export;
7
+use App\Models\Import;
8
+use App\Models\Notification;
6
 use App\Services\DateRangeService;
9
 use App\Services\DateRangeService;
10
+use Filament\Actions\Exports\Models\Export as BaseExport;
11
+use Filament\Actions\Imports\Models\Import as BaseImport;
7
 use Filament\Http\Responses\Auth\Contracts\LoginResponse;
12
 use Filament\Http\Responses\Auth\Contracts\LoginResponse;
8
 use Filament\Notifications\Livewire\Notifications;
13
 use Filament\Notifications\Livewire\Notifications;
9
 use Filament\Support\Assets\Js;
14
 use Filament\Support\Assets\Js;
10
 use Filament\Support\Enums\Alignment;
15
 use Filament\Support\Enums\Alignment;
11
 use Filament\Support\Facades\FilamentAsset;
16
 use Filament\Support\Facades\FilamentAsset;
17
+use Illuminate\Notifications\DatabaseNotification;
12
 use Illuminate\Support\ServiceProvider;
18
 use Illuminate\Support\ServiceProvider;
13
 
19
 
14
 class AppServiceProvider extends ServiceProvider
20
 class AppServiceProvider extends ServiceProvider
27
      */
33
      */
28
     public function boot(): void
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
         Notifications::alignment(Alignment::Center);
43
         Notifications::alignment(Alignment::Center);
31
 
44
 
32
         FilamentAsset::register([
45
         FilamentAsset::register([

+ 5
- 0
app/Providers/Filament/CompanyPanelProvider.php Прегледај датотеку

268
     {
268
     {
269
         $this->configureSelect();
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
         Actions\CreateAction::configureUsing(static fn (Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
276
         Actions\CreateAction::configureUsing(static fn (Actions\CreateAction $action) => FilamentComponentConfigurator::configureActionModals($action));
272
         Actions\EditAction::configureUsing(static fn (Actions\EditAction $action) => FilamentComponentConfigurator::configureActionModals($action));
277
         Actions\EditAction::configureUsing(static fn (Actions\EditAction $action) => FilamentComponentConfigurator::configureActionModals($action));
273
         Actions\DeleteAction::configureUsing(static fn (Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));
278
         Actions\DeleteAction::configureUsing(static fn (Actions\DeleteAction $action) => FilamentComponentConfigurator::configureDeleteAction($action));

+ 33
- 21
app/Providers/MacroServiceProvider.php Прегледај датотеку

5
 use Akaunting\Money\Currency;
5
 use Akaunting\Money\Currency;
6
 use Akaunting\Money\Money;
6
 use Akaunting\Money\Money;
7
 use App\Enums\Accounting\AdjustmentComputation;
7
 use App\Enums\Accounting\AdjustmentComputation;
8
-use App\Enums\Setting\DateFormat;
9
 use App\Models\Accounting\AccountSubtype;
8
 use App\Models\Accounting\AccountSubtype;
10
-use App\Models\Setting\Localization;
11
 use App\Services\CompanySettingsService;
9
 use App\Services\CompanySettingsService;
12
 use App\Utilities\Accounting\AccountCode;
10
 use App\Utilities\Accounting\AccountCode;
13
 use App\Utilities\Currency\CurrencyAccessor;
11
 use App\Utilities\Currency\CurrencyAccessor;
173
         });
171
         });
174
 
172
 
175
         TextColumn::macro('defaultDateFormat', function (): static {
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
             return $this;
178
             return $this;
184
         });
179
         });
185
 
180
 
186
         DatePicker::macro('defaultDateFormat', function (): static {
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
             return $this;
186
             return $this;
196
         });
187
         });
357
             return $this;
348
             return $this;
358
         });
349
         });
359
 
350
 
351
+        // In your macro - simpler logic
360
         TextColumn::macro('asRelativeDay', function (?string $timezone = null): static {
352
         TextColumn::macro('asRelativeDay', function (?string $timezone = null): static {
361
             $this->formatStateUsing(function (TextColumn $column, mixed $state) use ($timezone) {
353
             $this->formatStateUsing(function (TextColumn $column, mixed $state) use ($timezone) {
362
                 if (blank($state)) {
354
                 if (blank($state)) {
363
                     return null;
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
                 if ($date->isToday()) {
364
                 if ($date->isToday()) {
370
                     return 'Today';
365
                     return 'Today';
366
+                } elseif ($date->isTomorrow()) {
367
+                    return 'Tomorrow';
368
+                } elseif ($date->isYesterday()) {
369
+                    return 'Yesterday';
371
                 }
370
                 }
372
 
371
 
373
                 return $date->diffForHumans([
372
                 return $date->diffForHumans([
374
                     'options' => CarbonInterface::ONE_DAY_WORDS,
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
                     return null;
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
                 if ($date->isToday()) {
395
                 if ($date->isToday()) {
391
                     return 'Today';
396
                     return 'Today';
397
+                } elseif ($date->isTomorrow()) {
398
+                    return 'Tomorrow';
399
+                } elseif ($date->isYesterday()) {
400
+                    return 'Yesterday';
392
                 }
401
                 }
393
 
402
 
394
                 return $date->diffForHumans([
403
                 return $date->diffForHumans([
395
                     'options' => CarbonInterface::ONE_DAY_WORDS,
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
         });
483
         });
472
 
484
 
473
         Carbon::macro('toDefaultDateFormat', function () {
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
         ExportColumn::macro('money', function () {
493
         ExportColumn::macro('money', function () {

+ 4
- 9
app/Scopes/CurrentCompanyScope.php Прегледај датотеку

16
      */
16
      */
17
     public function apply(Builder $builder, Model $model): void
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
             return;
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
         if ($companyId) {
29
         if ($companyId) {

+ 3
- 3
app/Services/AccountService.php Прегледај датотеку

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

+ 2
- 0
app/Services/CompanySettingsService.php Прегледај датотеку

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

+ 2
- 2
app/Services/ExportService.php Прегледај датотеку

28
             $dateLabel = $formattedAsOfDate;
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
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.csv';
33
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.csv';
34
 
34
 
269
             $dateLabel = $formattedAsOfDate;
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
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.pdf';
274
         $filename = $company->name . ' ' . $report->getTitle() . ' ' . $dateLabel . ' ' . $timestamp . '.pdf';
275
 
275
 

+ 1
- 1
app/Support/ScheduleHandler.php Прегледај датотеку

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

+ 17
- 17
composer.lock Прегледај датотеку

497
         },
497
         },
498
         {
498
         {
499
             "name": "aws/aws-sdk-php",
499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.351.4",
500
+            "version": "3.351.7",
501
             "source": {
501
             "source": {
502
                 "type": "git",
502
                 "type": "git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "19dfd2db0f6ce35e1947e8bff4d956614b4aaaaf"
504
+                "reference": "9506d7fdb3cb84f8d7b175c594db9993264814be"
505
             },
505
             },
506
             "dist": {
506
             "dist": {
507
                 "type": "zip",
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
                 "shasum": ""
510
                 "shasum": ""
511
             },
511
             },
512
             "require": {
512
             "require": {
588
             "support": {
588
             "support": {
589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
589
                 "forum": "https://github.com/aws/aws-sdk-php/discussions",
590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
590
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
591
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.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
             "name": "aws/aws-sdk-php-laravel",
596
             "name": "aws/aws-sdk-php-laravel",
3516
         },
3516
         },
3517
         {
3517
         {
3518
             "name": "laravel/socialite",
3518
             "name": "laravel/socialite",
3519
-            "version": "v5.22.0",
3519
+            "version": "v5.23.0",
3520
             "source": {
3520
             "source": {
3521
                 "type": "git",
3521
                 "type": "git",
3522
                 "url": "https://github.com/laravel/socialite.git",
3522
                 "url": "https://github.com/laravel/socialite.git",
3523
-                "reference": "99d0fe750a7c68e5b60d8b1850de2554f3ea4072"
3523
+                "reference": "e9e0fc83b9d8d71c8385a5da20e5b95ca6234cf5"
3524
             },
3524
             },
3525
             "dist": {
3525
             "dist": {
3526
                 "type": "zip",
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
                 "shasum": ""
3529
                 "shasum": ""
3530
             },
3530
             },
3531
             "require": {
3531
             "require": {
3584
                 "issues": "https://github.com/laravel/socialite/issues",
3584
                 "issues": "https://github.com/laravel/socialite/issues",
3585
                 "source": "https://github.com/laravel/socialite"
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
             "name": "laravel/tinker",
3590
             "name": "laravel/tinker",
4448
         },
4448
         },
4449
         {
4449
         {
4450
             "name": "masterminds/html5",
4450
             "name": "masterminds/html5",
4451
-            "version": "2.9.0",
4451
+            "version": "2.10.0",
4452
             "source": {
4452
             "source": {
4453
                 "type": "git",
4453
                 "type": "git",
4454
                 "url": "https://github.com/Masterminds/html5-php.git",
4454
                 "url": "https://github.com/Masterminds/html5-php.git",
4455
-                "reference": "f5ac2c0b0a2eefca70b2ce32a5809992227e75a6"
4455
+                "reference": "fcf91eb64359852f00d921887b219479b4f21251"
4456
             },
4456
             },
4457
             "dist": {
4457
             "dist": {
4458
                 "type": "zip",
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
                 "shasum": ""
4461
                 "shasum": ""
4462
             },
4462
             },
4463
             "require": {
4463
             "require": {
4509
             ],
4509
             ],
4510
             "support": {
4510
             "support": {
4511
                 "issues": "https://github.com/Masterminds/html5-php/issues",
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
             "name": "matomo/device-detector",
4517
             "name": "matomo/device-detector",

+ 29
- 0
database/migrations/2025_07_13_042000_add_company_id_to_notifications_table.php Прегледај датотеку

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 Прегледај датотеку

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
     "requires": true,
4
     "requires": true,
5
     "packages": {
5
     "packages": {
6
         "": {
6
         "": {
7
+            "name": "erpsaas",
7
             "devDependencies": {
8
             "devDependencies": {
8
                 "@tailwindcss/forms": "^0.5.9",
9
                 "@tailwindcss/forms": "^0.5.9",
9
                 "@tailwindcss/typography": "^0.5.15",
10
                 "@tailwindcss/typography": "^0.5.15",
1022
             }
1023
             }
1023
         },
1024
         },
1024
         "node_modules/brace-expansion": {
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
             "dev": true,
1029
             "dev": true,
1029
             "license": "MIT",
1030
             "license": "MIT",
1030
             "dependencies": {
1031
             "dependencies": {
1483
             }
1484
             }
1484
         },
1485
         },
1485
         "node_modules/form-data": {
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
             "dev": true,
1490
             "dev": true,
1490
             "license": "MIT",
1491
             "license": "MIT",
1491
             "dependencies": {
1492
             "dependencies": {
1492
                 "asynckit": "^0.4.0",
1493
                 "asynckit": "^0.4.0",
1493
                 "combined-stream": "^1.0.8",
1494
                 "combined-stream": "^1.0.8",
1494
                 "es-set-tostringtag": "^2.1.0",
1495
                 "es-set-tostringtag": "^2.1.0",
1496
+                "hasown": "^2.0.2",
1495
                 "mime-types": "^2.1.12"
1497
                 "mime-types": "^2.1.12"
1496
             },
1498
             },
1497
             "engines": {
1499
             "engines": {

+ 1
- 1
tests/Feature/Accounting/TransactionTest.php Прегледај датотеку

233
     livewire(ListTransactions::class)
233
     livewire(ListTransactions::class)
234
         ->mountAction($actionName)
234
         ->mountAction($actionName)
235
         ->assertActionDataSet([
235
         ->assertActionDataSet([
236
-            'posted_at' => today(),
236
+            'posted_at' => company_today()->toDateString(),
237
             'type' => $transactionType,
237
             'type' => $transactionType,
238
             'bank_account_id' => $defaultBankAccount->id,
238
             'bank_account_id' => $defaultBankAccount->id,
239
             'amount' => '0.00',
239
             'amount' => '0.00',

Loading…
Откажи
Сачувај