浏览代码

refactor: invoice preview and currency logic

3.x
wallo 2 年前
父节点
当前提交
7fb533b4cc

+ 7
- 0
app/Enums/PaymentTerms.php 查看文件

41
             self::Net90 => 90,
41
             self::Net90 => 90,
42
         };
42
         };
43
     }
43
     }
44
+
45
+    public function getDueDate(): string
46
+    {
47
+        $days = $this->getDays() ?? 0;
48
+
49
+        return now()->addDays($days)->format('M d, Y');
50
+    }
44
 }
51
 }

+ 3
- 1
app/Events/DefaultCurrencyChanged.php 查看文件

9
 
9
 
10
 class DefaultCurrencyChanged
10
 class DefaultCurrencyChanged
11
 {
11
 {
12
-    use Dispatchable, InteractsWithSockets, SerializesModels;
12
+    use Dispatchable;
13
+    use InteractsWithSockets;
14
+    use SerializesModels;
13
 
15
 
14
     public Currency $currency;
16
     public Currency $currency;
15
 
17
 

+ 63
- 24
app/Filament/Company/Pages/Setting/Invoice.php 查看文件

6
 use App\Models\Setting\DocumentDefault as InvoiceModel;
6
 use App\Models\Setting\DocumentDefault as InvoiceModel;
7
 use Filament\Actions\{Action, ActionGroup};
7
 use Filament\Actions\{Action, ActionGroup};
8
 use Filament\Forms\Components\{Checkbox, ColorPicker, Component, FileUpload, Group, Section, Select, TextInput, Textarea, ViewField};
8
 use Filament\Forms\Components\{Checkbox, ColorPicker, Component, FileUpload, Group, Section, Select, TextInput, Textarea, ViewField};
9
-use Filament\Forms\{Form, Get};
9
+use Filament\Forms\{Form, Get, Set};
10
 use Filament\Notifications\Notification;
10
 use Filament\Notifications\Notification;
11
 use Filament\Pages\Concerns\InteractsWithFormActions;
11
 use Filament\Pages\Concerns\InteractsWithFormActions;
12
 use Filament\Pages\Page;
12
 use Filament\Pages\Page;
119
     public function form(Form $form): Form
119
     public function form(Form $form): Form
120
     {
120
     {
121
         return $form
121
         return $form
122
+            ->live()
122
             ->schema([
123
             ->schema([
123
                 $this->getGeneralSection(),
124
                 $this->getGeneralSection(),
124
                 $this->getContentSection(),
125
                 $this->getContentSection(),
135
             ->schema([
136
             ->schema([
136
                 TextInput::make('number_prefix')
137
                 TextInput::make('number_prefix')
137
                     ->label('Number Prefix')
138
                     ->label('Number Prefix')
138
-                    ->live()
139
-                    ->required(),
139
+                    ->nullable(),
140
                 Select::make('number_digits')
140
                 Select::make('number_digits')
141
                     ->label('Number Digits')
141
                     ->label('Number Digits')
142
                     ->options(InvoiceModel::availableNumberDigits())
142
                     ->options(InvoiceModel::availableNumberDigits())
143
+                    ->selectablePlaceholder(false)
143
                     ->native(false)
144
                     ->native(false)
144
-                    ->live()
145
-                    ->required(),
145
+                    ->rule('required'),
146
                 TextInput::make('number_next')
146
                 TextInput::make('number_next')
147
                     ->label('Next Number')
147
                     ->label('Next Number')
148
-                    ->live()
149
                     ->maxLength(static fn (Get $get) => $get('number_digits'))
148
                     ->maxLength(static fn (Get $get) => $get('number_digits'))
150
                     ->suffix(static function (Get $get, $state) {
149
                     ->suffix(static function (Get $get, $state) {
151
                         $number_prefix = $get('number_prefix');
150
                         $number_prefix = $get('number_prefix');
154
 
153
 
155
                         return InvoiceModel::getNumberNext(true, true, $number_prefix, $number_digits, $number_next);
154
                         return InvoiceModel::getNumberNext(true, true, $number_prefix, $number_digits, $number_next);
156
                     })
155
                     })
157
-                    ->required(),
156
+                    ->rule('required'),
158
                 Select::make('payment_terms')
157
                 Select::make('payment_terms')
159
                     ->label('Payment Terms')
158
                     ->label('Payment Terms')
160
                     ->options(PaymentTerms::class)
159
                     ->options(PaymentTerms::class)
160
+                    ->selectablePlaceholder(false)
161
                     ->native(false)
161
                     ->native(false)
162
-                    ->live()
163
-                    ->required(),
162
+                    ->rule('required'),
164
             ])->columns();
163
             ])->columns();
165
     }
164
     }
166
 
165
 
170
             ->schema([
169
             ->schema([
171
                 TextInput::make('header')
170
                 TextInput::make('header')
172
                     ->label('Header')
171
                     ->label('Header')
173
-                    ->live()
174
-                    ->required(),
172
+                    ->nullable(),
175
                 TextInput::make('subheader')
173
                 TextInput::make('subheader')
176
                     ->label('Subheader')
174
                     ->label('Subheader')
177
-                    ->live()
178
                     ->nullable(),
175
                     ->nullable(),
179
                 Textarea::make('terms')
176
                 Textarea::make('terms')
180
                     ->label('Terms')
177
                     ->label('Terms')
181
-                    ->live()
182
                     ->nullable(),
178
                     ->nullable(),
183
                 Textarea::make('footer')
179
                 Textarea::make('footer')
184
                     ->label('Footer / Notes')
180
                     ->label('Footer / Notes')
185
-                    ->live()
186
                     ->nullable(),
181
                     ->nullable(),
187
             ])->columns();
182
             ])->columns();
188
     }
183
     }
193
             ->description('Choose the template and edit the column names.')
188
             ->description('Choose the template and edit the column names.')
194
             ->schema([
189
             ->schema([
195
                 Group::make()
190
                 Group::make()
196
-                    ->live()
197
                     ->schema([
191
                     ->schema([
198
                         FileUpload::make('logo')
192
                         FileUpload::make('logo')
199
                             ->label('Logo')
193
                             ->label('Logo')
231
                             ->label('Template')
225
                             ->label('Template')
232
                             ->native(false)
226
                             ->native(false)
233
                             ->options(Template::class)
227
                             ->options(Template::class)
234
-                            ->required(),
228
+                            ->selectablePlaceholder(false)
229
+                            ->rule('required'),
235
                         Select::make('item_name.option')
230
                         Select::make('item_name.option')
236
                             ->label('Item Name')
231
                             ->label('Item Name')
237
                             ->native(false)
232
                             ->native(false)
238
-                            ->required()
239
-                            ->options(InvoiceModel::getAvailableItemNameOptions()),
233
+                            ->options(InvoiceModel::getAvailableItemNameOptions())
234
+                            ->selectablePlaceholder(false)
235
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
236
+                                if ($state !== 'other' && $old === 'other' && filled($get('item_name.custom'))) {
237
+                                    $set('item_name.old_custom', $get('item_name.custom'));
238
+                                    $set('item_name.custom', null);
239
+                                }
240
+
241
+                                if ($state === 'other' && $old !== 'other') {
242
+                                    $set('item_name.custom', $get('item_name.old_custom'));
243
+                                }
244
+                            })
245
+                            ->rule('required'),
240
                         TextInput::make('item_name.custom')
246
                         TextInput::make('item_name.custom')
241
                             ->hiddenLabel()
247
                             ->hiddenLabel()
242
                             ->disabled(static fn (callable $get) => $get('item_name.option') !== 'other')
248
                             ->disabled(static fn (callable $get) => $get('item_name.option') !== 'other')
244
                         Select::make('unit_name.option')
250
                         Select::make('unit_name.option')
245
                             ->label('Unit Name')
251
                             ->label('Unit Name')
246
                             ->native(false)
252
                             ->native(false)
247
-                            ->required()
248
-                            ->options(InvoiceModel::getAvailableUnitNameOptions()),
253
+                            ->options(InvoiceModel::getAvailableUnitNameOptions())
254
+                            ->selectablePlaceholder(false)
255
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
256
+                                if ($state !== 'other' && $old === 'other' && filled($get('unit_name.custom'))) {
257
+                                    $set('unit_name.old_custom', $get('unit_name.custom'));
258
+                                    $set('unit_name.custom', null);
259
+                                }
260
+
261
+                                if ($state === 'other' && $old !== 'other') {
262
+                                    $set('unit_name.custom', $get('unit_name.old_custom'));
263
+                                }
264
+                            })
265
+                            ->rule('required'),
249
                         TextInput::make('unit_name.custom')
266
                         TextInput::make('unit_name.custom')
250
                             ->hiddenLabel()
267
                             ->hiddenLabel()
251
                             ->disabled(static fn (callable $get) => $get('unit_name.option') !== 'other')
268
                             ->disabled(static fn (callable $get) => $get('unit_name.option') !== 'other')
253
                         Select::make('price_name.option')
270
                         Select::make('price_name.option')
254
                             ->label('Price Name')
271
                             ->label('Price Name')
255
                             ->native(false)
272
                             ->native(false)
256
-                            ->required()
257
-                            ->options(InvoiceModel::getAvailablePriceNameOptions()),
273
+                            ->options(InvoiceModel::getAvailablePriceNameOptions())
274
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
275
+                                if ($state !== 'other' && $old === 'other' && filled($get('price_name.custom'))) {
276
+                                    $set('price_name.old_custom', $get('price_name.custom'));
277
+                                    $set('price_name.custom', null);
278
+                                }
279
+
280
+                                if ($state === 'other' && $old !== 'other') {
281
+                                    $set('price_name.custom', $get('price_name.old_custom'));
282
+                                }
283
+                            })
284
+                            ->selectablePlaceholder(false)
285
+                            ->rule('required'),
258
                         TextInput::make('price_name.custom')
286
                         TextInput::make('price_name.custom')
259
                             ->hiddenLabel()
287
                             ->hiddenLabel()
260
                             ->disabled(static fn (callable $get) => $get('price_name.option') !== 'other')
288
                             ->disabled(static fn (callable $get) => $get('price_name.option') !== 'other')
262
                         Select::make('amount_name.option')
290
                         Select::make('amount_name.option')
263
                             ->label('Amount Name')
291
                             ->label('Amount Name')
264
                             ->native(false)
292
                             ->native(false)
265
-                            ->required()
266
-                            ->options(InvoiceModel::getAvailableAmountNameOptions()),
293
+                            ->options(InvoiceModel::getAvailableAmountNameOptions())
294
+                            ->selectablePlaceholder(false)
295
+                            ->afterStateUpdated(static function (Get $get, Set $set, $state, $old) {
296
+                                if ($state !== 'other' && $old === 'other' && filled($get('amount_name.custom'))) {
297
+                                    $set('amount_name.old_custom', $get('amount_name.custom'));
298
+                                    $set('amount_name.custom', null);
299
+                                }
300
+
301
+                                if ($state === 'other' && $old !== 'other') {
302
+                                    $set('amount_name.custom', $get('amount_name.old_custom'));
303
+                                }
304
+                            })
305
+                            ->rule('required'),
267
                         TextInput::make('amount_name.custom')
306
                         TextInput::make('amount_name.custom')
268
                             ->hiddenLabel()
307
                             ->hiddenLabel()
269
                             ->disabled(static fn (callable $get) => $get('amount_name.option') !== 'other')
308
                             ->disabled(static fn (callable $get) => $get('amount_name.option') !== 'other')

+ 12
- 10
app/Filament/Company/Resources/Setting/CurrencyResource.php 查看文件

43
                             ->required()
43
                             ->required()
44
                             ->hidden(static fn (Forms\Get $get, $state): bool => $get('enabled') && $state !== null)
44
                             ->hidden(static fn (Forms\Get $get, $state): bool => $get('enabled') && $state !== null)
45
                             ->afterStateUpdated(static function (Forms\Set $set, $state) {
45
                             ->afterStateUpdated(static function (Forms\Set $set, $state) {
46
+                                $fields = ['name', 'rate', 'precision', 'symbol', 'symbol_first', 'decimal_mark', 'thousands_separator'];
47
+
46
                                 if ($state === null) {
48
                                 if ($state === null) {
49
+                                    foreach ($fields as $field) {
50
+                                        $set($field, null);
51
+                                    }
47
                                     return;
52
                                     return;
48
                                 }
53
                                 }
49
 
54
 
56
 
61
 
57
                                 $rate = $defaultCurrencyCode ? $currencyService->getCachedExchangeRate($defaultCurrencyCode, $code) : 1;
62
                                 $rate = $defaultCurrencyCode ? $currencyService->getCachedExchangeRate($defaultCurrencyCode, $code) : 1;
58
 
63
 
59
-                                $set('name', $selectedCurrencyCode['name'] ?? '');
60
-                                $set('rate', $rate ?? '');
61
-                                $set('precision', $selectedCurrencyCode['precision'] ?? '');
62
-                                $set('symbol', $selectedCurrencyCode['symbol'] ?? '');
63
-                                $set('symbol_first', $selectedCurrencyCode['symbol_first'] ?? '');
64
-                                $set('decimal_mark', $selectedCurrencyCode['decimal_mark'] ?? '');
65
-                                $set('thousands_separator', $selectedCurrencyCode['thousands_separator'] ?? '');
64
+                                foreach ($fields as $field) {
65
+                                    $set($field, $selectedCurrencyCode[$field] ?? ($field === 'rate' ? $rate : ''));
66
+                                }
66
                             }),
67
                             }),
67
                         Forms\Components\TextInput::make('code')
68
                         Forms\Components\TextInput::make('code')
68
                             ->label('Code')
69
                             ->label('Code')
69
-                            ->hidden(static fn (Forms\Get $get): bool => !($get('enabled') && $get('code') !== null))
70
+                            ->hidden(static fn (Forms\Get $get): bool => ! ($get('enabled') && $get('code') !== null))
70
                             ->disabled(static fn (Forms\Get $get): bool => $get('enabled'))
71
                             ->disabled(static fn (Forms\Get $get): bool => $get('enabled'))
72
+                            ->dehydrated()
71
                             ->required(),
73
                             ->required(),
72
                         Forms\Components\TextInput::make('name')
74
                         Forms\Components\TextInput::make('name')
73
                             ->label('Name')
75
                             ->label('Name')
94
                             ->label('Symbol Position')
96
                             ->label('Symbol Position')
95
                             ->native(false)
97
                             ->native(false)
96
                             ->selectablePlaceholder(false)
98
                             ->selectablePlaceholder(false)
97
-                            ->formatStateUsing(static fn($state) => isset($state) ? (int) $state : null)
99
+                            ->formatStateUsing(static fn ($state) => isset($state) ? (int) $state : null)
98
                             ->boolean('Before Amount', 'After Amount', 'Select a symbol position...')
100
                             ->boolean('Before Amount', 'After Amount', 'Select a symbol position...')
99
                             ->required(),
101
                             ->required(),
100
                         Forms\Components\TextInput::make('decimal_mark')
102
                         Forms\Components\TextInput::make('decimal_mark')
120
                             ->offColor('danger')
122
                             ->offColor('danger')
121
                             ->onColor('primary')
123
                             ->onColor('primary')
122
                             ->afterStateUpdated(static function (Forms\Set $set, Forms\Get $get, $state) {
124
                             ->afterStateUpdated(static function (Forms\Set $set, Forms\Get $get, $state) {
123
-                                $enabledState = (bool)$state;
125
+                                $enabledState = (bool) $state;
124
                                 $code = $get('code');
126
                                 $code = $get('code');
125
 
127
 
126
                                 $defaultCurrencyCode = Currency::getDefaultCurrencyCode();
128
                                 $defaultCurrencyCode = Currency::getDefaultCurrencyCode();

+ 1
- 1
app/Filament/Company/Resources/Setting/CurrencyResource/Pages/EditCurrency.php 查看文件

40
     /**
40
     /**
41
      * @throws Halt
41
      * @throws Halt
42
      */
42
      */
43
-    protected function handleRecordUpdate(Model|Currency $record, array $data): Model|Currency
43
+    protected function handleRecordUpdate(Model | Currency $record, array $data): Model | Currency
44
     {
44
     {
45
         $user = Auth::user();
45
         $user = Auth::user();
46
 
46
 

+ 18
- 33
app/View/Models/InvoiceViewModel.php 查看文件

95
 
95
 
96
     public function payment_terms(): string
96
     public function payment_terms(): string
97
     {
97
     {
98
-        return $this->data['payment_terms'] ?? $this->invoice->payment_terms ?? PaymentTerms::DEFAULT;
98
+        return $this->data['payment_terms'] ?? $this->invoice->payment_terms?->value ?? PaymentTerms::DEFAULT;
99
     }
99
     }
100
 
100
 
101
     public function invoice_due_date(): string
101
     public function invoice_due_date(): string
102
     {
102
     {
103
-        $enumPaymentTerms = PaymentTerms::tryFrom($this->payment_terms());
104
-        $days = $enumPaymentTerms ? $enumPaymentTerms->getDays() : 0;
105
-
106
-        return now()->addDays($days)->format('M d, Y');
103
+        return PaymentTerms::tryFrom($this->payment_terms())?->getDueDate();
107
     }
104
     }
108
 
105
 
109
     // Invoice header related methods
106
     // Invoice header related methods
146
         return $this->data['terms'] ?? $this->invoice->terms ?? 'Payment is due within thirty (30) days from the date of invoice. Any discrepancies should be reported within fourteen (14) days of receipt.';
143
         return $this->data['terms'] ?? $this->invoice->terms ?? 'Payment is due within thirty (30) days from the date of invoice. Any discrepancies should be reported within fourteen (14) days of receipt.';
147
     }
144
     }
148
 
145
 
149
-    // Invoice column related methods
150
-    public function item_name(): string
146
+    public function getItemColumnName(string $column, string $default): string
151
     {
147
     {
152
-        $custom_item_name = $this->data['item_name']['custom'] ?? null;
148
+        $custom = $this->data[$column]['custom'] ?? $this->invoice->{$column . '_custom'} ?? null;
153
 
149
 
154
-        if ($custom_item_name) {
155
-            return $custom_item_name;
150
+        if ($custom) {
151
+            return $custom;
156
         }
152
         }
157
 
153
 
158
-        return ucwords($this->data['item_name']['option']) ?? ucwords($this->invoice->item_name_option) ?? $this->invoice->item_name_custom ?? 'Items';
154
+        $option = $this->data[$column]['option'] ?? $this->invoice->{$column . '_option'} ?? null;
155
+
156
+        return $option ? ucwords($option) : $default;
159
     }
157
     }
160
 
158
 
161
-    public function unit_name(): string
159
+    // Invoice column related methods
160
+    public function item_name(): string
162
     {
161
     {
163
-        $custom_unit_name = $this->data['unit_name']['custom'] ?? null;
164
-
165
-        if ($custom_unit_name) {
166
-            return $custom_unit_name;
167
-        }
162
+        return $this->getItemColumnName('item_name', 'Items');
163
+    }
168
 
164
 
169
-        return ucwords($this->data['unit_name']['option']) ?? ucwords($this->invoice->unit_name_option) ?? $this->invoice->unit_name_custom ?? 'Quantity';
165
+    public function unit_name(): string
166
+    {
167
+        return $this->getItemColumnName('unit_name', 'Quantity');
170
     }
168
     }
171
 
169
 
172
     public function price_name(): string
170
     public function price_name(): string
173
     {
171
     {
174
-        $custom_price_name = $this->data['price_name']['custom'] ?? null;
175
-
176
-        if ($custom_price_name) {
177
-            return $custom_price_name;
178
-        }
179
-
180
-        return ucwords($this->data['price_name']['option']) ?? ucwords($this->invoice->price_name_option) ?? $this->invoice->price_name_custom ?? 'Price';
172
+        return $this->getItemColumnName('price_name', 'Price');
181
     }
173
     }
182
 
174
 
183
     public function amount_name(): string
175
     public function amount_name(): string
184
     {
176
     {
185
-        $custom_amount_name = $this->data['amount_name']['custom'] ?? $this->invoice->amount_name_custom ?? null;
186
-
187
-        if ($custom_amount_name) {
188
-            return $custom_amount_name;
189
-        }
190
-
191
-        return ucwords($this->data['amount_name']['option']) ?? ucwords($this->invoice->amount_name_option) ?? 'Amount';
177
+        return $this->getItemColumnName('amount_name', 'Amount');
192
     }
178
     }
193
 
179
 
194
     public function buildViewData(): array
180
     public function buildViewData(): array
208
             'number_next' => $this->number_next(),
194
             'number_next' => $this->number_next(),
209
             'invoice_number' => $this->invoice_number(),
195
             'invoice_number' => $this->invoice_number(),
210
             'invoice_date' => $this->invoice_date(),
196
             'invoice_date' => $this->invoice_date(),
211
-            'payment_terms' => $this->payment_terms(),
212
             'invoice_due_date' => $this->invoice_due_date(),
197
             'invoice_due_date' => $this->invoice_due_date(),
213
             'header' => $this->header(),
198
             'header' => $this->header(),
214
             'subheader' => $this->subheader(),
199
             'subheader' => $this->subheader(),

+ 2
- 2
database/migrations/2023_09_12_032057_create_document_defaults_table.php 查看文件

18
             $table->string('type');
18
             $table->string('type');
19
             $table->string('logo')->nullable();
19
             $table->string('logo')->nullable();
20
             $table->boolean('show_logo')->default(false);
20
             $table->boolean('show_logo')->default(false);
21
-            $table->string('number_prefix');
21
+            $table->string('number_prefix')->nullable();
22
             $table->unsignedTinyInteger('number_digits')->default(5);
22
             $table->unsignedTinyInteger('number_digits')->default(5);
23
             $table->unsignedBigInteger('number_next')->default(1);
23
             $table->unsignedBigInteger('number_next')->default(1);
24
             $table->string('payment_terms')->default(PaymentTerms::DEFAULT);
24
             $table->string('payment_terms')->default(PaymentTerms::DEFAULT);
25
-            $table->string('header');
25
+            $table->string('header')->nullable();
26
             $table->string('subheader')->nullable();
26
             $table->string('subheader')->nullable();
27
             $table->text('terms')->nullable();
27
             $table->text('terms')->nullable();
28
             $table->text('footer')->nullable();
28
             $table->text('footer')->nullable();

正在加载...
取消
保存