Andrew Wallo 2 лет назад
Родитель
Сommit
76c99c99dc
55 измененных файлов: 5178 добавлений и 345 удалений
  1. 29
    0
      app/Actions/Banking/CreateCurrencyFromAccount.php
  2. 52
    10
      app/Filament/Pages/Widgets/Companies/Charts/CompanyStatsOverview.php
  3. 241
    0
      app/Filament/Resources/AccountResource.php
  4. 52
    0
      app/Filament/Resources/AccountResource/Pages/CreateAccount.php
  5. 59
    0
      app/Filament/Resources/AccountResource/Pages/EditAccount.php
  6. 19
    0
      app/Filament/Resources/AccountResource/Pages/ListAccounts.php
  7. 228
    0
      app/Filament/Resources/CurrencyResource.php
  8. 53
    0
      app/Filament/Resources/CurrencyResource/Pages/CreateCurrency.php
  9. 59
    0
      app/Filament/Resources/CurrencyResource/Pages/EditCurrency.php
  10. 19
    0
      app/Filament/Resources/CurrencyResource/Pages/ListCurrencies.php
  11. 216
    0
      app/Filament/Resources/CustomerResource.php
  12. 25
    0
      app/Filament/Resources/CustomerResource/Pages/CreateCustomer.php
  13. 19
    0
      app/Filament/Resources/CustomerResource/Pages/EditCustomer.php
  14. 19
    0
      app/Filament/Resources/CustomerResource/Pages/ListCustomers.php
  15. 76
    0
      app/Filament/Resources/InvoiceResource.php
  16. 12
    0
      app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php
  17. 19
    0
      app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php
  18. 19
    0
      app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php
  19. 81
    0
      app/Models/Banking/Account.php
  20. 123
    0
      app/Models/Company.php
  21. 96
    0
      app/Models/Contact.php
  22. 100
    0
      app/Models/Document/Document.php
  23. 97
    0
      app/Models/Document/DocumentItem.php
  24. 60
    0
      app/Models/Document/DocumentTotal.php
  25. 82
    0
      app/Models/Item.php
  26. 68
    0
      app/Models/Setting/Category.php
  27. 68
    0
      app/Models/Setting/Currency.php
  28. 65
    0
      app/Models/Setting/Discount.php
  29. 72
    0
      app/Models/Setting/Tax.php
  30. 66
    0
      app/Policies/AccountPolicy.php
  31. 77
    80
      composer.lock
  32. 1808
    0
      config/money.php
  33. 84
    0
      database/factories/AccountFactory.php
  34. 31
    0
      database/factories/CategoryFactory.php
  35. 31
    0
      database/factories/ContactFactory.php
  36. 79
    0
      database/factories/CurrencyFactory.php
  37. 31
    0
      database/factories/DiscountFactory.php
  38. 31
    0
      database/factories/DocumentFactory.php
  39. 31
    0
      database/factories/DocumentItemFactory.php
  40. 31
    0
      database/factories/DocumentTotalFactory.php
  41. 29
    0
      database/factories/ItemFactory.php
  42. 31
    0
      database/factories/TaxFactory.php
  43. 40
    0
      database/migrations/2023_05_10_040940_create_currencies_table.php
  44. 41
    0
      database/migrations/2023_05_11_044321_create_accounts_table.php
  45. 33
    0
      database/migrations/2023_05_12_042255_create_categories_table.php
  46. 47
    0
      database/migrations/2023_05_19_042232_create_contacts_table.php
  47. 36
    0
      database/migrations/2023_05_20_080131_create_taxes_table.php
  48. 35
    0
      database/migrations/2023_05_21_163808_create_discounts_table.php
  49. 40
    0
      database/migrations/2023_05_22_073252_create_items_table.php
  50. 46
    0
      database/migrations/2023_05_23_141215_create_documents_table.php
  51. 39
    0
      database/migrations/2023_05_23_151550_create_document_items_table.php
  52. 37
    0
      database/migrations/2023_05_23_173412_create_document_totals_table.php
  53. 20
    0
      database/seeders/AccountSeeder.php
  54. 20
    0
      database/seeders/CurrencySeeder.php
  55. 256
    255
      package-lock.json

+ 29
- 0
app/Actions/Banking/CreateCurrencyFromAccount.php Просмотреть файл

@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+namespace App\Actions\Banking;
4
+
5
+use App\Models\Setting\Currency;
6
+use Illuminate\Support\Facades\Auth;
7
+
8
+class CreateCurrencyFromAccount
9
+{
10
+    public function create(string $code, string $name, string $rate): Currency
11
+    {
12
+        $companyId = Auth::user()->currentCompany->id;
13
+
14
+        $hasDefaultCurrency = Currency::where('company_id', $companyId)->where('enabled', true)->exists();
15
+
16
+        return Currency::create([
17
+            'name' => $name,
18
+            'code' => $code,
19
+            'rate' => $rate,
20
+            'precision' => config("money.{$code}.precision"),
21
+            'symbol' => config("money.{$code}.symbol"),
22
+            'symbol_first' => config("money.{$code}.symbol_first"),
23
+            'decimal_mark' => config("money.{$code}.decimal_mark"),
24
+            'thousands_separator' => config("money.{$code}.thousands_separator"),
25
+            'enabled' => !$hasDefaultCurrency,
26
+            'company_id' => $companyId,
27
+        ]);
28
+    }
29
+}

+ 52
- 10
app/Filament/Pages/Widgets/Companies/Charts/CompanyStatsOverview.php Просмотреть файл

@@ -15,7 +15,7 @@ class CompanyStatsOverview extends StatsOverviewWidget
15 15
     }
16 16
 
17 17
     /**
18
-     * Holt's Linear Trend
18
+     * Holt's Linear Trend Method
19 19
      */
20 20
     protected function holtLinearTrend($data, $alpha, $beta): array
21 21
     {
@@ -33,6 +33,44 @@ class CompanyStatsOverview extends StatsOverviewWidget
33 33
         return $forecast;
34 34
     }
35 35
 
36
+    /**
37
+     * Adjusts the alpha and beta parameters based on the model's performance
38
+     */
39
+    protected function adjustTrendParameters($data, $alpha, $beta): array
40
+    {
41
+        $minError = PHP_INT_MAX;
42
+        $bestAlpha = $alpha;
43
+        $bestBeta = $beta;
44
+
45
+        // try different alpha and beta values within a reasonable range
46
+        for ($alpha = 0.1; $alpha <= 1; $alpha += 0.1) {
47
+            for ($beta = 0.1; $beta <= 1; $beta += 0.1) {
48
+                $forecast = $this->holtLinearTrend($data, $alpha, $beta);
49
+                $error = $this->calculateError($data, $forecast);
50
+                if ($error < $minError) {
51
+                    $minError = $error;
52
+                    $bestAlpha = $alpha;
53
+                    $bestBeta = $beta;
54
+                }
55
+            }
56
+        }
57
+
58
+        return [$bestAlpha, $bestBeta];
59
+    }
60
+
61
+    /**
62
+     * Calculates the sum of squared errors between the actual data and the forecast
63
+     */
64
+    protected function calculateError($data, $forecast): float
65
+    {
66
+        $error = 0;
67
+        for ($i = 0; $i < count($data); $i++) {
68
+            $error += pow($data[$i] - $forecast[$i], 2);
69
+        }
70
+
71
+        return $error;
72
+    }
73
+
36 74
     /**
37 75
      * Chart Options
38 76
      */
@@ -58,19 +96,19 @@ class CompanyStatsOverview extends StatsOverviewWidget
58 96
             $weeks[$week->format('oW')] = 0;
59 97
         }
60 98
 
61
-        // Get Weekly Data
99
+        // Get Weekly Data for Company Data
62 100
         $weeklyData = collect($weeks)->mapWithKeys(static function ($value, $week) use ($companyData) {
63 101
             $matchingData = $companyData->firstWhere('week', $week);
64 102
             return [$week => $matchingData ? $matchingData->aggregate : 0];
65 103
         });
66 104
 
67
-        // Calculate total companies
105
+        // Calculate total companies per week
68 106
         $totalCompanies = $weeklyData->reduce(static function ($carry, $value) {
69 107
             $carry[] = ($carry ? end($carry) : 0) + $value;
70 108
             return $carry;
71 109
         }, []);
72 110
 
73
-        // Calculate new companies and percentage change
111
+        // Calculate new companies and percentage change per week
74 112
         $newCompanies = [0];
75 113
         $weeklyPercentageChange = [0];
76 114
         for ($i = 1; $i < count($totalCompanies); $i++) {
@@ -80,16 +118,20 @@ class CompanyStatsOverview extends StatsOverviewWidget
80 118
 
81 119
         // Calculate average weekly growth rate
82 120
         $totalWeeks = $startOfYear->diffInWeeks($today);
83
-        $averageWeeklyGrowthRate = round(array_sum($weeklyPercentageChange) / ($totalWeeks), 2);
121
+        $averageWeeklyGrowthRate = round(array_sum($weeklyPercentageChange) / $totalWeeks, 2);
84 122
 
85
-        // Calculate Holt's forecast
86 123
         $weeklyDataArray = $weeklyData->values()->toArray();
87
-        $holt_forecast = $this->holtLinearTrend($weeklyDataArray, $alpha, $beta);
88
-        $expectedNewCompanies = round(end($holt_forecast));
89 124
 
90
-        // Prepare cards for return
125
+        // Adjust alpha and beta parameters
126
+        [$alpha, $beta] = $this->adjustTrendParameters($weeklyDataArray, $alpha, $beta);
127
+
128
+        // Calculate Holt's Linear Trend Forecast for next week
129
+        $holtForecast = $this->holtLinearTrend($weeklyDataArray, $alpha, $beta);
130
+        $expectedNewCompanies = round(end($holtForecast));
131
+
132
+        // Company Stats Overview Cards
91 133
         return [
92
-            StatsOverviewWidget\Card::make("New Companies Forecast (Holt's Trend)", $expectedNewCompanies),
134
+            StatsOverviewWidget\Card::make("New Companies Forecast (Holt's Linear Trend)", $expectedNewCompanies),
93 135
             StatsOverviewWidget\Card::make('Average Weekly Growth Rate', $averageWeeklyGrowthRate . '%'),
94 136
             StatsOverviewWidget\Card::make('Personal Companies', Company::sum('personal_company')),
95 137
         ];

+ 241
- 0
app/Filament/Resources/AccountResource.php Просмотреть файл

@@ -0,0 +1,241 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources;
4
+
5
+use App\Actions\Banking\CreateCurrencyFromAccount;
6
+use App\Filament\Resources\AccountResource\Pages;
7
+use App\Filament\Resources\AccountResource\RelationManagers;
8
+use App\Models\Banking\Account;
9
+use Closure;
10
+use Exception;
11
+use Filament\Forms;
12
+use Filament\Forms\Components\TextInput\Mask;
13
+use Filament\Notifications\Notification;
14
+use Filament\Resources\Form;
15
+use Filament\Resources\Resource;
16
+use Filament\Resources\Table;
17
+use Filament\Tables;
18
+use Illuminate\Database\Eloquent\Builder;
19
+use Illuminate\Database\Eloquent\Model;
20
+use Illuminate\Database\Eloquent\SoftDeletingScope;
21
+use Illuminate\Support\Collection;
22
+use Illuminate\Support\Facades\Auth;
23
+use Illuminate\Support\Facades\DB;
24
+use Illuminate\Validation\Rule;
25
+use Illuminate\Validation\Rules\Unique;
26
+
27
+class AccountResource extends Resource
28
+{
29
+    protected static ?string $model = Account::class;
30
+
31
+    protected static ?string $navigationIcon = 'heroicon-o-credit-card';
32
+
33
+    protected static ?string $navigationGroup = 'Banking';
34
+
35
+    public static function getEloquentQuery(): Builder
36
+    {
37
+        return parent::getEloquentQuery()->where('company_id', Auth::user()->currentCompany->id);
38
+    }
39
+
40
+    public static function form(Form $form): Form
41
+    {
42
+        return $form
43
+            ->schema([
44
+                Forms\Components\Section::make('General')
45
+                    ->schema([
46
+                        Forms\Components\Radio::make('type')
47
+                            ->label('Type')
48
+                            ->options(Account::getAccountTypes())
49
+                            ->inline()
50
+                            ->default('bank')
51
+                            ->reactive()
52
+                            ->afterStateUpdated(static fn (Closure $set, $state) => $state === 'card' ? $set('enabled', 'hidden'): $set('enabled', null))
53
+                            ->required()
54
+                            ->columnSpanFull(),
55
+                        Forms\Components\TextInput::make('name')
56
+                            ->label('Name')
57
+                            ->maxLength(100)
58
+                            ->required(),
59
+                        Forms\Components\TextInput::make('number')
60
+                            ->label('Account Number')
61
+                            ->unique(callback: static function (Unique $rule, $state) {
62
+                                $companyId = Auth::user()->currentCompany->id;
63
+
64
+                                return $rule->where('company_id', $companyId)->where('number', $state);
65
+                            }, ignoreRecord: true)
66
+                            ->maxLength(20)
67
+                            ->required(),
68
+                        Forms\Components\Select::make('currency_code')
69
+                            ->label('Currency')
70
+                            ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
71
+                            ->preload()
72
+                            ->default(Account::getDefaultCurrencyCode())
73
+                            ->searchable()
74
+                            ->reactive()
75
+                            ->required()
76
+                            ->createOptionForm([
77
+                                Forms\Components\Select::make('currency.code')
78
+                                    ->label('Code')
79
+                                    ->searchable()
80
+                                    ->options(Account::getCurrencyCodes())
81
+                                    ->reactive()
82
+                                    ->afterStateUpdated(static function (callable $set, $state) {
83
+                                        $code = $state;
84
+                                        $name = config("money.{$code}.name");
85
+                                        $set('currency.name', $name);
86
+                                    })
87
+                                    ->required(),
88
+                                Forms\Components\TextInput::make('currency.name')
89
+                                    ->label('Name')
90
+                                    ->maxLength(100)
91
+                                    ->required(),
92
+                                Forms\Components\TextInput::make('currency.rate')
93
+                                    ->label('Rate')
94
+                                    ->numeric()
95
+                                    ->mask(static fn (Forms\Components\TextInput\Mask $mask) => $mask
96
+                                        ->numeric()
97
+                                        ->decimalPlaces(4)
98
+                                        ->signed(false)
99
+                                        ->padFractionalZeros(false)
100
+                                        ->normalizeZeros(false)
101
+                                        ->minValue(0.0001)
102
+                                        ->maxValue(999999.9999)
103
+                                        ->lazyPlaceholder(false))
104
+                                    ->required(),
105
+                            ])->createOptionAction(static function (Forms\Components\Actions\Action $action) {
106
+                                return $action
107
+                                    ->label('Add Currency')
108
+                                    ->modalHeading('Add Currency')
109
+                                    ->modalButton('Add')
110
+                                    ->action(static function (array $data) {
111
+                                        return DB::transaction(static function () use ($data) {
112
+                                            $code = $data['currency']['code'];
113
+                                            $name = $data['currency']['name'];
114
+                                            $rate = $data['currency']['rate'];
115
+
116
+                                            return (new CreateCurrencyFromAccount())->create($code, $name, $rate);
117
+                                        });
118
+                                    });
119
+                            }),
120
+                        Forms\Components\TextInput::make('opening_balance')
121
+                            ->label('Opening Balance')
122
+                            ->required()
123
+                            ->default('0')
124
+                            ->numeric()
125
+                            ->mask(static fn (Forms\Components\TextInput\Mask $mask, Closure $get) => $mask
126
+                                ->patternBlocks([
127
+                                    'money' => static fn (Mask $mask) => $mask
128
+                                        ->numeric()
129
+                                        ->decimalPlaces(config('money.' . $get('currency_code') . '.precision'))
130
+                                        ->decimalSeparator(config('money.' . $get('currency_code') . '.decimal_mark'))
131
+                                        ->thousandsSeparator(config('money.' . $get('currency_code') . '.thousands_separator'))
132
+                                        ->signed()
133
+                                        ->padFractionalZeros()
134
+                                        ->normalizeZeros(false),
135
+                            ])
136
+                            ->pattern(config('money.' . $get('currency_code') . '.symbol_first') ? config('money.' . $get('currency_code') . '.symbol') . 'money' : 'money' . config('money.' . $get('currency_code') . '.symbol'))
137
+                            ->lazyPlaceholder(false)),
138
+                        Forms\Components\Toggle::make('enabled')
139
+                            ->hidden(fn (Closure $get) => $get('type') === 'card')
140
+                            ->label('Default Account'),
141
+                    ])->columns(),
142
+                Forms\Components\Section::make('Bank')
143
+                    ->schema([
144
+                        Forms\Components\TextInput::make('bank_name')
145
+                            ->label('Bank Name')
146
+                            ->maxLength(100),
147
+                        Forms\Components\TextInput::make('bank_phone')
148
+                            ->label('Bank Phone')
149
+                            ->mask(static fn (Forms\Components\TextInput\Mask $mask) => $mask->pattern('(000) 000-0000'))
150
+                            ->maxLength(20),
151
+                        Forms\Components\Textarea::make('bank_address')
152
+                            ->label('Bank Address')
153
+                            ->columnSpanFull(),
154
+                        ])->columns(),
155
+            ]);
156
+    }
157
+
158
+    /**
159
+     * @throws Exception
160
+     */
161
+    public static function table(Table $table): Table
162
+    {
163
+        return $table
164
+            ->columns([
165
+                Tables\Columns\TextColumn::make('name')
166
+                    ->searchable()
167
+                    ->weight('semibold')
168
+                    ->icon(static fn (Account $record) => $record->enabled ? 'heroicon-o-lock-closed' : null)
169
+                    ->tooltip(static fn (Account $record) => $record->enabled ? 'Default Account' : null)
170
+                    ->iconPosition('after')
171
+                    ->sortable(),
172
+                Tables\Columns\TextColumn::make('number')
173
+                    ->label('Account Number')
174
+                    ->searchable()
175
+                    ->sortable(),
176
+                Tables\Columns\TextColumn::make('bank_name')
177
+                    ->label('Bank Name')
178
+                    ->searchable()
179
+                    ->sortable(),
180
+                Tables\Columns\TextColumn::make('bank_phone')
181
+                    ->label('Phone')
182
+                    ->formatStateUsing(static fn ($record) => ($record->bank_phone !== '') ? vsprintf('(%d%d%d) %d%d%d-%d%d%d%d', str_split($record->bank_phone)) : '-'),
183
+                Tables\Columns\TextColumn::make('opening_balance')
184
+                    ->label('Current Balance')
185
+                    ->sortable()
186
+                    ->money(static fn ($record) => $record->currency_code, true),
187
+            ])
188
+            ->filters([
189
+                //
190
+            ])
191
+            ->actions([
192
+                Tables\Actions\EditAction::make(),
193
+                Tables\Actions\DeleteAction::make()
194
+                    ->before(static function (Tables\Actions\DeleteAction $action, Account $record) {
195
+                        if ($record->enabled) {
196
+                            Notification::make()
197
+                                ->danger()
198
+                                ->title('Action Denied')
199
+                                ->body(__('The :name account is currently set as your default account and cannot be deleted. Please set a different account as your default before attempting to delete this one.', ['name' => $record->name]))
200
+                                ->persistent()
201
+                                ->send();
202
+
203
+                            $action->cancel();
204
+                        }
205
+                    }),
206
+            ])
207
+            ->bulkActions([
208
+                Tables\Actions\DeleteBulkAction::make()
209
+                    ->before(static function (Tables\Actions\DeleteBulkAction $action, Collection $records) {
210
+                        foreach ($records as $record) {
211
+                            if ($record->enabled) {
212
+                                Notification::make()
213
+                                    ->danger()
214
+                                    ->title('Action Denied')
215
+                                    ->body(__('The :name account is currently set as your default account and cannot be deleted. Please set a different account as your default before attempting to delete this one.', ['name' => $record->name]))
216
+                                    ->persistent()
217
+                                    ->send();
218
+
219
+                                $action->cancel();
220
+                            }
221
+                        }
222
+                    }),
223
+            ]);
224
+    }
225
+    
226
+    public static function getRelations(): array
227
+    {
228
+        return [
229
+            //
230
+        ];
231
+    }
232
+    
233
+    public static function getPages(): array
234
+    {
235
+        return [
236
+            'index' => Pages\ListAccounts::route('/'),
237
+            'create' => Pages\CreateAccount::route('/create'),
238
+            'edit' => Pages\EditAccount::route('/{record}/edit'),
239
+        ];
240
+    }    
241
+}

+ 52
- 0
app/Filament/Resources/AccountResource/Pages/CreateAccount.php Просмотреть файл

@@ -0,0 +1,52 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\AccountResource\Pages;
4
+
5
+use App\Filament\Resources\AccountResource;
6
+use App\Models\Banking\Account;
7
+use Filament\Pages\Actions;
8
+use Filament\Resources\Pages\CreateRecord;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Support\Facades\DB;
11
+
12
+class CreateAccount extends CreateRecord
13
+{
14
+    protected static string $resource = AccountResource::class;
15
+
16
+    protected function getRedirectUrl(): string
17
+    {
18
+        return $this->getResource()::getUrl('index');
19
+    }
20
+
21
+    protected function mutateFormDataBeforeCreate(array $data): array
22
+    {
23
+        $data['company_id'] = auth()->user()->currentCompany->id;
24
+        return $data;
25
+    }
26
+
27
+    protected function handleRecordCreation(array $data): Model
28
+    {
29
+        $currentCompanyId = auth()->user()->currentCompany->id;
30
+        $accountId = $data['id'] ?? null;
31
+        $enabledAccountsCount = Account::where('company_id', $currentCompanyId)
32
+            ->where('enabled', true)
33
+            ->where('id', '!=', $accountId)
34
+            ->count();
35
+
36
+        if ($data['enabled'] === true && $enabledAccountsCount > 0) {
37
+            $this->disableOtherAccounts($currentCompanyId, $accountId);
38
+        } elseif ($data['enabled'] === false && $enabledAccountsCount < 1) {
39
+            $data['enabled'] = true;
40
+        }
41
+
42
+        return parent::handleRecordCreation($data);
43
+    }
44
+
45
+    protected function disableOtherAccounts($companyId, $accountId): void
46
+    {
47
+        DB::table('accounts')
48
+            ->where('company_id', $companyId)
49
+            ->where('id', '!=', $accountId)
50
+            ->update(['enabled' => false]);
51
+    }
52
+}

+ 59
- 0
app/Filament/Resources/AccountResource/Pages/EditAccount.php Просмотреть файл

@@ -0,0 +1,59 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\AccountResource\Pages;
4
+
5
+use App\Filament\Resources\AccountResource;
6
+use App\Models\Banking\Account;
7
+use Filament\Pages\Actions;
8
+use Filament\Resources\Pages\EditRecord;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Support\Facades\DB;
11
+
12
+class EditAccount extends EditRecord
13
+{
14
+    protected static string $resource = AccountResource::class;
15
+
16
+    protected function getActions(): array
17
+    {
18
+        return [
19
+            Actions\DeleteAction::make(),
20
+        ];
21
+    }
22
+
23
+    protected function getRedirectUrl(): string
24
+    {
25
+        return $this->getResource()::getUrl('index');
26
+    }
27
+
28
+    protected function mutateFormDataBeforeSave(array $data): array
29
+    {
30
+        $data['company_id'] = auth()->user()->currentCompany->id;
31
+        return $data;
32
+    }
33
+
34
+    protected function handleRecordUpdate(Model|Account $record, array $data): Model|Account
35
+    {
36
+        $currentCompanyId = auth()->user()->currentCompany->id;
37
+        $accountId = $record->id;
38
+        $enabledAccountsCount = Account::where('company_id', $currentCompanyId)
39
+            ->where('enabled', true)
40
+            ->where('id', '!=', $accountId)
41
+            ->count();
42
+
43
+        if ($data['enabled'] === true && $enabledAccountsCount > 0) {
44
+            $this->disableOtherAccounts($currentCompanyId, $accountId);
45
+        } elseif ($data['enabled'] === false && $enabledAccountsCount < 1) {
46
+            $data['enabled'] = true;
47
+        }
48
+
49
+        return parent::handleRecordUpdate($record, $data);
50
+    }
51
+
52
+    protected function disableOtherAccounts($companyId, $accountId): void
53
+    {
54
+        DB::table('accounts')
55
+            ->where('company_id', $companyId)
56
+            ->where('id', '!=', $accountId)
57
+            ->update(['enabled' => false]);
58
+    }
59
+}

+ 19
- 0
app/Filament/Resources/AccountResource/Pages/ListAccounts.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\AccountResource\Pages;
4
+
5
+use App\Filament\Resources\AccountResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\ListRecords;
8
+
9
+class ListAccounts extends ListRecords
10
+{
11
+    protected static string $resource = AccountResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\CreateAction::make(),
17
+        ];
18
+    }
19
+}

+ 228
- 0
app/Filament/Resources/CurrencyResource.php Просмотреть файл

@@ -0,0 +1,228 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources;
4
+
5
+use App\Filament\Resources\CurrencyResource\Pages;
6
+use App\Filament\Resources\CurrencyResource\RelationManagers;
7
+use App\Models\Banking\Account;
8
+use App\Models\Setting\Currency;
9
+use Closure;
10
+use Exception;
11
+use Filament\Forms;
12
+use Filament\Notifications\Notification;
13
+use Filament\Resources\Form;
14
+use Filament\Resources\Resource;
15
+use Filament\Resources\Table;
16
+use Filament\Tables;
17
+use Illuminate\Database\Eloquent\Builder;
18
+use Illuminate\Database\Eloquent\Model;
19
+use Illuminate\Database\Eloquent\SoftDeletingScope;
20
+use Illuminate\Support\Collection;
21
+use Illuminate\Support\Facades\Auth;
22
+
23
+class CurrencyResource extends Resource
24
+{
25
+    protected static ?string $model = Currency::class;
26
+
27
+    protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
28
+
29
+    protected static ?string $navigationGroup = 'Settings';
30
+
31
+    public static function getEloquentQuery(): Builder
32
+    {
33
+        return parent::getEloquentQuery()->where('company_id', Auth::user()->currentCompany->id);
34
+    }
35
+
36
+    public static function form(Form $form): Form
37
+    {
38
+        return $form
39
+            ->schema([
40
+                Forms\Components\Section::make('General')
41
+                    ->schema([
42
+                        Forms\Components\Select::make('code')
43
+                            ->label('Code')
44
+                            ->options(Currency::getCurrencyCodes())
45
+                            ->searchable()
46
+                            ->placeholder('- Select Code -')
47
+                            ->reactive()
48
+                            ->afterStateUpdated(static function (Closure $set, $state) {
49
+                                $code = $state;
50
+                                $name = config("money.{$code}.name");
51
+                                $precision = config("money.{$code}.precision");
52
+                                $symbol = config("money.{$code}.symbol");
53
+                                $symbol_first = config("money.{$code}.symbol_first");
54
+                                $decimal_mark = config("money.{$code}.decimal_mark");
55
+                                $thousands_separator = config("money.{$code}.thousands_separator");
56
+
57
+                                $set('name', $name);
58
+                                $set('precision', $precision);
59
+                                $set('symbol', $symbol);
60
+                                $set('symbol_first', $symbol_first);
61
+                                $set('decimal_mark', $decimal_mark);
62
+                                $set('thousands_separator', $thousands_separator);
63
+                            })
64
+                            ->required(),
65
+                        Forms\Components\TextInput::make('name')
66
+                            ->translateLabel()
67
+                            ->placeholder('Enter Name')
68
+                            ->maxLength(100)
69
+                            ->required(),
70
+                        Forms\Components\TextInput::make('rate')
71
+                            ->label('Rate')
72
+                            ->placeholder('Enter Rate')
73
+                            ->dehydrateStateUsing(static fn (Closure $get, $state): bool => $get('enabled') === true ? '1' : $state) // rate is 1 when enabled is true
74
+                            ->numeric()
75
+                            ->disabled(static fn (Closure $get): bool => $get('enabled') === true) // disabled is true when enabled is true
76
+                            ->mask(static fn (Forms\Components\TextInput\Mask $mask) => $mask
77
+                                ->numeric()
78
+                                ->decimalPlaces(4)
79
+                                ->signed(false)
80
+                                ->padFractionalZeros(false)
81
+                                ->normalizeZeros(false)
82
+                                ->minValue(0.0001)
83
+                                ->maxValue(999999.9999)
84
+                                ->lazyPlaceholder(false))
85
+                            ->required(),
86
+                        Forms\Components\Select::make('precision')
87
+                            ->label('Precision')
88
+                            ->searchable()
89
+                            ->placeholder('- Select Precision -')
90
+                            ->options(['0', '1', '2', '3', '4'])
91
+                            ->required(),
92
+                        Forms\Components\TextInput::make('symbol')
93
+                            ->label('Symbol')
94
+                            ->placeholder('Enter Symbol')
95
+                            ->maxLength(5)
96
+                            ->required(),
97
+                        Forms\Components\Select::make('symbol_first')
98
+                            ->label('Symbol Position')
99
+                            ->searchable()
100
+                            ->boolean('Before Amount', 'After Amount', '- Select Symbol Position -')
101
+                            ->required(),
102
+                        Forms\Components\TextInput::make('decimal_mark')
103
+                            ->label('Decimal Separator')
104
+                            ->placeholder('Enter Decimal Separator')
105
+                            ->maxLength(1)
106
+                            ->required(),
107
+                        Forms\Components\TextInput::make('thousands_separator')
108
+                            ->label('Thousands Separator')
109
+                            ->placeholder('Enter Thousands Separator')
110
+                            ->maxLength(1)
111
+                            ->required(),
112
+                        Forms\Components\Toggle::make('enabled')
113
+                            ->label('Default Currency')
114
+                            ->reactive()
115
+                            ->inline()
116
+                            ->afterStateUpdated(static fn (Closure $set, $state) => $state ? $set('rate', '1') : $set('rate', null))
117
+                            ->default(false),
118
+                    ])->columns(),
119
+            ]);
120
+    }
121
+
122
+    /**
123
+     * @throws Exception
124
+     */
125
+    public static function table(Table $table): Table
126
+    {
127
+        return $table
128
+            ->columns([
129
+                Tables\Columns\TextColumn::make('name')
130
+                    ->label('Name')
131
+                    ->weight('semibold')
132
+                    ->icon(static fn (Currency $record) => $record->enabled ? 'heroicon-o-lock-closed' : null)
133
+                    ->tooltip(static fn (Currency $record) => $record->enabled ? 'Default Currency' : null)
134
+                    ->iconPosition('after')
135
+                    ->searchable()
136
+                    ->sortable(),
137
+                Tables\Columns\TextColumn::make('code')
138
+                    ->label('Code')
139
+                    ->searchable()
140
+                    ->sortable(),
141
+                Tables\Columns\TextColumn::make('symbol')
142
+                    ->label('Symbol')
143
+                    ->searchable()
144
+                    ->sortable(),
145
+                Tables\Columns\TextColumn::make('rate')
146
+                    ->label('Rate')
147
+                    ->formatStateUsing(static fn ($state) => str_contains($state, '.') ? rtrim(rtrim($state, '0'), '.') : null)
148
+                    ->searchable()
149
+                    ->sortable(),
150
+            ])
151
+            ->filters([
152
+                //
153
+            ])
154
+            ->actions([
155
+                Tables\Actions\EditAction::make(),
156
+                Tables\Actions\DeleteAction::make()
157
+                    ->before(static function (Tables\Actions\DeleteAction $action, Currency $record) {
158
+                        $defaultCurrency = $record->enabled;
159
+                        $accountUsesCurrency = Account::where('currency_code', $record->code)->exists();
160
+
161
+                        if ($defaultCurrency) {
162
+                            Notification::make()
163
+                                ->danger()
164
+                                ->title('Action Denied')
165
+                                ->body(__('The :name currency is currently set as the default currency and cannot be deleted. Please set a different currency as your default before attempting to delete this one.', ['name' => $record->name]))
166
+                                ->persistent()
167
+                                ->send();
168
+
169
+                            $action->cancel();
170
+                        } elseif ($accountUsesCurrency) {
171
+                            Notification::make()
172
+                                ->danger()
173
+                                ->title('Action Denied')
174
+                                ->body(__('The :name currency is currently in use by one or more accounts and cannot be deleted. Please remove this currency from all accounts before attempting to delete it.', ['name' => $record->name]))
175
+                                ->persistent()
176
+                                ->send();
177
+
178
+                            $action->cancel();
179
+                        }
180
+                    }),
181
+            ])
182
+            ->bulkActions([
183
+                Tables\Actions\DeleteBulkAction::make()
184
+                    ->before(static function (Tables\Actions\DeleteBulkAction $action, Collection $records) {
185
+                        foreach ($records as $record) {
186
+                            $defaultCurrency = $record->enabled;
187
+                            $accountUsesCurrency = Account::where('currency_code', $record->code)->exists();
188
+
189
+                            if ($defaultCurrency) {
190
+                                Notification::make()
191
+                                    ->danger()
192
+                                    ->title('Action Denied')
193
+                                    ->body(__('The :name currency is currently set as the default currency and cannot be deleted. Please set a different currency as your default before attempting to delete this one.', ['name' => $record->name]))
194
+                                    ->persistent()
195
+                                    ->send();
196
+
197
+                                $action->cancel();
198
+                            } elseif ($accountUsesCurrency) {
199
+                                Notification::make()
200
+                                    ->danger()
201
+                                    ->title('Action Denied')
202
+                                    ->body(__('The :name currency is currently in use by one or more accounts and cannot be deleted. Please remove this currency from all accounts before attempting to delete it.', ['name' => $record->name]))
203
+                                    ->persistent()
204
+                                    ->send();
205
+
206
+                                $action->cancel();
207
+                            }
208
+                        }
209
+                    }),
210
+            ]);
211
+    }
212
+    
213
+    public static function getRelations(): array
214
+    {
215
+        return [
216
+            //
217
+        ];
218
+    }
219
+    
220
+    public static function getPages(): array
221
+    {
222
+        return [
223
+            'index' => Pages\ListCurrencies::route('/'),
224
+            'create' => Pages\CreateCurrency::route('/create'),
225
+            'edit' => Pages\EditCurrency::route('/{record}/edit'),
226
+        ];
227
+    }    
228
+}

+ 53
- 0
app/Filament/Resources/CurrencyResource/Pages/CreateCurrency.php Просмотреть файл

@@ -0,0 +1,53 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CurrencyResource\Pages;
4
+
5
+use App\Filament\Resources\CurrencyResource;
6
+use App\Models\Setting\Currency;
7
+use Filament\Pages\Actions;
8
+use Filament\Resources\Pages\CreateRecord;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Support\Facades\DB;
11
+
12
+class CreateCurrency extends CreateRecord
13
+{
14
+    protected static string $resource = CurrencyResource::class;
15
+
16
+    protected function getRedirectUrl(): string
17
+    {
18
+        return $this->getResource()::getUrl('index');
19
+    }
20
+
21
+    protected function mutateFormDataBeforeCreate(array $data): array
22
+    {
23
+        $data['company_id'] = auth()->user()->currentCompany->id;
24
+
25
+        return $data;
26
+    }
27
+
28
+    protected function handleRecordCreation(array $data): Model
29
+    {
30
+        $currentCompanyId = auth()->user()->currentCompany->id;
31
+        $currencyId = $data['id'] ?? null;
32
+        $enabledCurrenciesCount = Currency::where('company_id', $currentCompanyId)
33
+            ->where('enabled', '1')
34
+            ->where('id', '!=', $currencyId)
35
+            ->count();
36
+
37
+        if ($data['enabled'] === '1' && $enabledCurrenciesCount > 0) {
38
+            $this->disableOtherCurrencies($currentCompanyId, $currencyId);
39
+        } elseif ($data['enabled'] === '0' && $enabledCurrenciesCount < 1) {
40
+            $data['enabled'] = '1';
41
+        }
42
+
43
+        return parent::handleRecordCreation($data);
44
+    }
45
+
46
+    protected function disableOtherCurrencies($companyId, $currencyId): void
47
+    {
48
+        DB::table('currencies')
49
+            ->where('company_id', $companyId)
50
+            ->where('id', '!=', $currencyId)
51
+            ->update(['enabled' => '0']);
52
+    }
53
+}

+ 59
- 0
app/Filament/Resources/CurrencyResource/Pages/EditCurrency.php Просмотреть файл

@@ -0,0 +1,59 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CurrencyResource\Pages;
4
+
5
+use App\Filament\Resources\CurrencyResource;
6
+use App\Models\Setting\Currency;
7
+use Filament\Pages\Actions;
8
+use Filament\Resources\Pages\EditRecord;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Support\Facades\DB;
11
+
12
+class EditCurrency extends EditRecord
13
+{
14
+    protected static string $resource = CurrencyResource::class;
15
+
16
+    protected function getActions(): array
17
+    {
18
+        return [
19
+            Actions\DeleteAction::make(),
20
+        ];
21
+    }
22
+
23
+    protected function getRedirectUrl(): string
24
+    {
25
+        return $this->getResource()::getUrl('index');
26
+    }
27
+
28
+    protected function mutateFormDataBeforeSave(array $data): array
29
+    {
30
+        $data['company_id'] = auth()->user()->currentCompany->id;
31
+        return $data;
32
+    }
33
+
34
+    protected function handleRecordUpdate(Model|Currency $record, array $data): Model|Currency
35
+    {
36
+        $currentCompanyId = auth()->user()->currentCompany->id;
37
+        $currencyId = $record->id;
38
+        $enabledCurrenciesCount = Currency::where('company_id', $currentCompanyId)
39
+            ->where('enabled', true)
40
+            ->where('id', '!=', $currencyId)
41
+            ->count();
42
+
43
+        if ($data['enabled'] === true && $enabledCurrenciesCount > 0) {
44
+            $this->disableOtherCurrencies($currentCompanyId, $currencyId);
45
+        } elseif ($data['enabled'] === false && $enabledCurrenciesCount < 1) {
46
+            $data['enabled'] = true;
47
+        }
48
+
49
+        return parent::handleRecordUpdate($record, $data);
50
+    }
51
+
52
+    protected function disableOtherCurrencies($companyId, $currencyId): void
53
+    {
54
+        DB::table('currencies')
55
+            ->where('company_id', $companyId)
56
+            ->where('id', '!=', $currencyId)
57
+            ->update(['enabled' => false]);
58
+    }
59
+}

+ 19
- 0
app/Filament/Resources/CurrencyResource/Pages/ListCurrencies.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CurrencyResource\Pages;
4
+
5
+use App\Filament\Resources\CurrencyResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\ListRecords;
8
+
9
+class ListCurrencies extends ListRecords
10
+{
11
+    protected static string $resource = CurrencyResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\CreateAction::make(),
17
+        ];
18
+    }
19
+}

+ 216
- 0
app/Filament/Resources/CustomerResource.php Просмотреть файл

@@ -0,0 +1,216 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources;
4
+
5
+use App\Actions\Banking\CreateCurrencyFromAccount;
6
+use App\Filament\Resources\CustomerResource\Pages;
7
+use App\Filament\Resources\CustomerResource\RelationManagers;
8
+use App\Models\Banking\Account;
9
+use App\Models\Contact;
10
+use Filament\Forms;
11
+use Filament\Resources\Form;
12
+use Filament\Resources\Resource;
13
+use Filament\Resources\Table;
14
+use Filament\Tables;
15
+use Illuminate\Database\Eloquent\Builder;
16
+use Illuminate\Database\Eloquent\SoftDeletingScope;
17
+use Illuminate\Support\Facades\Auth;
18
+use Illuminate\Support\Facades\DB;
19
+
20
+class CustomerResource extends Resource
21
+{
22
+    protected static ?string $model = Contact::class;
23
+
24
+    protected static ?string $navigationIcon = 'heroicon-o-collection';
25
+
26
+    protected static ?string $navigationGroup = 'Sales';
27
+
28
+    protected static ?string $navigationLabel = 'Customers';
29
+
30
+    protected static ?string $modelLabel = 'customer';
31
+
32
+    public static function getEloquentQuery(): Builder
33
+    {
34
+        return parent::getEloquentQuery()
35
+            ->where('type', 'customer')
36
+            ->where('company_id', Auth::user()->currentCompany->id);
37
+    }
38
+
39
+    public static function form(Form $form): Form
40
+    {
41
+        return $form
42
+            ->schema([
43
+                Forms\Components\Section::make('General')
44
+                    ->schema([
45
+                        Forms\Components\Radio::make('entity')
46
+                            ->options([
47
+                                'company' => 'Company',
48
+                                'individual' => 'Individual',
49
+                            ])
50
+                            ->inline()
51
+                            ->default('company')
52
+                            ->required()
53
+                            ->columnSpanFull(),
54
+                        Forms\Components\TextInput::make('name')
55
+                            ->maxLength(100)
56
+                            ->placeholder('Enter Name')
57
+                            ->required(),
58
+                        Forms\Components\TextInput::make('email')
59
+                            ->email()
60
+                            ->placeholder('Enter Email')
61
+                            ->nullable(),
62
+                        Forms\Components\TextInput::make('phone')
63
+                            ->label('Phone')
64
+                            ->tel()
65
+                            ->placeholder('Enter Phone')
66
+                            ->maxLength(20),
67
+                        Forms\Components\TextInput::make('website')
68
+                            ->maxLength(100)
69
+                            ->prefix('https://')
70
+                            ->placeholder('Enter Website')
71
+                            ->nullable(),
72
+                        Forms\Components\TextInput::make('reference')
73
+                            ->maxLength(100)
74
+                            ->placeholder('Enter Reference')
75
+                            ->nullable(),
76
+                    ])->columns(2),
77
+                Forms\Components\Section::make('Billing')
78
+                    ->schema([
79
+                        Forms\Components\TextInput::make('tax_number')
80
+                            ->maxLength(100)
81
+                            ->placeholder('Enter Tax Number')
82
+                            ->nullable(),
83
+                        Forms\Components\Select::make('currency_code')
84
+                            ->label('Currency')
85
+                            ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
86
+                            ->preload()
87
+                            ->default(Account::getDefaultCurrencyCode())
88
+                            ->searchable()
89
+                            ->reactive()
90
+                            ->required()
91
+                            ->createOptionForm([
92
+                                Forms\Components\Select::make('currency.code')
93
+                                    ->label('Code')
94
+                                    ->searchable()
95
+                                    ->options(Account::getCurrencyCodes())
96
+                                    ->reactive()
97
+                                    ->afterStateUpdated(static function (callable $set, $state) {
98
+                                        $code = $state;
99
+                                        $name = config("money.{$code}.name");
100
+                                        $set('currency.name', $name);
101
+                                    })
102
+                                    ->required(),
103
+                                Forms\Components\TextInput::make('currency.name')
104
+                                    ->label('Name')
105
+                                    ->maxLength(100)
106
+                                    ->required(),
107
+                                Forms\Components\TextInput::make('currency.rate')
108
+                                    ->label('Rate')
109
+                                    ->numeric()
110
+                                    ->mask(static fn (Forms\Components\TextInput\Mask $mask) => $mask
111
+                                        ->numeric()
112
+                                        ->decimalPlaces(4)
113
+                                        ->signed(false)
114
+                                        ->padFractionalZeros(false)
115
+                                        ->normalizeZeros(false)
116
+                                        ->minValue(0.0001)
117
+                                        ->maxValue(999999.9999)
118
+                                        ->lazyPlaceholder(false))
119
+                                    ->required(),
120
+                            ])->createOptionAction(static function (Forms\Components\Actions\Action $action) {
121
+                                return $action
122
+                                    ->label('Add Currency')
123
+                                    ->modalHeading('Add Currency')
124
+                                    ->modalButton('Add')
125
+                                    ->action(static function (array $data) {
126
+                                        return DB::transaction(static function () use ($data) {
127
+                                            $code = $data['currency']['code'];
128
+                                            $name = $data['currency']['name'];
129
+                                            $rate = $data['currency']['rate'];
130
+
131
+                                            return (new CreateCurrencyFromAccount())->create($code, $name, $rate);
132
+                                        });
133
+                                    });
134
+                            }),
135
+                ])->columns(2),
136
+                Forms\Components\Section::make('Address')
137
+                    ->schema([
138
+                        Forms\Components\Textarea::make('address')
139
+                            ->label('Address')
140
+                            ->maxLength(100)
141
+                            ->placeholder('Enter Address')
142
+                            ->columnSpanFull()
143
+                            ->nullable(),
144
+                        Forms\Components\TextInput::make('city')
145
+                            ->label('Town/City')
146
+                            ->maxLength(100)
147
+                            ->placeholder('Enter Town/City')
148
+                            ->nullable(),
149
+                        Forms\Components\TextInput::make('zip_code')
150
+                            ->label('Postal/Zip Code')
151
+                            ->maxLength(100)
152
+                            ->placeholder('Enter Postal/Zip Code')
153
+                            ->nullable(),
154
+                        Forms\Components\TextInput::make('state')
155
+                            ->label('Province/State')
156
+                            ->maxLength(100)
157
+                            ->placeholder('Enter Province/State')
158
+                            ->required(),
159
+                        Forms\Components\TextInput::make('country')
160
+                            ->label('Country')
161
+                            ->maxLength(100)
162
+                            ->placeholder('Enter Country')
163
+                            ->required(),
164
+                    ])->columns(2),
165
+            ]);
166
+    }
167
+
168
+    public static function table(Table $table): Table
169
+    {
170
+        return $table
171
+            ->columns([
172
+                Tables\Columns\TextColumn::make('name')
173
+                    ->weight('semibold')
174
+                    ->searchable()
175
+                    ->sortable(),
176
+                Tables\Columns\TextColumn::make('tax_number')
177
+                    ->searchable()
178
+                    ->sortable(),
179
+                Tables\Columns\TextColumn::make('email')
180
+                    ->searchable(),
181
+                Tables\Columns\TextColumn::make('phone')
182
+                    ->searchable(),
183
+                Tables\Columns\TextColumn::make('country')
184
+                    ->searchable()
185
+                    ->sortable(),
186
+                Tables\Columns\TextColumn::make('currency.name')
187
+                    ->searchable()
188
+                    ->sortable(),
189
+            ])
190
+            ->filters([
191
+                //
192
+            ])
193
+            ->actions([
194
+                Tables\Actions\EditAction::make(),
195
+            ])
196
+            ->bulkActions([
197
+                Tables\Actions\DeleteBulkAction::make(),
198
+            ]);
199
+    }
200
+    
201
+    public static function getRelations(): array
202
+    {
203
+        return [
204
+            //
205
+        ];
206
+    }
207
+    
208
+    public static function getPages(): array
209
+    {
210
+        return [
211
+            'index' => Pages\ListCustomers::route('/'),
212
+            'create' => Pages\CreateCustomer::route('/create'),
213
+            'edit' => Pages\EditCustomer::route('/{record}/edit'),
214
+        ];
215
+    }    
216
+}

+ 25
- 0
app/Filament/Resources/CustomerResource/Pages/CreateCustomer.php Просмотреть файл

@@ -0,0 +1,25 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CustomerResource\Pages;
4
+
5
+use App\Filament\Resources\CustomerResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\CreateRecord;
8
+
9
+class CreateCustomer extends CreateRecord
10
+{
11
+    protected static string $resource = CustomerResource::class;
12
+
13
+    protected function getRedirectUrl(): string
14
+    {
15
+        return $this->getResource()::getUrl('index');
16
+    }
17
+
18
+    protected function mutateFormDataBeforeCreate(array $data): array
19
+    {
20
+        $data['company_id'] = auth()->user()->currentCompany->id;
21
+        $data['type'] = 'customer';
22
+        $data['created_by'] = auth()->id();
23
+        return $data;
24
+    }
25
+}

+ 19
- 0
app/Filament/Resources/CustomerResource/Pages/EditCustomer.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CustomerResource\Pages;
4
+
5
+use App\Filament\Resources\CustomerResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\EditRecord;
8
+
9
+class EditCustomer extends EditRecord
10
+{
11
+    protected static string $resource = CustomerResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\DeleteAction::make(),
17
+        ];
18
+    }
19
+}

+ 19
- 0
app/Filament/Resources/CustomerResource/Pages/ListCustomers.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\CustomerResource\Pages;
4
+
5
+use App\Filament\Resources\CustomerResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\ListRecords;
8
+
9
+class ListCustomers extends ListRecords
10
+{
11
+    protected static string $resource = CustomerResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\CreateAction::make(),
17
+        ];
18
+    }
19
+}

+ 76
- 0
app/Filament/Resources/InvoiceResource.php Просмотреть файл

@@ -0,0 +1,76 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources;
4
+
5
+use App\Filament\Resources\InvoiceResource\Pages;
6
+use App\Filament\Resources\InvoiceResource\RelationManagers;
7
+use App\Models\Document\Document;
8
+use Filament\Forms;
9
+use Filament\Resources\Form;
10
+use Filament\Resources\Resource;
11
+use Filament\Resources\Table;
12
+use Filament\Tables;
13
+use Illuminate\Database\Eloquent\Builder;
14
+use Illuminate\Database\Eloquent\SoftDeletingScope;
15
+use Illuminate\Support\Facades\Auth;
16
+
17
+class InvoiceResource extends Resource
18
+{
19
+    protected static ?string $model = Document::class;
20
+
21
+    protected static ?string $navigationIcon = 'heroicon-o-collection';
22
+
23
+    protected static ?string $navigationGroup = 'Sales';
24
+
25
+    protected static ?string $navigationLabel = 'Invoices';
26
+
27
+    protected static ?string $modelLabel = 'invoice';
28
+
29
+    public static function getEloquentQuery(): Builder
30
+    {
31
+        return parent::getEloquentQuery()
32
+            ->where('type', 'invoice')
33
+            ->where('company_id', Auth::user()->currentCompany->id);
34
+    }
35
+
36
+    public static function form(Form $form): Form
37
+    {
38
+        return $form
39
+            ->schema([
40
+                //
41
+            ]);
42
+    }
43
+
44
+    public static function table(Table $table): Table
45
+    {
46
+        return $table
47
+            ->columns([
48
+                //
49
+            ])
50
+            ->filters([
51
+                //
52
+            ])
53
+            ->actions([
54
+                Tables\Actions\EditAction::make(),
55
+            ])
56
+            ->bulkActions([
57
+                Tables\Actions\DeleteBulkAction::make(),
58
+            ]);
59
+    }
60
+    
61
+    public static function getRelations(): array
62
+    {
63
+        return [
64
+            //
65
+        ];
66
+    }
67
+    
68
+    public static function getPages(): array
69
+    {
70
+        return [
71
+            'index' => Pages\ListInvoices::route('/'),
72
+            'create' => Pages\CreateInvoice::route('/create'),
73
+            'edit' => Pages\EditInvoice::route('/{record}/edit'),
74
+        ];
75
+    }    
76
+}

+ 12
- 0
app/Filament/Resources/InvoiceResource/Pages/CreateInvoice.php Просмотреть файл

@@ -0,0 +1,12 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\InvoiceResource\Pages;
4
+
5
+use App\Filament\Resources\InvoiceResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\CreateRecord;
8
+
9
+class CreateInvoice extends CreateRecord
10
+{
11
+    protected static string $resource = InvoiceResource::class;
12
+}

+ 19
- 0
app/Filament/Resources/InvoiceResource/Pages/EditInvoice.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\InvoiceResource\Pages;
4
+
5
+use App\Filament\Resources\InvoiceResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\EditRecord;
8
+
9
+class EditInvoice extends EditRecord
10
+{
11
+    protected static string $resource = InvoiceResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\DeleteAction::make(),
17
+        ];
18
+    }
19
+}

+ 19
- 0
app/Filament/Resources/InvoiceResource/Pages/ListInvoices.php Просмотреть файл

@@ -0,0 +1,19 @@
1
+<?php
2
+
3
+namespace App\Filament\Resources\InvoiceResource\Pages;
4
+
5
+use App\Filament\Resources\InvoiceResource;
6
+use Filament\Pages\Actions;
7
+use Filament\Resources\Pages\ListRecords;
8
+
9
+class ListInvoices extends ListRecords
10
+{
11
+    protected static string $resource = InvoiceResource::class;
12
+
13
+    protected function getActions(): array
14
+    {
15
+        return [
16
+            Actions\CreateAction::make(),
17
+        ];
18
+    }
19
+}

+ 81
- 0
app/Models/Banking/Account.php Просмотреть файл

@@ -0,0 +1,81 @@
1
+<?php
2
+
3
+namespace App\Models\Banking;
4
+
5
+use App\Models\Setting\Currency;
6
+use Database\Factories\AccountFactory;
7
+use Illuminate\Database\Eloquent\Factories\Factory;
8
+use Illuminate\Database\Eloquent\Factories\HasFactory;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
11
+use Illuminate\Support\Facades\Auth;
12
+use Illuminate\Support\Facades\Config;
13
+use Wallo\FilamentCompanies\FilamentCompanies;
14
+
15
+class Account extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'accounts';
20
+
21
+    protected $fillable = [
22
+        'type',
23
+        'name',
24
+        'number',
25
+        'currency_code',
26
+        'opening_balance',
27
+        'enabled',
28
+        'bank_name',
29
+        'bank_phone',
30
+        'bank_address',
31
+        'company_id',
32
+    ];
33
+
34
+    protected $casts = [
35
+        'enabled' => 'boolean',
36
+    ];
37
+
38
+    public function company(): BelongsTo
39
+    {
40
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
41
+    }
42
+
43
+    public function owner(): BelongsTo
44
+    {
45
+        return $this->company->owner;
46
+    }
47
+
48
+    public function currency(): BelongsTo
49
+    {
50
+        return $this->belongsTo(Currency::class, 'currency_code', 'code');
51
+    }
52
+
53
+    public static function getAccountTypes(): array
54
+    {
55
+        return [
56
+            'bank' => 'Bank',
57
+            'card' => 'Credit Card',
58
+        ];
59
+    }
60
+
61
+    public static function getCurrencyCodes(): array
62
+    {
63
+        $codes = array_keys(Config::get('money'));
64
+
65
+        return array_combine($codes, $codes);
66
+    }
67
+
68
+    public static function getDefaultCurrencyCode(): ?string
69
+    {
70
+        $defaultCurrency = Currency::where('enabled', true)
71
+            ->where('company_id', Auth::user()->currentCompany->id)
72
+            ->first();
73
+
74
+        return $defaultCurrency?->code;
75
+    }
76
+
77
+    protected static function newFactory(): Factory
78
+    {
79
+        return AccountFactory::new();
80
+    }
81
+}

+ 123
- 0
app/Models/Company.php Просмотреть файл

@@ -2,7 +2,16 @@
2 2
 
3 3
 namespace App\Models;
4 4
 
5
+use App\Models\Banking\Account;
6
+use App\Models\Document\Document;
7
+use App\Models\Document\DocumentItem;
8
+use App\Models\Document\DocumentTotal;
9
+use App\Models\Setting\Currency;
10
+use App\Models\Setting\Category;
11
+use App\Models\Setting\Discount;
12
+use App\Models\Setting\Tax;
5 13
 use Illuminate\Database\Eloquent\Factories\HasFactory;
14
+use Illuminate\Database\Eloquent\Relations\HasMany;
6 15
 use Wallo\FilamentCompanies\Company as FilamentCompaniesCompany;
7 16
 use Wallo\FilamentCompanies\Events\CompanyCreated;
8 17
 use Wallo\FilamentCompanies\Events\CompanyDeleted;
@@ -41,4 +50,118 @@ class Company extends FilamentCompaniesCompany
41 50
         'updated' => CompanyUpdated::class,
42 51
         'deleted' => CompanyDeleted::class,
43 52
     ];
53
+
54
+    public function accounts(): HasMany
55
+    {
56
+        return $this->hasMany(Account::class);
57
+    }
58
+
59
+    public function currencies(): HasMany
60
+    {
61
+        return $this->hasMany(Currency::class);
62
+    }
63
+
64
+    public function categories(): HasMany
65
+    {
66
+        return $this->hasMany(Category::class);
67
+    }
68
+
69
+    public function taxes(): HasMany
70
+    {
71
+        return $this->hasMany(Tax::class);
72
+    }
73
+
74
+    public function discounts(): HasMany
75
+    {
76
+        return $this->hasMany(Discount::class);
77
+    }
78
+
79
+    public function contacts(): HasMany
80
+    {
81
+        return $this->hasMany(Contact::class);
82
+    }
83
+
84
+    public function customers(): HasMany
85
+    {
86
+        return $this->contacts()->where('type', 'customer');
87
+    }
88
+
89
+    public function company_customers(): HasMany
90
+    {
91
+        return $this->contacts()->where('type', 'customer')
92
+            ->where('entity', 'company');
93
+    }
94
+
95
+    public function individual_customers(): HasMany
96
+    {
97
+        return $this->contacts()->where('type', 'customer')
98
+            ->where('entity', 'individual');
99
+    }
100
+
101
+    public function vendors(): HasMany
102
+    {
103
+        return $this->contacts()->where('type', 'vendor');
104
+    }
105
+
106
+    public function company_vendors(): HasMany
107
+    {
108
+        return $this->contacts()->where('type', 'vendor')
109
+            ->where('entity', 'company');
110
+    }
111
+
112
+    public function individual_vendors(): HasMany
113
+    {
114
+        return $this->contacts()->where('type', 'vendor')
115
+            ->where('entity', 'individual');
116
+    }
117
+
118
+    public function items(): HasMany
119
+    {
120
+        return $this->hasMany(Item::class);
121
+    }
122
+
123
+    public function documents(): HasMany
124
+    {
125
+        return $this->hasMany(Document::class);
126
+    }
127
+
128
+    public function document_items(): HasMany
129
+    {
130
+        return $this->hasMany(DocumentItem::class);
131
+    }
132
+
133
+    public function document_totals(): HasMany
134
+    {
135
+        return $this->hasMany(DocumentTotal::class);
136
+    }
137
+
138
+    public function bills(): HasMany
139
+    {
140
+        return $this->documents()->where('type', 'bill');
141
+    }
142
+
143
+    public function invoices(): HasMany
144
+    {
145
+        return $this->documents()->where('type', 'invoice');
146
+    }
147
+
148
+    public function bill_items(): HasMany
149
+    {
150
+        return $this->document_items()->where('type', 'bill');
151
+    }
152
+
153
+    public function bill_totals(): HasMany
154
+    {
155
+        return $this->document_totals()->where('type', 'bill');
156
+    }
157
+
158
+    public function invoice_items(): HasMany
159
+    {
160
+        return $this->document_items()->where('type', 'invoice');
161
+    }
162
+
163
+    public function invoice_totals(): HasMany
164
+    {
165
+        return $this->document_totals()->where('type', 'invoice');
166
+    }
44 167
 }

+ 96
- 0
app/Models/Contact.php Просмотреть файл

@@ -0,0 +1,96 @@
1
+<?php
2
+
3
+namespace App\Models;
4
+
5
+use App\Models\Document\Document;
6
+use App\Models\Setting\Currency;
7
+use Illuminate\Database\Eloquent\Factories\HasFactory;
8
+use Illuminate\Database\Eloquent\Model;
9
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
+use Illuminate\Database\Eloquent\Relations\HasMany;
11
+use Wallo\FilamentCompanies\FilamentCompanies;
12
+
13
+class Contact extends Model
14
+{
15
+    use HasFactory;
16
+
17
+    protected $table = 'contacts';
18
+
19
+    protected $fillable = [
20
+        'company_id',
21
+        'entity',
22
+        'type',
23
+        'name',
24
+        'email',
25
+        'tax_number',
26
+        'phone',
27
+        'address',
28
+        'city',
29
+        'zip_code',
30
+        'state',
31
+        'country',
32
+        'website',
33
+        'currency_code',
34
+        'reference',
35
+        'created_by',
36
+    ];
37
+
38
+    protected $casts = [
39
+        'enabled' => 'boolean',
40
+    ];
41
+
42
+    public function company(): BelongsTo
43
+    {
44
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
45
+    }
46
+
47
+    public function createdBy(): BelongsTo
48
+    {
49
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
50
+    }
51
+
52
+    public function currency(): BelongsTo
53
+    {
54
+        return $this->belongsTo(Currency::class, 'currency_code', 'code');
55
+    }
56
+
57
+    public function documents(): HasMany
58
+    {
59
+        return $this->hasMany(Document::class);
60
+    }
61
+
62
+    public function bills(): HasMany
63
+    {
64
+        return $this->documents()->where('type', 'bill');
65
+    }
66
+
67
+    public function invoices(): HasMany
68
+    {
69
+        return $this->documents()->where('type', 'invoice');
70
+    }
71
+
72
+    public function scopeVendor($query)
73
+    {
74
+        return $query->where('type', 'vendor');
75
+    }
76
+
77
+    public function scopeCustomer($query)
78
+    {
79
+        return $query->where('type', 'customer');
80
+    }
81
+
82
+    public function scopeEmployee($query)
83
+    {
84
+        return $query->where('type', 'employee');
85
+    }
86
+
87
+    public function scopeCompany($query)
88
+    {
89
+        return $query->where('entity', 'company');
90
+    }
91
+
92
+    public function scopeIndividual($query)
93
+    {
94
+        return $query->where('entity', 'individual');
95
+    }
96
+}

+ 100
- 0
app/Models/Document/Document.php Просмотреть файл

@@ -0,0 +1,100 @@
1
+<?php
2
+
3
+namespace App\Models\Document;
4
+
5
+use App\Models\Contact;
6
+use App\Models\Setting\Category;
7
+use App\Models\Setting\Currency;
8
+use App\Models\Setting\Discount;
9
+use App\Models\Setting\Tax;
10
+use Database\Factories\DocumentFactory;
11
+use Illuminate\Database\Eloquent\Factories\Factory;
12
+use Illuminate\Database\Eloquent\Factories\HasFactory;
13
+use Illuminate\Database\Eloquent\Model;
14
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
15
+use Illuminate\Database\Eloquent\Relations\HasMany;
16
+use Illuminate\Database\Eloquent\Relations\HasOne;
17
+use Wallo\FilamentCompanies\FilamentCompanies;
18
+
19
+class Document extends Model
20
+{
21
+    use HasFactory;
22
+
23
+    protected $table = 'documents';
24
+
25
+    protected $fillable = [
26
+        'company_id',
27
+        'type',
28
+        'document_number',
29
+        'order_number',
30
+        'status',
31
+        'document_date',
32
+        'due_date',
33
+        'paid_date',
34
+        'amount',
35
+        'tax_id',
36
+        'discount_id',
37
+        'reference',
38
+        'currency_code',
39
+        'category_id',
40
+        'contact_id',
41
+        'notes',
42
+        'created_by',
43
+    ];
44
+
45
+    protected $casts = [
46
+        'document_date' => 'datetime',
47
+        'due_date' => 'datetime',
48
+        'paid_date' => 'datetime',
49
+    ];
50
+
51
+    public function company(): BelongsTo
52
+    {
53
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
54
+    }
55
+
56
+    public function createdBy(): BelongsTo
57
+    {
58
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
59
+    }
60
+
61
+    public function tax(): BelongsTo
62
+    {
63
+        return $this->belongsTo(Tax::class);
64
+    }
65
+
66
+    public function discount(): BelongsTo
67
+    {
68
+        return $this->belongsTo(Discount::class);
69
+    }
70
+
71
+    public function currency(): BelongsTo
72
+    {
73
+        return $this->belongsTo(Currency::class, 'currency_code', 'code');
74
+    }
75
+
76
+    public function category(): BelongsTo
77
+    {
78
+        return $this->belongsTo(Category::class);
79
+    }
80
+
81
+    public function contact(): BelongsTo
82
+    {
83
+        return $this->belongsTo(Contact::class);
84
+    }
85
+
86
+    public function items(): HasMany
87
+    {
88
+        return $this->hasMany(DocumentItem::class);
89
+    }
90
+
91
+    public function total(): HasOne
92
+    {
93
+        return $this->hasOne(DocumentTotal::class);
94
+    }
95
+
96
+    protected static function newFactory(): Factory
97
+    {
98
+        return DocumentFactory::new();
99
+    }
100
+}

+ 97
- 0
app/Models/Document/DocumentItem.php Просмотреть файл

@@ -0,0 +1,97 @@
1
+<?php
2
+
3
+namespace App\Models\Document;
4
+
5
+use App\Models\Item;
6
+use App\Models\Setting\Discount;
7
+use App\Models\Setting\Tax;
8
+use Database\Factories\DocumentItemFactory;
9
+use Illuminate\Database\Eloquent\Factories\Factory;
10
+use Illuminate\Database\Eloquent\Factories\HasFactory;
11
+use Illuminate\Database\Eloquent\Model;
12
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
13
+use Wallo\FilamentCompanies\FilamentCompanies;
14
+
15
+class DocumentItem extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'document_items';
20
+
21
+    protected $fillable = [
22
+        'company_id',
23
+        'document_id',
24
+        'item_id',
25
+        'type',
26
+        'name',
27
+        'description',
28
+        'quantity',
29
+        'price',
30
+        'tax_id',
31
+        'discount_id',
32
+        'total',
33
+        'created_by',
34
+    ];
35
+
36
+    protected $casts = [
37
+        'quantity' => 'decimal:2',
38
+        'price' => 'decimal:4',
39
+        'total' => 'decimal:4',
40
+    ];
41
+
42
+    public function company(): BelongsTo
43
+    {
44
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
45
+    }
46
+
47
+    public function createdBy(): BelongsTo
48
+    {
49
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
50
+    }
51
+
52
+    public function document(): BelongsTo
53
+    {
54
+        return $this->belongsTo(Document::class);
55
+    }
56
+
57
+    public function item(): BelongsTo
58
+    {
59
+        return $this->belongsTo(Item::class);
60
+    }
61
+
62
+    public function tax(): BelongsTo
63
+    {
64
+        return $this->belongsTo(Tax::class);
65
+    }
66
+
67
+    public function discount(): BelongsTo
68
+    {
69
+        return $this->belongsTo(Discount::class);
70
+    }
71
+
72
+    public function scopeBill($query)
73
+    {
74
+        return $query->where('type', 'bill');
75
+    }
76
+
77
+    public function scopeInvoice($query)
78
+    {
79
+        return $query->where('type', 'invoice');
80
+    }
81
+
82
+    /**
83
+     * Calculate and return the net price (price - discount + tax)
84
+     */
85
+    public function getNetPriceAttribute()
86
+    {
87
+        $discountAmount = $this->discount ? ($this->price * $this->discount->rate / 100) : 0;
88
+        $taxAmount = $this->tax ? ($this->price * $this->tax->rate / 100) : 0;
89
+
90
+        return $this->price - $discountAmount + $taxAmount;
91
+    }
92
+
93
+    protected static function newFactory(): Factory
94
+    {
95
+        return DocumentItemFactory::new();
96
+    }
97
+}

+ 60
- 0
app/Models/Document/DocumentTotal.php Просмотреть файл

@@ -0,0 +1,60 @@
1
+<?php
2
+
3
+namespace App\Models\Document;
4
+
5
+use Database\Factories\DocumentTotalFactory;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+use Illuminate\Database\Eloquent\Factories\HasFactory;
8
+use Illuminate\Database\Eloquent\Model;
9
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
+use Wallo\FilamentCompanies\FilamentCompanies;
11
+
12
+class DocumentTotal extends Model
13
+{
14
+    use HasFactory;
15
+
16
+    protected $table = 'document_totals';
17
+
18
+    protected $fillable = [
19
+        'company_id',
20
+        'document_id',
21
+        'type',
22
+        'code',
23
+        'name',
24
+        'subtotal',
25
+        'discount',
26
+        'tax',
27
+        'total',
28
+        'created_by',
29
+    ];
30
+
31
+    public function company(): BelongsTo
32
+    {
33
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
34
+    }
35
+
36
+    public function createdBy(): BelongsTo
37
+    {
38
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
39
+    }
40
+
41
+    public function document(): BelongsTo
42
+    {
43
+        return $this->belongsTo(Document::class);
44
+    }
45
+
46
+    public function scopeInvoice($query)
47
+    {
48
+        return $query->where('type', 'invoice');
49
+    }
50
+
51
+    public function scopeBill($query)
52
+    {
53
+        return $query->where('type', 'bill');
54
+    }
55
+
56
+    protected static function newFactory(): Factory
57
+    {
58
+        return DocumentTotalFactory::new();
59
+    }
60
+}

+ 82
- 0
app/Models/Item.php Просмотреть файл

@@ -0,0 +1,82 @@
1
+<?php
2
+
3
+namespace App\Models;
4
+
5
+use App\Models\Document\DocumentItem;
6
+use App\Models\Setting\Category;
7
+use App\Models\Setting\Discount;
8
+use App\Models\Setting\Tax;
9
+use Illuminate\Database\Eloquent\Factories\HasFactory;
10
+use Illuminate\Database\Eloquent\Model;
11
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
+use Illuminate\Database\Eloquent\Relations\HasMany;
13
+use Wallo\FilamentCompanies\FilamentCompanies;
14
+
15
+class Item extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'items';
20
+
21
+    protected $fillable = [
22
+        'company_id',
23
+        'type',
24
+        'name',
25
+        'sku',
26
+        'description',
27
+        'sale_price',
28
+        'purchase_price',
29
+        'quantity',
30
+        'category_id',
31
+        'tax_id',
32
+        'discount_id',
33
+        'enabled',
34
+        'created_by',
35
+    ];
36
+
37
+    protected $casts = [
38
+        'enabled' => 'boolean',
39
+    ];
40
+
41
+    public function company(): BelongsTo
42
+    {
43
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
44
+    }
45
+
46
+    public function createdBy(): BelongsTo
47
+    {
48
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
49
+    }
50
+
51
+    public function category(): BelongsTo
52
+    {
53
+        return $this->belongsTo(Category::class, 'category_id')->withDefault([
54
+            'name' => 'General',
55
+        ]);
56
+    }
57
+
58
+    public function tax(): BelongsTo
59
+    {
60
+        return $this->belongsTo(Tax::class, 'tax_id');
61
+    }
62
+
63
+    public function discount(): BelongsTo
64
+    {
65
+        return $this->belongsTo(Discount::class, 'discount_id');
66
+    }
67
+
68
+    public function document_items(): HasMany
69
+    {
70
+        return $this->hasMany(DocumentItem::class);
71
+    }
72
+
73
+    public function bill_items(): HasMany
74
+    {
75
+        return $this->document_items()->where('type', 'bill');
76
+    }
77
+
78
+    public function invoice_items(): HasMany
79
+    {
80
+        return $this->document_items()->where('type', 'invoice');
81
+    }
82
+}

+ 68
- 0
app/Models/Setting/Category.php Просмотреть файл

@@ -0,0 +1,68 @@
1
+<?php
2
+
3
+namespace App\Models\Setting;
4
+
5
+use App\Models\Document\Document;
6
+use App\Models\Item;
7
+use Database\Factories\CategoryFactory;
8
+use Illuminate\Database\Eloquent\Factories\Factory;
9
+use Illuminate\Database\Eloquent\Factories\HasFactory;
10
+use Illuminate\Database\Eloquent\Model;
11
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
+use Illuminate\Database\Eloquent\Relations\HasMany;
13
+use Wallo\FilamentCompanies\FilamentCompanies;
14
+
15
+class Category extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'categories';
20
+
21
+    protected $fillable = [
22
+        'company_id',
23
+        'name',
24
+        'type',
25
+        'color',
26
+        'enabled',
27
+        'created_by',
28
+    ];
29
+
30
+    protected $casts = [
31
+        'enabled' => 'boolean',
32
+    ];
33
+
34
+    public function company(): BelongsTo
35
+    {
36
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
37
+    }
38
+
39
+    public function createdBy(): BelongsTo
40
+    {
41
+        return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
42
+    }
43
+
44
+    public function items(): HasMany
45
+    {
46
+        return $this->hasMany(Item::class);
47
+    }
48
+
49
+    public function documents(): HasMany
50
+    {
51
+        return $this->hasMany(Document::class);
52
+    }
53
+
54
+    public function bills(): HasMany
55
+    {
56
+        return $this->documents()->where('type', 'bill');
57
+    }
58
+
59
+    public function invoices(): HasMany
60
+    {
61
+        return $this->documents()->where('type', 'invoice');
62
+    }
63
+
64
+    protected static function newFactory(): Factory
65
+    {
66
+        return CategoryFactory::new();
67
+    }
68
+}

+ 68
- 0
app/Models/Setting/Currency.php Просмотреть файл

@@ -0,0 +1,68 @@
1
+<?php
2
+
3
+namespace App\Models\Setting;
4
+
5
+use App\Models\Banking\Account;
6
+use Database\Factories\CurrencyFactory;
7
+use Illuminate\Database\Eloquent\Factories\Factory;
8
+use Illuminate\Database\Eloquent\Factories\HasFactory;
9
+use Illuminate\Database\Eloquent\Model;
10
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
11
+use Illuminate\Database\Eloquent\Relations\HasMany;
12
+use Illuminate\Support\Facades\Auth;
13
+use Illuminate\Support\Facades\Config;
14
+use Wallo\FilamentCompanies\FilamentCompanies;
15
+
16
+class Currency extends Model
17
+{
18
+    use HasFactory;
19
+
20
+    protected $table = 'currencies';
21
+
22
+    protected $fillable = [
23
+        'name',
24
+        'code',
25
+        'rate',
26
+        'precision',
27
+        'symbol',
28
+        'symbol_first',
29
+        'decimal_mark',
30
+        'thousands_separator',
31
+        'enabled',
32
+        'company_id',
33
+    ];
34
+
35
+    protected $casts = [
36
+        'enabled' => 'boolean',
37
+        'symbol_first' => 'boolean',
38
+    ];
39
+
40
+    public function accounts(): HasMany
41
+    {
42
+        return $this->hasMany(Account::class, 'currency_code', 'code');
43
+    }
44
+
45
+    public function company(): BelongsTo
46
+    {
47
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
48
+    }
49
+
50
+    public static function getCurrencyCodes(): array
51
+    {
52
+        $allCodes = array_keys(Config::get('money'));
53
+
54
+        $storedCodes = static::query()
55
+            ->where('company_id', Auth::user()->currentCompany->id)
56
+            ->pluck('code')
57
+            ->toArray();
58
+
59
+        $codes = array_diff($allCodes, $storedCodes);
60
+
61
+        return array_combine($codes, $codes);
62
+    }
63
+
64
+    protected static function newFactory(): Factory
65
+    {
66
+        return CurrencyFactory::new();
67
+    }
68
+}

+ 65
- 0
app/Models/Setting/Discount.php Просмотреть файл

@@ -0,0 +1,65 @@
1
+<?php
2
+
3
+namespace App\Models\Setting;
4
+
5
+use App\Models\Document\DocumentItem;
6
+use App\Models\Item;
7
+use App\Models\User;
8
+use Database\Factories\DiscountFactory;
9
+use Illuminate\Database\Eloquent\Factories\Factory;
10
+use Illuminate\Database\Eloquent\Factories\HasFactory;
11
+use Illuminate\Database\Eloquent\Model;
12
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
13
+use Illuminate\Database\Eloquent\Relations\HasMany;
14
+
15
+class Discount extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'discounts';
20
+
21
+    protected $fillable = [
22
+        'name',
23
+        'description',
24
+        'rate',
25
+        'computation',
26
+        'type',
27
+        'scope',
28
+        'enabled',
29
+        'created_by',
30
+    ];
31
+
32
+    protected $casts = [
33
+        'enabled' => 'boolean',
34
+    ];
35
+
36
+    public function createdBy(): BelongsTo
37
+    {
38
+        return $this->belongsTo(User::class, 'created_by');
39
+    }
40
+
41
+    public function items(): HasMany
42
+    {
43
+        return $this->hasMany(Item::class);
44
+    }
45
+
46
+    public function document_items(): HasMany
47
+    {
48
+        return $this->hasMany(DocumentItem::class);
49
+    }
50
+
51
+    public function bill_items(): HasMany
52
+    {
53
+        return $this->document_items()->where('type', 'bill');
54
+    }
55
+
56
+    public function invoice_items(): HasMany
57
+    {
58
+        return $this->document_items()->where('type', 'invoice');
59
+    }
60
+
61
+    protected static function newFactory(): Factory
62
+    {
63
+        return DiscountFactory::new();
64
+    }
65
+}

+ 72
- 0
app/Models/Setting/Tax.php Просмотреть файл

@@ -0,0 +1,72 @@
1
+<?php
2
+
3
+namespace App\Models\Setting;
4
+
5
+use App\Models\Company;
6
+use App\Models\Document\DocumentItem;
7
+use App\Models\Item;
8
+use App\Models\User;
9
+use Database\Factories\TaxFactory;
10
+use Illuminate\Database\Eloquent\Factories\Factory;
11
+use Illuminate\Database\Eloquent\Factories\HasFactory;
12
+use Illuminate\Database\Eloquent\Model;
13
+use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
+use Illuminate\Database\Eloquent\Relations\HasMany;
15
+
16
+class Tax extends Model
17
+{
18
+    use HasFactory;
19
+
20
+    protected $table = 'taxes';
21
+
22
+    protected $fillable = [
23
+        'company_id',
24
+        'name',
25
+        'description',
26
+        'rate',
27
+        'computation',
28
+        'type',
29
+        'scope',
30
+        'enabled',
31
+        'created_by',
32
+    ];
33
+
34
+    protected $casts = [
35
+        'enabled' => 'boolean',
36
+    ];
37
+
38
+    public function company(): BelongsTo
39
+    {
40
+        return $this->belongsTo(Company::class, 'company_id');
41
+    }
42
+
43
+    public function createdBy(): BelongsTo
44
+    {
45
+        return $this->belongsTo(User::class, 'created_by');
46
+    }
47
+
48
+    public function items(): HasMany
49
+    {
50
+        return $this->hasMany(Item::class);
51
+    }
52
+
53
+    public function document_items(): HasMany
54
+    {
55
+        return $this->hasMany(DocumentItem::class);
56
+    }
57
+
58
+    public function bill_items(): HasMany
59
+    {
60
+        return $this->document_items()->where('type', 'bill');
61
+    }
62
+
63
+    public function invoice_items(): HasMany
64
+    {
65
+        return $this->document_items()->where('type', 'invoice');
66
+    }
67
+
68
+    protected static function newFactory(): Factory
69
+    {
70
+        return TaxFactory::new();
71
+    }
72
+}

+ 66
- 0
app/Policies/AccountPolicy.php Просмотреть файл

@@ -0,0 +1,66 @@
1
+<?php
2
+
3
+namespace App\Policies;
4
+
5
+use App\Models\Banking\Account;
6
+use App\Models\User;
7
+use Illuminate\Auth\Access\Response;
8
+
9
+class AccountPolicy
10
+{
11
+    /**
12
+     * Determine whether the user can view any models.
13
+     */
14
+    public function viewAny(User $user): bool
15
+    {
16
+        return true;
17
+    }
18
+
19
+    /**
20
+     * Determine whether the user can view the model.
21
+     */
22
+    public function view(User $user, Account $account): bool
23
+    {
24
+        return $user->belongsToCompany($account->company);
25
+    }
26
+
27
+    /**
28
+     * Determine whether the user can create models.
29
+     */
30
+    public function create(User $user): bool
31
+    {
32
+        return true;
33
+    }
34
+
35
+    /**
36
+     * Determine whether the user can update the model.
37
+     */
38
+    public function update(User $user, Account $account): bool
39
+    {
40
+        return $user->ownsCompany($account->company);
41
+    }
42
+
43
+    /**
44
+     * Determine whether the user can delete the model.
45
+     */
46
+    public function delete(User $user, Account $account): bool
47
+    {
48
+        return $user->ownsCompany($account->company);
49
+    }
50
+
51
+    /**
52
+     * Determine whether the user can restore the model.
53
+     */
54
+    public function restore(User $user, Account $account): bool
55
+    {
56
+        return $user->ownsCompany($account->company);
57
+    }
58
+
59
+    /**
60
+     * Determine whether the user can permanently delete the model.
61
+     */
62
+    public function forceDelete(User $user, Account $account): bool
63
+    {
64
+        return $user->ownsCompany($account->company);
65
+    }
66
+}

+ 77
- 80
composer.lock Просмотреть файл

@@ -934,16 +934,16 @@
934 934
         },
935 935
         {
936 936
             "name": "filament/filament",
937
-            "version": "v2.17.38",
937
+            "version": "v2.17.43",
938 938
             "source": {
939 939
                 "type": "git",
940 940
                 "url": "https://github.com/filamentphp/app.git",
941
-                "reference": "0719a1fcebcad120dd4e254938cfb08470c90105"
941
+                "reference": "d3e067e44b981f8212740ad32e9c8d733e562b2c"
942 942
             },
943 943
             "dist": {
944 944
                 "type": "zip",
945
-                "url": "https://api.github.com/repos/filamentphp/app/zipball/0719a1fcebcad120dd4e254938cfb08470c90105",
946
-                "reference": "0719a1fcebcad120dd4e254938cfb08470c90105",
945
+                "url": "https://api.github.com/repos/filamentphp/app/zipball/d3e067e44b981f8212740ad32e9c8d733e562b2c",
946
+                "reference": "d3e067e44b981f8212740ad32e9c8d733e562b2c",
947 947
                 "shasum": ""
948 948
             },
949 949
             "require": {
@@ -993,20 +993,20 @@
993 993
                 "issues": "https://github.com/filamentphp/filament/issues",
994 994
                 "source": "https://github.com/filamentphp/filament"
995 995
             },
996
-            "time": "2023-05-08T15:39:08+00:00"
996
+            "time": "2023-05-22T21:18:35+00:00"
997 997
         },
998 998
         {
999 999
             "name": "filament/forms",
1000
-            "version": "v2.17.38",
1000
+            "version": "v2.17.43",
1001 1001
             "source": {
1002 1002
                 "type": "git",
1003 1003
                 "url": "https://github.com/filamentphp/forms.git",
1004
-                "reference": "49bafbf87f0d7f23e7b62a6d40591fd3ea81a55b"
1004
+                "reference": "a8acd3681f2b2163d6cad3478a5fe14489d53217"
1005 1005
             },
1006 1006
             "dist": {
1007 1007
                 "type": "zip",
1008
-                "url": "https://api.github.com/repos/filamentphp/forms/zipball/49bafbf87f0d7f23e7b62a6d40591fd3ea81a55b",
1009
-                "reference": "49bafbf87f0d7f23e7b62a6d40591fd3ea81a55b",
1008
+                "url": "https://api.github.com/repos/filamentphp/forms/zipball/a8acd3681f2b2163d6cad3478a5fe14489d53217",
1009
+                "reference": "a8acd3681f2b2163d6cad3478a5fe14489d53217",
1010 1010
                 "shasum": ""
1011 1011
             },
1012 1012
             "require": {
@@ -1051,11 +1051,11 @@
1051 1051
                 "issues": "https://github.com/filamentphp/filament/issues",
1052 1052
                 "source": "https://github.com/filamentphp/filament"
1053 1053
             },
1054
-            "time": "2023-05-08T09:55:16+00:00"
1054
+            "time": "2023-05-22T21:18:31+00:00"
1055 1055
         },
1056 1056
         {
1057 1057
             "name": "filament/notifications",
1058
-            "version": "v2.17.38",
1058
+            "version": "v2.17.43",
1059 1059
             "source": {
1060 1060
                 "type": "git",
1061 1061
                 "url": "https://github.com/filamentphp/notifications.git",
@@ -1108,16 +1108,16 @@
1108 1108
         },
1109 1109
         {
1110 1110
             "name": "filament/support",
1111
-            "version": "v2.17.38",
1111
+            "version": "v2.17.43",
1112 1112
             "source": {
1113 1113
                 "type": "git",
1114 1114
                 "url": "https://github.com/filamentphp/support.git",
1115
-                "reference": "1a411197122524886c81a74c7044258d64d56308"
1115
+                "reference": "1e03d08cb6df27fd64e658b5316fd34f81554a8d"
1116 1116
             },
1117 1117
             "dist": {
1118 1118
                 "type": "zip",
1119
-                "url": "https://api.github.com/repos/filamentphp/support/zipball/1a411197122524886c81a74c7044258d64d56308",
1120
-                "reference": "1a411197122524886c81a74c7044258d64d56308",
1119
+                "url": "https://api.github.com/repos/filamentphp/support/zipball/1e03d08cb6df27fd64e658b5316fd34f81554a8d",
1120
+                "reference": "1e03d08cb6df27fd64e658b5316fd34f81554a8d",
1121 1121
                 "shasum": ""
1122 1122
             },
1123 1123
             "require": {
@@ -1154,20 +1154,20 @@
1154 1154
                 "issues": "https://github.com/filamentphp/filament/issues",
1155 1155
                 "source": "https://github.com/filamentphp/filament"
1156 1156
             },
1157
-            "time": "2023-05-04T23:08:15+00:00"
1157
+            "time": "2023-05-22T21:18:29+00:00"
1158 1158
         },
1159 1159
         {
1160 1160
             "name": "filament/tables",
1161
-            "version": "v2.17.38",
1161
+            "version": "v2.17.43",
1162 1162
             "source": {
1163 1163
                 "type": "git",
1164 1164
                 "url": "https://github.com/filamentphp/tables.git",
1165
-                "reference": "b23b5cc95c7b1afb650b366545942b1ccad993af"
1165
+                "reference": "1a57eac2388d2336c66dedf840b20a6b66a9b127"
1166 1166
             },
1167 1167
             "dist": {
1168 1168
                 "type": "zip",
1169
-                "url": "https://api.github.com/repos/filamentphp/tables/zipball/b23b5cc95c7b1afb650b366545942b1ccad993af",
1170
-                "reference": "b23b5cc95c7b1afb650b366545942b1ccad993af",
1169
+                "url": "https://api.github.com/repos/filamentphp/tables/zipball/1a57eac2388d2336c66dedf840b20a6b66a9b127",
1170
+                "reference": "1a57eac2388d2336c66dedf840b20a6b66a9b127",
1171 1171
                 "shasum": ""
1172 1172
             },
1173 1173
             "require": {
@@ -1210,7 +1210,7 @@
1210 1210
                 "issues": "https://github.com/filamentphp/filament/issues",
1211 1211
                 "source": "https://github.com/filamentphp/filament"
1212 1212
             },
1213
-            "time": "2023-05-08T09:55:33+00:00"
1213
+            "time": "2023-05-22T21:18:35+00:00"
1214 1214
         },
1215 1215
         {
1216 1216
             "name": "flowframe/laravel-trend",
@@ -1421,21 +1421,21 @@
1421 1421
         },
1422 1422
         {
1423 1423
             "name": "guzzlehttp/guzzle",
1424
-            "version": "7.5.1",
1424
+            "version": "7.7.0",
1425 1425
             "source": {
1426 1426
                 "type": "git",
1427 1427
                 "url": "https://github.com/guzzle/guzzle.git",
1428
-                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9"
1428
+                "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5"
1429 1429
             },
1430 1430
             "dist": {
1431 1431
                 "type": "zip",
1432
-                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/b964ca597e86b752cd994f27293e9fa6b6a95ed9",
1433
-                "reference": "b964ca597e86b752cd994f27293e9fa6b6a95ed9",
1432
+                "url": "https://api.github.com/repos/guzzle/guzzle/zipball/fb7566caccf22d74d1ab270de3551f72a58399f5",
1433
+                "reference": "fb7566caccf22d74d1ab270de3551f72a58399f5",
1434 1434
                 "shasum": ""
1435 1435
             },
1436 1436
             "require": {
1437 1437
                 "ext-json": "*",
1438
-                "guzzlehttp/promises": "^1.5",
1438
+                "guzzlehttp/promises": "^1.5.3 || ^2.0",
1439 1439
                 "guzzlehttp/psr7": "^1.9.1 || ^2.4.5",
1440 1440
                 "php": "^7.2.5 || ^8.0",
1441 1441
                 "psr/http-client": "^1.0",
@@ -1447,7 +1447,8 @@
1447 1447
             "require-dev": {
1448 1448
                 "bamarni/composer-bin-plugin": "^1.8.1",
1449 1449
                 "ext-curl": "*",
1450
-                "php-http/client-integration-tests": "^3.0",
1450
+                "php-http/client-integration-tests": "dev-master#2c025848417c1135031fdf9c728ee53d0a7ceaee as 3.0.999",
1451
+                "php-http/message-factory": "^1.1",
1451 1452
                 "phpunit/phpunit": "^8.5.29 || ^9.5.23",
1452 1453
                 "psr/log": "^1.1 || ^2.0 || ^3.0"
1453 1454
             },
@@ -1461,9 +1462,6 @@
1461 1462
                 "bamarni-bin": {
1462 1463
                     "bin-links": true,
1463 1464
                     "forward-command": false
1464
-                },
1465
-                "branch-alias": {
1466
-                    "dev-master": "7.5-dev"
1467 1465
                 }
1468 1466
             },
1469 1467
             "autoload": {
@@ -1529,7 +1527,7 @@
1529 1527
             ],
1530 1528
             "support": {
1531 1529
                 "issues": "https://github.com/guzzle/guzzle/issues",
1532
-                "source": "https://github.com/guzzle/guzzle/tree/7.5.1"
1530
+                "source": "https://github.com/guzzle/guzzle/tree/7.7.0"
1533 1531
             },
1534 1532
             "funding": [
1535 1533
                 {
@@ -1545,38 +1543,37 @@
1545 1543
                     "type": "tidelift"
1546 1544
                 }
1547 1545
             ],
1548
-            "time": "2023-04-17T16:30:08+00:00"
1546
+            "time": "2023-05-21T14:04:53+00:00"
1549 1547
         },
1550 1548
         {
1551 1549
             "name": "guzzlehttp/promises",
1552
-            "version": "1.5.2",
1550
+            "version": "2.0.0",
1553 1551
             "source": {
1554 1552
                 "type": "git",
1555 1553
                 "url": "https://github.com/guzzle/promises.git",
1556
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598"
1554
+                "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6"
1557 1555
             },
1558 1556
             "dist": {
1559 1557
                 "type": "zip",
1560
-                "url": "https://api.github.com/repos/guzzle/promises/zipball/b94b2807d85443f9719887892882d0329d1e2598",
1561
-                "reference": "b94b2807d85443f9719887892882d0329d1e2598",
1558
+                "url": "https://api.github.com/repos/guzzle/promises/zipball/3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
1559
+                "reference": "3a494dc7dc1d7d12e511890177ae2d0e6c107da6",
1562 1560
                 "shasum": ""
1563 1561
             },
1564 1562
             "require": {
1565
-                "php": ">=5.5"
1563
+                "php": "^7.2.5 || ^8.0"
1566 1564
             },
1567 1565
             "require-dev": {
1568
-                "symfony/phpunit-bridge": "^4.4 || ^5.1"
1566
+                "bamarni/composer-bin-plugin": "^1.8.1",
1567
+                "phpunit/phpunit": "^8.5.29 || ^9.5.23"
1569 1568
             },
1570 1569
             "type": "library",
1571 1570
             "extra": {
1572
-                "branch-alias": {
1573
-                    "dev-master": "1.5-dev"
1571
+                "bamarni-bin": {
1572
+                    "bin-links": true,
1573
+                    "forward-command": false
1574 1574
                 }
1575 1575
             },
1576 1576
             "autoload": {
1577
-                "files": [
1578
-                    "src/functions_include.php"
1579
-                ],
1580 1577
                 "psr-4": {
1581 1578
                     "GuzzleHttp\\Promise\\": "src/"
1582 1579
                 }
@@ -1613,7 +1610,7 @@
1613 1610
             ],
1614 1611
             "support": {
1615 1612
                 "issues": "https://github.com/guzzle/promises/issues",
1616
-                "source": "https://github.com/guzzle/promises/tree/1.5.2"
1613
+                "source": "https://github.com/guzzle/promises/tree/2.0.0"
1617 1614
             },
1618 1615
             "funding": [
1619 1616
                 {
@@ -1629,7 +1626,7 @@
1629 1626
                     "type": "tidelift"
1630 1627
                 }
1631 1628
             ],
1632
-            "time": "2022-08-28T14:55:35+00:00"
1629
+            "time": "2023-05-21T13:50:22+00:00"
1633 1630
         },
1634 1631
         {
1635 1632
             "name": "guzzlehttp/psr7",
@@ -1897,16 +1894,16 @@
1897 1894
         },
1898 1895
         {
1899 1896
             "name": "laravel/framework",
1900
-            "version": "v10.10.0",
1897
+            "version": "v10.11.0",
1901 1898
             "source": {
1902 1899
                 "type": "git",
1903 1900
                 "url": "https://github.com/laravel/framework.git",
1904
-                "reference": "0da22a8d179f79b49d4e71f4822f759651f35012"
1901
+                "reference": "21a5b6d9b669f32c10cc8ba776511b5f62599fea"
1905 1902
             },
1906 1903
             "dist": {
1907 1904
                 "type": "zip",
1908
-                "url": "https://api.github.com/repos/laravel/framework/zipball/0da22a8d179f79b49d4e71f4822f759651f35012",
1909
-                "reference": "0da22a8d179f79b49d4e71f4822f759651f35012",
1905
+                "url": "https://api.github.com/repos/laravel/framework/zipball/21a5b6d9b669f32c10cc8ba776511b5f62599fea",
1906
+                "reference": "21a5b6d9b669f32c10cc8ba776511b5f62599fea",
1910 1907
                 "shasum": ""
1911 1908
             },
1912 1909
             "require": {
@@ -2093,7 +2090,7 @@
2093 2090
                 "issues": "https://github.com/laravel/framework/issues",
2094 2091
                 "source": "https://github.com/laravel/framework"
2095 2092
             },
2096
-            "time": "2023-05-09T13:08:05+00:00"
2093
+            "time": "2023-05-16T13:59:23+00:00"
2097 2094
         },
2098 2095
         {
2099 2096
             "name": "laravel/sanctum",
@@ -3583,16 +3580,16 @@
3583 3580
         },
3584 3581
         {
3585 3582
             "name": "nikic/php-parser",
3586
-            "version": "v4.15.4",
3583
+            "version": "v4.15.5",
3587 3584
             "source": {
3588 3585
                 "type": "git",
3589 3586
                 "url": "https://github.com/nikic/PHP-Parser.git",
3590
-                "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
3587
+                "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
3591 3588
             },
3592 3589
             "dist": {
3593 3590
                 "type": "zip",
3594
-                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
3595
-                "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
3591
+                "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
3592
+                "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
3596 3593
                 "shasum": ""
3597 3594
             },
3598 3595
             "require": {
@@ -3633,9 +3630,9 @@
3633 3630
             ],
3634 3631
             "support": {
3635 3632
                 "issues": "https://github.com/nikic/PHP-Parser/issues",
3636
-                "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
3633
+                "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
3637 3634
             },
3638
-            "time": "2023-03-05T19:49:14+00:00"
3635
+            "time": "2023-05-19T20:20:00+00:00"
3639 3636
         },
3640 3637
         {
3641 3638
             "name": "nunomaduro/termwind",
@@ -4283,16 +4280,16 @@
4283 4280
         },
4284 4281
         {
4285 4282
             "name": "psy/psysh",
4286
-            "version": "v0.11.17",
4283
+            "version": "v0.11.18",
4287 4284
             "source": {
4288 4285
                 "type": "git",
4289 4286
                 "url": "https://github.com/bobthecow/psysh.git",
4290
-                "reference": "3dc5d4018dabd80bceb8fe1e3191ba8460569f0a"
4287
+                "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec"
4291 4288
             },
4292 4289
             "dist": {
4293 4290
                 "type": "zip",
4294
-                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3dc5d4018dabd80bceb8fe1e3191ba8460569f0a",
4295
-                "reference": "3dc5d4018dabd80bceb8fe1e3191ba8460569f0a",
4291
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/4f00ee9e236fa6a48f4560d1300b9c961a70a7ec",
4292
+                "reference": "4f00ee9e236fa6a48f4560d1300b9c961a70a7ec",
4296 4293
                 "shasum": ""
4297 4294
             },
4298 4295
             "require": {
@@ -4353,9 +4350,9 @@
4353 4350
             ],
4354 4351
             "support": {
4355 4352
                 "issues": "https://github.com/bobthecow/psysh/issues",
4356
-                "source": "https://github.com/bobthecow/psysh/tree/v0.11.17"
4353
+                "source": "https://github.com/bobthecow/psysh/tree/v0.11.18"
4357 4354
             },
4358
-            "time": "2023-05-05T20:02:42+00:00"
4355
+            "time": "2023-05-23T02:31:11+00:00"
4359 4356
         },
4360 4357
         {
4361 4358
             "name": "ralouphie/getallheaders",
@@ -7642,16 +7639,16 @@
7642 7639
         },
7643 7640
         {
7644 7641
             "name": "fakerphp/faker",
7645
-            "version": "v1.21.0",
7642
+            "version": "v1.22.0",
7646 7643
             "source": {
7647 7644
                 "type": "git",
7648 7645
                 "url": "https://github.com/FakerPHP/Faker.git",
7649
-                "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d"
7646
+                "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2"
7650 7647
             },
7651 7648
             "dist": {
7652 7649
                 "type": "zip",
7653
-                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/92efad6a967f0b79c499705c69b662f738cc9e4d",
7654
-                "reference": "92efad6a967f0b79c499705c69b662f738cc9e4d",
7650
+                "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/f85772abd508bd04e20bb4b1bbe260a68d0066d2",
7651
+                "reference": "f85772abd508bd04e20bb4b1bbe260a68d0066d2",
7655 7652
                 "shasum": ""
7656 7653
             },
7657 7654
             "require": {
@@ -7704,9 +7701,9 @@
7704 7701
             ],
7705 7702
             "support": {
7706 7703
                 "issues": "https://github.com/FakerPHP/Faker/issues",
7707
-                "source": "https://github.com/FakerPHP/Faker/tree/v1.21.0"
7704
+                "source": "https://github.com/FakerPHP/Faker/tree/v1.22.0"
7708 7705
             },
7709
-            "time": "2022-12-13T13:54:32+00:00"
7706
+            "time": "2023-05-14T12:31:37+00:00"
7710 7707
         },
7711 7708
         {
7712 7709
             "name": "filp/whoops",
@@ -8301,16 +8298,16 @@
8301 8298
         },
8302 8299
         {
8303 8300
             "name": "phpunit/php-code-coverage",
8304
-            "version": "10.1.1",
8301
+            "version": "10.1.2",
8305 8302
             "source": {
8306 8303
                 "type": "git",
8307 8304
                 "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
8308
-                "reference": "884a0da7f9f46f28b2cb69134217fd810b793974"
8305
+                "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e"
8309 8306
             },
8310 8307
             "dist": {
8311 8308
                 "type": "zip",
8312
-                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/884a0da7f9f46f28b2cb69134217fd810b793974",
8313
-                "reference": "884a0da7f9f46f28b2cb69134217fd810b793974",
8309
+                "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/db1497ec8dd382e82c962f7abbe0320e4882ee4e",
8310
+                "reference": "db1497ec8dd382e82c962f7abbe0320e4882ee4e",
8314 8311
                 "shasum": ""
8315 8312
             },
8316 8313
             "require": {
@@ -8367,7 +8364,7 @@
8367 8364
             "support": {
8368 8365
                 "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
8369 8366
                 "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
8370
-                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.1"
8367
+                "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.2"
8371 8368
             },
8372 8369
             "funding": [
8373 8370
                 {
@@ -8375,7 +8372,7 @@
8375 8372
                     "type": "github"
8376 8373
                 }
8377 8374
             ],
8378
-            "time": "2023-04-17T12:15:40+00:00"
8375
+            "time": "2023-05-22T09:04:27+00:00"
8379 8376
         },
8380 8377
         {
8381 8378
             "name": "phpunit/php-file-iterator",
@@ -8621,16 +8618,16 @@
8621 8618
         },
8622 8619
         {
8623 8620
             "name": "phpunit/phpunit",
8624
-            "version": "10.1.2",
8621
+            "version": "10.1.3",
8625 8622
             "source": {
8626 8623
                 "type": "git",
8627 8624
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
8628
-                "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b"
8625
+                "reference": "2379ebafc1737e71cdc84f402acb6b7f04198b9d"
8629 8626
             },
8630 8627
             "dist": {
8631 8628
                 "type": "zip",
8632
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6f0cd95be71add539f8fd2be25b2a4a29789000b",
8633
-                "reference": "6f0cd95be71add539f8fd2be25b2a4a29789000b",
8629
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2379ebafc1737e71cdc84f402acb6b7f04198b9d",
8630
+                "reference": "2379ebafc1737e71cdc84f402acb6b7f04198b9d",
8634 8631
                 "shasum": ""
8635 8632
             },
8636 8633
             "require": {
@@ -8702,7 +8699,7 @@
8702 8699
             "support": {
8703 8700
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
8704 8701
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
8705
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.2"
8702
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.1.3"
8706 8703
             },
8707 8704
             "funding": [
8708 8705
                 {
@@ -8718,7 +8715,7 @@
8718 8715
                     "type": "tidelift"
8719 8716
                 }
8720 8717
             ],
8721
-            "time": "2023-04-22T07:38:19+00:00"
8718
+            "time": "2023-05-11T05:16:22+00:00"
8722 8719
         },
8723 8720
         {
8724 8721
             "name": "psr/cache",

+ 1808
- 0
config/money.php
Разница между файлами не показана из-за своего большого размера
Просмотреть файл


+ 84
- 0
database/factories/AccountFactory.php Просмотреть файл

@@ -0,0 +1,84 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Banking\Account;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Account>
10
+ */
11
+class AccountFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Account::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        $types = ['bank', 'card'];
28
+
29
+        return [
30
+            'type' => $this->faker->randomElement($types),
31
+            'name' => $this->faker->text(15),
32
+            'number' => (string) $this->faker->randomNumber(12, true),
33
+            'currency_code' => $this->company->currencies()->enabled()->get()->random(1)->pluck('code')->first(),
34
+            'opening_balance' => '0',
35
+            'bank_name' => $this->faker->text(15),
36
+            'bank_phone' => $this->faker->phoneNumber,
37
+            'bank_address' => $this->faker->address,
38
+            'enabled' => $this->faker->boolean,
39
+            'company_id' => $this->company->id,
40
+        ];
41
+    }
42
+
43
+    /**
44
+     * Indicate that the model is enabled.
45
+     *
46
+     * @return Factory<Account>
47
+     */
48
+    public function enabled(): Factory
49
+    {
50
+        return $this->state(function (array $attributes) {
51
+            return [
52
+                'enabled' => true,
53
+            ];
54
+        });
55
+    }
56
+
57
+    /**
58
+     * Indicate that the model is disabled.
59
+     *
60
+     * @return Factory<Account>
61
+     */
62
+    public function disabled(): Factory
63
+    {
64
+        return $this->state(function (array $attributes) {
65
+            return [
66
+                'enabled' => false,
67
+            ];
68
+        });
69
+    }
70
+
71
+    /**
72
+     * Indicate that the default currency is used.
73
+     *
74
+     * @return Factory<Account>
75
+     */
76
+    public function default_currency(): Factory
77
+    {
78
+        return $this->state(function (array $attributes) {
79
+            return [
80
+                'currency_code' => $this->default_currency(),
81
+            ];
82
+        });
83
+    }
84
+}

+ 31
- 0
database/factories/CategoryFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Setting\Category;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Category>
10
+ */
11
+class CategoryFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Category::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 31
- 0
database/factories/ContactFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Contact;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Contact>
10
+ */
11
+class ContactFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Contact::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 79
- 0
database/factories/CurrencyFactory.php Просмотреть файл

@@ -0,0 +1,79 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Setting\Currency;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Currency>
10
+ */
11
+class CurrencyFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Currency::class;
19
+
20
+
21
+    /**
22
+     * Define the model's default state.
23
+     *
24
+     * @return array<string, mixed>
25
+     */
26
+    public function definition(): array
27
+    {
28
+        $currencies = config('money');
29
+
30
+        $existingCodes = Currency::query()->pluck('code')->toArray();
31
+
32
+        foreach ($existingCodes as $code) {
33
+            unset($currencies[$code]);
34
+        }
35
+
36
+        $randomCode = $this->faker->randomElement(array_keys($currencies));
37
+
38
+        $code = $randomCode;
39
+
40
+        $currency = $currencies[$randomCode];
41
+
42
+        return [
43
+            'name' => $currency['name'],
44
+            'code' => $code,
45
+            'rate' => $this->faker->randomFloat($currency['precision'], 1, 10),
46
+            'precision' => $currency['precision'],
47
+            'symbol' => $currency['symbol'],
48
+            'symbol_first' => $currency['symbol_first'],
49
+            'decimal_mark' => $currency['decimal_mark'],
50
+            'thousands_separator' => $currency['thousands_separator'],
51
+            'enabled' => $this->faker->boolean,
52
+            'company_id' => $this->company->id,
53
+        ];
54
+    }
55
+
56
+    /**
57
+     * Indicate that the currency is enabled.
58
+     */
59
+    public function enabled(): Factory
60
+    {
61
+        return $this->state(static function (array $attributes) {
62
+            return [
63
+                'enabled' => true,
64
+            ];
65
+        });
66
+    }
67
+
68
+    /**
69
+     * Indicate that the currency is disabled.
70
+     */
71
+    public function disabled(): Factory
72
+    {
73
+        return $this->state(static function (array $attributes) {
74
+            return [
75
+                'enabled' => false,
76
+            ];
77
+        });
78
+    }
79
+}

+ 31
- 0
database/factories/DiscountFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Setting\Discount;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Discount>
10
+ */
11
+class DiscountFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Discount::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 31
- 0
database/factories/DocumentFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Document\Document;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Document>
10
+ */
11
+class DocumentFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Document::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 31
- 0
database/factories/DocumentItemFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Document\DocumentItem;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<DocumentItem>
10
+ */
11
+class DocumentItemFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = DocumentItem::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 31
- 0
database/factories/DocumentTotalFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Document\DocumentTotal;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<DocumentTotal>
10
+ */
11
+class DocumentTotalFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = DocumentTotal::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 29
- 0
database/factories/ItemFactory.php Просмотреть файл

@@ -0,0 +1,29 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Item;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Item>
10
+ */
11
+class ItemFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     */
16
+    protected $model = Item::class;
17
+
18
+    /**
19
+     * Define the model's default state.
20
+     *
21
+     * @return array<string, mixed>
22
+     */
23
+    public function definition(): array
24
+    {
25
+        return [
26
+            //
27
+        ];
28
+    }
29
+}

+ 31
- 0
database/factories/TaxFactory.php Просмотреть файл

@@ -0,0 +1,31 @@
1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Setting\Tax;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<Tax>
10
+ */
11
+class TaxFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = Tax::class;
19
+
20
+    /**
21
+     * Define the model's default state.
22
+     *
23
+     * @return array<string, mixed>
24
+     */
25
+    public function definition(): array
26
+    {
27
+        return [
28
+            //
29
+        ];
30
+    }
31
+}

+ 40
- 0
database/migrations/2023_05_10_040940_create_currencies_table.php Просмотреть файл

@@ -0,0 +1,40 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('currencies', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('name', 100);
18
+            $table->string('code')->index();
19
+            $table->decimal('rate', 15, 8);
20
+            $table->unsignedTinyInteger('precision')->default(2);
21
+            $table->string('symbol')->default('$');
22
+            $table->boolean('symbol_first')->default(true);
23
+            $table->string('decimal_mark')->default('.');
24
+            $table->string('thousands_separator')->default(',');
25
+            $table->boolean('enabled')->default(true);
26
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
27
+            $table->timestamps();
28
+
29
+            $table->unique(['company_id', 'code']);
30
+        });
31
+    }
32
+
33
+    /**
34
+     * Reverse the migrations.
35
+     */
36
+    public function down(): void
37
+    {
38
+        Schema::dropIfExists('currencies');
39
+    }
40
+};

+ 41
- 0
database/migrations/2023_05_11_044321_create_accounts_table.php Просмотреть файл

@@ -0,0 +1,41 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('accounts', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('type')->default('bank');
18
+            $table->string('name', 100);
19
+            $table->string('number', 20);
20
+            $table->string('currency_code')->default('USD');
21
+            $table->decimal('opening_balance', 15, 4)->default(0.0000);
22
+            $table->string('bank_name', 100)->nullable();
23
+            $table->string('bank_phone', 20)->nullable();
24
+            $table->text('bank_address')->nullable();
25
+            $table->boolean('enabled')->default(true);
26
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
27
+            $table->timestamps();
28
+
29
+            $table->unique(['company_id', 'number']);
30
+            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
31
+        });
32
+    }
33
+
34
+    /**
35
+     * Reverse the migrations.
36
+     */
37
+    public function down(): void
38
+    {
39
+        Schema::dropIfExists('accounts');
40
+    }
41
+};

+ 33
- 0
database/migrations/2023_05_12_042255_create_categories_table.php Просмотреть файл

@@ -0,0 +1,33 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('categories', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('name');
18
+            $table->string('type'); // expense, income, item, other
19
+            $table->string('color');
20
+            $table->boolean('enabled')->default(true);
21
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
22
+            $table->timestamps();
23
+        });
24
+    }
25
+
26
+    /**
27
+     * Reverse the migrations.
28
+     */
29
+    public function down(): void
30
+    {
31
+        Schema::dropIfExists('categories');
32
+    }
33
+};

+ 47
- 0
database/migrations/2023_05_19_042232_create_contacts_table.php Просмотреть файл

@@ -0,0 +1,47 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('contacts', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->onDelete('cascade');
17
+            $table->string('entity')->default('company'); // company, individual (person)
18
+            $table->string('type'); // vendor, customer, employee
19
+            $table->string('name');
20
+            $table->string('email')->nullable();
21
+            $table->string('tax_number')->nullable();
22
+            $table->string('phone')->nullable();
23
+            $table->text('address')->nullable();
24
+            $table->string('city')->nullable();
25
+            $table->string('zip_code')->nullable();
26
+            $table->string('state')->nullable();
27
+            $table->string('country')->nullable();
28
+            $table->string('website')->nullable();
29
+            $table->string('currency_code')->default('USD');
30
+            $table->string('reference')->nullable();
31
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
32
+            $table->timestamps();
33
+
34
+            $table->index(['company_id', 'type']);
35
+            $table->unique(['company_id', 'type', 'email']);
36
+            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
37
+        });
38
+    }
39
+
40
+    /**
41
+     * Reverse the migrations.
42
+     */
43
+    public function down(): void
44
+    {
45
+        Schema::dropIfExists('contacts');
46
+    }
47
+};

+ 36
- 0
database/migrations/2023_05_20_080131_create_taxes_table.php Просмотреть файл

@@ -0,0 +1,36 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('taxes', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('name');
18
+            $table->text('description')->nullable();
19
+            $table->decimal('rate', 15, 4);
20
+            $table->string('computation')->default('percentage'); // percentage, fixed, compound, inclusive, withholding
21
+            $table->string('type')->default('sales'); // sales, purchases
22
+            $table->string('scope')->nullable(); // product, service, none
23
+            $table->boolean('enabled')->default(true);
24
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
25
+            $table->timestamps();
26
+        });
27
+    }
28
+
29
+    /**
30
+     * Reverse the migrations.
31
+     */
32
+    public function down(): void
33
+    {
34
+        Schema::dropIfExists('taxes');
35
+    }
36
+};

+ 35
- 0
database/migrations/2023_05_21_163808_create_discounts_table.php Просмотреть файл

@@ -0,0 +1,35 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('discounts', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->string('name');
17
+            $table->text('description')->nullable();
18
+            $table->decimal('rate', 15, 4);
19
+            $table->string('computation')->default('percentage'); // percentage, fixed
20
+            $table->string('type')->default('sales'); // sales, purchases
21
+            $table->string('scope')->nullable(); // product, service, none
22
+            $table->boolean('enabled')->default(true);
23
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
24
+            $table->timestamps();
25
+        });
26
+    }
27
+
28
+    /**
29
+     * Reverse the migrations.
30
+     */
31
+    public function down(): void
32
+    {
33
+        Schema::dropIfExists('discounts');
34
+    }
35
+};

+ 40
- 0
database/migrations/2023_05_22_073252_create_items_table.php Просмотреть файл

@@ -0,0 +1,40 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('items', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('type'); // product, service
18
+            $table->string('name');
19
+            $table->string('sku')->unique();
20
+            $table->string('description')->nullable();
21
+            $table->decimal('sale_price', 15, 4);
22
+            $table->decimal('purchase_price', 15, 4);
23
+            $table->integer('quantity')->default(1);
24
+            $table->foreignId('category_id')->default(1)->constrained()->restrictOnDelete();
25
+            $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
26
+            $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
27
+            $table->boolean('enabled')->default(true);
28
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
29
+            $table->timestamps();
30
+        });
31
+    }
32
+
33
+    /**
34
+     * Reverse the migrations.
35
+     */
36
+    public function down(): void
37
+    {
38
+        Schema::dropIfExists('items');
39
+    }
40
+};

+ 46
- 0
database/migrations/2023_05_23_141215_create_documents_table.php Просмотреть файл

@@ -0,0 +1,46 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('documents', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('type'); // invoice, bill
18
+            $table->string('document_number');
19
+            $table->string('order_number')->nullable();
20
+            $table->string('status');
21
+            $table->dateTime('document_date');
22
+            $table->dateTime('due_date');
23
+            $table->dateTime('paid_date')->nullable();
24
+            $table->decimal('amount', 15, 4);
25
+            $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
26
+            $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
27
+            $table->string('reference')->nullable();
28
+            $table->string('currency_code')->default('USD');
29
+            $table->foreignId('category_id')->default(1)->constrained()->restrictOnDelete();
30
+            $table->foreignId('contact_id')->nullable()->constrained()->nullOnDelete();
31
+            $table->text('notes')->nullable();
32
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
33
+            $table->timestamps();
34
+
35
+            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
36
+        });
37
+    }
38
+
39
+    /**
40
+     * Reverse the migrations.
41
+     */
42
+    public function down(): void
43
+    {
44
+        Schema::dropIfExists('documents');
45
+    }
46
+};

+ 39
- 0
database/migrations/2023_05_23_151550_create_document_items_table.php Просмотреть файл

@@ -0,0 +1,39 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('document_items', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->foreignId('document_id')->constrained()->cascadeOnDelete();
18
+            $table->foreignId('item_id')->constrained()->cascadeOnDelete();
19
+            $table->string('type');
20
+            $table->string('name');
21
+            $table->text('description')->nullable();
22
+            $table->decimal('quantity', 7, 2);
23
+            $table->decimal('price', 15, 4);
24
+            $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
25
+            $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
26
+            $table->decimal('total', 15, 4);
27
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
28
+            $table->timestamps();
29
+        });
30
+    }
31
+
32
+    /**
33
+     * Reverse the migrations.
34
+     */
35
+    public function down(): void
36
+    {
37
+        Schema::dropIfExists('document_items');
38
+    }
39
+};

+ 37
- 0
database/migrations/2023_05_23_173412_create_document_totals_table.php Просмотреть файл

@@ -0,0 +1,37 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('document_totals', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->foreignId('document_id')->constrained()->cascadeOnDelete();
18
+            $table->string('type');
19
+            $table->string('code');
20
+            $table->string('name');
21
+            $table->decimal('subtotal', 15, 4)->default(0);
22
+            $table->decimal('discount', 15, 4)->default(0);
23
+            $table->decimal('tax', 15, 4)->default(0);
24
+            $table->decimal('total', 15, 4);
25
+            $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
26
+            $table->timestamps();
27
+        });
28
+    }
29
+
30
+    /**
31
+     * Reverse the migrations.
32
+     */
33
+    public function down(): void
34
+    {
35
+        Schema::dropIfExists('document_totals');
36
+    }
37
+};

+ 20
- 0
database/seeders/AccountSeeder.php Просмотреть файл

@@ -0,0 +1,20 @@
1
+<?php
2
+
3
+namespace Database\Seeders;
4
+
5
+use App\Models\Banking\Account;
6
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
7
+use Illuminate\Database\Seeder;
8
+
9
+class AccountSeeder extends Seeder
10
+{
11
+    /**
12
+     * Run the database seeds.
13
+     */
14
+    public function run(): void
15
+    {
16
+        Account::factory()
17
+            ->count(5)
18
+            ->create();
19
+    }
20
+}

+ 20
- 0
database/seeders/CurrencySeeder.php Просмотреть файл

@@ -0,0 +1,20 @@
1
+<?php
2
+
3
+namespace Database\Seeders;
4
+
5
+use App\Models\Setting\Currency;
6
+use Illuminate\Database\Console\Seeds\WithoutModelEvents;
7
+use Illuminate\Database\Seeder;
8
+
9
+class CurrencySeeder extends Seeder
10
+{
11
+    /**
12
+     * Run the database seeds.
13
+     */
14
+    public function run(): void
15
+    {
16
+        Currency::factory()
17
+            ->count(5)
18
+            ->create();
19
+    }
20
+}

+ 256
- 255
package-lock.json Просмотреть файл

@@ -32,9 +32,9 @@
32 32
             }
33 33
         },
34 34
         "node_modules/@alpinejs/focus": {
35
-            "version": "3.12.0",
36
-            "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.12.0.tgz",
37
-            "integrity": "sha512-KfFS6W2Ao71e/zWMg5B51QR8Djocc2grDBhZ4VYNYFh5cUnb3TELQ3luY0kjl9phy2NToi52Gz360G889Pz1Bg==",
35
+            "version": "3.12.1",
36
+            "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.12.1.tgz",
37
+            "integrity": "sha512-8LXkzY9yHqTqsB+aqdY1SL4fv+IbMtn2vEwGPw3YFio0+vfS+hGnZS1E4aDkm+JIUUtQvqRbV3cnK2WRZkVGnA==",
38 38
             "dev": true,
39 39
             "dependencies": {
40 40
                 "focus-trap": "^6.6.1"
@@ -47,9 +47,9 @@
47 47
             "dev": true
48 48
         },
49 49
         "node_modules/@esbuild/android-arm": {
50
-            "version": "0.17.18",
51
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
52
-            "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
50
+            "version": "0.17.19",
51
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
52
+            "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
53 53
             "cpu": [
54 54
                 "arm"
55 55
             ],
@@ -63,9 +63,9 @@
63 63
             }
64 64
         },
65 65
         "node_modules/@esbuild/android-arm64": {
66
-            "version": "0.17.18",
67
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
68
-            "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
66
+            "version": "0.17.19",
67
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
68
+            "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
69 69
             "cpu": [
70 70
                 "arm64"
71 71
             ],
@@ -79,9 +79,9 @@
79 79
             }
80 80
         },
81 81
         "node_modules/@esbuild/android-x64": {
82
-            "version": "0.17.18",
83
-            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
84
-            "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
82
+            "version": "0.17.19",
83
+            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
84
+            "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
85 85
             "cpu": [
86 86
                 "x64"
87 87
             ],
@@ -95,9 +95,9 @@
95 95
             }
96 96
         },
97 97
         "node_modules/@esbuild/darwin-arm64": {
98
-            "version": "0.17.18",
99
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
100
-            "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
98
+            "version": "0.17.19",
99
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
100
+            "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
101 101
             "cpu": [
102 102
                 "arm64"
103 103
             ],
@@ -111,9 +111,9 @@
111 111
             }
112 112
         },
113 113
         "node_modules/@esbuild/darwin-x64": {
114
-            "version": "0.17.18",
115
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
116
-            "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
114
+            "version": "0.17.19",
115
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
116
+            "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
117 117
             "cpu": [
118 118
                 "x64"
119 119
             ],
@@ -127,9 +127,9 @@
127 127
             }
128 128
         },
129 129
         "node_modules/@esbuild/freebsd-arm64": {
130
-            "version": "0.17.18",
131
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
132
-            "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
130
+            "version": "0.17.19",
131
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
132
+            "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
133 133
             "cpu": [
134 134
                 "arm64"
135 135
             ],
@@ -143,9 +143,9 @@
143 143
             }
144 144
         },
145 145
         "node_modules/@esbuild/freebsd-x64": {
146
-            "version": "0.17.18",
147
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
148
-            "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
146
+            "version": "0.17.19",
147
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
148
+            "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
149 149
             "cpu": [
150 150
                 "x64"
151 151
             ],
@@ -159,9 +159,9 @@
159 159
             }
160 160
         },
161 161
         "node_modules/@esbuild/linux-arm": {
162
-            "version": "0.17.18",
163
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
164
-            "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
162
+            "version": "0.17.19",
163
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
164
+            "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
165 165
             "cpu": [
166 166
                 "arm"
167 167
             ],
@@ -175,9 +175,9 @@
175 175
             }
176 176
         },
177 177
         "node_modules/@esbuild/linux-arm64": {
178
-            "version": "0.17.18",
179
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
180
-            "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
178
+            "version": "0.17.19",
179
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
180
+            "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
181 181
             "cpu": [
182 182
                 "arm64"
183 183
             ],
@@ -191,9 +191,9 @@
191 191
             }
192 192
         },
193 193
         "node_modules/@esbuild/linux-ia32": {
194
-            "version": "0.17.18",
195
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
196
-            "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
194
+            "version": "0.17.19",
195
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
196
+            "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
197 197
             "cpu": [
198 198
                 "ia32"
199 199
             ],
@@ -207,9 +207,9 @@
207 207
             }
208 208
         },
209 209
         "node_modules/@esbuild/linux-loong64": {
210
-            "version": "0.17.18",
211
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
212
-            "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
210
+            "version": "0.17.19",
211
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
212
+            "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
213 213
             "cpu": [
214 214
                 "loong64"
215 215
             ],
@@ -223,9 +223,9 @@
223 223
             }
224 224
         },
225 225
         "node_modules/@esbuild/linux-mips64el": {
226
-            "version": "0.17.18",
227
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
228
-            "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
226
+            "version": "0.17.19",
227
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
228
+            "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
229 229
             "cpu": [
230 230
                 "mips64el"
231 231
             ],
@@ -239,9 +239,9 @@
239 239
             }
240 240
         },
241 241
         "node_modules/@esbuild/linux-ppc64": {
242
-            "version": "0.17.18",
243
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
244
-            "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
242
+            "version": "0.17.19",
243
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
244
+            "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
245 245
             "cpu": [
246 246
                 "ppc64"
247 247
             ],
@@ -255,9 +255,9 @@
255 255
             }
256 256
         },
257 257
         "node_modules/@esbuild/linux-riscv64": {
258
-            "version": "0.17.18",
259
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
260
-            "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
258
+            "version": "0.17.19",
259
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
260
+            "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
261 261
             "cpu": [
262 262
                 "riscv64"
263 263
             ],
@@ -271,9 +271,9 @@
271 271
             }
272 272
         },
273 273
         "node_modules/@esbuild/linux-s390x": {
274
-            "version": "0.17.18",
275
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
276
-            "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
274
+            "version": "0.17.19",
275
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
276
+            "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
277 277
             "cpu": [
278 278
                 "s390x"
279 279
             ],
@@ -287,9 +287,9 @@
287 287
             }
288 288
         },
289 289
         "node_modules/@esbuild/linux-x64": {
290
-            "version": "0.17.18",
291
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
292
-            "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
290
+            "version": "0.17.19",
291
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
292
+            "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
293 293
             "cpu": [
294 294
                 "x64"
295 295
             ],
@@ -303,9 +303,9 @@
303 303
             }
304 304
         },
305 305
         "node_modules/@esbuild/netbsd-x64": {
306
-            "version": "0.17.18",
307
-            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
308
-            "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
306
+            "version": "0.17.19",
307
+            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
308
+            "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
309 309
             "cpu": [
310 310
                 "x64"
311 311
             ],
@@ -319,9 +319,9 @@
319 319
             }
320 320
         },
321 321
         "node_modules/@esbuild/openbsd-x64": {
322
-            "version": "0.17.18",
323
-            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
324
-            "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
322
+            "version": "0.17.19",
323
+            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
324
+            "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
325 325
             "cpu": [
326 326
                 "x64"
327 327
             ],
@@ -335,9 +335,9 @@
335 335
             }
336 336
         },
337 337
         "node_modules/@esbuild/sunos-x64": {
338
-            "version": "0.17.18",
339
-            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
340
-            "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
338
+            "version": "0.17.19",
339
+            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
340
+            "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
341 341
             "cpu": [
342 342
                 "x64"
343 343
             ],
@@ -351,9 +351,9 @@
351 351
             }
352 352
         },
353 353
         "node_modules/@esbuild/win32-arm64": {
354
-            "version": "0.17.18",
355
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
356
-            "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
354
+            "version": "0.17.19",
355
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
356
+            "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
357 357
             "cpu": [
358 358
                 "arm64"
359 359
             ],
@@ -367,9 +367,9 @@
367 367
             }
368 368
         },
369 369
         "node_modules/@esbuild/win32-ia32": {
370
-            "version": "0.17.18",
371
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
372
-            "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
370
+            "version": "0.17.19",
371
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
372
+            "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
373 373
             "cpu": [
374 374
                 "ia32"
375 375
             ],
@@ -383,9 +383,9 @@
383 383
             }
384 384
         },
385 385
         "node_modules/@esbuild/win32-x64": {
386
-            "version": "0.17.18",
387
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
388
-            "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
386
+            "version": "0.17.19",
387
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
388
+            "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
389 389
             "cpu": [
390 390
                 "x64"
391 391
             ],
@@ -540,9 +540,9 @@
540 540
             "dev": true
541 541
         },
542 542
         "node_modules/alpinejs": {
543
-            "version": "3.12.0",
544
-            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
545
-            "integrity": "sha512-YENcRBA9dlwR8PsZNFMTHbmdlTNwd1BkCeivPvOzzCKHas6AfwNRsDK9UEFmE5dXTMEZjnnpCTxV8vkdpWiOCw==",
543
+            "version": "3.12.1",
544
+            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.1.tgz",
545
+            "integrity": "sha512-GjEmZscHuCPv/XRbzabQLFH3I1+M3TbZqEsLDuYai/Yw+pE1IL7DK4ejRfqBSaysi4wWZ1k5IqVG2aefhWWqsQ==",
546 546
             "dev": true,
547 547
             "dependencies": {
548 548
                 "@vue/reactivity": "~3.1.1"
@@ -698,9 +698,9 @@
698 698
             }
699 699
         },
700 700
         "node_modules/caniuse-lite": {
701
-            "version": "1.0.30001482",
702
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz",
703
-            "integrity": "sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ==",
701
+            "version": "1.0.30001489",
702
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
703
+            "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
704 704
             "dev": true,
705 705
             "funding": [
706 706
                 {
@@ -817,15 +817,15 @@
817 817
             "dev": true
818 818
         },
819 819
         "node_modules/electron-to-chromium": {
820
-            "version": "1.4.383",
821
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.383.tgz",
822
-            "integrity": "sha512-BQyvFauIMzCJqILViJNs0kIBEAlx1bYLS5CRLyJtlun1KAnZlhNSgyfyWifPWagQ5s8KYPY6BpNHZsEMkxZAQQ==",
820
+            "version": "1.4.405",
821
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.405.tgz",
822
+            "integrity": "sha512-JdDgnwU69FMZURoesf9gNOej2Cms1XJFfLk24y1IBtnAdhTcJY/mXnokmpmxHN59PcykBP4bgUU98vLY44Lhuw==",
823 823
             "dev": true
824 824
         },
825 825
         "node_modules/esbuild": {
826
-            "version": "0.17.18",
827
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
828
-            "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
826
+            "version": "0.17.19",
827
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
828
+            "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
829 829
             "dev": true,
830 830
             "hasInstallScript": true,
831 831
             "bin": {
@@ -835,28 +835,28 @@
835 835
                 "node": ">=12"
836 836
             },
837 837
             "optionalDependencies": {
838
-                "@esbuild/android-arm": "0.17.18",
839
-                "@esbuild/android-arm64": "0.17.18",
840
-                "@esbuild/android-x64": "0.17.18",
841
-                "@esbuild/darwin-arm64": "0.17.18",
842
-                "@esbuild/darwin-x64": "0.17.18",
843
-                "@esbuild/freebsd-arm64": "0.17.18",
844
-                "@esbuild/freebsd-x64": "0.17.18",
845
-                "@esbuild/linux-arm": "0.17.18",
846
-                "@esbuild/linux-arm64": "0.17.18",
847
-                "@esbuild/linux-ia32": "0.17.18",
848
-                "@esbuild/linux-loong64": "0.17.18",
849
-                "@esbuild/linux-mips64el": "0.17.18",
850
-                "@esbuild/linux-ppc64": "0.17.18",
851
-                "@esbuild/linux-riscv64": "0.17.18",
852
-                "@esbuild/linux-s390x": "0.17.18",
853
-                "@esbuild/linux-x64": "0.17.18",
854
-                "@esbuild/netbsd-x64": "0.17.18",
855
-                "@esbuild/openbsd-x64": "0.17.18",
856
-                "@esbuild/sunos-x64": "0.17.18",
857
-                "@esbuild/win32-arm64": "0.17.18",
858
-                "@esbuild/win32-ia32": "0.17.18",
859
-                "@esbuild/win32-x64": "0.17.18"
838
+                "@esbuild/android-arm": "0.17.19",
839
+                "@esbuild/android-arm64": "0.17.19",
840
+                "@esbuild/android-x64": "0.17.19",
841
+                "@esbuild/darwin-arm64": "0.17.19",
842
+                "@esbuild/darwin-x64": "0.17.19",
843
+                "@esbuild/freebsd-arm64": "0.17.19",
844
+                "@esbuild/freebsd-x64": "0.17.19",
845
+                "@esbuild/linux-arm": "0.17.19",
846
+                "@esbuild/linux-arm64": "0.17.19",
847
+                "@esbuild/linux-ia32": "0.17.19",
848
+                "@esbuild/linux-loong64": "0.17.19",
849
+                "@esbuild/linux-mips64el": "0.17.19",
850
+                "@esbuild/linux-ppc64": "0.17.19",
851
+                "@esbuild/linux-riscv64": "0.17.19",
852
+                "@esbuild/linux-s390x": "0.17.19",
853
+                "@esbuild/linux-x64": "0.17.19",
854
+                "@esbuild/netbsd-x64": "0.17.19",
855
+                "@esbuild/openbsd-x64": "0.17.19",
856
+                "@esbuild/sunos-x64": "0.17.19",
857
+                "@esbuild/win32-arm64": "0.17.19",
858
+                "@esbuild/win32-ia32": "0.17.19",
859
+                "@esbuild/win32-x64": "0.17.19"
860 860
             }
861 861
         },
862 862
         "node_modules/escalade": {
@@ -1072,9 +1072,9 @@
1072 1072
             }
1073 1073
         },
1074 1074
         "node_modules/is-core-module": {
1075
-            "version": "2.12.0",
1076
-            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz",
1077
-            "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==",
1075
+            "version": "2.12.1",
1076
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
1077
+            "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
1078 1078
             "dev": true,
1079 1079
             "dependencies": {
1080 1080
                 "has": "^1.0.3"
@@ -1123,9 +1123,9 @@
1123 1123
             }
1124 1124
         },
1125 1125
         "node_modules/laravel-vite-plugin": {
1126
-            "version": "0.7.4",
1127
-            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.4.tgz",
1128
-            "integrity": "sha512-NlIuXbeuI+4NZzRpWNpGHRVTwuFWessvD7QoD+o2MlyAi7qyUS4J8r4/yTlu1dl9lxcR7iKoYUmHQqZDcrw2KA==",
1126
+            "version": "0.7.7",
1127
+            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.7.tgz",
1128
+            "integrity": "sha512-/KsnyNUOMylBVLvGz1VlL1ukxyQeMQUz4zmnMflYe8fAWzLvjHDXnbh6fLgIrzAmevzNjYm/TUqmD5Io0+kqhg==",
1129 1129
             "dev": true,
1130 1130
             "dependencies": {
1131 1131
                 "picocolors": "^1.0.0",
@@ -1265,9 +1265,9 @@
1265 1265
             }
1266 1266
         },
1267 1267
         "node_modules/node-releases": {
1268
-            "version": "2.0.10",
1269
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
1270
-            "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
1268
+            "version": "2.0.11",
1269
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.11.tgz",
1270
+            "integrity": "sha512-+M0PwXeU80kRohZ3aT4J/OnR+l9/KD2nVLNNoRgFtnf+umQVFdGBAO2N8+nCnEi0xlh/Wk3zOGC+vNNx+uM79Q==",
1271 1271
             "dev": true
1272 1272
         },
1273 1273
         "node_modules/normalize-path": {
@@ -1479,9 +1479,9 @@
1479 1479
             }
1480 1480
         },
1481 1481
         "node_modules/postcss-nested/node_modules/postcss-selector-parser": {
1482
-            "version": "6.0.12",
1483
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz",
1484
-            "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==",
1482
+            "version": "6.0.13",
1483
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
1484
+            "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
1485 1485
             "dev": true,
1486 1486
             "dependencies": {
1487 1487
                 "cssesc": "^3.0.0",
@@ -1585,9 +1585,9 @@
1585 1585
             }
1586 1586
         },
1587 1587
         "node_modules/rollup": {
1588
-            "version": "3.21.5",
1589
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz",
1590
-            "integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==",
1588
+            "version": "3.23.0",
1589
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz",
1590
+            "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==",
1591 1591
             "dev": true,
1592 1592
             "bin": {
1593 1593
                 "rollup": "dist/bin/rollup"
@@ -1711,9 +1711,9 @@
1711 1711
             }
1712 1712
         },
1713 1713
         "node_modules/tailwindcss/node_modules/postcss-selector-parser": {
1714
-            "version": "6.0.12",
1715
-            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz",
1716
-            "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==",
1714
+            "version": "6.0.13",
1715
+            "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
1716
+            "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
1717 1717
             "dev": true,
1718 1718
             "dependencies": {
1719 1719
                 "cssesc": "^3.0.0",
@@ -1808,9 +1808,9 @@
1808 1808
             "dev": true
1809 1809
         },
1810 1810
         "node_modules/vite": {
1811
-            "version": "4.3.4",
1812
-            "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.4.tgz",
1813
-            "integrity": "sha512-f90aqGBoxSFxWph2b39ae2uHAxm5jFBBdnfueNxZAT1FTpM13ccFQExCaKbR2xFW5atowjleRniQ7onjJ22QEg==",
1811
+            "version": "4.3.8",
1812
+            "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz",
1813
+            "integrity": "sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==",
1814 1814
             "dev": true,
1815 1815
             "dependencies": {
1816 1816
                 "esbuild": "^0.17.5",
@@ -1875,12 +1875,13 @@
1875 1875
             "dev": true
1876 1876
         },
1877 1877
         "node_modules/yaml": {
1878
-            "version": "2.2.2",
1879
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
1880
-            "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
1878
+            "version": "2.3.0",
1879
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz",
1880
+            "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==",
1881 1881
             "dev": true,
1882 1882
             "engines": {
1883
-                "node": ">= 14"
1883
+                "node": ">= 14",
1884
+                "npm": ">= 7"
1884 1885
             }
1885 1886
         }
1886 1887
     },
@@ -1892,9 +1893,9 @@
1892 1893
             "dev": true
1893 1894
         },
1894 1895
         "@alpinejs/focus": {
1895
-            "version": "3.12.0",
1896
-            "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.12.0.tgz",
1897
-            "integrity": "sha512-KfFS6W2Ao71e/zWMg5B51QR8Djocc2grDBhZ4VYNYFh5cUnb3TELQ3luY0kjl9phy2NToi52Gz360G889Pz1Bg==",
1896
+            "version": "3.12.1",
1897
+            "resolved": "https://registry.npmjs.org/@alpinejs/focus/-/focus-3.12.1.tgz",
1898
+            "integrity": "sha512-8LXkzY9yHqTqsB+aqdY1SL4fv+IbMtn2vEwGPw3YFio0+vfS+hGnZS1E4aDkm+JIUUtQvqRbV3cnK2WRZkVGnA==",
1898 1899
             "dev": true,
1899 1900
             "requires": {
1900 1901
                 "focus-trap": "^6.6.1"
@@ -1907,156 +1908,156 @@
1907 1908
             "dev": true
1908 1909
         },
1909 1910
         "@esbuild/android-arm": {
1910
-            "version": "0.17.18",
1911
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.18.tgz",
1912
-            "integrity": "sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==",
1911
+            "version": "0.17.19",
1912
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
1913
+            "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
1913 1914
             "dev": true,
1914 1915
             "optional": true
1915 1916
         },
1916 1917
         "@esbuild/android-arm64": {
1917
-            "version": "0.17.18",
1918
-            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.18.tgz",
1919
-            "integrity": "sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==",
1918
+            "version": "0.17.19",
1919
+            "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
1920
+            "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
1920 1921
             "dev": true,
1921 1922
             "optional": true
1922 1923
         },
1923 1924
         "@esbuild/android-x64": {
1924
-            "version": "0.17.18",
1925
-            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.18.tgz",
1926
-            "integrity": "sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==",
1925
+            "version": "0.17.19",
1926
+            "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
1927
+            "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
1927 1928
             "dev": true,
1928 1929
             "optional": true
1929 1930
         },
1930 1931
         "@esbuild/darwin-arm64": {
1931
-            "version": "0.17.18",
1932
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.18.tgz",
1933
-            "integrity": "sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==",
1932
+            "version": "0.17.19",
1933
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
1934
+            "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
1934 1935
             "dev": true,
1935 1936
             "optional": true
1936 1937
         },
1937 1938
         "@esbuild/darwin-x64": {
1938
-            "version": "0.17.18",
1939
-            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.18.tgz",
1940
-            "integrity": "sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==",
1939
+            "version": "0.17.19",
1940
+            "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
1941
+            "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
1941 1942
             "dev": true,
1942 1943
             "optional": true
1943 1944
         },
1944 1945
         "@esbuild/freebsd-arm64": {
1945
-            "version": "0.17.18",
1946
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.18.tgz",
1947
-            "integrity": "sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==",
1946
+            "version": "0.17.19",
1947
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
1948
+            "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
1948 1949
             "dev": true,
1949 1950
             "optional": true
1950 1951
         },
1951 1952
         "@esbuild/freebsd-x64": {
1952
-            "version": "0.17.18",
1953
-            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.18.tgz",
1954
-            "integrity": "sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==",
1953
+            "version": "0.17.19",
1954
+            "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
1955
+            "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
1955 1956
             "dev": true,
1956 1957
             "optional": true
1957 1958
         },
1958 1959
         "@esbuild/linux-arm": {
1959
-            "version": "0.17.18",
1960
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.18.tgz",
1961
-            "integrity": "sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==",
1960
+            "version": "0.17.19",
1961
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
1962
+            "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
1962 1963
             "dev": true,
1963 1964
             "optional": true
1964 1965
         },
1965 1966
         "@esbuild/linux-arm64": {
1966
-            "version": "0.17.18",
1967
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.18.tgz",
1968
-            "integrity": "sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==",
1967
+            "version": "0.17.19",
1968
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
1969
+            "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
1969 1970
             "dev": true,
1970 1971
             "optional": true
1971 1972
         },
1972 1973
         "@esbuild/linux-ia32": {
1973
-            "version": "0.17.18",
1974
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.18.tgz",
1975
-            "integrity": "sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==",
1974
+            "version": "0.17.19",
1975
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
1976
+            "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
1976 1977
             "dev": true,
1977 1978
             "optional": true
1978 1979
         },
1979 1980
         "@esbuild/linux-loong64": {
1980
-            "version": "0.17.18",
1981
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.18.tgz",
1982
-            "integrity": "sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==",
1981
+            "version": "0.17.19",
1982
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
1983
+            "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
1983 1984
             "dev": true,
1984 1985
             "optional": true
1985 1986
         },
1986 1987
         "@esbuild/linux-mips64el": {
1987
-            "version": "0.17.18",
1988
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.18.tgz",
1989
-            "integrity": "sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==",
1988
+            "version": "0.17.19",
1989
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
1990
+            "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
1990 1991
             "dev": true,
1991 1992
             "optional": true
1992 1993
         },
1993 1994
         "@esbuild/linux-ppc64": {
1994
-            "version": "0.17.18",
1995
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.18.tgz",
1996
-            "integrity": "sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==",
1995
+            "version": "0.17.19",
1996
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
1997
+            "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
1997 1998
             "dev": true,
1998 1999
             "optional": true
1999 2000
         },
2000 2001
         "@esbuild/linux-riscv64": {
2001
-            "version": "0.17.18",
2002
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.18.tgz",
2003
-            "integrity": "sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==",
2002
+            "version": "0.17.19",
2003
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
2004
+            "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
2004 2005
             "dev": true,
2005 2006
             "optional": true
2006 2007
         },
2007 2008
         "@esbuild/linux-s390x": {
2008
-            "version": "0.17.18",
2009
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.18.tgz",
2010
-            "integrity": "sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==",
2009
+            "version": "0.17.19",
2010
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
2011
+            "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
2011 2012
             "dev": true,
2012 2013
             "optional": true
2013 2014
         },
2014 2015
         "@esbuild/linux-x64": {
2015
-            "version": "0.17.18",
2016
-            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.18.tgz",
2017
-            "integrity": "sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==",
2016
+            "version": "0.17.19",
2017
+            "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
2018
+            "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
2018 2019
             "dev": true,
2019 2020
             "optional": true
2020 2021
         },
2021 2022
         "@esbuild/netbsd-x64": {
2022
-            "version": "0.17.18",
2023
-            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.18.tgz",
2024
-            "integrity": "sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==",
2023
+            "version": "0.17.19",
2024
+            "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
2025
+            "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
2025 2026
             "dev": true,
2026 2027
             "optional": true
2027 2028
         },
2028 2029
         "@esbuild/openbsd-x64": {
2029
-            "version": "0.17.18",
2030
-            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.18.tgz",
2031
-            "integrity": "sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==",
2030
+            "version": "0.17.19",
2031
+            "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
2032
+            "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
2032 2033
             "dev": true,
2033 2034
             "optional": true
2034 2035
         },
2035 2036
         "@esbuild/sunos-x64": {
2036
-            "version": "0.17.18",
2037
-            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.18.tgz",
2038
-            "integrity": "sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==",
2037
+            "version": "0.17.19",
2038
+            "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
2039
+            "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
2039 2040
             "dev": true,
2040 2041
             "optional": true
2041 2042
         },
2042 2043
         "@esbuild/win32-arm64": {
2043
-            "version": "0.17.18",
2044
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.18.tgz",
2045
-            "integrity": "sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==",
2044
+            "version": "0.17.19",
2045
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
2046
+            "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
2046 2047
             "dev": true,
2047 2048
             "optional": true
2048 2049
         },
2049 2050
         "@esbuild/win32-ia32": {
2050
-            "version": "0.17.18",
2051
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.18.tgz",
2052
-            "integrity": "sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==",
2051
+            "version": "0.17.19",
2052
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
2053
+            "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
2053 2054
             "dev": true,
2054 2055
             "optional": true
2055 2056
         },
2056 2057
         "@esbuild/win32-x64": {
2057
-            "version": "0.17.18",
2058
-            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.18.tgz",
2059
-            "integrity": "sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==",
2058
+            "version": "0.17.19",
2059
+            "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
2060
+            "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
2060 2061
             "dev": true,
2061 2062
             "optional": true
2062 2063
         },
@@ -2176,9 +2177,9 @@
2176 2177
             "dev": true
2177 2178
         },
2178 2179
         "alpinejs": {
2179
-            "version": "3.12.0",
2180
-            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.0.tgz",
2181
-            "integrity": "sha512-YENcRBA9dlwR8PsZNFMTHbmdlTNwd1BkCeivPvOzzCKHas6AfwNRsDK9UEFmE5dXTMEZjnnpCTxV8vkdpWiOCw==",
2180
+            "version": "3.12.1",
2181
+            "resolved": "https://registry.npmjs.org/alpinejs/-/alpinejs-3.12.1.tgz",
2182
+            "integrity": "sha512-GjEmZscHuCPv/XRbzabQLFH3I1+M3TbZqEsLDuYai/Yw+pE1IL7DK4ejRfqBSaysi4wWZ1k5IqVG2aefhWWqsQ==",
2182 2183
             "dev": true,
2183 2184
             "requires": {
2184 2185
                 "@vue/reactivity": "~3.1.1"
@@ -2287,9 +2288,9 @@
2287 2288
             "dev": true
2288 2289
         },
2289 2290
         "caniuse-lite": {
2290
-            "version": "1.0.30001482",
2291
-            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001482.tgz",
2292
-            "integrity": "sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ==",
2291
+            "version": "1.0.30001489",
2292
+            "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
2293
+            "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
2293 2294
             "dev": true
2294 2295
         },
2295 2296
         "chokidar": {
@@ -2365,39 +2366,39 @@
2365 2366
             "dev": true
2366 2367
         },
2367 2368
         "electron-to-chromium": {
2368
-            "version": "1.4.383",
2369
-            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.383.tgz",
2370
-            "integrity": "sha512-BQyvFauIMzCJqILViJNs0kIBEAlx1bYLS5CRLyJtlun1KAnZlhNSgyfyWifPWagQ5s8KYPY6BpNHZsEMkxZAQQ==",
2369
+            "version": "1.4.405",
2370
+            "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.405.tgz",
2371
+            "integrity": "sha512-JdDgnwU69FMZURoesf9gNOej2Cms1XJFfLk24y1IBtnAdhTcJY/mXnokmpmxHN59PcykBP4bgUU98vLY44Lhuw==",
2371 2372
             "dev": true
2372 2373
         },
2373 2374
         "esbuild": {
2374
-            "version": "0.17.18",
2375
-            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.18.tgz",
2376
-            "integrity": "sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==",
2375
+            "version": "0.17.19",
2376
+            "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
2377
+            "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
2377 2378
             "dev": true,
2378 2379
             "requires": {
2379
-                "@esbuild/android-arm": "0.17.18",
2380
-                "@esbuild/android-arm64": "0.17.18",
2381
-                "@esbuild/android-x64": "0.17.18",
2382
-                "@esbuild/darwin-arm64": "0.17.18",
2383
-                "@esbuild/darwin-x64": "0.17.18",
2384
-                "@esbuild/freebsd-arm64": "0.17.18",
2385
-                "@esbuild/freebsd-x64": "0.17.18",
2386
-                "@esbuild/linux-arm": "0.17.18",
2387
-                "@esbuild/linux-arm64": "0.17.18",
2388
-                "@esbuild/linux-ia32": "0.17.18",
2389
-                "@esbuild/linux-loong64": "0.17.18",
2390
-                "@esbuild/linux-mips64el": "0.17.18",
2391
-                "@esbuild/linux-ppc64": "0.17.18",
2392
-                "@esbuild/linux-riscv64": "0.17.18",
2393
-                "@esbuild/linux-s390x": "0.17.18",
2394
-                "@esbuild/linux-x64": "0.17.18",
2395
-                "@esbuild/netbsd-x64": "0.17.18",
2396
-                "@esbuild/openbsd-x64": "0.17.18",
2397
-                "@esbuild/sunos-x64": "0.17.18",
2398
-                "@esbuild/win32-arm64": "0.17.18",
2399
-                "@esbuild/win32-ia32": "0.17.18",
2400
-                "@esbuild/win32-x64": "0.17.18"
2380
+                "@esbuild/android-arm": "0.17.19",
2381
+                "@esbuild/android-arm64": "0.17.19",
2382
+                "@esbuild/android-x64": "0.17.19",
2383
+                "@esbuild/darwin-arm64": "0.17.19",
2384
+                "@esbuild/darwin-x64": "0.17.19",
2385
+                "@esbuild/freebsd-arm64": "0.17.19",
2386
+                "@esbuild/freebsd-x64": "0.17.19",
2387
+                "@esbuild/linux-arm": "0.17.19",
2388
+                "@esbuild/linux-arm64": "0.17.19",
2389
+                "@esbuild/linux-ia32": "0.17.19",
2390
+                "@esbuild/linux-loong64": "0.17.19",
2391
+                "@esbuild/linux-mips64el": "0.17.19",
2392
+                "@esbuild/linux-ppc64": "0.17.19",
2393
+                "@esbuild/linux-riscv64": "0.17.19",
2394
+                "@esbuild/linux-s390x": "0.17.19",
2395
+                "@esbuild/linux-x64": "0.17.19",
2396
+                "@esbuild/netbsd-x64": "0.17.19",
2397
+                "@esbuild/openbsd-x64": "0.17.19",
2398
+                "@esbuild/sunos-x64": "0.17.19",
2399
+                "@esbuild/win32-arm64": "0.17.19",
2400
+                "@esbuild/win32-ia32": "0.17.19",
2401
+                "@esbuild/win32-x64": "0.17.19"
2401 2402
             }
2402 2403
         },
2403 2404
         "escalade": {
@@ -2557,9 +2558,9 @@
2557 2558
             }
2558 2559
         },
2559 2560
         "is-core-module": {
2560
-            "version": "2.12.0",
2561
-            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.0.tgz",
2562
-            "integrity": "sha512-RECHCBCd/viahWmwj6enj19sKbHfJrddi/6cBDsNTKbNq0f7VeaUkBo60BqzvPqo/W54ChS62Z5qyun7cfOMqQ==",
2561
+            "version": "2.12.1",
2562
+            "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz",
2563
+            "integrity": "sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==",
2563 2564
             "dev": true,
2564 2565
             "requires": {
2565 2566
                 "has": "^1.0.3"
@@ -2593,9 +2594,9 @@
2593 2594
             "dev": true
2594 2595
         },
2595 2596
         "laravel-vite-plugin": {
2596
-            "version": "0.7.4",
2597
-            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.4.tgz",
2598
-            "integrity": "sha512-NlIuXbeuI+4NZzRpWNpGHRVTwuFWessvD7QoD+o2MlyAi7qyUS4J8r4/yTlu1dl9lxcR7iKoYUmHQqZDcrw2KA==",
2597
+            "version": "0.7.7",
2598
+            "resolved": "https://registry.npmjs.org/laravel-vite-plugin/-/laravel-vite-plugin-0.7.7.tgz",
2599
+            "integrity": "sha512-/KsnyNUOMylBVLvGz1VlL1ukxyQeMQUz4zmnMflYe8fAWzLvjHDXnbh6fLgIrzAmevzNjYm/TUqmD5Io0+kqhg==",
2599 2600
             "dev": true,
2600 2601
             "requires": {
2601 2602
                 "picocolors": "^1.0.0",
@@ -2696,9 +2697,9 @@
2696 2697
             "dev": true
2697 2698
         },
2698 2699
         "node-releases": {
2699
-            "version": "2.0.10",
2700
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.10.tgz",
2701
-            "integrity": "sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==",
2700
+            "version": "2.0.11",
2701
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.11.tgz",
2702
+            "integrity": "sha512-+M0PwXeU80kRohZ3aT4J/OnR+l9/KD2nVLNNoRgFtnf+umQVFdGBAO2N8+nCnEi0xlh/Wk3zOGC+vNNx+uM79Q==",
2702 2703
             "dev": true
2703 2704
         },
2704 2705
         "normalize-path": {
@@ -2821,9 +2822,9 @@
2821 2822
             },
2822 2823
             "dependencies": {
2823 2824
                 "postcss-selector-parser": {
2824
-                    "version": "6.0.12",
2825
-                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz",
2826
-                    "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==",
2825
+                    "version": "6.0.13",
2826
+                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
2827
+                    "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
2827 2828
                     "dev": true,
2828 2829
                     "requires": {
2829 2830
                         "cssesc": "^3.0.0",
@@ -2896,9 +2897,9 @@
2896 2897
             "dev": true
2897 2898
         },
2898 2899
         "rollup": {
2899
-            "version": "3.21.5",
2900
-            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.21.5.tgz",
2901
-            "integrity": "sha512-a4NTKS4u9PusbUJcfF4IMxuqjFzjm6ifj76P54a7cKnvVzJaG12BLVR+hgU2YDGHzyMMQNxLAZWuALsn8q2oQg==",
2900
+            "version": "3.23.0",
2901
+            "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.23.0.tgz",
2902
+            "integrity": "sha512-h31UlwEi7FHihLe1zbk+3Q7z1k/84rb9BSwmBSr/XjOCEaBJ2YyedQDuM0t/kfOS0IxM+vk1/zI9XxYj9V+NJQ==",
2902 2903
             "dev": true,
2903 2904
             "requires": {
2904 2905
                 "fsevents": "~2.3.2"
@@ -2978,9 +2979,9 @@
2978 2979
             },
2979 2980
             "dependencies": {
2980 2981
                 "postcss-selector-parser": {
2981
-                    "version": "6.0.12",
2982
-                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.12.tgz",
2983
-                    "integrity": "sha512-NdxGCAZdRrwVI1sy59+Wzrh+pMMHxapGnpfenDVlMEXoOcvt4pGE0JLK9YY2F5dLxcFYA/YbVQKhcGU+FtSYQg==",
2982
+                    "version": "6.0.13",
2983
+                    "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.13.tgz",
2984
+                    "integrity": "sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==",
2984 2985
                     "dev": true,
2985 2986
                     "requires": {
2986 2987
                         "cssesc": "^3.0.0",
@@ -3048,9 +3049,9 @@
3048 3049
             "dev": true
3049 3050
         },
3050 3051
         "vite": {
3051
-            "version": "4.3.4",
3052
-            "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.4.tgz",
3053
-            "integrity": "sha512-f90aqGBoxSFxWph2b39ae2uHAxm5jFBBdnfueNxZAT1FTpM13ccFQExCaKbR2xFW5atowjleRniQ7onjJ22QEg==",
3052
+            "version": "4.3.8",
3053
+            "resolved": "https://registry.npmjs.org/vite/-/vite-4.3.8.tgz",
3054
+            "integrity": "sha512-uYB8PwN7hbMrf4j1xzGDk/lqjsZvCDbt/JC5dyfxc19Pg8kRm14LinK/uq+HSLNswZEoKmweGdtpbnxRtrAXiQ==",
3054 3055
             "dev": true,
3055 3056
             "requires": {
3056 3057
                 "esbuild": "^0.17.5",
@@ -3076,9 +3077,9 @@
3076 3077
             "dev": true
3077 3078
         },
3078 3079
         "yaml": {
3079
-            "version": "2.2.2",
3080
-            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
3081
-            "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
3080
+            "version": "2.3.0",
3081
+            "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.0.tgz",
3082
+            "integrity": "sha512-8/1wgzdKc7bc9E6my5wZjmdavHLvO/QOmLG1FBugblEvY4IXrLjlViIOmL24HthU042lWTDRO90Fz1Yp66UnMw==",
3082 3083
             "dev": true
3083 3084
         }
3084 3085
     }

Загрузка…
Отмена
Сохранить