Преглед на файлове

wip: Accounting Module

3.x
wallo преди 2 години
родител
ревизия
81ae0cec47
променени са 41 файла, в които са добавени 807 реда и са изтрити 191 реда
  1. 3
    12
      app/Filament/Pages/DefaultSetting.php
  2. 20
    0
      app/Filament/Pages/Invoice.php
  3. 1
    7
      app/Filament/Resources/AccountResource.php
  4. 0
    6
      app/Filament/Resources/CategoryResource.php
  5. 0
    6
      app/Filament/Resources/CurrencyResource.php
  6. 2
    4
      app/Filament/Resources/CustomerResource.php
  7. 0
    6
      app/Filament/Resources/DiscountResource.php
  8. 3
    5
      app/Filament/Resources/InvoiceResource.php
  9. 0
    6
      app/Filament/Resources/TaxResource.php
  10. 13
    0
      app/Http/Livewire/Bill.php
  11. 21
    10
      app/Http/Livewire/DefaultSetting.php
  12. 162
    0
      app/Http/Livewire/Invoice.php
  13. 12
    0
      app/Models/Banking/Account.php
  14. 7
    0
      app/Models/Company.php
  15. 6
    6
      app/Models/Contact.php
  16. 7
    0
      app/Models/Document/Document.php
  17. 17
    0
      app/Models/Setting/Category.php
  18. 15
    6
      app/Models/Setting/Currency.php
  19. 28
    37
      app/Models/Setting/DefaultSetting.php
  20. 17
    1
      app/Models/Setting/Discount.php
  21. 108
    0
      app/Models/Setting/DocumentDefault.php
  22. 17
    1
      app/Models/Setting/Tax.php
  23. 85
    0
      app/Observers/CompanyObserver.php
  24. 0
    1
      app/Providers/AppServiceProvider.php
  25. 9
    1
      app/Providers/EventServiceProvider.php
  26. 21
    0
      app/Scopes/CurrentCompanyScope.php
  27. 0
    6
      app/Services/CurrencyService.php
  28. 41
    41
      composer.lock
  29. 22
    0
      database/factories/DiscountFactory.php
  30. 60
    0
      database/factories/DocumentDefaultFactory.php
  31. 22
    0
      database/factories/TaxFactory.php
  32. 2
    2
      database/migrations/2023_05_11_044321_create_accounts_table.php
  33. 2
    2
      database/migrations/2023_05_19_042232_create_contacts_table.php
  34. 39
    0
      database/migrations/2023_05_22_000100_create_document_defaults_table.php
  35. 3
    2
      database/migrations/2023_05_23_141215_create_documents_table.php
  36. 9
    9
      database/migrations/2023_07_03_054805_create_default_settings_table.php
  37. 13
    13
      database/seeders/DatabaseSeeder.php
  38. 3
    0
      resources/views/filament/pages/invoice.blade.php
  39. 3
    0
      resources/views/livewire/bill.blade.php
  40. 1
    1
      resources/views/livewire/default-setting.blade.php
  41. 13
    0
      resources/views/livewire/invoice.blade.php

+ 3
- 12
app/Filament/Pages/DefaultSetting.php Целия файл

8
 {
8
 {
9
     protected static ?string $navigationIcon = 'heroicon-o-adjustments';
9
     protected static ?string $navigationIcon = 'heroicon-o-adjustments';
10
 
10
 
11
-    protected static ?string $navigationLabel = 'Defaults';
11
+    protected static ?string $navigationLabel = 'Default';
12
 
12
 
13
     protected static ?string $navigationGroup = 'Settings';
13
     protected static ?string $navigationGroup = 'Settings';
14
 
14
 
15
-    protected static ?string $title = 'Defaults';
15
+    protected static ?string $title = 'Default';
16
 
16
 
17
-    protected static ?string $slug = 'defaults';
17
+    protected static ?string $slug = 'default';
18
 
18
 
19
     protected static string $view = 'filament.pages.default-setting';
19
     protected static string $view = 'filament.pages.default-setting';
20
-
21
-    public function getViewData(): array
22
-    {
23
-        return [
24
-            'defaultSetting' => \App\Models\Setting\DefaultSetting::first(),
25
-        ];
26
-    }
27
-
28
-
29
 }
20
 }

+ 20
- 0
app/Filament/Pages/Invoice.php Целия файл

1
+<?php
2
+
3
+namespace App\Filament\Pages;
4
+
5
+use Filament\Pages\Page;
6
+
7
+class Invoice extends Page
8
+{
9
+    protected static ?string $navigationIcon = 'heroicon-o-document-text';
10
+
11
+    protected static ?string $navigationLabel = 'Invoice';
12
+
13
+    protected static ?string $navigationGroup = 'Settings';
14
+
15
+    protected static ?string $title = 'Invoice';
16
+
17
+    protected static ?string $slug = 'invoice';
18
+
19
+    protected static string $view = 'filament.pages.invoice';
20
+}

+ 1
- 7
app/Filament/Resources/AccountResource.php Целия файл

32
 
32
 
33
     protected static ?string $navigationGroup = 'Banking';
33
     protected static ?string $navigationGroup = 'Banking';
34
 
34
 
35
-    public static function getEloquentQuery(): Builder
36
-    {
37
-        return parent::getEloquentQuery()
38
-            ->where('company_id', Auth::user()->currentCompany->id);
39
-    }
40
-
41
     public static function form(Form $form): Form
35
     public static function form(Form $form): Form
42
     {
36
     {
43
         return $form
37
         return $form
78
                             ->schema([
72
                             ->schema([
79
                                 Forms\Components\Select::make('currency_code')
73
                                 Forms\Components\Select::make('currency_code')
80
                                     ->label('Currency')
74
                                     ->label('Currency')
81
-                                    ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
75
+                                    ->relationship('currency', 'name')
82
                                     ->preload()
76
                                     ->preload()
83
                                     ->default(Currency::getDefaultCurrency())
77
                                     ->default(Currency::getDefaultCurrency())
84
                                     ->searchable()
78
                                     ->searchable()

+ 0
- 6
app/Filament/Resources/CategoryResource.php Целия файл

25
 
25
 
26
     protected static ?string $navigationGroup = 'Settings';
26
     protected static ?string $navigationGroup = 'Settings';
27
 
27
 
28
-    public static function getEloquentQuery(): Builder
29
-    {
30
-        return parent::getEloquentQuery()
31
-            ->where('company_id', Auth::user()->currentCompany->id);
32
-    }
33
-
34
     public static function form(Form $form): Form
28
     public static function form(Form $form): Form
35
     {
29
     {
36
         return $form
30
         return $form

+ 0
- 6
app/Filament/Resources/CurrencyResource.php Целия файл

28
 
28
 
29
     protected static ?string $navigationGroup = 'Settings';
29
     protected static ?string $navigationGroup = 'Settings';
30
 
30
 
31
-    public static function getEloquentQuery(): Builder
32
-    {
33
-        return parent::getEloquentQuery()
34
-            ->where('company_id', Auth::user()->currentCompany->id);
35
-    }
36
-
37
     public static function form(Form $form): Form
31
     public static function form(Form $form): Form
38
     {
32
     {
39
         return $form
33
         return $form

+ 2
- 4
app/Filament/Resources/CustomerResource.php Целия файл

32
 
32
 
33
     public static function getEloquentQuery(): Builder
33
     public static function getEloquentQuery(): Builder
34
     {
34
     {
35
-        return parent::getEloquentQuery()
36
-            ->customer()
37
-            ->where('company_id', Auth::user()->currentCompany->id);
35
+        return parent::getEloquentQuery()->customer();
38
     }
36
     }
39
 
37
 
40
     public static function form(Form $form): Form
38
     public static function form(Form $form): Form
89
                             ->nullable(),
87
                             ->nullable(),
90
                         Forms\Components\Select::make('currency_code')
88
                         Forms\Components\Select::make('currency_code')
91
                             ->label('Currency')
89
                             ->label('Currency')
92
-                            ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
90
+                            ->relationship('currency', 'name')
93
                             ->preload()
91
                             ->preload()
94
                             ->default(Currency::getDefaultCurrency())
92
                             ->default(Currency::getDefaultCurrency())
95
                             ->searchable()
93
                             ->searchable()

+ 0
- 6
app/Filament/Resources/DiscountResource.php Целия файл

23
 
23
 
24
     protected static ?string $navigationGroup = 'Settings';
24
     protected static ?string $navigationGroup = 'Settings';
25
 
25
 
26
-    public static function getEloquentQuery(): Builder
27
-    {
28
-        return parent::getEloquentQuery()
29
-            ->where('company_id', Auth::user()->currentCompany->id);
30
-    }
31
-
32
     public static function form(Form $form): Form
26
     public static function form(Form $form): Form
33
     {
27
     {
34
         return $form
28
         return $form

+ 3
- 5
app/Filament/Resources/InvoiceResource.php Целия файл

6
 use App\Filament\Resources\InvoiceResource\RelationManagers;
6
 use App\Filament\Resources\InvoiceResource\RelationManagers;
7
 use App\Models\Setting\Currency;
7
 use App\Models\Setting\Currency;
8
 use Wallo\FilamentSelectify\Components\ButtonGroup;
8
 use Wallo\FilamentSelectify\Components\ButtonGroup;
9
-use App\Models\Banking\Account;
10
 use App\Models\Document\Document;
9
 use App\Models\Document\Document;
11
 use Filament\Forms;
10
 use Filament\Forms;
12
 use Filament\Resources\Form;
11
 use Filament\Resources\Form;
32
     public static function getEloquentQuery(): Builder
31
     public static function getEloquentQuery(): Builder
33
     {
32
     {
34
         return parent::getEloquentQuery()
33
         return parent::getEloquentQuery()
35
-            ->where('type', 'invoice')
36
-            ->where('company_id', Auth::user()->currentCompany->id);
34
+            ->where('type', 'invoice');
37
     }
35
     }
38
 
36
 
39
     public static function form(Form $form): Form
37
     public static function form(Form $form): Form
48
                                     ->label('Customer')
46
                                     ->label('Customer')
49
                                     ->preload()
47
                                     ->preload()
50
                                     ->placeholder('Select a customer')
48
                                     ->placeholder('Select a customer')
51
-                                    ->relationship('contact', 'name', static fn (Builder $query) => $query->where('type', 'customer')->where('company_id', Auth::user()->currentCompany->id))
49
+                                    ->relationship('contact', 'name', static fn (Builder $query) => $query->where('type', 'customer'))
52
                                     ->searchable()
50
                                     ->searchable()
53
                                     ->required()
51
                                     ->required()
54
                                     ->createOptionForm([
52
                                     ->createOptionForm([
74
                                             ->maxLength(20),
72
                                             ->maxLength(20),
75
                                         Forms\Components\Select::make('contact.currency_code')
73
                                         Forms\Components\Select::make('contact.currency_code')
76
                                             ->label('Currency')
74
                                             ->label('Currency')
77
-                                            ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
75
+                                            ->relationship('currency', 'name')
78
                                             ->preload()
76
                                             ->preload()
79
                                             ->default(Currency::getDefaultCurrency())
77
                                             ->default(Currency::getDefaultCurrency())
80
                                             ->searchable()
78
                                             ->searchable()

+ 0
- 6
app/Filament/Resources/TaxResource.php Целия файл

26
 
26
 
27
     protected static ?string $navigationGroup = 'Settings';
27
     protected static ?string $navigationGroup = 'Settings';
28
 
28
 
29
-    public static function getEloquentQuery(): Builder
30
-    {
31
-        return parent::getEloquentQuery()
32
-            ->where('company_id', Auth::user()->currentCompany->id);
33
-    }
34
-
35
     public static function form(Form $form): Form
29
     public static function form(Form $form): Form
36
     {
30
     {
37
         return $form
31
         return $form

+ 13
- 0
app/Http/Livewire/Bill.php Целия файл

1
+<?php
2
+
3
+namespace App\Http\Livewire;
4
+
5
+use Livewire\Component;
6
+
7
+class Bill extends Component
8
+{
9
+    public function render()
10
+    {
11
+        return view('livewire.bill');
12
+    }
13
+}

+ 21
- 10
app/Http/Livewire/DefaultSetting.php Целия файл

33
 
33
 
34
     public function mount():void
34
     public function mount():void
35
     {
35
     {
36
-        $this->form->fill();
36
+        $this->defaultSetting = Defaults::firstOrNew();
37
+
38
+        $this->form->fill([
39
+            'account_id' => Defaults::getDefaultAccount(),
40
+            'currency_code' => Defaults::getDefaultCurrency(),
41
+            'sales_tax_id' => Defaults::getDefaultSalesTax(),
42
+            'purchase_tax_id' => Defaults::getDefaultPurchaseTax(),
43
+            'sales_discount_id' => Defaults::getDefaultSalesDiscount(),
44
+            'purchase_discount_id' => Defaults::getDefaultPurchaseDiscount(),
45
+            'income_category_id' => Defaults::getDefaultIncomeCategory(),
46
+            'expense_category_id' => Defaults::getDefaultExpenseCategory(),
47
+        ]);
37
     }
48
     }
38
 
49
 
39
     protected function getFormSchema(): array
50
     protected function getFormSchema(): array
47
                         ->default(Defaults::getDefaultAccount())
58
                         ->default(Defaults::getDefaultAccount())
48
                         ->searchable()
59
                         ->searchable()
49
                         ->validationAttribute('Account')
60
                         ->validationAttribute('Account')
50
-                        ->required(),
61
+                        ->nullable(),
51
                     Select::make('currency_code')
62
                     Select::make('currency_code')
52
                         ->label('Currency')
63
                         ->label('Currency')
53
                         ->options(Defaults::getCurrencies())
64
                         ->options(Defaults::getCurrencies())
54
                         ->default(Defaults::getDefaultCurrency())
65
                         ->default(Defaults::getDefaultCurrency())
55
                         ->searchable()
66
                         ->searchable()
56
                         ->validationAttribute('Currency')
67
                         ->validationAttribute('Currency')
57
-                        ->required(),
68
+                        ->nullable(),
58
                 ])->columns(),
69
                 ])->columns(),
59
             Section::make('Taxes & Discounts')
70
             Section::make('Taxes & Discounts')
60
                 ->schema([
71
                 ->schema([
64
                         ->default(Defaults::getDefaultSalesTax())
75
                         ->default(Defaults::getDefaultSalesTax())
65
                         ->searchable()
76
                         ->searchable()
66
                         ->validationAttribute('Sales Tax')
77
                         ->validationAttribute('Sales Tax')
67
-                        ->required(),
78
+                        ->nullable(),
68
                     Select::make('purchase_tax_id')
79
                     Select::make('purchase_tax_id')
69
                         ->label('Purchase Tax')
80
                         ->label('Purchase Tax')
70
                         ->options(Defaults::getPurchaseTaxes())
81
                         ->options(Defaults::getPurchaseTaxes())
71
                         ->default(Defaults::getDefaultPurchaseTax())
82
                         ->default(Defaults::getDefaultPurchaseTax())
72
                         ->searchable()
83
                         ->searchable()
73
                         ->validationAttribute('Purchase Tax')
84
                         ->validationAttribute('Purchase Tax')
74
-                        ->required(),
85
+                        ->nullable(),
75
                     Select::make('sales_discount_id')
86
                     Select::make('sales_discount_id')
76
                         ->label('Sales Discount')
87
                         ->label('Sales Discount')
77
                         ->options(Defaults::getSalesDiscounts())
88
                         ->options(Defaults::getSalesDiscounts())
78
                         ->default(Defaults::getDefaultSalesDiscount())
89
                         ->default(Defaults::getDefaultSalesDiscount())
79
                         ->searchable()
90
                         ->searchable()
80
                         ->validationAttribute('Sales Discount')
91
                         ->validationAttribute('Sales Discount')
81
-                        ->required(),
92
+                        ->nullable(),
82
                     Select::make('purchase_discount_id')
93
                     Select::make('purchase_discount_id')
83
                         ->label('Purchase Discount')
94
                         ->label('Purchase Discount')
84
                         ->options(Defaults::getPurchaseDiscounts())
95
                         ->options(Defaults::getPurchaseDiscounts())
85
                         ->default(Defaults::getDefaultPurchaseDiscount())
96
                         ->default(Defaults::getDefaultPurchaseDiscount())
86
                         ->searchable()
97
                         ->searchable()
87
                         ->validationAttribute('Purchase Discount')
98
                         ->validationAttribute('Purchase Discount')
88
-                        ->required(),
99
+                        ->nullable(),
89
                 ])->columns(),
100
                 ])->columns(),
90
             Section::make('Categories')
101
             Section::make('Categories')
91
                 ->schema([
102
                 ->schema([
95
                         ->default(Defaults::getDefaultIncomeCategory())
106
                         ->default(Defaults::getDefaultIncomeCategory())
96
                         ->searchable()
107
                         ->searchable()
97
                         ->validationAttribute('Income Category')
108
                         ->validationAttribute('Income Category')
98
-                        ->required(),
109
+                        ->nullable(),
99
                     Select::make('expense_category_id')
110
                     Select::make('expense_category_id')
100
                         ->label('Expense Category')
111
                         ->label('Expense Category')
101
                         ->options(Defaults::getExpenseCategories())
112
                         ->options(Defaults::getExpenseCategories())
102
                         ->default(Defaults::getDefaultExpenseCategory())
113
                         ->default(Defaults::getDefaultExpenseCategory())
103
                         ->searchable()
114
                         ->searchable()
104
                         ->validationAttribute('Expense Category')
115
                         ->validationAttribute('Expense Category')
105
-                        ->required(),
116
+                        ->nullable(),
106
                 ])->columns(),
117
                 ])->columns(),
107
         ];
118
         ];
108
     }
119
     }
109
 
120
 
110
-    public function create(): void
121
+    public function save(): void
111
     {
122
     {
112
         $data = $this->form->getState();
123
         $data = $this->form->getState();
113
 
124
 

+ 162
- 0
app/Http/Livewire/Invoice.php Целия файл

1
+<?php
2
+
3
+namespace App\Http\Livewire;
4
+
5
+use App\Models\Setting\DocumentDefault;
6
+use Filament\Forms\ComponentContainer;
7
+use Filament\Forms\Components\Section;
8
+use Filament\Forms\Components\Select;
9
+use Filament\Forms\Components\Textarea;
10
+use Filament\Forms\Components\TextInput;
11
+use Filament\Forms\Concerns\InteractsWithForms;
12
+use Filament\Notifications\Notification;
13
+use Illuminate\Contracts\View\View;
14
+use Illuminate\Support\Facades\Auth;
15
+use Livewire\Component;
16
+use Filament\Forms\Contracts\HasForms;
17
+
18
+/**
19
+ * @property ComponentContainer $form
20
+ */
21
+class Invoice extends Component implements HasForms
22
+{
23
+    use InteractsWithForms;
24
+
25
+    public DocumentDefault $invoice;
26
+
27
+    public $data;
28
+
29
+    public $record;
30
+
31
+    public function mount(): void
32
+    {
33
+        $this->invoice = DocumentDefault::where('type', 'invoice')->firstOrNew();
34
+
35
+        $this->form->fill([
36
+            'document_number_prefix' => $this->invoice->document_number_prefix,
37
+            'document_number_digits' => $this->invoice->document_number_digits,
38
+            'document_number_next' => $this->invoice->document_number_next,
39
+            'payment_terms' => $this->invoice->payment_terms,
40
+            'template' => $this->invoice->template,
41
+            'title' => $this->invoice->title,
42
+            'subheading' => $this->invoice->subheading,
43
+            'notes' => $this->invoice->notes,
44
+            'footer' => $this->invoice->footer,
45
+            'terms' => $this->invoice->terms,
46
+        ]);
47
+    }
48
+
49
+    protected function getFormSchema(): array
50
+    {
51
+        return [
52
+            Section::make('General')
53
+                ->schema([
54
+                    TextInput::make('document_number_prefix')
55
+                        ->label('Number Prefix')
56
+                        ->default('INV-')
57
+                        ->required(),
58
+                    Select::make('document_number_digits')
59
+                        ->label('Number Digits')
60
+                        ->options(DocumentDefault::getDocumentNumberDigits())
61
+                        ->default(DocumentDefault::getDefaultDocumentNumberDigits())
62
+                        ->reactive()
63
+                        ->afterStateUpdated(static function (callable $set, $state) {
64
+                            $numDigits = $state;
65
+                            $nextNumber = DocumentDefault::getDefaultDocumentNumberNext($numDigits);
66
+
67
+                            return $set('document_number_next', $nextNumber);
68
+                        })
69
+                        ->searchable()
70
+                        ->required(),
71
+                    TextInput::make('document_number_next')
72
+                        ->label('Next Number')
73
+                        ->default(DocumentDefault::getDefaultDocumentNumberNext(DocumentDefault::getDefaultDocumentNumberDigits()))
74
+                        ->required(),
75
+                    Select::make('payment_terms')
76
+                        ->label('Payment Terms')
77
+                        ->options(DocumentDefault::getPaymentTerms())
78
+                        ->default(DocumentDefault::getDefaultPaymentTerms())
79
+                        ->searchable()
80
+                        ->required(),
81
+                ])->columns(),
82
+            Section::make('Template')
83
+                ->schema([
84
+                    Select::make('template')
85
+                        ->label('Template')
86
+                        ->options([
87
+                            'default' => 'Default',
88
+                            'simple' => 'Simple',
89
+                            'modern' => 'Modern',
90
+                        ])
91
+                        ->default('default')
92
+                        ->searchable()
93
+                        ->required(),
94
+                ])->columns(),
95
+            Section::make('Content')
96
+                ->schema([
97
+                    TextInput::make('title')
98
+                        ->label('Title')
99
+                        ->default('Invoice')
100
+                        ->nullable(),
101
+                    TextInput::make('subheading')
102
+                        ->label('Subheading')
103
+                        ->nullable(),
104
+                    Textarea::make('notes')
105
+                        ->label('Notes')
106
+                        ->nullable(),
107
+                    Textarea::make('footer')
108
+                        ->label('Footer')
109
+                        ->nullable(),
110
+                    Textarea::make('terms')
111
+                        ->label('Terms')
112
+                        ->nullable()
113
+                        ->columnSpanFull(),
114
+                ])->columns(),
115
+        ];
116
+    }
117
+
118
+    public function save(): void
119
+    {
120
+        $data = $this->form->getState();
121
+
122
+        $data = $this->mutateFormDataBeforeSave($data);
123
+
124
+        $this->record = $this->invoice->update($data);
125
+
126
+        $this->form->model($this->record)->saveRelationships();
127
+
128
+        $this->getSavedNotification()?->send();
129
+    }
130
+
131
+    protected function mutateFormDataBeforeSave(array $data): array
132
+    {
133
+        $data['company_id'] = Auth::user()->currentCompany->id;
134
+        $data['type'] = 'invoice';
135
+
136
+        return $data;
137
+    }
138
+
139
+    protected function getSavedNotification():?Notification
140
+    {
141
+        $title = $this->getSavedNotificationTitle();
142
+
143
+        if (blank($title)) {
144
+            return null;
145
+        }
146
+
147
+        return Notification::make()
148
+            ->success()
149
+            ->title($title);
150
+    }
151
+
152
+    protected function getSavedNotificationTitle(): ?string
153
+    {
154
+        return __('filament::resources/pages/edit-record.messages.saved');
155
+    }
156
+
157
+
158
+    public function render(): View
159
+    {
160
+        return view('livewire.invoice');
161
+    }
162
+}

+ 12
- 0
app/Models/Banking/Account.php Целия файл

2
 
2
 
3
 namespace App\Models\Banking;
3
 namespace App\Models\Banking;
4
 
4
 
5
+use App\Scopes\CurrentCompanyScope;
5
 use App\Models\Setting\Currency;
6
 use App\Models\Setting\Currency;
6
 use App\Models\Setting\DefaultSetting;
7
 use App\Models\Setting\DefaultSetting;
7
 use Database\Factories\AccountFactory;
8
 use Database\Factories\AccountFactory;
10
 use Illuminate\Database\Eloquent\Model;
11
 use Illuminate\Database\Eloquent\Model;
11
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
 use Illuminate\Database\Eloquent\Relations\HasMany;
13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
13
 use Illuminate\Support\Facades\Auth;
15
 use Illuminate\Support\Facades\Auth;
14
 use Illuminate\Support\Facades\Config;
16
 use Illuminate\Support\Facades\Config;
15
 use Spatie\Tags\HasTags;
17
 use Spatie\Tags\HasTags;
49
         'enabled' => 'boolean',
51
         'enabled' => 'boolean',
50
     ];
52
     ];
51
 
53
 
54
+    protected static function booted(): void
55
+    {
56
+        static::addGlobalScope(new CurrentCompanyScope);
57
+    }
58
+
52
     public function company(): BelongsTo
59
     public function company(): BelongsTo
53
     {
60
     {
54
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
61
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
55
     }
62
     }
56
 
63
 
64
+    public function defaultAccount(): HasOne
65
+    {
66
+        return $this->hasOne(DefaultSetting::class, 'account_id');
67
+    }
68
+
57
     public function owner(): BelongsTo
69
     public function owner(): BelongsTo
58
     {
70
     {
59
         return $this->company->owner;
71
         return $this->company->owner;

+ 7
- 0
app/Models/Company.php Целия файл

9
 use App\Models\Setting\Currency;
9
 use App\Models\Setting\Currency;
10
 use App\Models\Setting\Category;
10
 use App\Models\Setting\Category;
11
 use App\Models\Setting\Discount;
11
 use App\Models\Setting\Discount;
12
+use App\Models\Setting\DocumentDefault;
12
 use App\Models\Setting\Tax;
13
 use App\Models\Setting\Tax;
13
 use Illuminate\Database\Eloquent\Factories\HasFactory;
14
 use Illuminate\Database\Eloquent\Factories\HasFactory;
14
 use Illuminate\Database\Eloquent\Relations\HasMany;
15
 use Illuminate\Database\Eloquent\Relations\HasMany;
16
+use Illuminate\Database\Eloquent\Relations\HasOne;
15
 use Wallo\FilamentCompanies\Company as FilamentCompaniesCompany;
17
 use Wallo\FilamentCompanies\Company as FilamentCompaniesCompany;
16
 use Wallo\FilamentCompanies\Events\CompanyCreated;
18
 use Wallo\FilamentCompanies\Events\CompanyCreated;
17
 use Wallo\FilamentCompanies\Events\CompanyDeleted;
19
 use Wallo\FilamentCompanies\Events\CompanyDeleted;
115
             ->where('entity', 'individual');
117
             ->where('entity', 'individual');
116
     }
118
     }
117
 
119
 
120
+    public function document_defaults(): HasOne
121
+    {
122
+        return $this->hasOne(DocumentDefault::class);
123
+    }
124
+
118
     public function items(): HasMany
125
     public function items(): HasMany
119
     {
126
     {
120
         return $this->hasMany(Item::class);
127
         return $this->hasMany(Item::class);

+ 6
- 6
app/Models/Contact.php Целия файл

4
 
4
 
5
 use App\Models\Document\Document;
5
 use App\Models\Document\Document;
6
 use App\Models\Setting\Currency;
6
 use App\Models\Setting\Currency;
7
+use App\Scopes\CurrentCompanyScope;
7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
8
 use Illuminate\Database\Eloquent\Factories\HasFactory;
8
 use Illuminate\Database\Eloquent\Model;
9
 use Illuminate\Database\Eloquent\Model;
9
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
10
 use Illuminate\Database\Eloquent\Relations\HasMany;
11
 use Illuminate\Database\Eloquent\Relations\HasMany;
11
-use Illuminate\Http\Request;
12
 use Squire\Models\Country;
12
 use Squire\Models\Country;
13
 use Squire\Models\Region;
13
 use Squire\Models\Region;
14
 use Wallo\FilamentCompanies\FilamentCompanies;
14
 use Wallo\FilamentCompanies\FilamentCompanies;
39
         'updated_by',
39
         'updated_by',
40
     ];
40
     ];
41
 
41
 
42
+    protected static function booted(): void
43
+    {
44
+        static::addGlobalScope(new CurrentCompanyScope);
45
+    }
46
+
42
     public function company(): BelongsTo
47
     public function company(): BelongsTo
43
     {
48
     {
44
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
49
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
126
         return $query->where('type', 'customer');
131
         return $query->where('type', 'customer');
127
     }
132
     }
128
 
133
 
129
-    public function scopeEmployee($query)
130
-    {
131
-        return $query->where('type', 'employee');
132
-    }
133
-
134
     public function scopeCompany($query)
134
     public function scopeCompany($query)
135
     {
135
     {
136
         return $query->where('entity', 'company');
136
         return $query->where('entity', 'company');

+ 7
- 0
app/Models/Document/Document.php Целия файл

6
 use App\Models\Setting\Category;
6
 use App\Models\Setting\Category;
7
 use App\Models\Setting\Currency;
7
 use App\Models\Setting\Currency;
8
 use App\Models\Setting\Discount;
8
 use App\Models\Setting\Discount;
9
+use App\Models\Setting\DocumentDefault;
9
 use App\Models\Setting\Tax;
10
 use App\Models\Setting\Tax;
10
 use Database\Factories\DocumentFactory;
11
 use Database\Factories\DocumentFactory;
11
 use Illuminate\Database\Eloquent\Factories\Factory;
12
 use Illuminate\Database\Eloquent\Factories\Factory;
24
 
25
 
25
     protected $fillable = [
26
     protected $fillable = [
26
         'company_id',
27
         'company_id',
28
+        'document_default_id',
27
         'type',
29
         'type',
28
         'document_number',
30
         'document_number',
29
         'order_number',
31
         'order_number',
54
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
56
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
55
     }
57
     }
56
 
58
 
59
+    public function documentDefault(): BelongsTo
60
+    {
61
+        return $this->belongsTo(DocumentDefault::class);
62
+    }
63
+
57
     public function createdBy(): BelongsTo
64
     public function createdBy(): BelongsTo
58
     {
65
     {
59
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
66
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 17
- 0
app/Models/Setting/Category.php Целия файл

4
 
4
 
5
 use App\Models\Document\Document;
5
 use App\Models\Document\Document;
6
 use App\Models\Item;
6
 use App\Models\Item;
7
+use App\Scopes\CurrentCompanyScope;
7
 use Database\Factories\CategoryFactory;
8
 use Database\Factories\CategoryFactory;
8
 use Illuminate\Database\Eloquent\Factories\Factory;
9
 use Illuminate\Database\Eloquent\Factories\Factory;
9
 use Illuminate\Database\Eloquent\Factories\HasFactory;
10
 use Illuminate\Database\Eloquent\Factories\HasFactory;
10
 use Illuminate\Database\Eloquent\Model;
11
 use Illuminate\Database\Eloquent\Model;
11
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
 use Illuminate\Database\Eloquent\Relations\HasMany;
13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
13
 use Wallo\FilamentCompanies\FilamentCompanies;
15
 use Wallo\FilamentCompanies\FilamentCompanies;
14
 
16
 
15
 class Category extends Model
17
 class Category extends Model
32
         'enabled' => 'boolean',
34
         'enabled' => 'boolean',
33
     ];
35
     ];
34
 
36
 
37
+    protected static function booted(): void
38
+    {
39
+        static::addGlobalScope(new CurrentCompanyScope);
40
+    }
41
+
35
     public static function getCategoryTypes(): array
42
     public static function getCategoryTypes(): array
36
     {
43
     {
37
         return [
44
         return [
47
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
54
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
48
     }
55
     }
49
 
56
 
57
+    public function defaultIncomeCategory(): HasOne
58
+    {
59
+        return $this->hasOne(DefaultSetting::class, 'income_category_id');
60
+    }
61
+
62
+    public function defaultExpenseCategory(): HasOne
63
+    {
64
+        return $this->hasOne(DefaultSetting::class, 'expense_category_id');
65
+    }
66
+
50
     public function createdBy(): BelongsTo
67
     public function createdBy(): BelongsTo
51
     {
68
     {
52
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
69
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 15
- 6
app/Models/Setting/Currency.php Целия файл

3
 namespace App\Models\Setting;
3
 namespace App\Models\Setting;
4
 
4
 
5
 use App\Models\Banking\Account;
5
 use App\Models\Banking\Account;
6
+use App\Scopes\CurrentCompanyScope;
6
 use Database\Factories\CurrencyFactory;
7
 use Database\Factories\CurrencyFactory;
7
 use Illuminate\Database\Eloquent\Factories\Factory;
8
 use Illuminate\Database\Eloquent\Factories\Factory;
8
 use Illuminate\Database\Eloquent\Factories\HasFactory;
9
 use Illuminate\Database\Eloquent\Factories\HasFactory;
9
 use Illuminate\Database\Eloquent\Model;
10
 use Illuminate\Database\Eloquent\Model;
10
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
11
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
11
 use Illuminate\Database\Eloquent\Relations\HasMany;
12
 use Illuminate\Database\Eloquent\Relations\HasMany;
12
-use Illuminate\Support\Facades\Auth;
13
+use Illuminate\Database\Eloquent\Relations\HasOne;
13
 use Illuminate\Support\Facades\Config;
14
 use Illuminate\Support\Facades\Config;
14
 use Wallo\FilamentCompanies\FilamentCompanies;
15
 use Wallo\FilamentCompanies\FilamentCompanies;
15
 
16
 
20
     protected $table = 'currencies';
21
     protected $table = 'currencies';
21
 
22
 
22
     protected $fillable = [
23
     protected $fillable = [
24
+        'company_id',
23
         'name',
25
         'name',
24
         'code',
26
         'code',
25
         'rate',
27
         'rate',
29
         'decimal_mark',
31
         'decimal_mark',
30
         'thousands_separator',
32
         'thousands_separator',
31
         'enabled',
33
         'enabled',
32
-        'company_id',
33
         'created_by',
34
         'created_by',
34
         'updated_by',
35
         'updated_by',
35
     ];
36
     ];
39
         'symbol_first' => 'boolean',
40
         'symbol_first' => 'boolean',
40
     ];
41
     ];
41
 
42
 
42
-    public function accounts(): HasMany
43
+    protected static function booted(): void
43
     {
44
     {
44
-        return $this->hasMany(Account::class, 'currency_code', 'code');
45
+        static::addGlobalScope(new CurrentCompanyScope);
45
     }
46
     }
46
 
47
 
47
     public function company(): BelongsTo
48
     public function company(): BelongsTo
49
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
50
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
50
     }
51
     }
51
 
52
 
53
+    public function defaultCurrency(): HasOne
54
+    {
55
+        return $this->hasOne(DefaultSetting::class, 'currency_code', 'code');
56
+    }
57
+
58
+    public function accounts(): HasMany
59
+    {
60
+        return $this->hasMany(Account::class, 'currency_code', 'code');
61
+    }
62
+
52
     public static function getCurrencyCodes(): array
63
     public static function getCurrencyCodes(): array
53
     {
64
     {
54
         $allCodes = array_keys(Config::get('money'));
65
         $allCodes = array_keys(Config::get('money'));
55
 
66
 
56
         $storedCodes = static::query()
67
         $storedCodes = static::query()
57
-            ->where('company_id', Auth::user()->currentCompany->id)
58
             ->pluck('code')
68
             ->pluck('code')
59
             ->toArray();
69
             ->toArray();
60
 
70
 
66
     public static function getDefaultCurrency(): ?string
76
     public static function getDefaultCurrency(): ?string
67
     {
77
     {
68
         $defaultCurrency = self::where('enabled', true)
78
         $defaultCurrency = self::where('enabled', true)
69
-            ->where('company_id', Auth::user()->currentCompany->id)
70
             ->first();
79
             ->first();
71
 
80
 
72
         return $defaultCurrency->code ?? null;
81
         return $defaultCurrency->code ?? null;

+ 28
- 37
app/Models/Setting/DefaultSetting.php Целия файл

3
 namespace App\Models\Setting;
3
 namespace App\Models\Setting;
4
 
4
 
5
 use App\Models\Banking\Account;
5
 use App\Models\Banking\Account;
6
+use App\Scopes\CurrentCompanyScope;
6
 use Illuminate\Database\Eloquent\Factories\HasFactory;
7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
7
 use Illuminate\Database\Eloquent\Model;
8
 use Illuminate\Database\Eloquent\Model;
8
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
9
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
9
-use Illuminate\Support\Facades\Auth;
10
 use Wallo\FilamentCompanies\FilamentCompanies;
10
 use Wallo\FilamentCompanies\FilamentCompanies;
11
 
11
 
12
 class DefaultSetting extends Model
12
 class DefaultSetting extends Model
28
         'updated_by',
28
         'updated_by',
29
     ];
29
     ];
30
 
30
 
31
+    protected static function booted(): void
32
+    {
33
+        static::addGlobalScope(new CurrentCompanyScope);
34
+    }
35
+
31
     public function company(): BelongsTo
36
     public function company(): BelongsTo
32
     {
37
     {
33
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
38
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
45
 
50
 
46
     public function salesTax(): BelongsTo
51
     public function salesTax(): BelongsTo
47
     {
52
     {
48
-        return $this->belongsTo(Tax::class,'sales_tax_id', 'id');
53
+        return $this->belongsTo(Tax::class,'sales_tax_id', 'id')
54
+            ->where('type', 'sales');
49
     }
55
     }
50
 
56
 
51
     public function purchaseTax(): BelongsTo
57
     public function purchaseTax(): BelongsTo
52
     {
58
     {
53
-        return $this->belongsTo(Tax::class,'purchase_tax_id', 'id');
59
+        return $this->belongsTo(Tax::class,'purchase_tax_id', 'id')
60
+            ->where('type', 'purchase');
54
     }
61
     }
55
 
62
 
56
     public function salesDiscount(): BelongsTo
63
     public function salesDiscount(): BelongsTo
57
     {
64
     {
58
-        return $this->belongsTo(Discount::class,'sales_discount_id', 'id');
65
+        return $this->belongsTo(Discount::class,'sales_discount_id', 'id')
66
+            ->where('type', 'sales');
59
     }
67
     }
60
 
68
 
61
     public function purchaseDiscount(): BelongsTo
69
     public function purchaseDiscount(): BelongsTo
62
     {
70
     {
63
-        return $this->belongsTo(Discount::class,'purchase_discount_id', 'id');
71
+        return $this->belongsTo(Discount::class,'purchase_discount_id', 'id')
72
+            ->where('type', 'purchase');
64
     }
73
     }
65
 
74
 
66
     public function incomeCategory(): BelongsTo
75
     public function incomeCategory(): BelongsTo
67
     {
76
     {
68
-        return $this->belongsTo(Category::class,'income_category_id', 'id');
77
+        return $this->belongsTo(Category::class,'income_category_id', 'id')
78
+            ->where('type', 'income');
69
     }
79
     }
70
 
80
 
71
     public function expenseCategory(): BelongsTo
81
     public function expenseCategory(): BelongsTo
72
     {
82
     {
73
-        return $this->belongsTo(Category::class,'expense_category_id', 'id');
83
+        return $this->belongsTo(Category::class,'expense_category_id', 'id')
84
+            ->where('type', 'expense');
74
     }
85
     }
75
 
86
 
76
     public function updatedBy(): BelongsTo
87
     public function updatedBy(): BelongsTo
80
 
91
 
81
     public static function getAccounts(): array
92
     public static function getAccounts(): array
82
     {
93
     {
83
-        return Account::where('company_id', Auth::user()->currentCompany->id)
84
-            ->pluck('name', 'id')
85
-            ->toArray();
94
+        return Account::pluck('name', 'id')->toArray();
86
     }
95
     }
87
 
96
 
88
     public static function getCurrencies(): array
97
     public static function getCurrencies(): array
89
     {
98
     {
90
-        return Currency::where('company_id', Auth::user()->currentCompany->id)
91
-            ->pluck('name', 'code')
92
-            ->toArray();
99
+        return Currency::pluck('name', 'code')->toArray();
93
     }
100
     }
94
 
101
 
95
     public static function getSalesTaxes(): array
102
     public static function getSalesTaxes(): array
96
     {
103
     {
97
-        return Tax::where('company_id', Auth::user()->currentCompany->id)
98
-            ->where('type', 'sales')
104
+        return Tax::where('type', 'sales')
99
             ->pluck('name', 'id')
105
             ->pluck('name', 'id')
100
             ->toArray();
106
             ->toArray();
101
     }
107
     }
102
 
108
 
103
     public static function getPurchaseTaxes(): array
109
     public static function getPurchaseTaxes(): array
104
     {
110
     {
105
-        return Tax::where('company_id', Auth::user()->currentCompany->id)
106
-            ->where('type', 'purchase')
111
+        return Tax::where('type', 'purchase')
107
             ->pluck('name', 'id')
112
             ->pluck('name', 'id')
108
             ->toArray();
113
             ->toArray();
109
     }
114
     }
110
 
115
 
111
     public static function getSalesDiscounts(): array
116
     public static function getSalesDiscounts(): array
112
     {
117
     {
113
-        return Discount::where('company_id', Auth::user()->currentCompany->id)
114
-            ->where('type', 'sales')
118
+        return Discount::where('type', 'sales')
115
             ->pluck('name', 'id')
119
             ->pluck('name', 'id')
116
             ->toArray();
120
             ->toArray();
117
     }
121
     }
118
 
122
 
119
     public static function getPurchaseDiscounts(): array
123
     public static function getPurchaseDiscounts(): array
120
     {
124
     {
121
-        return Discount::where('company_id', Auth::user()->currentCompany->id)
122
-            ->where('type', 'purchase')
125
+        return Discount::where('type', 'purchase')
123
             ->pluck('name', 'id')
126
             ->pluck('name', 'id')
124
             ->toArray();
127
             ->toArray();
125
     }
128
     }
126
 
129
 
127
     public static function getIncomeCategories(): array
130
     public static function getIncomeCategories(): array
128
     {
131
     {
129
-        return Category::where('company_id', Auth::user()->currentCompany->id)
130
-            ->where('type', 'income')
132
+        return Category::where('type', 'income')
131
             ->pluck('name', 'id')
133
             ->pluck('name', 'id')
132
             ->toArray();
134
             ->toArray();
133
     }
135
     }
134
 
136
 
135
     public static function getExpenseCategories(): array
137
     public static function getExpenseCategories(): array
136
     {
138
     {
137
-        return Category::where('company_id', Auth::user()->currentCompany->id)
138
-            ->where('type', 'expense')
139
+        return Category::where('type', 'expense')
139
             ->pluck('name', 'id')
140
             ->pluck('name', 'id')
140
             ->toArray();
141
             ->toArray();
141
     }
142
     }
142
 
143
 
143
     public static function getDefaultAccount()
144
     public static function getDefaultAccount()
144
     {
145
     {
145
-        $defaultAccount = Account::where('enabled', true)
146
-            ->where('company_id', Auth::user()->currentCompany->id)
147
-            ->first();
146
+        $defaultAccount = Account::where('enabled', true)->first();
148
 
147
 
149
         return $defaultAccount->id ?? null;
148
         return $defaultAccount->id ?? null;
150
     }
149
     }
151
 
150
 
152
     public static function getDefaultCurrency()
151
     public static function getDefaultCurrency()
153
     {
152
     {
154
-        $defaultCurrency = Currency::where('enabled', true)
155
-            ->where('company_id', Auth::user()->currentCompany->id)
156
-            ->first();
153
+        $defaultCurrency = Currency::where('enabled', true)->first();
157
 
154
 
158
         return $defaultCurrency->code ?? null;
155
         return $defaultCurrency->code ?? null;
159
     }
156
     }
161
     public static function getDefaultSalesTax()
158
     public static function getDefaultSalesTax()
162
     {
159
     {
163
         $defaultSalesTax = Tax::where('enabled', true)
160
         $defaultSalesTax = Tax::where('enabled', true)
164
-            ->where('company_id', Auth::user()->currentCompany->id)
165
             ->where('type', 'sales')
161
             ->where('type', 'sales')
166
             ->first();
162
             ->first();
167
 
163
 
171
     public static function getDefaultPurchaseTax()
167
     public static function getDefaultPurchaseTax()
172
     {
168
     {
173
         $defaultPurchaseTax = Tax::where('enabled', true)
169
         $defaultPurchaseTax = Tax::where('enabled', true)
174
-            ->where('company_id', Auth::user()->currentCompany->id)
175
             ->where('type', 'purchase')
170
             ->where('type', 'purchase')
176
             ->first();
171
             ->first();
177
 
172
 
181
     public static function getDefaultSalesDiscount()
176
     public static function getDefaultSalesDiscount()
182
     {
177
     {
183
         $defaultSalesDiscount = Discount::where('enabled', true)
178
         $defaultSalesDiscount = Discount::where('enabled', true)
184
-            ->where('company_id', Auth::user()->currentCompany->id)
185
             ->where('type', 'sales')
179
             ->where('type', 'sales')
186
             ->first();
180
             ->first();
187
 
181
 
191
     public static function getDefaultPurchaseDiscount()
185
     public static function getDefaultPurchaseDiscount()
192
     {
186
     {
193
         $defaultPurchaseDiscount = Discount::where('enabled', true)
187
         $defaultPurchaseDiscount = Discount::where('enabled', true)
194
-            ->where('company_id', Auth::user()->currentCompany->id)
195
             ->where('type', 'purchase')
188
             ->where('type', 'purchase')
196
             ->first();
189
             ->first();
197
 
190
 
201
     public static function getDefaultIncomeCategory()
194
     public static function getDefaultIncomeCategory()
202
     {
195
     {
203
         $defaultIncomeCategory = Category::where('enabled', true)
196
         $defaultIncomeCategory = Category::where('enabled', true)
204
-            ->where('company_id', Auth::user()->currentCompany->id)
205
             ->where('type', 'income')
197
             ->where('type', 'income')
206
             ->first();
198
             ->first();
207
 
199
 
211
     public static function getDefaultExpenseCategory()
203
     public static function getDefaultExpenseCategory()
212
     {
204
     {
213
         $defaultExpenseCategory = Category::where('enabled', true)
205
         $defaultExpenseCategory = Category::where('enabled', true)
214
-            ->where('company_id', Auth::user()->currentCompany->id)
215
             ->where('type', 'expense')
206
             ->where('type', 'expense')
216
             ->first();
207
             ->first();
217
 
208
 

+ 17
- 1
app/Models/Setting/Discount.php Целия файл

4
 
4
 
5
 use App\Models\Document\DocumentItem;
5
 use App\Models\Document\DocumentItem;
6
 use App\Models\Item;
6
 use App\Models\Item;
7
-use App\Models\User;
7
+use App\Scopes\CurrentCompanyScope;
8
 use Database\Factories\DiscountFactory;
8
 use Database\Factories\DiscountFactory;
9
 use Illuminate\Database\Eloquent\Factories\Factory;
9
 use Illuminate\Database\Eloquent\Factories\Factory;
10
 use Illuminate\Database\Eloquent\Factories\HasFactory;
10
 use Illuminate\Database\Eloquent\Factories\HasFactory;
11
 use Illuminate\Database\Eloquent\Model;
11
 use Illuminate\Database\Eloquent\Model;
12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
13
 use Illuminate\Database\Eloquent\Relations\HasMany;
13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
14
 use Wallo\FilamentCompanies\FilamentCompanies;
15
 use Wallo\FilamentCompanies\FilamentCompanies;
15
 
16
 
16
 class Discount extends Model
17
 class Discount extends Model
40
         'end_date' => 'datetime',
41
         'end_date' => 'datetime',
41
     ];
42
     ];
42
 
43
 
44
+    protected static function booted(): void
45
+    {
46
+        static::addGlobalScope(new CurrentCompanyScope);
47
+    }
48
+
43
     public function company(): BelongsTo
49
     public function company(): BelongsTo
44
     {
50
     {
45
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
51
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
46
     }
52
     }
47
 
53
 
54
+    public function defaultSalesDiscount(): HasOne
55
+    {
56
+        return $this->hasOne(DefaultSetting::class, 'sales_discount_id');
57
+    }
58
+
59
+    public function defaultPurchaseDiscount(): HasOne
60
+    {
61
+        return $this->hasOne(DefaultSetting::class, 'purchase_discount_id');
62
+    }
63
+
48
     public function createdBy(): BelongsTo
64
     public function createdBy(): BelongsTo
49
     {
65
     {
50
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
66
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 108
- 0
app/Models/Setting/DocumentDefault.php Целия файл

1
+<?php
2
+
3
+namespace App\Models\Setting;
4
+
5
+use App\Models\Document\Document;
6
+use App\Scopes\CurrentCompanyScope;
7
+use Database\Factories\DocumentDefaultFactory;
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 DocumentDefault extends Model
16
+{
17
+    use HasFactory;
18
+
19
+    protected $table = 'document_defaults';
20
+
21
+    protected $fillable = [
22
+        'company_id',
23
+        'type',
24
+        'document_number_prefix',
25
+        'document_number_digits',
26
+        'document_number_next',
27
+        'payment_terms',
28
+        'template',
29
+        'title',
30
+        'subheading',
31
+        'notes',
32
+        'terms',
33
+        'footer',
34
+    ];
35
+
36
+    protected static function booted(): void
37
+    {
38
+        static::addGlobalScope(new CurrentCompanyScope);
39
+    }
40
+
41
+    public function company(): BelongsTo
42
+    {
43
+        return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
44
+    }
45
+
46
+    public function documents(): HasMany
47
+    {
48
+        return $this->hasMany(Document::class);
49
+    }
50
+
51
+    public static function getDocumentNumberDigits(): array
52
+    {
53
+        return array_combine(range(1, 20), range(1, 20));
54
+    }
55
+
56
+    public static function getDefaultDocumentNumberDigits(string $type = 'invoice'): int
57
+    {
58
+        $documentNumberDigits = self::where('type', $type)->pluck('document_number_digits', 'id')->toArray();
59
+
60
+        return array_key_first($documentNumberDigits) ?? 5;
61
+    }
62
+
63
+    public static function getDefaultDocumentNumberNext(int|null $numDigits = null, string $type = 'invoice'): string
64
+    {
65
+        // Fetch the latest document
66
+        $latestDocument = Document::where('type', $type)->orderBy('id', 'desc')->first();
67
+
68
+        // If there are no documents yet, start from 1
69
+        if (!$latestDocument) {
70
+            $nextNumber = 1;
71
+        } else {
72
+            // Otherwise, increment the latest document's number
73
+            $nextNumber = (int)$latestDocument->document_number + 1;
74
+        }
75
+
76
+        return str_pad($nextNumber, $numDigits, '0', STR_PAD_LEFT);
77
+    }
78
+
79
+    public static function getPaymentTerms(): array
80
+    {
81
+        return [
82
+            0 => 'Due on Receipt',
83
+            7 => 'Net 7',
84
+            10 => 'Net 10',
85
+            15 => 'Net 15',
86
+            30 => 'Net 30',
87
+            60 => 'Net 60',
88
+            90 => 'Net 90',
89
+        ];
90
+    }
91
+
92
+    public static function getDefaultPaymentTerms(string $type = 'invoice'): int
93
+    {
94
+        $paymentTerms = self::where('type', $type)->pluck('payment_terms', 'id')->toArray();
95
+
96
+        return array_key_first($paymentTerms) ?? 30;
97
+    }
98
+
99
+    public function getDocumentNumberAttribute(): string
100
+    {
101
+        return $this->document_number_prefix . str_pad($this->document_number_next, $this->document_number_digits, '0', STR_PAD_LEFT);
102
+    }
103
+
104
+    protected static function newFactory(): Factory
105
+    {
106
+        return DocumentDefaultFactory::new();
107
+    }
108
+}

+ 17
- 1
app/Models/Setting/Tax.php Целия файл

5
 use App\Models\Company;
5
 use App\Models\Company;
6
 use App\Models\Document\DocumentItem;
6
 use App\Models\Document\DocumentItem;
7
 use App\Models\Item;
7
 use App\Models\Item;
8
-use App\Models\User;
8
+use App\Scopes\CurrentCompanyScope;
9
 use Database\Factories\TaxFactory;
9
 use Database\Factories\TaxFactory;
10
 use Illuminate\Database\Eloquent\Factories\Factory;
10
 use Illuminate\Database\Eloquent\Factories\Factory;
11
 use Illuminate\Database\Eloquent\Factories\HasFactory;
11
 use Illuminate\Database\Eloquent\Factories\HasFactory;
12
 use Illuminate\Database\Eloquent\Model;
12
 use Illuminate\Database\Eloquent\Model;
13
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
13
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
14
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
 use Illuminate\Database\Eloquent\Relations\HasMany;
15
+use Illuminate\Database\Eloquent\Relations\HasOne;
15
 use Wallo\FilamentCompanies\FilamentCompanies;
16
 use Wallo\FilamentCompanies\FilamentCompanies;
16
 
17
 
17
 class Tax extends Model
18
 class Tax extends Model
37
         'enabled' => 'boolean',
38
         'enabled' => 'boolean',
38
     ];
39
     ];
39
 
40
 
41
+    protected static function booted(): void
42
+    {
43
+        static::addGlobalScope(new CurrentCompanyScope);
44
+    }
45
+
40
     public function company(): BelongsTo
46
     public function company(): BelongsTo
41
     {
47
     {
42
         return $this->belongsTo(Company::class, 'company_id');
48
         return $this->belongsTo(Company::class, 'company_id');
43
     }
49
     }
44
 
50
 
51
+    public function defaultSalesTax(): HasOne
52
+    {
53
+        return $this->hasOne(DefaultSetting::class, 'sales_tax_id');
54
+    }
55
+
56
+    public function defaultPurchaseTax(): HasOne
57
+    {
58
+        return $this->hasOne(DefaultSetting::class, 'purchase_tax_id');
59
+    }
60
+
45
     public function createdBy(): BelongsTo
61
     public function createdBy(): BelongsTo
46
     {
62
     {
47
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');
63
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 85
- 0
app/Observers/CompanyObserver.php Целия файл

1
+<?php
2
+
3
+namespace App\Observers;
4
+
5
+use App\Models\Company;
6
+use App\Models\Setting\Currency;
7
+use App\Models\Setting\Discount;
8
+use App\Models\Setting\DocumentDefault;
9
+use App\Models\Setting\Tax;
10
+use Database\Factories\CategoryFactory;
11
+use Illuminate\Support\Carbon;
12
+
13
+class CompanyObserver
14
+{
15
+    /**
16
+     * Handle the Company "created" event.
17
+     */
18
+    public function created(Company $company): void
19
+    {
20
+        DocumentDefault::factory()->invoice()->create([
21
+            'company_id' => $company->id,
22
+        ]);
23
+
24
+        DocumentDefault::factory()->bill()->create([
25
+            'company_id' => $company->id,
26
+        ]);
27
+
28
+        Currency::factory()->create([
29
+            'company_id' => $company->id,
30
+            'created_by' => $company->user_id,
31
+        ]);
32
+
33
+        Tax::factory()->salesTax()->create([
34
+            'company_id' => $company->id,
35
+            'created_by' => $company->user_id,
36
+        ]);
37
+
38
+        Tax::factory()->purchaseTax()->create([
39
+            'company_id' => $company->id,
40
+            'created_by' => $company->user_id,
41
+        ]);
42
+
43
+        Discount::factory()->salesDiscount()->create([
44
+            'company_id' => $company->id,
45
+            'created_by' => $company->user_id,
46
+        ]);
47
+
48
+        Discount::factory()->purchaseDiscount()->create([
49
+            'company_id' => $company->id,
50
+            'created_by' => $company->user_id,
51
+        ]);
52
+    }
53
+
54
+    /**
55
+     * Handle the Company "updated" event.
56
+     */
57
+    public function updated(Company $company): void
58
+    {
59
+        //
60
+    }
61
+
62
+    /**
63
+     * Handle the Company "deleted" event.
64
+     */
65
+    public function deleted(Company $company): void
66
+    {
67
+        //
68
+    }
69
+
70
+    /**
71
+     * Handle the Company "restored" event.
72
+     */
73
+    public function restored(Company $company): void
74
+    {
75
+        //
76
+    }
77
+
78
+    /**
79
+     * Handle the Company "force deleted" event.
80
+     */
81
+    public function forceDeleted(Company $company): void
82
+    {
83
+        //
84
+    }
85
+}

+ 0
- 1
app/Providers/AppServiceProvider.php Целия файл

5
 use Filament\Facades\Filament;
5
 use Filament\Facades\Filament;
6
 use Filament\Forms\Components\Field;
6
 use Filament\Forms\Components\Field;
7
 use Filament\Forms\Components\Actions\Action;
7
 use Filament\Forms\Components\Actions\Action;
8
-use Illuminate\Support\HtmlString;
9
 use Illuminate\Support\ServiceProvider;
8
 use Illuminate\Support\ServiceProvider;
10
 
9
 
11
 class AppServiceProvider extends ServiceProvider
10
 class AppServiceProvider extends ServiceProvider

+ 9
- 1
app/Providers/EventServiceProvider.php Целия файл

2
 
2
 
3
 namespace App\Providers;
3
 namespace App\Providers;
4
 
4
 
5
+use App\Observers\CompanyObserver;
6
+use App\Models\Company;
5
 use Illuminate\Auth\Events\Registered;
7
 use Illuminate\Auth\Events\Registered;
6
 use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
8
 use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
7
 use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
9
 use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
8
-use Illuminate\Support\Facades\Event;
9
 
10
 
10
 class EventServiceProvider extends ServiceProvider
11
 class EventServiceProvider extends ServiceProvider
11
 {
12
 {
35
     {
36
     {
36
         return false;
37
         return false;
37
     }
38
     }
39
+
40
+    /**
41
+     * The model observers for the application.
42
+     */
43
+    protected $observers = [
44
+        Company::class => [CompanyObserver::class],
45
+    ];
38
 }
46
 }

+ 21
- 0
app/Scopes/CurrentCompanyScope.php Целия файл

1
+<?php
2
+
3
+namespace App\Scopes;
4
+
5
+use Illuminate\Database\Eloquent\Builder;
6
+use Illuminate\Database\Eloquent\Model;
7
+use Illuminate\Database\Eloquent\Scope;
8
+use Illuminate\Support\Facades\Auth;
9
+
10
+class CurrentCompanyScope implements Scope
11
+{
12
+    /**
13
+     * Apply the scope to a given Eloquent query builder.
14
+     */
15
+    public function apply(Builder $builder, Model $model): void
16
+    {
17
+        if (Auth::check() && Auth::user()->currentCompany) {
18
+            $builder->where('company_id', Auth::user()->currentCompany->id);
19
+        }
20
+    }
21
+}

+ 0
- 6
app/Services/CurrencyService.php Целия файл

28
 
28
 
29
     public function getCachedExchangeRate(string $defaultCurrencyCode, string $code): ?float
29
     public function getCachedExchangeRate(string $defaultCurrencyCode, string $code): ?float
30
     {
30
     {
31
-        // Include both the default currency code and the target currency code in the cache key
32
         $cacheKey = 'currency_data_' . $defaultCurrencyCode . '_' . $code;
31
         $cacheKey = 'currency_data_' . $defaultCurrencyCode . '_' . $code;
33
 
32
 
34
-        // Attempt to retrieve the cached exchange rate
35
         $cachedData = Cache::get($cacheKey);
33
         $cachedData = Cache::get($cacheKey);
36
 
34
 
37
-        // If the cached exchange rate exists, return it
38
         if ($cachedData !== null) {
35
         if ($cachedData !== null) {
39
             return $cachedData['rate'];
36
             return $cachedData['rate'];
40
         }
37
         }
41
 
38
 
42
-        // If the cached exchange rate does not exist, retrieve it from the API
43
         $rate = $this->getExchangeRate($defaultCurrencyCode, $code);
39
         $rate = $this->getExchangeRate($defaultCurrencyCode, $code);
44
 
40
 
45
-        // If the API call was successful, cache the exchange rate
46
         if ($rate !== null) {
41
         if ($rate !== null) {
47
-            // Store the exchange rate in the cache for 24 hours
48
             $dataToCache = compact('rate');
42
             $dataToCache = compact('rate');
49
             $expirationTimeInSeconds = 60 * 60 * 24; // 24 hours
43
             $expirationTimeInSeconds = 60 * 60 * 24; // 24 hours
50
             Cache::put($cacheKey, $dataToCache, $expirationTimeInSeconds);
44
             Cache::put($cacheKey, $dataToCache, $expirationTimeInSeconds);

+ 41
- 41
composer.lock Целия файл

1992
         },
1992
         },
1993
         {
1993
         {
1994
             "name": "laravel/framework",
1994
             "name": "laravel/framework",
1995
-            "version": "v10.14.1",
1995
+            "version": "v10.15.0",
1996
             "source": {
1996
             "source": {
1997
                 "type": "git",
1997
                 "type": "git",
1998
                 "url": "https://github.com/laravel/framework.git",
1998
                 "url": "https://github.com/laravel/framework.git",
1999
-                "reference": "6f89a2b74b232d8bf2e1d9ed87e311841263dfcb"
1999
+                "reference": "c7599dc92e04532824bafbd226c2936ce6a905b8"
2000
             },
2000
             },
2001
             "dist": {
2001
             "dist": {
2002
                 "type": "zip",
2002
                 "type": "zip",
2003
-                "url": "https://api.github.com/repos/laravel/framework/zipball/6f89a2b74b232d8bf2e1d9ed87e311841263dfcb",
2004
-                "reference": "6f89a2b74b232d8bf2e1d9ed87e311841263dfcb",
2003
+                "url": "https://api.github.com/repos/laravel/framework/zipball/c7599dc92e04532824bafbd226c2936ce6a905b8",
2004
+                "reference": "c7599dc92e04532824bafbd226c2936ce6a905b8",
2005
                 "shasum": ""
2005
                 "shasum": ""
2006
             },
2006
             },
2007
             "require": {
2007
             "require": {
2188
                 "issues": "https://github.com/laravel/framework/issues",
2188
                 "issues": "https://github.com/laravel/framework/issues",
2189
                 "source": "https://github.com/laravel/framework"
2189
                 "source": "https://github.com/laravel/framework"
2190
             },
2190
             },
2191
-            "time": "2023-06-28T14:25:16+00:00"
2191
+            "time": "2023-07-11T13:43:52+00:00"
2192
         },
2192
         },
2193
         {
2193
         {
2194
             "name": "laravel/sanctum",
2194
             "name": "laravel/sanctum",
2318
         },
2318
         },
2319
         {
2319
         {
2320
             "name": "laravel/socialite",
2320
             "name": "laravel/socialite",
2321
-            "version": "v5.6.3",
2321
+            "version": "v5.8.0",
2322
             "source": {
2322
             "source": {
2323
                 "type": "git",
2323
                 "type": "git",
2324
                 "url": "https://github.com/laravel/socialite.git",
2324
                 "url": "https://github.com/laravel/socialite.git",
2325
-                "reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e"
2325
+                "reference": "50148edf24b6cd3e428aa9bc06a5d915b24376bb"
2326
             },
2326
             },
2327
             "dist": {
2327
             "dist": {
2328
                 "type": "zip",
2328
                 "type": "zip",
2329
-                "url": "https://api.github.com/repos/laravel/socialite/zipball/00ea7f8630673ea49304fc8a9fca5a64eb838c7e",
2330
-                "reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e",
2329
+                "url": "https://api.github.com/repos/laravel/socialite/zipball/50148edf24b6cd3e428aa9bc06a5d915b24376bb",
2330
+                "reference": "50148edf24b6cd3e428aa9bc06a5d915b24376bb",
2331
                 "shasum": ""
2331
                 "shasum": ""
2332
             },
2332
             },
2333
             "require": {
2333
             "require": {
2384
                 "issues": "https://github.com/laravel/socialite/issues",
2384
                 "issues": "https://github.com/laravel/socialite/issues",
2385
                 "source": "https://github.com/laravel/socialite"
2385
                 "source": "https://github.com/laravel/socialite"
2386
             },
2386
             },
2387
-            "time": "2023-06-06T13:42:43+00:00"
2387
+            "time": "2023-07-14T14:22:58+00:00"
2388
         },
2388
         },
2389
         {
2389
         {
2390
             "name": "laravel/tinker",
2390
             "name": "laravel/tinker",
5110
         },
5110
         },
5111
         {
5111
         {
5112
             "name": "squirephp/countries",
5112
             "name": "squirephp/countries",
5113
-            "version": "v3.4.1",
5113
+            "version": "v3.4.2",
5114
             "source": {
5114
             "source": {
5115
                 "type": "git",
5115
                 "type": "git",
5116
                 "url": "https://github.com/squirephp/countries.git",
5116
                 "url": "https://github.com/squirephp/countries.git",
5164
         },
5164
         },
5165
         {
5165
         {
5166
             "name": "squirephp/countries-en",
5166
             "name": "squirephp/countries-en",
5167
-            "version": "v3.4.1",
5167
+            "version": "v3.4.2",
5168
             "source": {
5168
             "source": {
5169
                 "type": "git",
5169
                 "type": "git",
5170
                 "url": "https://github.com/squirephp/countries-en.git",
5170
                 "url": "https://github.com/squirephp/countries-en.git",
5218
         },
5218
         },
5219
         {
5219
         {
5220
             "name": "squirephp/model",
5220
             "name": "squirephp/model",
5221
-            "version": "v3.4.1",
5221
+            "version": "v3.4.2",
5222
             "source": {
5222
             "source": {
5223
                 "type": "git",
5223
                 "type": "git",
5224
                 "url": "https://github.com/squirephp/model.git",
5224
                 "url": "https://github.com/squirephp/model.git",
5272
         },
5272
         },
5273
         {
5273
         {
5274
             "name": "squirephp/regions",
5274
             "name": "squirephp/regions",
5275
-            "version": "v3.4.1",
5275
+            "version": "v3.4.2",
5276
             "source": {
5276
             "source": {
5277
                 "type": "git",
5277
                 "type": "git",
5278
                 "url": "https://github.com/squirephp/regions.git",
5278
                 "url": "https://github.com/squirephp/regions.git",
5326
         },
5326
         },
5327
         {
5327
         {
5328
             "name": "squirephp/regions-en",
5328
             "name": "squirephp/regions-en",
5329
-            "version": "v3.4.1",
5329
+            "version": "v3.4.2",
5330
             "source": {
5330
             "source": {
5331
                 "type": "git",
5331
                 "type": "git",
5332
                 "url": "https://github.com/squirephp/regions-en.git",
5332
                 "url": "https://github.com/squirephp/regions-en.git",
5380
         },
5380
         },
5381
         {
5381
         {
5382
             "name": "squirephp/repository",
5382
             "name": "squirephp/repository",
5383
-            "version": "v3.4.1",
5383
+            "version": "v3.4.2",
5384
             "source": {
5384
             "source": {
5385
                 "type": "git",
5385
                 "type": "git",
5386
                 "url": "https://github.com/squirephp/repository.git",
5386
                 "url": "https://github.com/squirephp/repository.git",
5435
         },
5435
         },
5436
         {
5436
         {
5437
             "name": "squirephp/rule",
5437
             "name": "squirephp/rule",
5438
-            "version": "v3.4.1",
5438
+            "version": "v3.4.2",
5439
             "source": {
5439
             "source": {
5440
                 "type": "git",
5440
                 "type": "git",
5441
                 "url": "https://github.com/squirephp/rule.git",
5441
                 "url": "https://github.com/squirephp/rule.git",
8456
         },
8456
         },
8457
         {
8457
         {
8458
             "name": "filp/whoops",
8458
             "name": "filp/whoops",
8459
-            "version": "2.15.2",
8459
+            "version": "2.15.3",
8460
             "source": {
8460
             "source": {
8461
                 "type": "git",
8461
                 "type": "git",
8462
                 "url": "https://github.com/filp/whoops.git",
8462
                 "url": "https://github.com/filp/whoops.git",
8463
-                "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73"
8463
+                "reference": "c83e88a30524f9360b11f585f71e6b17313b7187"
8464
             },
8464
             },
8465
             "dist": {
8465
             "dist": {
8466
                 "type": "zip",
8466
                 "type": "zip",
8467
-                "url": "https://api.github.com/repos/filp/whoops/zipball/aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
8468
-                "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73",
8467
+                "url": "https://api.github.com/repos/filp/whoops/zipball/c83e88a30524f9360b11f585f71e6b17313b7187",
8468
+                "reference": "c83e88a30524f9360b11f585f71e6b17313b7187",
8469
                 "shasum": ""
8469
                 "shasum": ""
8470
             },
8470
             },
8471
             "require": {
8471
             "require": {
8515
             ],
8515
             ],
8516
             "support": {
8516
             "support": {
8517
                 "issues": "https://github.com/filp/whoops/issues",
8517
                 "issues": "https://github.com/filp/whoops/issues",
8518
-                "source": "https://github.com/filp/whoops/tree/2.15.2"
8518
+                "source": "https://github.com/filp/whoops/tree/2.15.3"
8519
             },
8519
             },
8520
             "funding": [
8520
             "funding": [
8521
                 {
8521
                 {
8523
                     "type": "github"
8523
                     "type": "github"
8524
                 }
8524
                 }
8525
             ],
8525
             ],
8526
-            "time": "2023-04-12T12:00:00+00:00"
8526
+            "time": "2023-07-13T12:00:00+00:00"
8527
         },
8527
         },
8528
         {
8528
         {
8529
             "name": "hamcrest/hamcrest-php",
8529
             "name": "hamcrest/hamcrest-php",
8578
         },
8578
         },
8579
         {
8579
         {
8580
             "name": "laravel/pint",
8580
             "name": "laravel/pint",
8581
-            "version": "v1.10.3",
8581
+            "version": "v1.10.5",
8582
             "source": {
8582
             "source": {
8583
                 "type": "git",
8583
                 "type": "git",
8584
                 "url": "https://github.com/laravel/pint.git",
8584
                 "url": "https://github.com/laravel/pint.git",
8585
-                "reference": "c472786bca01e4812a9bb7933b23edfc5b6877b7"
8585
+                "reference": "a458fb057bfa2f5a09888a8aa349610be807b0c3"
8586
             },
8586
             },
8587
             "dist": {
8587
             "dist": {
8588
                 "type": "zip",
8588
                 "type": "zip",
8589
-                "url": "https://api.github.com/repos/laravel/pint/zipball/c472786bca01e4812a9bb7933b23edfc5b6877b7",
8590
-                "reference": "c472786bca01e4812a9bb7933b23edfc5b6877b7",
8589
+                "url": "https://api.github.com/repos/laravel/pint/zipball/a458fb057bfa2f5a09888a8aa349610be807b0c3",
8590
+                "reference": "a458fb057bfa2f5a09888a8aa349610be807b0c3",
8591
                 "shasum": ""
8591
                 "shasum": ""
8592
             },
8592
             },
8593
             "require": {
8593
             "require": {
8598
                 "php": "^8.1.0"
8598
                 "php": "^8.1.0"
8599
             },
8599
             },
8600
             "require-dev": {
8600
             "require-dev": {
8601
-                "friendsofphp/php-cs-fixer": "^3.18.0",
8601
+                "friendsofphp/php-cs-fixer": "^3.21.1",
8602
                 "illuminate/view": "^10.5.1",
8602
                 "illuminate/view": "^10.5.1",
8603
-                "laravel-zero/framework": "^10.0.2",
8603
+                "laravel-zero/framework": "^10.1.1",
8604
                 "mockery/mockery": "^1.5.1",
8604
                 "mockery/mockery": "^1.5.1",
8605
                 "nunomaduro/larastan": "^2.5.1",
8605
                 "nunomaduro/larastan": "^2.5.1",
8606
                 "nunomaduro/termwind": "^1.15.1",
8606
                 "nunomaduro/termwind": "^1.15.1",
8640
                 "issues": "https://github.com/laravel/pint/issues",
8640
                 "issues": "https://github.com/laravel/pint/issues",
8641
                 "source": "https://github.com/laravel/pint"
8641
                 "source": "https://github.com/laravel/pint"
8642
             },
8642
             },
8643
-            "time": "2023-06-20T15:55:03+00:00"
8643
+            "time": "2023-07-14T10:26:01+00:00"
8644
         },
8644
         },
8645
         {
8645
         {
8646
             "name": "laravel/sail",
8646
             "name": "laravel/sail",
8647
-            "version": "v1.23.0",
8647
+            "version": "v1.23.1",
8648
             "source": {
8648
             "source": {
8649
                 "type": "git",
8649
                 "type": "git",
8650
                 "url": "https://github.com/laravel/sail.git",
8650
                 "url": "https://github.com/laravel/sail.git",
8651
-                "reference": "a2e046f748e87d3ef8b2b381e0e5c5a11f34e46b"
8651
+                "reference": "62582606f80466aa81fba40b193b289106902853"
8652
             },
8652
             },
8653
             "dist": {
8653
             "dist": {
8654
                 "type": "zip",
8654
                 "type": "zip",
8655
-                "url": "https://api.github.com/repos/laravel/sail/zipball/a2e046f748e87d3ef8b2b381e0e5c5a11f34e46b",
8656
-                "reference": "a2e046f748e87d3ef8b2b381e0e5c5a11f34e46b",
8655
+                "url": "https://api.github.com/repos/laravel/sail/zipball/62582606f80466aa81fba40b193b289106902853",
8656
+                "reference": "62582606f80466aa81fba40b193b289106902853",
8657
                 "shasum": ""
8657
                 "shasum": ""
8658
             },
8658
             },
8659
             "require": {
8659
             "require": {
8705
                 "issues": "https://github.com/laravel/sail/issues",
8705
                 "issues": "https://github.com/laravel/sail/issues",
8706
                 "source": "https://github.com/laravel/sail"
8706
                 "source": "https://github.com/laravel/sail"
8707
             },
8707
             },
8708
-            "time": "2023-06-16T21:20:12+00:00"
8708
+            "time": "2023-06-28T18:31:28+00:00"
8709
         },
8709
         },
8710
         {
8710
         {
8711
             "name": "mockery/mockery",
8711
             "name": "mockery/mockery",
9373
         },
9373
         },
9374
         {
9374
         {
9375
             "name": "phpunit/phpunit",
9375
             "name": "phpunit/phpunit",
9376
-            "version": "10.2.4",
9376
+            "version": "10.2.5",
9377
             "source": {
9377
             "source": {
9378
                 "type": "git",
9378
                 "type": "git",
9379
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
9379
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
9380
-                "reference": "68484779b5a2ed711fbdeba6ca01910d87acdff2"
9380
+                "reference": "15a89f123d8ca9c1e1598d6d87a56a8bf28c72cd"
9381
             },
9381
             },
9382
             "dist": {
9382
             "dist": {
9383
                 "type": "zip",
9383
                 "type": "zip",
9384
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/68484779b5a2ed711fbdeba6ca01910d87acdff2",
9385
-                "reference": "68484779b5a2ed711fbdeba6ca01910d87acdff2",
9384
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/15a89f123d8ca9c1e1598d6d87a56a8bf28c72cd",
9385
+                "reference": "15a89f123d8ca9c1e1598d6d87a56a8bf28c72cd",
9386
                 "shasum": ""
9386
                 "shasum": ""
9387
             },
9387
             },
9388
             "require": {
9388
             "require": {
9454
             "support": {
9454
             "support": {
9455
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
9455
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
9456
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
9456
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
9457
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.4"
9457
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/10.2.5"
9458
             },
9458
             },
9459
             "funding": [
9459
             "funding": [
9460
                 {
9460
                 {
9470
                     "type": "tidelift"
9470
                     "type": "tidelift"
9471
                 }
9471
                 }
9472
             ],
9472
             ],
9473
-            "time": "2023-07-10T04:06:08+00:00"
9473
+            "time": "2023-07-14T04:18:47+00:00"
9474
         },
9474
         },
9475
         {
9475
         {
9476
             "name": "psr/cache",
9476
             "name": "psr/cache",

+ 22
- 0
database/factories/DiscountFactory.php Целия файл

24
      */
24
      */
25
     public function definition(): array
25
     public function definition(): array
26
     {
26
     {
27
+        // Common fields
27
         $startDate = $this->faker->dateTimeBetween('now', '+1 year');
28
         $startDate = $this->faker->dateTimeBetween('now', '+1 year');
28
         $endDate = $this->faker->dateTimeBetween($startDate, strtotime('+1 year'));
29
         $endDate = $this->faker->dateTimeBetween($startDate, strtotime('+1 year'));
29
 
30
 
34
             'scope' => $this->faker->randomElement(Discount::getDiscountScopes()),
35
             'scope' => $this->faker->randomElement(Discount::getDiscountScopes()),
35
             'start_date' => $startDate,
36
             'start_date' => $startDate,
36
             'end_date' => $endDate,
37
             'end_date' => $endDate,
38
+            'enabled' => true,
37
         ];
39
         ];
38
     }
40
     }
41
+
42
+    public function salesDiscount(): self
43
+    {
44
+        return $this->state(function (array $attributes) {
45
+            return [
46
+                'name' => 'Summer Sale',
47
+                'type' => 'sales',
48
+            ];
49
+        });
50
+    }
51
+
52
+    public function purchaseDiscount(): self
53
+    {
54
+        return $this->state(function (array $attributes) {
55
+            return [
56
+                'name' => 'Bulk Purchase',
57
+                'type' => 'purchase',
58
+            ];
59
+        });
60
+    }
39
 }
61
 }

+ 60
- 0
database/factories/DocumentDefaultFactory.php Целия файл

1
+<?php
2
+
3
+namespace Database\Factories;
4
+
5
+use App\Models\Setting\DocumentDefault;
6
+use Illuminate\Database\Eloquent\Factories\Factory;
7
+
8
+/**
9
+ * @extends Factory<DocumentDefault>
10
+ */
11
+class DocumentDefaultFactory extends Factory
12
+{
13
+    /**
14
+     * The name of the factory's corresponding model.
15
+     *
16
+     * @var string
17
+     */
18
+    protected $model = DocumentDefault::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
+            'document_number_digits' => '5',
29
+            'document_number_next' => '1',
30
+            'payment_terms' => '30',
31
+            'template' => 'default',
32
+        ];
33
+    }
34
+
35
+    /**
36
+     * Indicate that the model's type is invoice.
37
+     */
38
+    public function invoice(): self
39
+    {
40
+        return $this->state(function (array $attributes) {
41
+            return [
42
+                'type' => 'invoice',
43
+                'document_number_prefix' => 'INV-',
44
+            ];
45
+        });
46
+    }
47
+
48
+    /**
49
+     * Indicate that the model's type is bill.
50
+     */
51
+    public function bill(): self
52
+    {
53
+        return $this->state(function (array $attributes) {
54
+            return [
55
+                'type' => 'bill',
56
+                'document_number_prefix' => 'BILL-',
57
+            ];
58
+        });
59
+    }
60
+}

+ 22
- 0
database/factories/TaxFactory.php Целия файл

24
      */
24
      */
25
     public function definition(): array
25
     public function definition(): array
26
     {
26
     {
27
+        // Common fields
27
         return [
28
         return [
28
             'description' => $this->faker->sentence,
29
             'description' => $this->faker->sentence,
29
             'rate' => $this->faker->randomFloat(4, 0, 20),
30
             'rate' => $this->faker->randomFloat(4, 0, 20),
30
             'computation' => $this->faker->randomElement(Tax::getComputationTypes()),
31
             'computation' => $this->faker->randomElement(Tax::getComputationTypes()),
31
             'scope' => $this->faker->randomElement(Tax::getTaxScopes()),
32
             'scope' => $this->faker->randomElement(Tax::getTaxScopes()),
33
+            'enabled' => true,
32
         ];
34
         ];
33
     }
35
     }
36
+
37
+    public function salesTax(): self
38
+    {
39
+        return $this->state(function (array $attributes) {
40
+            return [
41
+                'name' => 'State Sales Tax',
42
+                'type' => 'sales',
43
+            ];
44
+        });
45
+    }
46
+
47
+    public function purchaseTax(): self
48
+    {
49
+        return $this->state(function (array $attributes) {
50
+            return [
51
+                'name' => 'State Purchase Tax',
52
+                'type' => 'purchase',
53
+            ];
54
+        });
55
+    }
34
 }
56
 }

+ 2
- 2
database/migrations/2023_05_11_044321_create_accounts_table.php Целия файл

17
             $table->string('type')->default('checking');
17
             $table->string('type')->default('checking');
18
             $table->string('name', 100)->index();
18
             $table->string('name', 100)->index();
19
             $table->string('number', 20);
19
             $table->string('number', 20);
20
-            $table->string('currency_code')->default('USD');
20
+            $table->string('currency_code')->nullable();
21
             $table->double('opening_balance', 15, 4)->default(0.0000);
21
             $table->double('opening_balance', 15, 4)->default(0.0000);
22
             $table->string('description')->nullable();
22
             $table->string('description')->nullable();
23
             $table->text('notes')->nullable();
23
             $table->text('notes')->nullable();
36
             $table->timestamps();
36
             $table->timestamps();
37
 
37
 
38
             $table->unique(['company_id', 'number']);
38
             $table->unique(['company_id', 'number']);
39
-            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
39
+            $table->foreign('currency_code')->references('code')->on('currencies')->nullOnDelete();
40
         });
40
         });
41
     }
41
     }
42
 
42
 

+ 2
- 2
database/migrations/2023_05_19_042232_create_contacts_table.php Целия файл

26
             $table->string('state')->nullable();
26
             $table->string('state')->nullable();
27
             $table->string('country')->nullable();
27
             $table->string('country')->nullable();
28
             $table->string('website')->nullable();
28
             $table->string('website')->nullable();
29
-            $table->string('currency_code')->default('USD');
29
+            $table->string('currency_code')->nullable();
30
             $table->string('reference')->nullable();
30
             $table->string('reference')->nullable();
31
             $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
31
             $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
32
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
32
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
34
 
34
 
35
             $table->index(['company_id', 'type']);
35
             $table->index(['company_id', 'type']);
36
             $table->unique(['company_id', 'type', 'email']);
36
             $table->unique(['company_id', 'type', 'email']);
37
-            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
37
+            $table->foreign('currency_code')->references('code')->on('currencies')->nullOnDelete();
38
         });
38
         });
39
     }
39
     }
40
 
40
 

+ 39
- 0
database/migrations/2023_05_22_000100_create_document_defaults_table.php Целия файл

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_defaults', function (Blueprint $table) {
15
+            $table->id();
16
+            $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->string('type');
18
+            $table->string('document_number_prefix')->nullable();
19
+            $table->string('document_number_digits')->default(5);
20
+            $table->string('document_number_next')->default(1);
21
+            $table->string('payment_terms')->nullable();
22
+            $table->string('template')->default('default');
23
+            $table->string('title')->nullable();
24
+            $table->string('subheading')->nullable();
25
+            $table->text('notes')->nullable();
26
+            $table->text('terms')->nullable();
27
+            $table->string('footer')->nullable();
28
+            $table->timestamps();
29
+        });
30
+    }
31
+
32
+    /**
33
+     * Reverse the migrations.
34
+     */
35
+    public function down(): void
36
+    {
37
+        Schema::dropIfExists('document_defaults');
38
+    }
39
+};

+ 3
- 2
database/migrations/2023_05_23_141215_create_documents_table.php Целия файл

14
         Schema::create('documents', function (Blueprint $table) {
14
         Schema::create('documents', function (Blueprint $table) {
15
             $table->id();
15
             $table->id();
16
             $table->foreignId('company_id')->constrained()->cascadeOnDelete();
16
             $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->foreignId('document_default_id')->nullable()->constrained()->nullOnDelete();
17
             $table->string('type');
18
             $table->string('type');
18
             $table->string('document_number');
19
             $table->string('document_number');
19
             $table->string('order_number')->nullable();
20
             $table->string('order_number')->nullable();
25
             $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
26
             $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
26
             $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
27
             $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
27
             $table->string('reference')->nullable();
28
             $table->string('reference')->nullable();
28
-            $table->string('currency_code')->default('USD');
29
+            $table->string('currency_code')->nullable();
29
             $table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
30
             $table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
30
             $table->foreignId('contact_id')->nullable()->constrained()->nullOnDelete();
31
             $table->foreignId('contact_id')->nullable()->constrained()->nullOnDelete();
31
             $table->text('notes')->nullable();
32
             $table->text('notes')->nullable();
33
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
34
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
34
             $table->timestamps();
35
             $table->timestamps();
35
 
36
 
36
-            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
37
+            $table->foreign('currency_code')->references('code')->on('currencies')->nullOnDelete();
37
         });
38
         });
38
     }
39
     }
39
 
40
 

+ 9
- 9
database/migrations/2023_07_03_054805_create_default_settings_table.php Целия файл

14
         Schema::create('default_settings', function (Blueprint $table) {
14
         Schema::create('default_settings', function (Blueprint $table) {
15
             $table->id();
15
             $table->id();
16
             $table->foreignId('company_id')->constrained()->onDelete('cascade');
16
             $table->foreignId('company_id')->constrained()->onDelete('cascade');
17
-            $table->foreignId('account_id')->constrained('accounts')->restrictOnDelete();
18
-            $table->string('currency_code')->default('USD');
19
-            $table->foreignId('sales_tax_id')->constrained('taxes')->restrictOnDelete();
20
-            $table->foreignId('purchase_tax_id')->constrained('taxes')->restrictOnDelete();
21
-            $table->foreignId('sales_discount_id')->constrained('discounts')->restrictOnDelete();
22
-            $table->foreignId('purchase_discount_id')->constrained('discounts')->restrictOnDelete();
23
-            $table->foreignId('income_category_id')->constrained('categories')->restrictOnDelete();
24
-            $table->foreignId('expense_category_id')->constrained('categories')->restrictOnDelete();
17
+            $table->foreignId('account_id')->nullable()->constrained('accounts')->nullOnDelete();
18
+            $table->string('currency_code')->nullable();
19
+            $table->foreignId('sales_tax_id')->nullable()->constrained('taxes')->nullOnDelete();
20
+            $table->foreignId('purchase_tax_id')->nullable()->constrained('taxes')->nullOnDelete();
21
+            $table->foreignId('sales_discount_id')->nullable()->constrained('discounts')->nullOnDelete();
22
+            $table->foreignId('purchase_discount_id')->nullable()->constrained('discounts')->nullOnDelete();
23
+            $table->foreignId('income_category_id')->nullable()->constrained('categories')->nullOnDelete();
24
+            $table->foreignId('expense_category_id')->nullable()->constrained('categories')->nullOnDelete();
25
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
25
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
26
             $table->timestamps();
26
             $table->timestamps();
27
 
27
 
28
-            $table->foreign('currency_code')->references('code')->on('currencies')->restrictOnDelete();
28
+            $table->foreign('currency_code')->references('code')->on('currencies')->nullOnDelete();
29
         });
29
         });
30
     }
30
     }
31
 
31
 

+ 13
- 13
database/seeders/DatabaseSeeder.php Целия файл

62
         };
62
         };
63
 
63
 
64
         // Users for the first company (excluding the first company owner)
64
         // Users for the first company (excluding the first company owner)
65
-        $createUsers(1, 10, 0, 0.1);
65
+        $createUsers(1, 5, 0, 0.1);
66
 
66
 
67
         // Second company owner
67
         // Second company owner
68
         $secondCompanyOwner = User::factory()
68
         $secondCompanyOwner = User::factory()
76
         $secondCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.1)]);
76
         $secondCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.1)]);
77
 
77
 
78
         // Users for the second company (excluding the second company owner)
78
         // Users for the second company (excluding the second company owner)
79
-        $createUsers(2, 20, 0.1, 0.2);
79
+        $createUsers(2, 5, 0.1, 0.2);
80
 
80
 
81
         // Third company owner
81
         // Third company owner
82
         $thirdCompanyOwner = User::factory()
82
         $thirdCompanyOwner = User::factory()
90
         $thirdCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.2)]);
90
         $thirdCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.2)]);
91
 
91
 
92
         // Users for the third company (excluding the third company owner)
92
         // Users for the third company (excluding the third company owner)
93
-        $createUsers(3, 40, 0.2, 0.3);
93
+        $createUsers(3, 5, 0.2, 0.3);
94
 
94
 
95
         // Create employees for each company (each employee has a company)
95
         // Create employees for each company (each employee has a company)
96
-        $createUsers(4, 20, 0.3, 0.4);
97
-        $createUsers(5, 10, 0.4, 0.5);
98
-        $createUsers(6, 25, 0.5, 0.6);
99
-        $createUsers(7, 60, 0.6, 0.7);
100
-        $createUsers(8, 40, 0.7, 0.8);
101
-        $createUsers(9, 15, 0.8, 0.9);
102
-        $createUsers(10, 50, 0.9, 1);
96
+        $createUsers(4, 5, 0.3, 0.4);
97
+        $createUsers(5, 5, 0.4, 0.5);
98
+        $createUsers(6, 5, 0.5, 0.6);
99
+        $createUsers(7, 5, 0.6, 0.7);
100
+        $createUsers(8, 5, 0.7, 0.8);
101
+        $createUsers(9, 5, 0.8, 0.9);
102
+        $createUsers(10, 5, 0.9, 1);
103
 
103
 
104
         $this->call([
104
         $this->call([
105
-            CurrencySeeder::class,
105
+            //CurrencySeeder::class,
106
             CategorySeeder::class,
106
             CategorySeeder::class,
107
-            TaxSeeder::class,
108
-            DiscountSeeder::class,
107
+            //TaxSeeder::class,
108
+            //DiscountSeeder::class,
109
         ]);
109
         ]);
110
     }
110
     }
111
 }
111
 }

+ 3
- 0
resources/views/filament/pages/invoice.blade.php Целия файл

1
+<x-filament::page>
2
+    @livewire('invoice')
3
+</x-filament::page>

+ 3
- 0
resources/views/livewire/bill.blade.php Целия файл

1
+<div>
2
+    {{-- Success is as dangerous as failure. --}}
3
+</div>

+ 1
- 1
resources/views/livewire/default-setting.blade.php Целия файл

1
 <div>
1
 <div>
2
-    <form wire:submit.prevent="create">
2
+    <form wire:submit.prevent="save">
3
         {{ $this->form }}
3
         {{ $this->form }}
4
 
4
 
5
         <div class="mt-6">
5
         <div class="mt-6">

+ 13
- 0
resources/views/livewire/invoice.blade.php Целия файл

1
+<div>
2
+    <form wire:submit.prevent="save">
3
+        {{ $this->form }}
4
+
5
+        <div class="mt-6">
6
+            <div class="flex flex-wrap items-center gap-4 justify-start">
7
+                <x-filament::button type="submit">
8
+                    {{ __('Save Changes') }}
9
+                </x-filament::button>
10
+            </div>
11
+        </div>
12
+    </form>
13
+</div>

Loading…
Отказ
Запис