浏览代码

wip refactor

3.x
Andrew Wallo 9 个月前
父节点
当前提交
458dd5f5f8

+ 12
- 5
app/Filament/Company/Resources/Purchases/BillResource.php 查看文件

129
                             })
129
                             })
130
                             ->schema([
130
                             ->schema([
131
                                 Forms\Components\Select::make('offering_id')
131
                                 Forms\Components\Select::make('offering_id')
132
+                                    ->label('Item')
132
                                     ->relationship('purchasableOffering', 'name')
133
                                     ->relationship('purchasableOffering', 'name')
133
                                     ->preload()
134
                                     ->preload()
134
                                     ->searchable()
135
                                     ->searchable()
156
                                     ->live()
157
                                     ->live()
157
                                     ->default(1),
158
                                     ->default(1),
158
                                 Forms\Components\TextInput::make('unit_price')
159
                                 Forms\Components\TextInput::make('unit_price')
160
+                                    ->label('Price')
159
                                     ->hiddenLabel()
161
                                     ->hiddenLabel()
160
                                     ->numeric()
162
                                     ->numeric()
161
                                     ->live()
163
                                     ->live()
162
                                     ->default(0),
164
                                     ->default(0),
163
                                 Forms\Components\Select::make('purchaseTaxes')
165
                                 Forms\Components\Select::make('purchaseTaxes')
166
+                                    ->label('Taxes')
164
                                     ->relationship('purchaseTaxes', 'name')
167
                                     ->relationship('purchaseTaxes', 'name')
165
                                     ->saveRelationshipsUsing(null)
168
                                     ->saveRelationshipsUsing(null)
166
                                     ->dehydrated(true)
169
                                     ->dehydrated(true)
169
                                     ->live()
172
                                     ->live()
170
                                     ->searchable(),
173
                                     ->searchable(),
171
                                 Forms\Components\Select::make('purchaseDiscounts')
174
                                 Forms\Components\Select::make('purchaseDiscounts')
175
+                                    ->label('Discounts')
172
                                     ->relationship('purchaseDiscounts', 'name')
176
                                     ->relationship('purchaseDiscounts', 'name')
173
                                     ->saveRelationshipsUsing(null)
177
                                     ->saveRelationshipsUsing(null)
174
                                     ->dehydrated(true)
178
                                     ->dehydrated(true)
183
                                     ->searchable(),
187
                                     ->searchable(),
184
                                 Forms\Components\Placeholder::make('total')
188
                                 Forms\Components\Placeholder::make('total')
185
                                     ->hiddenLabel()
189
                                     ->hiddenLabel()
190
+                                    ->extraAttributes(['class' => 'text-left sm:text-right'])
186
                                     ->content(function (Forms\Get $get) {
191
                                     ->content(function (Forms\Get $get) {
187
                                         $quantity = max((float) ($get('quantity') ?? 0), 0);
192
                                         $quantity = max((float) ($get('quantity') ?? 0), 0);
188
                                         $unitPrice = max((float) ($get('unit_price') ?? 0), 0);
193
                                         $unitPrice = max((float) ($get('unit_price') ?? 0), 0);
253
                 Tables\Columns\TextColumn::make('vendor.name')
258
                 Tables\Columns\TextColumn::make('vendor.name')
254
                     ->sortable(),
259
                     ->sortable(),
255
                 Tables\Columns\TextColumn::make('total')
260
                 Tables\Columns\TextColumn::make('total')
256
-                    ->currency()
257
-                    ->sortable(),
261
+                    ->currencyWithConversion(static fn (Bill $record) => $record->currency_code)
262
+                    ->sortable()
263
+                    ->toggleable(),
258
                 Tables\Columns\TextColumn::make('amount_paid')
264
                 Tables\Columns\TextColumn::make('amount_paid')
259
                     ->label('Amount Paid')
265
                     ->label('Amount Paid')
260
-                    ->currency()
261
-                    ->sortable(),
266
+                    ->currencyWithConversion(static fn (Bill $record) => $record->currency_code)
267
+                    ->sortable()
268
+                    ->toggleable(),
262
                 Tables\Columns\TextColumn::make('amount_due')
269
                 Tables\Columns\TextColumn::make('amount_due')
263
                     ->label('Amount Due')
270
                     ->label('Amount Due')
264
-                    ->currency()
271
+                    ->currencyWithConversion(static fn (Bill $record) => $record->currency_code)
265
                     ->sortable(),
272
                     ->sortable(),
266
             ])
273
             ])
267
             ->filters([
274
             ->filters([

+ 1
- 0
app/Filament/Company/Resources/Sales/InvoiceResource.php 查看文件

240
                                     ->searchable(),
240
                                     ->searchable(),
241
                                 Forms\Components\Placeholder::make('total')
241
                                 Forms\Components\Placeholder::make('total')
242
                                     ->hiddenLabel()
242
                                     ->hiddenLabel()
243
+                                    ->extraAttributes(['class' => 'text-left sm:text-right'])
243
                                     ->content(function (Forms\Get $get) {
244
                                     ->content(function (Forms\Get $get) {
244
                                         $quantity = max((float) ($get('quantity') ?? 0), 0);
245
                                         $quantity = max((float) ($get('quantity') ?? 0), 0);
245
                                         $unitPrice = max((float) ($get('unit_price') ?? 0), 0);
246
                                         $unitPrice = max((float) ($get('unit_price') ?? 0), 0);

+ 10
- 9
app/Filament/Forms/Components/DocumentTotals.php 查看文件

20
         parent::setUp();
20
         parent::setUp();
21
 
21
 
22
         $this->schema([
22
         $this->schema([
23
-            TextInput::make('discount_rate')
24
-                ->label('Discount Rate')
25
-                ->hiddenLabel()
26
-                ->live()
27
-                ->rate(computation: static fn (Get $get) => $get('discount_computation'), showAffix: false),
28
             Select::make('discount_computation')
23
             Select::make('discount_computation')
29
                 ->label('Discount Computation')
24
                 ->label('Discount Computation')
30
                 ->hiddenLabel()
25
                 ->hiddenLabel()
31
-                ->options([
32
-                    'percentage' => '%',
33
-                    'fixed' => '$',
34
-                ])
26
+                ->options(AdjustmentComputation::class)
35
                 ->default(AdjustmentComputation::Percentage)
27
                 ->default(AdjustmentComputation::Percentage)
36
                 ->selectablePlaceholder(false)
28
                 ->selectablePlaceholder(false)
37
                 ->live(),
29
                 ->live(),
30
+            TextInput::make('discount_rate')
31
+                ->label('Discount Rate')
32
+                ->hiddenLabel()
33
+                ->live()
34
+                ->extraInputAttributes(['class' => 'text-right'])
35
+                ->rate(
36
+                    computation: static fn (Get $get) => $get('discount_computation'),
37
+                    currency: static fn (Get $get) => $get('currency_code'),
38
+                ),
38
         ]);
39
         ]);
39
     }
40
     }
40
 
41
 

+ 29
- 35
app/Helpers/format.php 查看文件

1
 <?php
1
 <?php
2
 
2
 
3
+use App\Enums\Accounting\AdjustmentComputation;
3
 use App\Enums\Setting\NumberFormat;
4
 use App\Enums\Setting\NumberFormat;
4
 use App\Models\Setting\Localization;
5
 use App\Models\Setting\Localization;
5
 use Filament\Support\RawJs;
6
 use Filament\Support\RawJs;
44
 if (! function_exists('ratePrefix')) {
45
 if (! function_exists('ratePrefix')) {
45
     function ratePrefix($computation, ?string $currency = null): ?string
46
     function ratePrefix($computation, ?string $currency = null): ?string
46
     {
47
     {
47
-        if ($computation instanceof BackedEnum) {
48
-            $computation = $computation->value;
49
-        }
48
+        $computationEnum = AdjustmentComputation::parse($computation);
49
+        $localization = Localization::firstOrFail();
50
 
50
 
51
-        if ($computation === 'fixed') {
52
-            return currency($currency)->getCodePrefix();
51
+        if ($computationEnum->isFixed() && currency($currency)->isSymbolFirst()) {
52
+            return currency($currency)->getPrefix();
53
         }
53
         }
54
 
54
 
55
-        if ($computation === 'percentage' || $computation === 'compound') {
56
-            $percent_first = Localization::firstOrFail()->percent_first;
57
-
58
-            return $percent_first ? '%' : null;
55
+        if ($computationEnum->isPercentage() && $localization->percent_first) {
56
+            return '%';
59
         }
57
         }
60
 
58
 
61
         return null;
59
         return null;
65
 if (! function_exists('rateSuffix')) {
63
 if (! function_exists('rateSuffix')) {
66
     function rateSuffix($computation, ?string $currency = null): ?string
64
     function rateSuffix($computation, ?string $currency = null): ?string
67
     {
65
     {
68
-        if ($computation instanceof BackedEnum) {
69
-            $computation = $computation->value;
70
-        }
71
-
72
-        if ($computation === 'percentage' || $computation === 'compound') {
73
-            $percent_first = Localization::firstOrFail()->percent_first;
66
+        $computationEnum = AdjustmentComputation::parse($computation);
67
+        $localization = Localization::firstOrFail();
74
 
68
 
75
-            return $percent_first ? null : '%';
69
+        if ($computationEnum->isFixed() && ! currency($currency)->isSymbolFirst()) {
70
+            return currency($currency)->getSuffix();
76
         }
71
         }
77
 
72
 
78
-        if ($computation === 'fixed') {
79
-            return currency($currency)->getCodeSuffix();
73
+        if ($computationEnum->isPercentage() && ! $localization->percent_first) {
74
+            return '%';
80
         }
75
         }
81
 
76
 
82
         return null;
77
         return null;
84
 }
79
 }
85
 
80
 
86
 if (! function_exists('rateMask')) {
81
 if (! function_exists('rateMask')) {
87
-    function rateMask($computation, ?string $currency = null): RawJs
82
+    function rateMask($computation, ?string $currency = null): ?RawJs
88
     {
83
     {
89
-        if ($computation instanceof BackedEnum) {
90
-            $computation = $computation->value;
91
-        }
84
+        $computationEnum = AdjustmentComputation::parse($computation);
92
 
85
 
93
-        if ($computation === 'percentage' || $computation === 'compound') {
86
+        if ($computationEnum->isPercentage()) {
94
             return percentMask(4);
87
             return percentMask(4);
95
         }
88
         }
96
 
89
 
97
-        $precision = currency($currency)->getPrecision();
90
+        if ($computationEnum->isFixed()) {
91
+            $precision = currency($currency)->getPrecision();
98
 
92
 
99
-        return RawJs::make(generateJsCode($precision, $currency));
93
+            return RawJs::make(generateJsCode($precision, $currency));
94
+        }
95
+
96
+        return null;
100
     }
97
     }
101
 }
98
 }
102
 
99
 
107
             return null;
104
             return null;
108
         }
105
         }
109
 
106
 
110
-        if ($computation instanceof BackedEnum) {
111
-            $computation = $computation->value;
112
-        }
113
-
114
-        if ($computation === 'percentage' || $computation === 'compound') {
115
-            $percent_first = Localization::firstOrFail()->percent_first;
107
+        $computationEnum = AdjustmentComputation::parse($computation);
108
+        $localization = Localization::firstOrFail();
116
 
109
 
117
-            if ($percent_first) {
118
-                return '%' . $state;
119
-            }
110
+        if ($computationEnum->isPercentage() && $localization->percent_first) {
111
+            return '%' . $state;
112
+        }
120
 
113
 
114
+        if ($computationEnum->isPercentage() && ! $localization->percent_first) {
121
             return $state . '%';
115
             return $state . '%';
122
         }
116
         }
123
 
117
 
124
-        if ($computation === 'fixed') {
118
+        if ($computationEnum->isFixed()) {
125
             return money($state, $currency, true)->formatWithCode();
119
             return money($state, $currency, true)->formatWithCode();
126
         }
120
         }
127
 
121
 

+ 51
- 43
app/Providers/MacroServiceProvider.php 查看文件

4
 
4
 
5
 use Akaunting\Money\Currency;
5
 use Akaunting\Money\Currency;
6
 use Akaunting\Money\Money;
6
 use Akaunting\Money\Money;
7
+use App\Enums\Accounting\AdjustmentComputation;
7
 use App\Enums\Setting\DateFormat;
8
 use App\Enums\Setting\DateFormat;
8
 use App\Models\Accounting\AccountSubtype;
9
 use App\Models\Accounting\AccountSubtype;
9
 use App\Models\Setting\Localization;
10
 use App\Models\Setting\Localization;
62
             return $this;
63
             return $this;
63
         });
64
         });
64
 
65
 
66
+        TextInput::macro('rate', function (string | Closure | null $computation = null, string | Closure | null $currency = null, bool $showAffix = true): static {
67
+            return $this
68
+                ->when(
69
+                    $showAffix,
70
+                    fn (TextInput $component) => $component
71
+                        ->prefix(function (TextInput $component) use ($computation, $currency) {
72
+                            $evaluatedComputation = $component->evaluate($computation);
73
+                            $evaluatedCurrency = $component->evaluate($currency);
74
+
75
+                            return ratePrefix($evaluatedComputation, $evaluatedCurrency);
76
+                        })
77
+                        ->suffix(function (TextInput $component) use ($computation, $currency) {
78
+                            $evaluatedComputation = $component->evaluate($computation);
79
+                            $evaluatedCurrency = $component->evaluate($currency);
80
+
81
+                            return rateSuffix($evaluatedComputation, $evaluatedCurrency);
82
+                        })
83
+                )
84
+                ->mask(static function (TextInput $component) use ($computation, $currency) {
85
+                    $computation = $component->evaluate($computation);
86
+                    $currency = $component->evaluate($currency);
87
+
88
+                    $computationEnum = AdjustmentComputation::parse($computation);
89
+
90
+                    if ($computationEnum->isPercentage()) {
91
+                        return rateMask(computation: $computation);
92
+                    }
93
+
94
+                    return moneyMask($currency);
95
+                })
96
+                ->rule(static function (TextInput $component) use ($computation) {
97
+                    return static function (string $attribute, $value, Closure $fail) use ($computation, $component) {
98
+                        $computation = $component->evaluate($computation);
99
+                        $numericValue = (float) $value;
100
+
101
+                        if ($computation instanceof BackedEnum) {
102
+                            $computation = $computation->value;
103
+                        }
104
+
105
+                        if ($computation === 'percentage' || $computation === 'compound') {
106
+                            if ($numericValue < 0 || $numericValue > 100) {
107
+                                $fail(translate('The rate must be between 0 and 100.'));
108
+                            }
109
+                        } elseif ($computation === 'fixed' && $numericValue < 0) {
110
+                            $fail(translate('The rate must be greater than 0.'));
111
+                        }
112
+                    };
113
+                });
114
+        });
115
+
65
         TextColumn::macro('defaultDateFormat', function (): static {
116
         TextColumn::macro('defaultDateFormat', function (): static {
66
             $localization = Localization::firstOrFail();
117
             $localization = Localization::firstOrFail();
67
 
118
 
190
             return $this;
241
             return $this;
191
         });
242
         });
192
 
243
 
193
-        TextInput::macro('rate', function (string | Closure | null $computation = null, bool $showAffix = true): static {
194
-            $this
195
-                ->when(
196
-                    $showAffix,
197
-                    fn (TextInput $component) => $component
198
-                        ->prefix(static function (TextInput $component) use ($computation) {
199
-                            $computation = $component->evaluate($computation);
200
-
201
-                            return ratePrefix(computation: $computation);
202
-                        })
203
-                        ->suffix(static function (TextInput $component) use ($computation) {
204
-                            $computation = $component->evaluate($computation);
205
-
206
-                            return rateSuffix(computation: $computation);
207
-                        })
208
-                )
209
-                ->mask(static function (TextInput $component) use ($computation) {
210
-                    $computation = $component->evaluate($computation);
211
-
212
-                    return rateMask(computation: $computation);
213
-                })
214
-                ->rule(static function (TextInput $component) use ($computation) {
215
-                    return static function (string $attribute, $value, Closure $fail) use ($computation, $component) {
216
-                        $computation = $component->evaluate($computation);
217
-                        $numericValue = (float) $value;
218
-
219
-                        if ($computation instanceof BackedEnum) {
220
-                            $computation = $computation->value;
221
-                        }
222
-
223
-                        if ($computation === 'percentage' || $computation === 'compound') {
224
-                            if ($numericValue < 0 || $numericValue > 100) {
225
-                                $fail(translate('The rate must be between 0 and 100.'));
226
-                            }
227
-                        } elseif ($computation === 'fixed' && $numericValue < 0) {
228
-                            $fail(translate('The rate must be greater than 0.'));
229
-                        }
230
-                    };
231
-                });
232
-
233
-            return $this;
234
-        });
235
-
236
         Field::macro('validateAccountCode', function (string | Closure | null $subtype = null): static {
244
         Field::macro('validateAccountCode', function (string | Closure | null $subtype = null): static {
237
             $this
245
             $this
238
                 ->rules([
246
                 ->rules([

+ 5
- 1
app/View/Models/DocumentTotalViewModel.php 查看文件

86
         }
86
         }
87
 
87
 
88
         $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage;
88
         $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage;
89
-        $discountRate = $this->data['discount_rate'] ?? '0';
89
+        $discountRate = blank($this->data['discount_rate']) ? '0' : $this->data['discount_rate'];
90
 
90
 
91
         if ($discountComputation->isPercentage()) {
91
         if ($discountComputation->isPercentage()) {
92
             $scaledDiscountRate = RateCalculator::parseLocalizedRate($discountRate);
92
             $scaledDiscountRate = RateCalculator::parseLocalizedRate($discountRate);
94
             return RateCalculator::calculatePercentage($subtotalInCents, $scaledDiscountRate);
94
             return RateCalculator::calculatePercentage($subtotalInCents, $scaledDiscountRate);
95
         }
95
         }
96
 
96
 
97
+        if (! CurrencyConverter::isValidAmount($discountRate)) {
98
+            $discountRate = '0';
99
+        }
100
+
97
         return CurrencyConverter::convertToCents($discountRate, $currencyCode);
101
         return CurrencyConverter::convertToCents($discountRate, $currencyCode);
98
     }
102
     }
99
 
103
 

+ 43
- 5
resources/views/filament/forms/components/document-totals.blade.php 查看文件

12
     $isPerDocumentDiscount = $discountMethod->isPerDocument();
12
     $isPerDocumentDiscount = $discountMethod->isPerDocument();
13
 @endphp
13
 @endphp
14
 
14
 
15
-<div class="totals-summary w-full pr-14">
16
-    <table class="w-full text-right table-fixed">
15
+<div class="totals-summary w-full sm:pr-14">
16
+    <table class="w-full text-right table-fixed hidden sm:table">
17
         <colgroup>
17
         <colgroup>
18
             <col class="w-[20%]"> {{-- Items --}}
18
             <col class="w-[20%]"> {{-- Items --}}
19
             <col class="w-[30%]"> {{-- Description --}}
19
             <col class="w-[30%]"> {{-- Description --}}
35
             </tr>
35
             </tr>
36
             @if($isPerDocumentDiscount)
36
             @if($isPerDocumentDiscount)
37
                 <tr>
37
                 <tr>
38
-                    <td colspan="4" class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white text-right">Discount:</td>
39
-                    <td class="text-sm px-4 py-2">
38
+                    <td colspan="3" class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white text-right">Discount:</td>
39
+                    <td colspan="2" class="text-sm px-4 py-2">
40
                         <div class="flex justify-between space-x-2">
40
                         <div class="flex justify-between space-x-2">
41
                             @foreach($getChildComponentContainer()->getComponents() as $component)
41
                             @foreach($getChildComponentContainer()->getComponents() as $component)
42
-                                <div class="flex-1">{{ $component }}</div>
42
+                                <div class="flex-1 text-left">{{ $component }}</div>
43
                             @endforeach
43
                             @endforeach
44
                         </div>
44
                         </div>
45
                     </td>
45
                     </td>
66
             @endif
66
             @endif
67
         </tbody>
67
         </tbody>
68
     </table>
68
     </table>
69
+
70
+    <!-- Mobile View -->
71
+    <div class="block sm:hidden p-5">
72
+        <div class="flex flex-col space-y-6">
73
+            <div class="flex justify-between items-center">
74
+                <span class="text-sm font-medium text-gray-950 dark:text-white">Subtotal:</span>
75
+                <span class="text-sm text-gray-950 dark:text-white">{{ $subtotal }}</span>
76
+            </div>
77
+            <div class="flex justify-between items-center">
78
+                <span class="text-sm font-medium text-gray-950 dark:text-white">Taxes:</span>
79
+                <span class="text-sm text-gray-950 dark:text-white">{{ $taxTotal }}</span>
80
+            </div>
81
+            @if($isPerDocumentDiscount)
82
+                <div class="flex flex-col space-y-2">
83
+                    <span class="text-sm font-medium text-gray-950 dark:text-white">Discount:</span>
84
+                    <div class="flex justify-between space-x-2">
85
+                        @foreach($getChildComponentContainer()->getComponents() as $component)
86
+                            <div class="w-1/2">{{ $component }}</div>
87
+                        @endforeach
88
+                    </div>
89
+                </div>
90
+            @else
91
+                <div class="flex justify-between items-center">
92
+                    <span class="text-sm font-medium text-gray-950 dark:text-white">Discounts:</span>
93
+                    <span class="text-sm text-gray-950 dark:text-white">({{ $discountTotal }})</span>
94
+                </div>
95
+            @endif
96
+            <div class="flex justify-between items-center font-semibold">
97
+                <span class="text-sm font-medium text-gray-950 dark:text-white">Total:</span>
98
+                <span class="text-sm text-gray-950 dark:text-white">{{ $grandTotal }}</span>
99
+            </div>
100
+            @if($conversionMessage)
101
+                <div class="text-sm text-gray-600">
102
+                    {{ $conversionMessage }}
103
+                </div>
104
+            @endif
105
+        </div>
106
+    </div>
69
 </div>
107
 </div>

正在加载...
取消
保存