Explorar el Código

wip: Accounting Module

3.x
wallo hace 2 años
padre
commit
81ae0cec47
Se han modificado 41 ficheros con 807 adiciones y 191 borrados
  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 Ver fichero

@@ -8,22 +8,13 @@ class DefaultSetting extends Page
8 8
 {
9 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 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 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 Ver fichero

@@ -0,0 +1,20 @@
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 Ver fichero

@@ -32,12 +32,6 @@ class AccountResource extends Resource
32 32
 
33 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 35
     public static function form(Form $form): Form
42 36
     {
43 37
         return $form
@@ -78,7 +72,7 @@ class AccountResource extends Resource
78 72
                             ->schema([
79 73
                                 Forms\Components\Select::make('currency_code')
80 74
                                     ->label('Currency')
81
-                                    ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
75
+                                    ->relationship('currency', 'name')
82 76
                                     ->preload()
83 77
                                     ->default(Currency::getDefaultCurrency())
84 78
                                     ->searchable()

+ 0
- 6
app/Filament/Resources/CategoryResource.php Ver fichero

@@ -25,12 +25,6 @@ class CategoryResource extends Resource
25 25
 
26 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 28
     public static function form(Form $form): Form
35 29
     {
36 30
         return $form

+ 0
- 6
app/Filament/Resources/CurrencyResource.php Ver fichero

@@ -28,12 +28,6 @@ class CurrencyResource extends Resource
28 28
 
29 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 31
     public static function form(Form $form): Form
38 32
     {
39 33
         return $form

+ 2
- 4
app/Filament/Resources/CustomerResource.php Ver fichero

@@ -32,9 +32,7 @@ class CustomerResource extends Resource
32 32
 
33 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 38
     public static function form(Form $form): Form
@@ -89,7 +87,7 @@ class CustomerResource extends Resource
89 87
                             ->nullable(),
90 88
                         Forms\Components\Select::make('currency_code')
91 89
                             ->label('Currency')
92
-                            ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
90
+                            ->relationship('currency', 'name')
93 91
                             ->preload()
94 92
                             ->default(Currency::getDefaultCurrency())
95 93
                             ->searchable()

+ 0
- 6
app/Filament/Resources/DiscountResource.php Ver fichero

@@ -23,12 +23,6 @@ class DiscountResource extends Resource
23 23
 
24 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 26
     public static function form(Form $form): Form
33 27
     {
34 28
         return $form

+ 3
- 5
app/Filament/Resources/InvoiceResource.php Ver fichero

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

+ 0
- 6
app/Filament/Resources/TaxResource.php Ver fichero

@@ -26,12 +26,6 @@ class TaxResource extends Resource
26 26
 
27 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 29
     public static function form(Form $form): Form
36 30
     {
37 31
         return $form

+ 13
- 0
app/Http/Livewire/Bill.php Ver fichero

@@ -0,0 +1,13 @@
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 Ver fichero

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

+ 162
- 0
app/Http/Livewire/Invoice.php Ver fichero

@@ -0,0 +1,162 @@
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 Ver fichero

@@ -2,6 +2,7 @@
2 2
 
3 3
 namespace App\Models\Banking;
4 4
 
5
+use App\Scopes\CurrentCompanyScope;
5 6
 use App\Models\Setting\Currency;
6 7
 use App\Models\Setting\DefaultSetting;
7 8
 use Database\Factories\AccountFactory;
@@ -10,6 +11,7 @@ use Illuminate\Database\Eloquent\Factories\HasFactory;
10 11
 use Illuminate\Database\Eloquent\Model;
11 12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12 13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
13 15
 use Illuminate\Support\Facades\Auth;
14 16
 use Illuminate\Support\Facades\Config;
15 17
 use Spatie\Tags\HasTags;
@@ -49,11 +51,21 @@ class Account extends Model
49 51
         'enabled' => 'boolean',
50 52
     ];
51 53
 
54
+    protected static function booted(): void
55
+    {
56
+        static::addGlobalScope(new CurrentCompanyScope);
57
+    }
58
+
52 59
     public function company(): BelongsTo
53 60
     {
54 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 69
     public function owner(): BelongsTo
58 70
     {
59 71
         return $this->company->owner;

+ 7
- 0
app/Models/Company.php Ver fichero

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

+ 6
- 6
app/Models/Contact.php Ver fichero

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

+ 7
- 0
app/Models/Document/Document.php Ver fichero

@@ -6,6 +6,7 @@ use App\Models\Contact;
6 6
 use App\Models\Setting\Category;
7 7
 use App\Models\Setting\Currency;
8 8
 use App\Models\Setting\Discount;
9
+use App\Models\Setting\DocumentDefault;
9 10
 use App\Models\Setting\Tax;
10 11
 use Database\Factories\DocumentFactory;
11 12
 use Illuminate\Database\Eloquent\Factories\Factory;
@@ -24,6 +25,7 @@ class Document extends Model
24 25
 
25 26
     protected $fillable = [
26 27
         'company_id',
28
+        'document_default_id',
27 29
         'type',
28 30
         'document_number',
29 31
         'order_number',
@@ -54,6 +56,11 @@ class Document extends Model
54 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 64
     public function createdBy(): BelongsTo
58 65
     {
59 66
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 17
- 0
app/Models/Setting/Category.php Ver fichero

@@ -4,12 +4,14 @@ namespace App\Models\Setting;
4 4
 
5 5
 use App\Models\Document\Document;
6 6
 use App\Models\Item;
7
+use App\Scopes\CurrentCompanyScope;
7 8
 use Database\Factories\CategoryFactory;
8 9
 use Illuminate\Database\Eloquent\Factories\Factory;
9 10
 use Illuminate\Database\Eloquent\Factories\HasFactory;
10 11
 use Illuminate\Database\Eloquent\Model;
11 12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
12 13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
13 15
 use Wallo\FilamentCompanies\FilamentCompanies;
14 16
 
15 17
 class Category extends Model
@@ -32,6 +34,11 @@ class Category extends Model
32 34
         'enabled' => 'boolean',
33 35
     ];
34 36
 
37
+    protected static function booted(): void
38
+    {
39
+        static::addGlobalScope(new CurrentCompanyScope);
40
+    }
41
+
35 42
     public static function getCategoryTypes(): array
36 43
     {
37 44
         return [
@@ -47,6 +54,16 @@ class Category extends Model
47 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 67
     public function createdBy(): BelongsTo
51 68
     {
52 69
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 15
- 6
app/Models/Setting/Currency.php Ver fichero

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

+ 28
- 37
app/Models/Setting/DefaultSetting.php Ver fichero

@@ -3,10 +3,10 @@
3 3
 namespace App\Models\Setting;
4 4
 
5 5
 use App\Models\Banking\Account;
6
+use App\Scopes\CurrentCompanyScope;
6 7
 use Illuminate\Database\Eloquent\Factories\HasFactory;
7 8
 use Illuminate\Database\Eloquent\Model;
8 9
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
9
-use Illuminate\Support\Facades\Auth;
10 10
 use Wallo\FilamentCompanies\FilamentCompanies;
11 11
 
12 12
 class DefaultSetting extends Model
@@ -28,6 +28,11 @@ class DefaultSetting extends Model
28 28
         'updated_by',
29 29
     ];
30 30
 
31
+    protected static function booted(): void
32
+    {
33
+        static::addGlobalScope(new CurrentCompanyScope);
34
+    }
35
+
31 36
     public function company(): BelongsTo
32 37
     {
33 38
         return $this->belongsTo(FilamentCompanies::companyModel(), 'company_id');
@@ -45,32 +50,38 @@ class DefaultSetting extends Model
45 50
 
46 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 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 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 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 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 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 87
     public function updatedBy(): BelongsTo
@@ -80,80 +91,66 @@ class DefaultSetting extends Model
80 91
 
81 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 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 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 105
             ->pluck('name', 'id')
100 106
             ->toArray();
101 107
     }
102 108
 
103 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 112
             ->pluck('name', 'id')
108 113
             ->toArray();
109 114
     }
110 115
 
111 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 119
             ->pluck('name', 'id')
116 120
             ->toArray();
117 121
     }
118 122
 
119 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 126
             ->pluck('name', 'id')
124 127
             ->toArray();
125 128
     }
126 129
 
127 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 133
             ->pluck('name', 'id')
132 134
             ->toArray();
133 135
     }
134 136
 
135 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 140
             ->pluck('name', 'id')
140 141
             ->toArray();
141 142
     }
142 143
 
143 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 148
         return $defaultAccount->id ?? null;
150 149
     }
151 150
 
152 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 155
         return $defaultCurrency->code ?? null;
159 156
     }
@@ -161,7 +158,6 @@ class DefaultSetting extends Model
161 158
     public static function getDefaultSalesTax()
162 159
     {
163 160
         $defaultSalesTax = Tax::where('enabled', true)
164
-            ->where('company_id', Auth::user()->currentCompany->id)
165 161
             ->where('type', 'sales')
166 162
             ->first();
167 163
 
@@ -171,7 +167,6 @@ class DefaultSetting extends Model
171 167
     public static function getDefaultPurchaseTax()
172 168
     {
173 169
         $defaultPurchaseTax = Tax::where('enabled', true)
174
-            ->where('company_id', Auth::user()->currentCompany->id)
175 170
             ->where('type', 'purchase')
176 171
             ->first();
177 172
 
@@ -181,7 +176,6 @@ class DefaultSetting extends Model
181 176
     public static function getDefaultSalesDiscount()
182 177
     {
183 178
         $defaultSalesDiscount = Discount::where('enabled', true)
184
-            ->where('company_id', Auth::user()->currentCompany->id)
185 179
             ->where('type', 'sales')
186 180
             ->first();
187 181
 
@@ -191,7 +185,6 @@ class DefaultSetting extends Model
191 185
     public static function getDefaultPurchaseDiscount()
192 186
     {
193 187
         $defaultPurchaseDiscount = Discount::where('enabled', true)
194
-            ->where('company_id', Auth::user()->currentCompany->id)
195 188
             ->where('type', 'purchase')
196 189
             ->first();
197 190
 
@@ -201,7 +194,6 @@ class DefaultSetting extends Model
201 194
     public static function getDefaultIncomeCategory()
202 195
     {
203 196
         $defaultIncomeCategory = Category::where('enabled', true)
204
-            ->where('company_id', Auth::user()->currentCompany->id)
205 197
             ->where('type', 'income')
206 198
             ->first();
207 199
 
@@ -211,7 +203,6 @@ class DefaultSetting extends Model
211 203
     public static function getDefaultExpenseCategory()
212 204
     {
213 205
         $defaultExpenseCategory = Category::where('enabled', true)
214
-            ->where('company_id', Auth::user()->currentCompany->id)
215 206
             ->where('type', 'expense')
216 207
             ->first();
217 208
 

+ 17
- 1
app/Models/Setting/Discount.php Ver fichero

@@ -4,13 +4,14 @@ namespace App\Models\Setting;
4 4
 
5 5
 use App\Models\Document\DocumentItem;
6 6
 use App\Models\Item;
7
-use App\Models\User;
7
+use App\Scopes\CurrentCompanyScope;
8 8
 use Database\Factories\DiscountFactory;
9 9
 use Illuminate\Database\Eloquent\Factories\Factory;
10 10
 use Illuminate\Database\Eloquent\Factories\HasFactory;
11 11
 use Illuminate\Database\Eloquent\Model;
12 12
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
13 13
 use Illuminate\Database\Eloquent\Relations\HasMany;
14
+use Illuminate\Database\Eloquent\Relations\HasOne;
14 15
 use Wallo\FilamentCompanies\FilamentCompanies;
15 16
 
16 17
 class Discount extends Model
@@ -40,11 +41,26 @@ class Discount extends Model
40 41
         'end_date' => 'datetime',
41 42
     ];
42 43
 
44
+    protected static function booted(): void
45
+    {
46
+        static::addGlobalScope(new CurrentCompanyScope);
47
+    }
48
+
43 49
     public function company(): BelongsTo
44 50
     {
45 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 64
     public function createdBy(): BelongsTo
49 65
     {
50 66
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 108
- 0
app/Models/Setting/DocumentDefault.php Ver fichero

@@ -0,0 +1,108 @@
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 Ver fichero

@@ -5,13 +5,14 @@ namespace App\Models\Setting;
5 5
 use App\Models\Company;
6 6
 use App\Models\Document\DocumentItem;
7 7
 use App\Models\Item;
8
-use App\Models\User;
8
+use App\Scopes\CurrentCompanyScope;
9 9
 use Database\Factories\TaxFactory;
10 10
 use Illuminate\Database\Eloquent\Factories\Factory;
11 11
 use Illuminate\Database\Eloquent\Factories\HasFactory;
12 12
 use Illuminate\Database\Eloquent\Model;
13 13
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
14 14
 use Illuminate\Database\Eloquent\Relations\HasMany;
15
+use Illuminate\Database\Eloquent\Relations\HasOne;
15 16
 use Wallo\FilamentCompanies\FilamentCompanies;
16 17
 
17 18
 class Tax extends Model
@@ -37,11 +38,26 @@ class Tax extends Model
37 38
         'enabled' => 'boolean',
38 39
     ];
39 40
 
41
+    protected static function booted(): void
42
+    {
43
+        static::addGlobalScope(new CurrentCompanyScope);
44
+    }
45
+
40 46
     public function company(): BelongsTo
41 47
     {
42 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 61
     public function createdBy(): BelongsTo
46 62
     {
47 63
         return $this->belongsTo(FilamentCompanies::userModel(), 'created_by');

+ 85
- 0
app/Observers/CompanyObserver.php Ver fichero

@@ -0,0 +1,85 @@
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 Ver fichero

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

+ 9
- 1
app/Providers/EventServiceProvider.php Ver fichero

@@ -2,10 +2,11 @@
2 2
 
3 3
 namespace App\Providers;
4 4
 
5
+use App\Observers\CompanyObserver;
6
+use App\Models\Company;
5 7
 use Illuminate\Auth\Events\Registered;
6 8
 use Illuminate\Auth\Listeners\SendEmailVerificationNotification;
7 9
 use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
8
-use Illuminate\Support\Facades\Event;
9 10
 
10 11
 class EventServiceProvider extends ServiceProvider
11 12
 {
@@ -35,4 +36,11 @@ class EventServiceProvider extends ServiceProvider
35 36
     {
36 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 Ver fichero

@@ -0,0 +1,21 @@
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 Ver fichero

@@ -28,23 +28,17 @@ class CurrencyService
28 28
 
29 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 31
         $cacheKey = 'currency_data_' . $defaultCurrencyCode . '_' . $code;
33 32
 
34
-        // Attempt to retrieve the cached exchange rate
35 33
         $cachedData = Cache::get($cacheKey);
36 34
 
37
-        // If the cached exchange rate exists, return it
38 35
         if ($cachedData !== null) {
39 36
             return $cachedData['rate'];
40 37
         }
41 38
 
42
-        // If the cached exchange rate does not exist, retrieve it from the API
43 39
         $rate = $this->getExchangeRate($defaultCurrencyCode, $code);
44 40
 
45
-        // If the API call was successful, cache the exchange rate
46 41
         if ($rate !== null) {
47
-            // Store the exchange rate in the cache for 24 hours
48 42
             $dataToCache = compact('rate');
49 43
             $expirationTimeInSeconds = 60 * 60 * 24; // 24 hours
50 44
             Cache::put($cacheKey, $dataToCache, $expirationTimeInSeconds);

+ 41
- 41
composer.lock Ver fichero

@@ -1992,16 +1992,16 @@
1992 1992
         },
1993 1993
         {
1994 1994
             "name": "laravel/framework",
1995
-            "version": "v10.14.1",
1995
+            "version": "v10.15.0",
1996 1996
             "source": {
1997 1997
                 "type": "git",
1998 1998
                 "url": "https://github.com/laravel/framework.git",
1999
-                "reference": "6f89a2b74b232d8bf2e1d9ed87e311841263dfcb"
1999
+                "reference": "c7599dc92e04532824bafbd226c2936ce6a905b8"
2000 2000
             },
2001 2001
             "dist": {
2002 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 2005
                 "shasum": ""
2006 2006
             },
2007 2007
             "require": {
@@ -2188,7 +2188,7 @@
2188 2188
                 "issues": "https://github.com/laravel/framework/issues",
2189 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 2194
             "name": "laravel/sanctum",
@@ -2318,16 +2318,16 @@
2318 2318
         },
2319 2319
         {
2320 2320
             "name": "laravel/socialite",
2321
-            "version": "v5.6.3",
2321
+            "version": "v5.8.0",
2322 2322
             "source": {
2323 2323
                 "type": "git",
2324 2324
                 "url": "https://github.com/laravel/socialite.git",
2325
-                "reference": "00ea7f8630673ea49304fc8a9fca5a64eb838c7e"
2325
+                "reference": "50148edf24b6cd3e428aa9bc06a5d915b24376bb"
2326 2326
             },
2327 2327
             "dist": {
2328 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 2331
                 "shasum": ""
2332 2332
             },
2333 2333
             "require": {
@@ -2384,7 +2384,7 @@
2384 2384
                 "issues": "https://github.com/laravel/socialite/issues",
2385 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 2390
             "name": "laravel/tinker",
@@ -5110,7 +5110,7 @@
5110 5110
         },
5111 5111
         {
5112 5112
             "name": "squirephp/countries",
5113
-            "version": "v3.4.1",
5113
+            "version": "v3.4.2",
5114 5114
             "source": {
5115 5115
                 "type": "git",
5116 5116
                 "url": "https://github.com/squirephp/countries.git",
@@ -5164,7 +5164,7 @@
5164 5164
         },
5165 5165
         {
5166 5166
             "name": "squirephp/countries-en",
5167
-            "version": "v3.4.1",
5167
+            "version": "v3.4.2",
5168 5168
             "source": {
5169 5169
                 "type": "git",
5170 5170
                 "url": "https://github.com/squirephp/countries-en.git",
@@ -5218,7 +5218,7 @@
5218 5218
         },
5219 5219
         {
5220 5220
             "name": "squirephp/model",
5221
-            "version": "v3.4.1",
5221
+            "version": "v3.4.2",
5222 5222
             "source": {
5223 5223
                 "type": "git",
5224 5224
                 "url": "https://github.com/squirephp/model.git",
@@ -5272,7 +5272,7 @@
5272 5272
         },
5273 5273
         {
5274 5274
             "name": "squirephp/regions",
5275
-            "version": "v3.4.1",
5275
+            "version": "v3.4.2",
5276 5276
             "source": {
5277 5277
                 "type": "git",
5278 5278
                 "url": "https://github.com/squirephp/regions.git",
@@ -5326,7 +5326,7 @@
5326 5326
         },
5327 5327
         {
5328 5328
             "name": "squirephp/regions-en",
5329
-            "version": "v3.4.1",
5329
+            "version": "v3.4.2",
5330 5330
             "source": {
5331 5331
                 "type": "git",
5332 5332
                 "url": "https://github.com/squirephp/regions-en.git",
@@ -5380,7 +5380,7 @@
5380 5380
         },
5381 5381
         {
5382 5382
             "name": "squirephp/repository",
5383
-            "version": "v3.4.1",
5383
+            "version": "v3.4.2",
5384 5384
             "source": {
5385 5385
                 "type": "git",
5386 5386
                 "url": "https://github.com/squirephp/repository.git",
@@ -5435,7 +5435,7 @@
5435 5435
         },
5436 5436
         {
5437 5437
             "name": "squirephp/rule",
5438
-            "version": "v3.4.1",
5438
+            "version": "v3.4.2",
5439 5439
             "source": {
5440 5440
                 "type": "git",
5441 5441
                 "url": "https://github.com/squirephp/rule.git",
@@ -8456,16 +8456,16 @@
8456 8456
         },
8457 8457
         {
8458 8458
             "name": "filp/whoops",
8459
-            "version": "2.15.2",
8459
+            "version": "2.15.3",
8460 8460
             "source": {
8461 8461
                 "type": "git",
8462 8462
                 "url": "https://github.com/filp/whoops.git",
8463
-                "reference": "aac9304c5ed61bf7b1b7a6064bf9806ab842ce73"
8463
+                "reference": "c83e88a30524f9360b11f585f71e6b17313b7187"
8464 8464
             },
8465 8465
             "dist": {
8466 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 8469
                 "shasum": ""
8470 8470
             },
8471 8471
             "require": {
@@ -8515,7 +8515,7 @@
8515 8515
             ],
8516 8516
             "support": {
8517 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 8520
             "funding": [
8521 8521
                 {
@@ -8523,7 +8523,7 @@
8523 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 8529
             "name": "hamcrest/hamcrest-php",
@@ -8578,16 +8578,16 @@
8578 8578
         },
8579 8579
         {
8580 8580
             "name": "laravel/pint",
8581
-            "version": "v1.10.3",
8581
+            "version": "v1.10.5",
8582 8582
             "source": {
8583 8583
                 "type": "git",
8584 8584
                 "url": "https://github.com/laravel/pint.git",
8585
-                "reference": "c472786bca01e4812a9bb7933b23edfc5b6877b7"
8585
+                "reference": "a458fb057bfa2f5a09888a8aa349610be807b0c3"
8586 8586
             },
8587 8587
             "dist": {
8588 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 8591
                 "shasum": ""
8592 8592
             },
8593 8593
             "require": {
@@ -8598,9 +8598,9 @@
8598 8598
                 "php": "^8.1.0"
8599 8599
             },
8600 8600
             "require-dev": {
8601
-                "friendsofphp/php-cs-fixer": "^3.18.0",
8601
+                "friendsofphp/php-cs-fixer": "^3.21.1",
8602 8602
                 "illuminate/view": "^10.5.1",
8603
-                "laravel-zero/framework": "^10.0.2",
8603
+                "laravel-zero/framework": "^10.1.1",
8604 8604
                 "mockery/mockery": "^1.5.1",
8605 8605
                 "nunomaduro/larastan": "^2.5.1",
8606 8606
                 "nunomaduro/termwind": "^1.15.1",
@@ -8640,20 +8640,20 @@
8640 8640
                 "issues": "https://github.com/laravel/pint/issues",
8641 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 8646
             "name": "laravel/sail",
8647
-            "version": "v1.23.0",
8647
+            "version": "v1.23.1",
8648 8648
             "source": {
8649 8649
                 "type": "git",
8650 8650
                 "url": "https://github.com/laravel/sail.git",
8651
-                "reference": "a2e046f748e87d3ef8b2b381e0e5c5a11f34e46b"
8651
+                "reference": "62582606f80466aa81fba40b193b289106902853"
8652 8652
             },
8653 8653
             "dist": {
8654 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 8657
                 "shasum": ""
8658 8658
             },
8659 8659
             "require": {
@@ -8705,7 +8705,7 @@
8705 8705
                 "issues": "https://github.com/laravel/sail/issues",
8706 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 8711
             "name": "mockery/mockery",
@@ -9373,16 +9373,16 @@
9373 9373
         },
9374 9374
         {
9375 9375
             "name": "phpunit/phpunit",
9376
-            "version": "10.2.4",
9376
+            "version": "10.2.5",
9377 9377
             "source": {
9378 9378
                 "type": "git",
9379 9379
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
9380
-                "reference": "68484779b5a2ed711fbdeba6ca01910d87acdff2"
9380
+                "reference": "15a89f123d8ca9c1e1598d6d87a56a8bf28c72cd"
9381 9381
             },
9382 9382
             "dist": {
9383 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 9386
                 "shasum": ""
9387 9387
             },
9388 9388
             "require": {
@@ -9454,7 +9454,7 @@
9454 9454
             "support": {
9455 9455
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
9456 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 9459
             "funding": [
9460 9460
                 {
@@ -9470,7 +9470,7 @@
9470 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 9476
             "name": "psr/cache",

+ 22
- 0
database/factories/DiscountFactory.php Ver fichero

@@ -24,6 +24,7 @@ class DiscountFactory extends Factory
24 24
      */
25 25
     public function definition(): array
26 26
     {
27
+        // Common fields
27 28
         $startDate = $this->faker->dateTimeBetween('now', '+1 year');
28 29
         $endDate = $this->faker->dateTimeBetween($startDate, strtotime('+1 year'));
29 30
 
@@ -34,6 +35,27 @@ class DiscountFactory extends Factory
34 35
             'scope' => $this->faker->randomElement(Discount::getDiscountScopes()),
35 36
             'start_date' => $startDate,
36 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 Ver fichero

@@ -0,0 +1,60 @@
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 Ver fichero

@@ -24,11 +24,33 @@ class TaxFactory extends Factory
24 24
      */
25 25
     public function definition(): array
26 26
     {
27
+        // Common fields
27 28
         return [
28 29
             'description' => $this->faker->sentence,
29 30
             'rate' => $this->faker->randomFloat(4, 0, 20),
30 31
             'computation' => $this->faker->randomElement(Tax::getComputationTypes()),
31 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 Ver fichero

@@ -17,7 +17,7 @@ return new class extends Migration
17 17
             $table->string('type')->default('checking');
18 18
             $table->string('name', 100)->index();
19 19
             $table->string('number', 20);
20
-            $table->string('currency_code')->default('USD');
20
+            $table->string('currency_code')->nullable();
21 21
             $table->double('opening_balance', 15, 4)->default(0.0000);
22 22
             $table->string('description')->nullable();
23 23
             $table->text('notes')->nullable();
@@ -36,7 +36,7 @@ return new class extends Migration
36 36
             $table->timestamps();
37 37
 
38 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 Ver fichero

@@ -26,7 +26,7 @@ return new class extends Migration
26 26
             $table->string('state')->nullable();
27 27
             $table->string('country')->nullable();
28 28
             $table->string('website')->nullable();
29
-            $table->string('currency_code')->default('USD');
29
+            $table->string('currency_code')->nullable();
30 30
             $table->string('reference')->nullable();
31 31
             $table->foreignId('created_by')->nullable()->constrained('users')->nullOnDelete();
32 32
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
@@ -34,7 +34,7 @@ return new class extends Migration
34 34
 
35 35
             $table->index(['company_id', 'type']);
36 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 Ver fichero

@@ -0,0 +1,39 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+return new class extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     */
12
+    public function up(): void
13
+    {
14
+        Schema::create('document_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 Ver fichero

@@ -14,6 +14,7 @@ return new class extends Migration
14 14
         Schema::create('documents', function (Blueprint $table) {
15 15
             $table->id();
16 16
             $table->foreignId('company_id')->constrained()->cascadeOnDelete();
17
+            $table->foreignId('document_default_id')->nullable()->constrained()->nullOnDelete();
17 18
             $table->string('type');
18 19
             $table->string('document_number');
19 20
             $table->string('order_number')->nullable();
@@ -25,7 +26,7 @@ return new class extends Migration
25 26
             $table->foreignId('tax_id')->nullable()->constrained()->nullOnDelete();
26 27
             $table->foreignId('discount_id')->nullable()->constrained()->nullOnDelete();
27 28
             $table->string('reference')->nullable();
28
-            $table->string('currency_code')->default('USD');
29
+            $table->string('currency_code')->nullable();
29 30
             $table->foreignId('category_id')->nullable()->constrained()->nullOnDelete();
30 31
             $table->foreignId('contact_id')->nullable()->constrained()->nullOnDelete();
31 32
             $table->text('notes')->nullable();
@@ -33,7 +34,7 @@ return new class extends Migration
33 34
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
34 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 Ver fichero

@@ -14,18 +14,18 @@ return new class extends Migration
14 14
         Schema::create('default_settings', function (Blueprint $table) {
15 15
             $table->id();
16 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 25
             $table->foreignId('updated_by')->nullable()->constrained('users')->nullOnDelete();
26 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 Ver fichero

@@ -62,7 +62,7 @@ class DatabaseSeeder extends Seeder
62 62
         };
63 63
 
64 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 67
         // Second company owner
68 68
         $secondCompanyOwner = User::factory()
@@ -76,7 +76,7 @@ class DatabaseSeeder extends Seeder
76 76
         $secondCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.1)]);
77 77
 
78 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 81
         // Third company owner
82 82
         $thirdCompanyOwner = User::factory()
@@ -90,22 +90,22 @@ class DatabaseSeeder extends Seeder
90 90
         $thirdCompanyOwner->ownedCompanies->first()->update(['created_at' => $startDate->addMinutes($endDate->diffInMinutes($startDate) * 0.2)]);
91 91
 
92 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 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 104
         $this->call([
105
-            CurrencySeeder::class,
105
+            //CurrencySeeder::class,
106 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 Ver fichero

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

+ 3
- 0
resources/views/livewire/bill.blade.php Ver fichero

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

+ 1
- 1
resources/views/livewire/default-setting.blade.php Ver fichero

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

+ 13
- 0
resources/views/livewire/invoice.blade.php Ver fichero

@@ -0,0 +1,13 @@
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…
Cancelar
Guardar