Bläddra i källkod

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

Development 3.x
3.x
Andrew Wallo 5 månader sedan
förälder
incheckning
332c3e32f8
Inget konto är kopplat till bidragsgivarens mejladress

+ 1
- 0
app/Filament/Company/Clusters/Settings/Pages/CompanyProfile.php Visa fil

@@ -201,6 +201,7 @@ class CompanyProfile extends Page
201 201
                 Hidden::make('type')
202 202
                     ->default('general'),
203 203
                 AddressFields::make()
204
+                    ->required()
204 205
                     ->softRequired()
205 206
                     ->disabledCountry(is_demo_environment()),
206 207
             ])

+ 95
- 80
app/Filament/Company/Resources/Common/OfferingResource.php Visa fil

@@ -56,92 +56,107 @@ class OfferingResource extends Resource
56 56
 
57 57
                         return new HtmlString($output);
58 58
                     }),
59
-                Forms\Components\Section::make('General')
60
-                    ->schema([
61
-                        RadioDeck::make('type')
62
-                            ->options(OfferingType::class)
63
-                            ->default(OfferingType::Product)
64
-                            ->icons(OfferingType::class)
65
-                            ->color('primary')
66
-                            ->columns()
67
-                            ->required(),
68
-                        Forms\Components\TextInput::make('name')
69
-                            ->autofocus()
70
-                            ->required()
71
-                            ->columnStart(1)
72
-                            ->maxLength(255),
73
-                        Forms\Components\TextInput::make('price')
74
-                            ->required()
75
-                            ->money(),
76
-                        Forms\Components\Textarea::make('description')
77
-                            ->label('Description')
78
-                            ->columnSpan(2)
79
-                            ->rows(3),
80
-                        Forms\Components\CheckboxList::make('attributes')
81
-                            ->options([
82
-                                'Sellable' => 'Sellable',
83
-                                'Purchasable' => 'Purchasable',
84
-                            ])
85
-                            ->hiddenLabel()
86
-                            ->required()
87
-                            ->live()
88
-                            ->bulkToggleable()
89
-                            ->validationMessages([
90
-                                'required' => 'The offering must be either sellable or purchasable.',
91
-                            ]),
92
-                    ])->columns(),
59
+                static::getGeneralSection(),
93 60
                 // Sellable Section
94
-                Forms\Components\Section::make('Sale Information')
95
-                    ->schema([
96
-                        CreateAccountSelect::make('income_account_id')
97
-                            ->label('Income account')
98
-                            ->category(AccountCategory::Revenue)
99
-                            ->type(AccountType::OperatingRevenue)
100
-                            ->required()
101
-                            ->validationMessages([
102
-                                'required' => 'The income account is required for sellable offerings.',
103
-                            ]),
104
-                        CreateAdjustmentSelect::make('salesTaxes')
105
-                            ->label('Sales tax')
106
-                            ->category(AdjustmentCategory::Tax)
107
-                            ->type(AdjustmentType::Sales)
108
-                            ->multiple(),
109
-                        CreateAdjustmentSelect::make('salesDiscounts')
110
-                            ->label('Sales discount')
111
-                            ->category(AdjustmentCategory::Discount)
112
-                            ->type(AdjustmentType::Sales)
113
-                            ->multiple(),
114
-                    ])
115
-                    ->columns()
116
-                    ->visible(static fn (Forms\Get $get) => in_array('Sellable', $get('attributes') ?? [])),
117
-
61
+                static::getSellableSection(),
118 62
                 // Purchasable Section
119
-                Forms\Components\Section::make('Purchase Information')
120
-                    ->schema([
121
-                        CreateAccountSelect::make('expense_account_id')
122
-                            ->label('Expense account')
123
-                            ->category(AccountCategory::Expense)
124
-                            ->type(AccountType::OperatingExpense)
125
-                            ->required()
126
-                            ->validationMessages([
127
-                                'required' => 'The expense account is required for purchasable offerings.',
128
-                            ]),
129
-                        CreateAdjustmentSelect::make('purchaseTaxes')
130
-                            ->label('Purchase tax')
131
-                            ->category(AdjustmentCategory::Tax)
132
-                            ->type(AdjustmentType::Purchase)
133
-                            ->multiple(),
134
-                        CreateAdjustmentSelect::make('purchaseDiscounts')
135
-                            ->label('Purchase discount')
136
-                            ->category(AdjustmentCategory::Discount)
137
-                            ->type(AdjustmentType::Purchase)
138
-                            ->multiple(),
139
-                    ])
63
+                static::getPurchasableSection(),
64
+            ])->columns();
65
+    }
66
+
67
+    public static function getGeneralSection(bool $hasAttributeChoices = true): Forms\Components\Section
68
+    {
69
+        return Forms\Components\Section::make('General')
70
+            ->schema([
71
+                RadioDeck::make('type')
72
+                    ->options(OfferingType::class)
73
+                    ->default(OfferingType::Product)
74
+                    ->icons(OfferingType::class)
75
+                    ->color('primary')
140 76
                     ->columns()
141
-                    ->visible(static fn (Forms\Get $get) => in_array('Purchasable', $get('attributes') ?? [])),
77
+                    ->required(),
78
+                Forms\Components\TextInput::make('name')
79
+                    ->autofocus()
80
+                    ->required()
81
+                    ->columnStart(1)
82
+                    ->maxLength(255),
83
+                Forms\Components\TextInput::make('price')
84
+                    ->required()
85
+                    ->money(),
86
+                Forms\Components\Textarea::make('description')
87
+                    ->label('Description')
88
+                    ->columnSpan(2)
89
+                    ->rows(3),
90
+                Forms\Components\CheckboxList::make('attributes')
91
+                    ->options([
92
+                        'Sellable' => 'Sellable',
93
+                        'Purchasable' => 'Purchasable',
94
+                    ])
95
+                    ->visible($hasAttributeChoices)
96
+                    ->hiddenLabel()
97
+                    ->required()
98
+                    ->live()
99
+                    ->bulkToggleable()
100
+                    ->validationMessages([
101
+                        'required' => 'The offering must be either sellable or purchasable.',
102
+                    ]),
142 103
             ])->columns();
143 104
     }
144 105
 
106
+    public static function getSellableSection(bool $showByDefault = false): Forms\Components\Section
107
+    {
108
+        return Forms\Components\Section::make('Sale Information')
109
+            ->schema([
110
+                CreateAccountSelect::make('income_account_id')
111
+                    ->label('Income account')
112
+                    ->category(AccountCategory::Revenue)
113
+                    ->type(AccountType::OperatingRevenue)
114
+                    ->required()
115
+                    ->validationMessages([
116
+                        'required' => 'The income account is required for sellable offerings.',
117
+                    ]),
118
+                CreateAdjustmentSelect::make('salesTaxes')
119
+                    ->label('Sales tax')
120
+                    ->category(AdjustmentCategory::Tax)
121
+                    ->type(AdjustmentType::Sales)
122
+                    ->multiple(),
123
+                CreateAdjustmentSelect::make('salesDiscounts')
124
+                    ->label('Sales discount')
125
+                    ->category(AdjustmentCategory::Discount)
126
+                    ->type(AdjustmentType::Sales)
127
+                    ->multiple(),
128
+            ])
129
+            ->columns()
130
+            ->visible(static fn (Forms\Get $get) => in_array('Sellable', $get('attributes') ?? []));
131
+    }
132
+
133
+    public static function getPurchasableSection(): Forms\Components\Section
134
+    {
135
+        return Forms\Components\Section::make('Purchase Information')
136
+            ->schema([
137
+                CreateAccountSelect::make('expense_account_id')
138
+                    ->label('Expense account')
139
+                    ->category(AccountCategory::Expense)
140
+                    ->type(AccountType::OperatingExpense)
141
+                    ->required()
142
+                    ->validationMessages([
143
+                        'required' => 'The expense account is required for purchasable offerings.',
144
+                    ]),
145
+                CreateAdjustmentSelect::make('purchaseTaxes')
146
+                    ->label('Purchase tax')
147
+                    ->category(AdjustmentCategory::Tax)
148
+                    ->type(AdjustmentType::Purchase)
149
+                    ->multiple(),
150
+                CreateAdjustmentSelect::make('purchaseDiscounts')
151
+                    ->label('Purchase discount')
152
+                    ->category(AdjustmentCategory::Discount)
153
+                    ->type(AdjustmentType::Purchase)
154
+                    ->multiple(),
155
+            ])
156
+            ->columns()
157
+            ->visible(static fn (Forms\Get $get) => in_array('Purchasable', $get('attributes') ?? []));
158
+    }
159
+
145 160
     public static function table(Table $table): Table
146 161
     {
147 162
         return $table

+ 6
- 8
app/Filament/Company/Resources/Purchases/BillResource.php Visa fil

@@ -14,6 +14,8 @@ use App\Filament\Company\Resources\Purchases\BillResource\Pages;
14 14
 use App\Filament\Company\Resources\Purchases\VendorResource\RelationManagers\BillsRelationManager;
15 15
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
16 16
 use App\Filament\Forms\Components\CreateCurrencySelect;
17
+use App\Filament\Forms\Components\CreateOfferingSelect;
18
+use App\Filament\Forms\Components\CreateVendorSelect;
17 19
 use App\Filament\Forms\Components\DocumentTotals;
18 20
 use App\Filament\Tables\Actions\ReplicateBulkAction;
19 21
 use App\Filament\Tables\Columns;
@@ -60,10 +62,8 @@ class BillResource extends Resource
60 62
                     ->schema([
61 63
                         Forms\Components\Split::make([
62 64
                             Forms\Components\Group::make([
63
-                                Forms\Components\Select::make('vendor_id')
64
-                                    ->relationship('vendor', 'name')
65
-                                    ->preload()
66
-                                    ->searchable()
65
+                                CreateVendorSelect::make('vendor_id')
66
+                                    ->label('Vendor')
67 67
                                     ->required()
68 68
                                     ->live()
69 69
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -209,13 +209,11 @@ class BillResource extends Resource
209 209
                                 return $headers;
210 210
                             })
211 211
                             ->schema([
212
-                                Forms\Components\Select::make('offering_id')
212
+                                CreateOfferingSelect::make('offering_id')
213 213
                                     ->label('Item')
214
-                                    ->relationship('purchasableOffering', 'name')
215
-                                    ->preload()
216
-                                    ->searchable()
217 214
                                     ->required()
218 215
                                     ->live()
216
+                                    ->purchasable()
219 217
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
220 218
                                         $offeringId = $state;
221 219
                                         $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));

+ 7
- 7
app/Filament/Company/Resources/Purchases/VendorResource.php Visa fil

@@ -45,7 +45,7 @@ class VendorResource extends Resource
45 45
                                     ->default(VendorType::Regular)
46 46
                                     ->columnSpanFull(),
47 47
                                 CreateCurrencySelect::make('currency_code')
48
-                                    ->nullable()
48
+                                    ->softRequired()
49 49
                                     ->visible(static fn (Forms\Get $get) => VendorType::parse($get('type')) === VendorType::Regular),
50 50
                                 Forms\Components\Select::make('contractor_type')
51 51
                                     ->label('Contractor type')
@@ -80,21 +80,21 @@ class VendorResource extends Resource
80 80
                             ]),
81 81
                         CustomSection::make('Primary Contact')
82 82
                             ->relationship('contact')
83
+                            ->saveRelationshipsUsing(null)
84
+                            ->saveRelationshipsBeforeChildrenUsing(null)
85
+                            ->dehydrated(true)
83 86
                             ->contained(false)
84 87
                             ->schema([
85 88
                                 Forms\Components\Hidden::make('is_primary')
86 89
                                     ->default(true),
87 90
                                 Forms\Components\TextInput::make('first_name')
88 91
                                     ->label('First name')
89
-                                    ->required()
90 92
                                     ->maxLength(255),
91 93
                                 Forms\Components\TextInput::make('last_name')
92 94
                                     ->label('Last name')
93
-                                    ->required()
94 95
                                     ->maxLength(255),
95 96
                                 Forms\Components\TextInput::make('email')
96 97
                                     ->label('Email')
97
-                                    ->required()
98 98
                                     ->email()
99 99
                                     ->columnSpanFull()
100 100
                                     ->maxLength(255),
@@ -110,21 +110,18 @@ class VendorResource extends Resource
110 110
                                             ->schema([
111 111
                                                 Forms\Components\TextInput::make('number')
112 112
                                                     ->label('Phone')
113
-                                                    ->required()
114 113
                                                     ->maxLength(15),
115 114
                                             ])->maxItems(1),
116 115
                                         Forms\Components\Builder\Block::make('mobile')
117 116
                                             ->schema([
118 117
                                                 Forms\Components\TextInput::make('number')
119 118
                                                     ->label('Mobile')
120
-                                                    ->required()
121 119
                                                     ->maxLength(15),
122 120
                                             ])->maxItems(1),
123 121
                                         Forms\Components\Builder\Block::make('toll_free')
124 122
                                             ->schema([
125 123
                                                 Forms\Components\TextInput::make('number')
126 124
                                                     ->label('Toll free')
127
-                                                    ->required()
128 125
                                                     ->maxLength(15),
129 126
                                             ])->maxItems(1),
130 127
                                         Forms\Components\Builder\Block::make('fax')
@@ -143,6 +140,9 @@ class VendorResource extends Resource
143 140
                     ])->columns(1),
144 141
                 Forms\Components\Section::make('Address Information')
145 142
                     ->relationship('address')
143
+                    ->saveRelationshipsUsing(null)
144
+                    ->saveRelationshipsBeforeChildrenUsing(null)
145
+                    ->dehydrated(true)
146 146
                     ->schema([
147 147
                         Forms\Components\Hidden::make('type')
148 148
                             ->default('general'),

+ 7
- 0
app/Filament/Company/Resources/Purchases/VendorResource/Pages/CreateVendor.php Visa fil

@@ -4,8 +4,10 @@ namespace App\Filament\Company\Resources\Purchases\VendorResource\Pages;
4 4
 
5 5
 use App\Concerns\HandlePageRedirect;
6 6
 use App\Filament\Company\Resources\Purchases\VendorResource;
7
+use App\Models\Common\Vendor;
7 8
 use Filament\Resources\Pages\CreateRecord;
8 9
 use Filament\Support\Enums\MaxWidth;
10
+use Illuminate\Database\Eloquent\Model;
9 11
 
10 12
 class CreateVendor extends CreateRecord
11 13
 {
@@ -13,6 +15,11 @@ class CreateVendor extends CreateRecord
13 15
 
14 16
     protected static string $resource = VendorResource::class;
15 17
 
18
+    protected function handleRecordCreation(array $data): Model
19
+    {
20
+        return Vendor::createWithRelations($data);
21
+    }
22
+
16 23
     public function getMaxContentWidth(): MaxWidth | string | null
17 24
     {
18 25
         return MaxWidth::FiveExtraLarge;

+ 10
- 0
app/Filament/Company/Resources/Purchases/VendorResource/Pages/EditVendor.php Visa fil

@@ -4,9 +4,11 @@ namespace App\Filament\Company\Resources\Purchases\VendorResource\Pages;
4 4
 
5 5
 use App\Concerns\HandlePageRedirect;
6 6
 use App\Filament\Company\Resources\Purchases\VendorResource;
7
+use App\Models\Common\Vendor;
7 8
 use Filament\Actions;
8 9
 use Filament\Resources\Pages\EditRecord;
9 10
 use Filament\Support\Enums\MaxWidth;
11
+use Illuminate\Database\Eloquent\Model;
10 12
 
11 13
 class EditVendor extends EditRecord
12 14
 {
@@ -14,6 +16,14 @@ class EditVendor extends EditRecord
14 16
 
15 17
     protected static string $resource = VendorResource::class;
16 18
 
19
+    protected function handleRecordUpdate(Model $record, array $data): Model
20
+    {
21
+        /** @var Vendor $record */
22
+        $record->updateWithRelations($data);
23
+
24
+        return $record;
25
+    }
26
+
17 27
     protected function getHeaderActions(): array
18 28
     {
19 29
         return [

+ 10
- 13
app/Filament/Company/Resources/Sales/ClientResource.php Visa fil

@@ -47,21 +47,21 @@ class ClientResource extends Resource
47 47
                             ]),
48 48
                         CustomSection::make('Primary Contact')
49 49
                             ->relationship('primaryContact')
50
+                            ->saveRelationshipsUsing(null)
51
+                            ->saveRelationshipsBeforeChildrenUsing(null)
52
+                            ->dehydrated(true)
50 53
                             ->contained(false)
51 54
                             ->schema([
52 55
                                 Forms\Components\Hidden::make('is_primary')
53 56
                                     ->default(true),
54 57
                                 Forms\Components\TextInput::make('first_name')
55 58
                                     ->label('First name')
56
-                                    ->required()
57 59
                                     ->maxLength(255),
58 60
                                 Forms\Components\TextInput::make('last_name')
59 61
                                     ->label('Last name')
60
-                                    ->required()
61 62
                                     ->maxLength(255),
62 63
                                 Forms\Components\TextInput::make('email')
63 64
                                     ->label('Email')
64
-                                    ->required()
65 65
                                     ->email()
66 66
                                     ->columnSpanFull()
67 67
                                     ->maxLength(255),
@@ -77,21 +77,18 @@ class ClientResource extends Resource
77 77
                                             ->schema([
78 78
                                                 Forms\Components\TextInput::make('number')
79 79
                                                     ->label('Phone')
80
-                                                    ->required()
81 80
                                                     ->maxLength(15),
82 81
                                             ])->maxItems(1),
83 82
                                         Forms\Components\Builder\Block::make('mobile')
84 83
                                             ->schema([
85 84
                                                 Forms\Components\TextInput::make('number')
86 85
                                                     ->label('Mobile')
87
-                                                    ->required()
88 86
                                                     ->maxLength(15),
89 87
                                             ])->maxItems(1),
90 88
                                         Forms\Components\Builder\Block::make('toll_free')
91 89
                                             ->schema([
92 90
                                                 Forms\Components\TextInput::make('number')
93 91
                                                     ->label('Toll free')
94
-                                                    ->required()
95 92
                                                     ->maxLength(15),
96 93
                                             ])->maxItems(1),
97 94
                                         Forms\Components\Builder\Block::make('fax')
@@ -109,6 +106,9 @@ class ClientResource extends Resource
109 106
                             ])->columns(),
110 107
                         Forms\Components\Repeater::make('secondaryContacts')
111 108
                             ->relationship()
109
+                            ->saveRelationshipsUsing(null)
110
+                            ->saveRelationshipsBeforeChildrenUsing(null)
111
+                            ->dehydrated(true)
112 112
                             ->hiddenLabel()
113 113
                             ->extraAttributes([
114 114
                                 'class' => 'uncontained',
@@ -138,17 +138,14 @@ class ClientResource extends Resource
138 138
                             ->schema([
139 139
                                 Forms\Components\TextInput::make('first_name')
140 140
                                     ->label('First name')
141
-                                    ->required()
142 141
                                     ->live(onBlur: true)
143 142
                                     ->maxLength(255),
144 143
                                 Forms\Components\TextInput::make('last_name')
145 144
                                     ->label('Last name')
146
-                                    ->required()
147 145
                                     ->live(onBlur: true)
148 146
                                     ->maxLength(255),
149 147
                                 Forms\Components\TextInput::make('email')
150 148
                                     ->label('Email')
151
-                                    ->required()
152 149
                                     ->email()
153 150
                                     ->maxLength(255),
154 151
                                 PhoneBuilder::make('phones')
@@ -162,7 +159,6 @@ class ClientResource extends Resource
162 159
                                             ->schema([
163 160
                                                 Forms\Components\TextInput::make('number')
164 161
                                                     ->label('Phone')
165
-                                                    ->required()
166 162
                                                     ->maxLength(255),
167 163
                                             ])->maxItems(1),
168 164
                                     ])
@@ -174,10 +170,12 @@ class ClientResource extends Resource
174 170
                     ])->columns(1),
175 171
                 Forms\Components\Section::make('Billing')
176 172
                     ->schema([
177
-                        CreateCurrencySelect::make('currency_code'),
173
+                        CreateCurrencySelect::make('currency_code')
174
+                            ->softRequired(),
178 175
                         CustomSection::make('Billing Address')
179 176
                             ->relationship('billingAddress')
180 177
                             ->saveRelationshipsUsing(null)
178
+                            ->saveRelationshipsBeforeChildrenUsing(null)
181 179
                             ->dehydrated(true)
182 180
                             ->contained(false)
183 181
                             ->schema([
@@ -190,17 +188,16 @@ class ClientResource extends Resource
190 188
                 Forms\Components\Section::make('Shipping')
191 189
                     ->relationship('shippingAddress')
192 190
                     ->saveRelationshipsUsing(null)
191
+                    ->saveRelationshipsBeforeChildrenUsing(null)
193 192
                     ->dehydrated(true)
194 193
                     ->schema([
195 194
                         Forms\Components\Hidden::make('type')
196 195
                             ->default('shipping'),
197 196
                         Forms\Components\TextInput::make('recipient')
198 197
                             ->label('Recipient')
199
-                            ->required()
200 198
                             ->maxLength(255),
201 199
                         Forms\Components\TextInput::make('phone')
202 200
                             ->label('Phone')
203
-                            ->required()
204 201
                             ->maxLength(255),
205 202
                         CustomSection::make('Shipping Address')
206 203
                             ->contained(false)

+ 1
- 48
app/Filament/Company/Resources/Sales/ClientResource/Pages/CreateClient.php Visa fil

@@ -3,9 +3,7 @@
3 3
 namespace App\Filament\Company\Resources\Sales\ClientResource\Pages;
4 4
 
5 5
 use App\Concerns\HandlePageRedirect;
6
-use App\Enums\Common\AddressType;
7 6
 use App\Filament\Company\Resources\Sales\ClientResource;
8
-use App\Models\Common\Address;
9 7
 use App\Models\Common\Client;
10 8
 use Filament\Resources\Pages\CreateRecord;
11 9
 use Filament\Support\Enums\MaxWidth;
@@ -24,51 +22,6 @@ class CreateClient extends CreateRecord
24 22
 
25 23
     protected function handleRecordCreation(array $data): Model
26 24
     {
27
-        /** @var Client $record */
28
-        $record = parent::handleRecordCreation($data);
29
-
30
-        // Create billing address first
31
-        /** @var Address $billingAddress */
32
-        $billingAddress = $record->addresses()->create([
33
-            ...$data['billingAddress'],
34
-            'type' => AddressType::Billing,
35
-        ]);
36
-
37
-        // Create shipping address with reference to billing if needed
38
-        $shippingData = $data['shippingAddress'];
39
-
40
-        $shippingAddress = [
41
-            'type' => AddressType::Shipping,
42
-            'recipient' => $shippingData['recipient'],
43
-            'phone' => $shippingData['phone'],
44
-            'notes' => $shippingData['notes'],
45
-        ];
46
-
47
-        if ($shippingData['same_as_billing']) {
48
-            $shippingAddress = [
49
-                ...$shippingAddress,
50
-                'parent_address_id' => $billingAddress->id,
51
-                'address_line_1' => $billingAddress->address_line_1,
52
-                'address_line_2' => $billingAddress->address_line_2,
53
-                'country_code' => $billingAddress->country_code,
54
-                'state_id' => $billingAddress->state_id,
55
-                'city' => $billingAddress->city,
56
-                'postal_code' => $billingAddress->postal_code,
57
-            ];
58
-        } else {
59
-            $shippingAddress = [
60
-                ...$shippingAddress,
61
-                'address_line_1' => $shippingData['address_line_1'],
62
-                'address_line_2' => $shippingData['address_line_2'],
63
-                'country_code' => $shippingData['country_code'],
64
-                'state_id' => $shippingData['state_id'],
65
-                'city' => $shippingData['city'],
66
-                'postal_code' => $shippingData['postal_code'],
67
-            ];
68
-        }
69
-
70
-        $record->addresses()->create($shippingAddress);
71
-
72
-        return $record;
25
+        return Client::createWithRelations($data);
73 26
     }
74 27
 }

+ 1
- 41
app/Filament/Company/Resources/Sales/ClientResource/Pages/EditClient.php Visa fil

@@ -31,47 +31,7 @@ class EditClient extends EditRecord
31 31
     protected function handleRecordUpdate(Model $record, array $data): Model
32 32
     {
33 33
         /** @var Client $record */
34
-        $record = parent::handleRecordUpdate($record, $data);
35
-
36
-        // Update billing address
37
-        $billingAddress = $record->billingAddress;
38
-        $billingAddress->update($data['billingAddress']);
39
-
40
-        // Update shipping address
41
-        $shippingAddress = $record->shippingAddress;
42
-        $shippingData = $data['shippingAddress'];
43
-
44
-        $shippingUpdateData = [
45
-            'recipient' => $shippingData['recipient'],
46
-            'phone' => $shippingData['phone'],
47
-            'notes' => $shippingData['notes'],
48
-        ];
49
-
50
-        if ($shippingData['same_as_billing']) {
51
-            $shippingUpdateData = [
52
-                ...$shippingUpdateData,
53
-                'parent_address_id' => $billingAddress->id,
54
-                'address_line_1' => $billingAddress->address_line_1,
55
-                'address_line_2' => $billingAddress->address_line_2,
56
-                'country_code' => $billingAddress->country_code,
57
-                'state_id' => $billingAddress->state_id,
58
-                'city' => $billingAddress->city,
59
-                'postal_code' => $billingAddress->postal_code,
60
-            ];
61
-        } else {
62
-            $shippingUpdateData = [
63
-                ...$shippingUpdateData,
64
-                'parent_address_id' => null,
65
-                'address_line_1' => $shippingData['address_line_1'],
66
-                'address_line_2' => $shippingData['address_line_2'],
67
-                'country_code' => $shippingData['country_code'],
68
-                'state_id' => $shippingData['state_id'],
69
-                'city' => $shippingData['city'],
70
-                'postal_code' => $shippingData['postal_code'],
71
-            ];
72
-        }
73
-
74
-        $shippingAddress->update($shippingUpdateData);
34
+        $record->updateWithRelations($data);
75 35
 
76 36
         return $record;
77 37
     }

+ 7
- 8
app/Filament/Company/Resources/Sales/EstimateResource.php Visa fil

@@ -13,7 +13,9 @@ use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\Estimat
13 13
 use App\Filament\Company\Resources\Sales\EstimateResource\Pages;
14 14
 use App\Filament\Company\Resources\Sales\EstimateResource\Widgets;
15 15
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
16
+use App\Filament\Forms\Components\CreateClientSelect;
16 17
 use App\Filament\Forms\Components\CreateCurrencySelect;
18
+use App\Filament\Forms\Components\CreateOfferingSelect;
17 19
 use App\Filament\Forms\Components\DocumentFooterSection;
18 20
 use App\Filament\Forms\Components\DocumentHeaderSection;
19 21
 use App\Filament\Forms\Components\DocumentTotals;
@@ -61,10 +63,8 @@ class EstimateResource extends Resource
61 63
                     ->schema([
62 64
                         Forms\Components\Split::make([
63 65
                             Forms\Components\Group::make([
64
-                                Forms\Components\Select::make('client_id')
65
-                                    ->relationship('client', 'name')
66
-                                    ->preload()
67
-                                    ->searchable()
66
+                                CreateClientSelect::make('client_id')
67
+                                    ->label('Client')
68 68
                                     ->required()
69 69
                                     ->live()
70 70
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -207,12 +207,11 @@ class EstimateResource extends Resource
207 207
                                 return $headers;
208 208
                             })
209 209
                             ->schema([
210
-                                Forms\Components\Select::make('offering_id')
211
-                                    ->relationship('sellableOffering', 'name')
212
-                                    ->preload()
213
-                                    ->searchable()
210
+                                CreateOfferingSelect::make('offering_id')
211
+                                    ->label('Item')
214 212
                                     ->required()
215 213
                                     ->live()
214
+                                    ->sellable()
216 215
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
217 216
                                         $offeringId = $state;
218 217
                                         $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));

+ 7
- 8
app/Filament/Company/Resources/Sales/InvoiceResource.php Visa fil

@@ -15,7 +15,9 @@ use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\Invoice
15 15
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
16 16
 use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets;
17 17
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
18
+use App\Filament\Forms\Components\CreateClientSelect;
18 19
 use App\Filament\Forms\Components\CreateCurrencySelect;
20
+use App\Filament\Forms\Components\CreateOfferingSelect;
19 21
 use App\Filament\Forms\Components\DocumentFooterSection;
20 22
 use App\Filament\Forms\Components\DocumentHeaderSection;
21 23
 use App\Filament\Forms\Components\DocumentTotals;
@@ -67,10 +69,8 @@ class InvoiceResource extends Resource
67 69
                     ->schema([
68 70
                         Forms\Components\Split::make([
69 71
                             Forms\Components\Group::make([
70
-                                Forms\Components\Select::make('client_id')
71
-                                    ->relationship('client', 'name')
72
-                                    ->preload()
73
-                                    ->searchable()
72
+                                CreateClientSelect::make('client_id')
73
+                                    ->label('Client')
74 74
                                     ->required()
75 75
                                     ->live()
76 76
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -220,12 +220,11 @@ class InvoiceResource extends Resource
220 220
                                 return $headers;
221 221
                             })
222 222
                             ->schema([
223
-                                Forms\Components\Select::make('offering_id')
224
-                                    ->relationship('sellableOffering', 'name')
225
-                                    ->preload()
226
-                                    ->searchable()
223
+                                CreateOfferingSelect::make('offering_id')
224
+                                    ->label('Item')
227 225
                                     ->required()
228 226
                                     ->live()
227
+                                    ->sellable()
229 228
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
230 229
                                         $offeringId = $state;
231 230
                                         $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));

+ 7
- 8
app/Filament/Company/Resources/Sales/RecurringInvoiceResource.php Visa fil

@@ -12,7 +12,9 @@ use App\Enums\Setting\PaymentTerms;
12 12
 use App\Filament\Company\Resources\Sales\ClientResource\RelationManagers\RecurringInvoicesRelationManager;
13 13
 use App\Filament\Company\Resources\Sales\RecurringInvoiceResource\Pages;
14 14
 use App\Filament\Forms\Components\CreateAdjustmentSelect;
15
+use App\Filament\Forms\Components\CreateClientSelect;
15 16
 use App\Filament\Forms\Components\CreateCurrencySelect;
17
+use App\Filament\Forms\Components\CreateOfferingSelect;
16 18
 use App\Filament\Forms\Components\DocumentFooterSection;
17 19
 use App\Filament\Forms\Components\DocumentHeaderSection;
18 20
 use App\Filament\Forms\Components\DocumentTotals;
@@ -53,10 +55,8 @@ class RecurringInvoiceResource extends Resource
53 55
                     ->schema([
54 56
                         Forms\Components\Split::make([
55 57
                             Forms\Components\Group::make([
56
-                                Forms\Components\Select::make('client_id')
57
-                                    ->relationship('client', 'name')
58
-                                    ->preload()
59
-                                    ->searchable()
58
+                                CreateClientSelect::make('client_id')
59
+                                    ->label('Client')
60 60
                                     ->required()
61 61
                                     ->live()
62 62
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state) {
@@ -133,12 +133,11 @@ class RecurringInvoiceResource extends Resource
133 133
                                 return $headers;
134 134
                             })
135 135
                             ->schema([
136
-                                Forms\Components\Select::make('offering_id')
137
-                                    ->relationship('sellableOffering', 'name')
138
-                                    ->preload()
139
-                                    ->searchable()
136
+                                CreateOfferingSelect::make('offering_id')
137
+                                    ->label('Item')
140 138
                                     ->required()
141 139
                                     ->live()
140
+                                    ->sellable()
142 141
                                     ->afterStateUpdated(function (Forms\Set $set, Forms\Get $get, $state, ?DocumentLineItem $record) {
143 142
                                         $offeringId = $state;
144 143
                                         $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method'));

+ 17
- 3
app/Filament/Forms/Components/AddressFields.php Visa fil

@@ -13,6 +13,8 @@ class AddressFields extends Grid
13 13
 
14 14
     protected bool | Closure $isCountryDisabled = false;
15 15
 
16
+    protected bool | Closure $isRequired = false;
17
+
16 18
     protected function setUp(): void
17 19
     {
18 20
         parent::setUp();
@@ -20,7 +22,7 @@ class AddressFields extends Grid
20 22
         $this->schema([
21 23
             TextInput::make('address_line_1')
22 24
                 ->label('Address line 1')
23
-                ->required()
25
+                ->required(fn () => $this->isRequired())
24 26
                 ->maxLength(255),
25 27
             TextInput::make('address_line_2')
26 28
                 ->label('Address line 2')
@@ -28,11 +30,11 @@ class AddressFields extends Grid
28 30
             CountrySelect::make('country_code')
29 31
                 ->disabled(fn () => $this->isCountryDisabled())
30 32
                 ->clearStateField()
31
-                ->required(),
33
+                ->required(fn () => $this->isRequired()),
32 34
             StateSelect::make('state_id'),
33 35
             TextInput::make('city')
34 36
                 ->label('City')
35
-                ->required()
37
+                ->required(fn () => $this->isRequired())
36 38
                 ->maxLength(255),
37 39
             TextInput::make('postal_code')
38 40
                 ->label('Postal code')
@@ -60,6 +62,18 @@ class AddressFields extends Grid
60 62
         }
61 63
     }
62 64
 
65
+    public function required(bool | Closure $condition = true): static
66
+    {
67
+        $this->isRequired = $condition;
68
+
69
+        return $this;
70
+    }
71
+
72
+    public function isRequired(): bool
73
+    {
74
+        return (bool) $this->evaluate($this->isRequired);
75
+    }
76
+
63 77
     public function disabledCountry(bool | Closure $condition = true): static
64 78
     {
65 79
         $this->isCountryDisabled = $condition;

+ 2
- 2
app/Filament/Forms/Components/CreateAdjustmentSelect.php Visa fil

@@ -211,12 +211,12 @@ class CreateAdjustmentSelect extends Select
211 211
     {
212 212
         $categoryLabel = $this->getCategory()?->getLabel() ?? 'Adjustment';
213 213
         $typeLabel = $this->getType()?->getLabel() ?? '';
214
-        $label = trim($typeLabel . ' ' . $categoryLabel);
214
+        $label = strtolower(trim($typeLabel . ' ' . $categoryLabel));
215 215
 
216 216
         return $action
217 217
             ->label('Create ' . $label)
218 218
             ->slideOver()
219 219
             ->modalWidth(MaxWidth::ExtraLarge)
220
-            ->modalHeading('Create a new ' . strtolower($label));
220
+            ->modalHeading('Create a new ' . $label);
221 221
     }
222 222
 }

+ 49
- 0
app/Filament/Forms/Components/CreateClientSelect.php Visa fil

@@ -0,0 +1,49 @@
1
+<?php
2
+
3
+namespace App\Filament\Forms\Components;
4
+
5
+use App\Filament\Company\Resources\Sales\ClientResource;
6
+use App\Models\Common\Client;
7
+use Filament\Forms\Components\Actions\Action;
8
+use Filament\Forms\Components\Select;
9
+use Filament\Forms\Form;
10
+use Filament\Support\Enums\MaxWidth;
11
+use Illuminate\Support\Facades\DB;
12
+
13
+class CreateClientSelect extends Select
14
+{
15
+    protected function setUp(): void
16
+    {
17
+        parent::setUp();
18
+
19
+        $this
20
+            ->searchable()
21
+            ->preload()
22
+            ->createOptionForm(fn (Form $form) => $this->createClientForm($form))
23
+            ->createOptionAction(fn (Action $action) => $this->createClientAction($action));
24
+
25
+        $this->relationship('client', 'name');
26
+
27
+        $this->createOptionUsing(static function (array $data) {
28
+            return DB::transaction(static function () use ($data) {
29
+                $client = Client::createWithRelations($data);
30
+
31
+                return $client->getKey();
32
+            });
33
+        });
34
+    }
35
+
36
+    protected function createClientForm(Form $form): Form
37
+    {
38
+        return ClientResource::form($form);
39
+    }
40
+
41
+    protected function createClientAction(Action $action): Action
42
+    {
43
+        return $action
44
+            ->label('Create client')
45
+            ->slideOver()
46
+            ->modalWidth(MaxWidth::ThreeExtraLarge)
47
+            ->modalHeading('Create a new client');
48
+    }
49
+}

+ 3
- 2
app/Filament/Forms/Components/CreateCurrencySelect.php Visa fil

@@ -63,8 +63,9 @@ class CreateCurrencySelect extends Select
63 63
     protected function createCurrencyAction(Action $action): Action
64 64
     {
65 65
         return $action
66
-            ->label('Add currency')
66
+            ->label('Create currency')
67 67
             ->slideOver()
68
-            ->modalWidth(MaxWidth::Medium);
68
+            ->modalWidth(MaxWidth::Medium)
69
+            ->modalHeading('Create a new currency');
69 70
     }
70 71
 }

+ 111
- 0
app/Filament/Forms/Components/CreateOfferingSelect.php Visa fil

@@ -0,0 +1,111 @@
1
+<?php
2
+
3
+namespace App\Filament\Forms\Components;
4
+
5
+use App\Filament\Company\Resources\Common\OfferingResource;
6
+use App\Models\Common\Offering;
7
+use Filament\Forms\Components\Actions\Action;
8
+use Filament\Forms\Components\Select;
9
+use Filament\Forms\Form;
10
+use Filament\Forms\Get;
11
+use Filament\Support\Enums\MaxWidth;
12
+
13
+class CreateOfferingSelect extends Select
14
+{
15
+    protected bool $isPurchasable = true;
16
+
17
+    protected bool $isSellable = true;
18
+
19
+    public function purchasable(bool $condition = true): static
20
+    {
21
+        $this->isPurchasable = $condition;
22
+        $this->isSellable = false;
23
+
24
+        return $this;
25
+    }
26
+
27
+    public function sellable(bool $condition = true): static
28
+    {
29
+        $this->isSellable = $condition;
30
+        $this->isPurchasable = false;
31
+
32
+        return $this;
33
+    }
34
+
35
+    protected function setUp(): void
36
+    {
37
+        parent::setUp();
38
+
39
+        $this
40
+            ->searchable()
41
+            ->preload()
42
+            ->createOptionForm(fn (Form $form) => $this->createOfferingForm($form))
43
+            ->createOptionAction(fn (Action $action) => $this->createOfferingAction($action));
44
+
45
+        $this->relationship(
46
+            name: fn () => $this->isPurchasable() && ! $this->isSellable() ? 'purchasableOffering' : ($this->isSellable() && ! $this->isPurchasable() ? 'sellableOffering' : 'offering'),
47
+            titleAttribute: 'name'
48
+        );
49
+
50
+        $this->createOptionUsing(function (array $data, Form $form) {
51
+            if ($this->isSellableAndPurchasable()) {
52
+                $attributes = array_flip($data['attributes'] ?? []);
53
+
54
+                $data['sellable'] = isset($attributes['Sellable']);
55
+                $data['purchasable'] = isset($attributes['Purchasable']);
56
+            } else {
57
+                $data['sellable'] = $this->isSellable;
58
+                $data['purchasable'] = $this->isPurchasable;
59
+            }
60
+
61
+            unset($data['attributes']);
62
+
63
+            $offering = Offering::create($data);
64
+
65
+            $form->model($offering)->saveRelationships();
66
+
67
+            return $offering->getKey();
68
+        });
69
+    }
70
+
71
+    protected function createOfferingForm(Form $form): Form
72
+    {
73
+        return $form->schema([
74
+            OfferingResource::getGeneralSection($this->isSellableAndPurchasable()),
75
+            OfferingResource::getSellableSection()->visible(
76
+                fn (Get $get) => $this->isSellableAndPurchasable()
77
+                    ? in_array('Sellable', $get('attributes') ?? [])
78
+                    : $this->isSellable()
79
+            ),
80
+            OfferingResource::getPurchasableSection()->visible(
81
+                fn (Get $get) => $this->isSellableAndPurchasable()
82
+                    ? in_array('Purchasable', $get('attributes') ?? [])
83
+                    : $this->isPurchasable()
84
+            ),
85
+        ]);
86
+    }
87
+
88
+    protected function createOfferingAction(Action $action): Action
89
+    {
90
+        return $action
91
+            ->label('Create offering')
92
+            ->slideOver()
93
+            ->modalWidth(MaxWidth::ThreeExtraLarge)
94
+            ->modalHeading('Create a new offering');
95
+    }
96
+
97
+    public function isSellable(): bool
98
+    {
99
+        return $this->isSellable;
100
+    }
101
+
102
+    public function isPurchasable(): bool
103
+    {
104
+        return $this->isPurchasable;
105
+    }
106
+
107
+    public function isSellableAndPurchasable(): bool
108
+    {
109
+        return $this->isSellable && $this->isPurchasable;
110
+    }
111
+}

+ 49
- 0
app/Filament/Forms/Components/CreateVendorSelect.php Visa fil

@@ -0,0 +1,49 @@
1
+<?php
2
+
3
+namespace App\Filament\Forms\Components;
4
+
5
+use App\Filament\Company\Resources\Purchases\VendorResource;
6
+use App\Models\Common\Vendor;
7
+use Filament\Forms\Components\Actions\Action;
8
+use Filament\Forms\Components\Select;
9
+use Filament\Forms\Form;
10
+use Filament\Support\Enums\MaxWidth;
11
+use Illuminate\Support\Facades\DB;
12
+
13
+class CreateVendorSelect extends Select
14
+{
15
+    protected function setUp(): void
16
+    {
17
+        parent::setUp();
18
+
19
+        $this
20
+            ->searchable()
21
+            ->preload()
22
+            ->createOptionForm(fn (Form $form) => $this->createVendorForm($form))
23
+            ->createOptionAction(fn (Action $action) => $this->createVendorAction($action));
24
+
25
+        $this->relationship('vendor', 'name');
26
+
27
+        $this->createOptionUsing(static function (array $data) {
28
+            return DB::transaction(static function () use ($data) {
29
+                $vendor = Vendor::createWithRelations($data);
30
+
31
+                return $vendor->getKey();
32
+            });
33
+        });
34
+    }
35
+
36
+    protected function createVendorForm(Form $form): Form
37
+    {
38
+        return VendorResource::form($form);
39
+    }
40
+
41
+    protected function createVendorAction(Action $action): Action
42
+    {
43
+        return $action
44
+            ->label('Create vendor')
45
+            ->slideOver()
46
+            ->modalWidth(MaxWidth::ThreeExtraLarge)
47
+            ->modalHeading('Create a new vendor');
48
+    }
49
+}

+ 180
- 0
app/Models/Common/Client.php Visa fil

@@ -35,6 +35,186 @@ class Client extends Model
35 35
         'updated_by',
36 36
     ];
37 37
 
38
+    public static function createWithRelations(array $data): self
39
+    {
40
+        /** @var Client $client */
41
+        $client = self::create($data);
42
+
43
+        if (isset($data['primaryContact'], $data['primaryContact']['first_name'])) {
44
+            $client->primaryContact()->create([
45
+                'is_primary' => true,
46
+                'first_name' => $data['primaryContact']['first_name'],
47
+                'last_name' => $data['primaryContact']['last_name'],
48
+                'email' => $data['primaryContact']['email'],
49
+                'phones' => $data['primaryContact']['phones'] ?? [],
50
+            ]);
51
+        }
52
+
53
+        if (isset($data['secondaryContacts'])) {
54
+            foreach ($data['secondaryContacts'] as $contactData) {
55
+                if (isset($contactData['first_name'])) {
56
+                    $client->secondaryContacts()->create([
57
+                        'is_primary' => false,
58
+                        'first_name' => $contactData['first_name'],
59
+                        'last_name' => $contactData['last_name'],
60
+                        'email' => $contactData['email'],
61
+                        'phones' => $contactData['phones'] ?? [],
62
+                    ]);
63
+                }
64
+            }
65
+        }
66
+
67
+        if (isset($data['billingAddress'], $data['billingAddress']['address_line_1'])) {
68
+            $client->billingAddress()->create([
69
+                'type' => AddressType::Billing,
70
+                'address_line_1' => $data['billingAddress']['address_line_1'],
71
+                'address_line_2' => $data['billingAddress']['address_line_2'] ?? null,
72
+                'country_code' => $data['billingAddress']['country_code'] ?? null,
73
+                'state_id' => $data['billingAddress']['state_id'] ?? null,
74
+                'city' => $data['billingAddress']['city'] ?? null,
75
+                'postal_code' => $data['billingAddress']['postal_code'] ?? null,
76
+            ]);
77
+        }
78
+
79
+        if (isset($data['shippingAddress'])) {
80
+            $shippingData = $data['shippingAddress'];
81
+            $shippingAddress = [
82
+                'type' => AddressType::Shipping,
83
+                'recipient' => $shippingData['recipient'] ?? null,
84
+                'phone' => $shippingData['phone'] ?? null,
85
+                'notes' => $shippingData['notes'] ?? null,
86
+            ];
87
+
88
+            if ($shippingData['same_as_billing'] ?? false) {
89
+                $billingAddress = $client->billingAddress;
90
+                if ($billingAddress) {
91
+                    $shippingAddress = [
92
+                        ...$shippingAddress,
93
+                        'parent_address_id' => $billingAddress->id,
94
+                        'address_line_1' => $billingAddress->address_line_1,
95
+                        'address_line_2' => $billingAddress->address_line_2,
96
+                        'country_code' => $billingAddress->country_code,
97
+                        'state_id' => $billingAddress->state_id,
98
+                        'city' => $billingAddress->city,
99
+                        'postal_code' => $billingAddress->postal_code,
100
+                    ];
101
+                    $client->shippingAddress()->create($shippingAddress);
102
+                }
103
+            } elseif (isset($shippingData['address_line_1'])) {
104
+                $shippingAddress = [
105
+                    ...$shippingAddress,
106
+                    'address_line_1' => $shippingData['address_line_1'],
107
+                    'address_line_2' => $shippingData['address_line_2'] ?? null,
108
+                    'country_code' => $shippingData['country_code'] ?? null,
109
+                    'state_id' => $shippingData['state_id'] ?? null,
110
+                    'city' => $shippingData['city'] ?? null,
111
+                    'postal_code' => $shippingData['postal_code'] ?? null,
112
+                ];
113
+
114
+                $client->shippingAddress()->create($shippingAddress);
115
+            }
116
+        }
117
+
118
+        return $client;
119
+    }
120
+
121
+    public function updateWithRelations(array $data): self
122
+    {
123
+        $this->update($data);
124
+
125
+        if (isset($data['primaryContact'], $data['primaryContact']['first_name'])) {
126
+            $this->primaryContact()->updateOrCreate(
127
+                ['is_primary' => true],
128
+                [
129
+                    'first_name' => $data['primaryContact']['first_name'],
130
+                    'last_name' => $data['primaryContact']['last_name'],
131
+                    'email' => $data['primaryContact']['email'],
132
+                    'phones' => $data['primaryContact']['phones'] ?? [],
133
+                ]
134
+            );
135
+        }
136
+
137
+        if (isset($data['secondaryContacts'])) {
138
+            // Delete removed contacts
139
+            $existingIds = collect($data['secondaryContacts'])->pluck('id')->filter()->all();
140
+            $this->secondaryContacts()->whereNotIn('id', $existingIds)->delete();
141
+
142
+            // Update or create contacts
143
+            foreach ($data['secondaryContacts'] as $contactData) {
144
+                if (isset($contactData['first_name'])) {
145
+                    $this->secondaryContacts()->updateOrCreate(
146
+                        ['id' => $contactData['id'] ?? null],
147
+                        [
148
+                            'is_primary' => false,
149
+                            'first_name' => $contactData['first_name'],
150
+                            'last_name' => $contactData['last_name'],
151
+                            'email' => $contactData['email'],
152
+                            'phones' => $contactData['phones'] ?? [],
153
+                        ]
154
+                    );
155
+                }
156
+            }
157
+        }
158
+
159
+        if (isset($data['billingAddress'], $data['billingAddress']['address_line_1'])) {
160
+            $this->billingAddress()->updateOrCreate(
161
+                ['type' => AddressType::Billing],
162
+                [
163
+                    'address_line_1' => $data['billingAddress']['address_line_1'],
164
+                    'address_line_2' => $data['billingAddress']['address_line_2'] ?? null,
165
+                    'country_code' => $data['billingAddress']['country_code'] ?? null,
166
+                    'state_id' => $data['billingAddress']['state_id'] ?? null,
167
+                    'city' => $data['billingAddress']['city'] ?? null,
168
+                    'postal_code' => $data['billingAddress']['postal_code'] ?? null,
169
+                ]
170
+            );
171
+        }
172
+
173
+        if (isset($data['shippingAddress'])) {
174
+            $shippingData = $data['shippingAddress'];
175
+            $shippingAddress = [
176
+                'type' => AddressType::Shipping,
177
+                'recipient' => $shippingData['recipient'] ?? null,
178
+                'phone' => $shippingData['phone'] ?? null,
179
+                'notes' => $shippingData['notes'] ?? null,
180
+            ];
181
+
182
+            if ($shippingData['same_as_billing'] ?? false) {
183
+                $billingAddress = $this->billingAddress;
184
+                if ($billingAddress) {
185
+                    $shippingAddress = [
186
+                        ...$shippingAddress,
187
+                        'parent_address_id' => $billingAddress->id,
188
+                        'address_line_1' => $billingAddress->address_line_1,
189
+                        'address_line_2' => $billingAddress->address_line_2,
190
+                        'country_code' => $billingAddress->country_code,
191
+                        'state_id' => $billingAddress->state_id,
192
+                        'city' => $billingAddress->city,
193
+                        'postal_code' => $billingAddress->postal_code,
194
+                    ];
195
+                }
196
+            } elseif (isset($shippingData['address_line_1'])) {
197
+                $shippingAddress = [
198
+                    ...$shippingAddress,
199
+                    'parent_address_id' => null,
200
+                    'address_line_1' => $shippingData['address_line_1'],
201
+                    'address_line_2' => $shippingData['address_line_2'] ?? null,
202
+                    'country_code' => $shippingData['country_code'] ?? null,
203
+                    'state_id' => $shippingData['state_id'] ?? null,
204
+                    'city' => $shippingData['city'] ?? null,
205
+                    'postal_code' => $shippingData['postal_code'] ?? null,
206
+                ];
207
+            }
208
+
209
+            $this->shippingAddress()->updateOrCreate(
210
+                ['type' => AddressType::Shipping],
211
+                $shippingAddress
212
+            );
213
+        }
214
+
215
+        return $this;
216
+    }
217
+
38 218
     public function contacts(): MorphMany
39 219
     {
40 220
         return $this->morphMany(Contact::class, 'contactable');

+ 63
- 0
app/Models/Common/Vendor.php Visa fil

@@ -44,6 +44,69 @@ class Vendor extends Model
44 44
         'ein' => 'encrypted',
45 45
     ];
46 46
 
47
+    public static function createWithRelations(array $data): self
48
+    {
49
+        /** @var Vendor $vendor */
50
+        $vendor = self::create($data);
51
+
52
+        if (isset($data['contact'], $data['contact']['first_name'])) {
53
+            $vendor->contact()->create([
54
+                'is_primary' => true,
55
+                'first_name' => $data['contact']['first_name'],
56
+                'last_name' => $data['contact']['last_name'],
57
+                'email' => $data['contact']['email'],
58
+                'phones' => $data['contact']['phones'] ?? [],
59
+            ]);
60
+        }
61
+
62
+        if (isset($data['address'], $data['address']['type'], $data['address']['address_line_1'])) {
63
+            $vendor->address()->create([
64
+                'type' => $data['address']['type'],
65
+                'address_line_1' => $data['address']['address_line_1'],
66
+                'address_line_2' => $data['address']['address_line_2'] ?? null,
67
+                'country_code' => $data['address']['country_code'] ?? null,
68
+                'state_id' => $data['address']['state_id'] ?? null,
69
+                'city' => $data['address']['city'] ?? null,
70
+                'postal_code' => $data['address']['postal_code'] ?? null,
71
+            ]);
72
+        }
73
+
74
+        return $vendor;
75
+    }
76
+
77
+    public function updateWithRelations(array $data): self
78
+    {
79
+        $this->update($data);
80
+
81
+        if (isset($data['contact'], $data['contact']['first_name'])) {
82
+            $this->contact()->updateOrCreate(
83
+                ['is_primary' => true],
84
+                [
85
+                    'first_name' => $data['contact']['first_name'],
86
+                    'last_name' => $data['contact']['last_name'],
87
+                    'email' => $data['contact']['email'],
88
+                    'phones' => $data['contact']['phones'] ?? [],
89
+                ]
90
+            );
91
+        }
92
+
93
+        if (isset($data['address'], $data['address']['type'], $data['address']['address_line_1'])) {
94
+            $this->address()->updateOrCreate(
95
+                ['type' => $data['address']['type']],
96
+                [
97
+                    'address_line_1' => $data['address']['address_line_1'],
98
+                    'address_line_2' => $data['address']['address_line_2'] ?? null,
99
+                    'country_code' => $data['address']['country_code'] ?? null,
100
+                    'state_id' => $data['address']['state_id'] ?? null,
101
+                    'city' => $data['address']['city'] ?? null,
102
+                    'postal_code' => $data['address']['postal_code'] ?? null,
103
+                ]
104
+            );
105
+        }
106
+
107
+        return $this;
108
+    }
109
+
47 110
     public function bills(): HasMany
48 111
     {
49 112
         return $this->hasMany(Bill::class);

+ 172
- 172
composer.lock
Filskillnaden har hållits tillbaka eftersom den är för stor
Visa fil


Laddar…
Avbryt
Spara