Andrew Wallo преди 10 месеца
родител
ревизия
4a3f7250c9

+ 3
- 0
app/Enums/Accounting/AdjustmentComputation.php Целия файл

@@ -2,10 +2,13 @@
2 2
 
3 3
 namespace App\Enums\Accounting;
4 4
 
5
+use App\Enums\Concerns\ParsesEnum;
5 6
 use Filament\Support\Contracts\HasLabel;
6 7
 
7 8
 enum AdjustmentComputation: string implements HasLabel
8 9
 {
10
+    use ParsesEnum;
11
+
9 12
     case Percentage = 'percentage';
10 13
     case Fixed = 'fixed';
11 14
 

+ 25
- 23
app/Filament/Company/Resources/Sales/InvoiceResource.php Целия файл

@@ -9,6 +9,7 @@ use App\Enums\Accounting\PaymentMethod;
9 9
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
10 10
 use App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
11 11
 use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets;
12
+use App\Filament\Forms\Components\InvoiceTotals;
12 13
 use App\Filament\Tables\Actions\ReplicateBulkAction;
13 14
 use App\Filament\Tables\Filters\DateRangeFilter;
14 15
 use App\Models\Accounting\Adjustment;
@@ -137,20 +138,14 @@ class InvoiceResource extends Resource
137 138
                                     ])
138 139
                                     ->selectablePlaceholder(false)
139 140
                                     ->default('line_items')
141
+                                    ->afterStateUpdated(function ($state, Forms\Set $set) {
142
+                                        $discountMethod = $state;
143
+
144
+                                        if ($discountMethod === 'invoice') {
145
+                                            $set('lineItems.*.salesDiscounts', []);
146
+                                        }
147
+                                    })
140 148
                                     ->live(),
141
-                                Forms\Components\Grid::make(2)
142
-                                    ->schema([
143
-                                        Forms\Components\Select::make('discount_computation')
144
-                                            ->label('Discount Computation')
145
-                                            ->options(AdjustmentComputation::class)
146
-                                            ->default(AdjustmentComputation::Percentage)
147
-                                            ->selectablePlaceholder(false)
148
-                                            ->live(),
149
-                                        Forms\Components\TextInput::make('discount_rate')
150
-                                            ->label('Discount Rate')
151
-                                            ->live()
152
-                                            ->rate(static fn (Forms\Get $get) => $get('discount_computation')),
153
-                                    ])->visible(fn (Forms\Get $get) => $get('discount_method') === 'invoice'),
154 149
                             ])->grow(true),
155 150
                         ])->from('md'),
156 151
                         TableRepeater::make('lineItems')
@@ -238,16 +233,29 @@ class InvoiceResource extends Resource
238 233
                                 $invoice = $component->getRecord();
239 234
 
240 235
                                 // Recalculate totals for line items
241
-                                $invoice->lineItems()->each(function (DocumentLineItem $lineItem) {
236
+                                // Recalculate totals for line items
237
+                                $invoice->lineItems()->each(function (DocumentLineItem $lineItem) use ($invoice) {
242 238
                                     $lineItem->updateQuietly([
243 239
                                         'tax_total' => $lineItem->calculateTaxTotal()->getAmount(),
244
-                                        'discount_total' => $lineItem->calculateDiscountTotal()->getAmount(),
240
+                                        'discount_total' => $invoice->discount_method === 'line_items'
241
+                                            ? $lineItem->calculateDiscountTotal()->getAmount()
242
+                                            : 0,
245 243
                                     ]);
246 244
                                 });
247 245
 
248 246
                                 $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
249 247
                                 $taxTotal = $invoice->lineItems()->sum('tax_total') / 100;
250
-                                $discountTotal = $invoice->lineItems()->sum('discount_total') / 100;
248
+                                if ($invoice->discount_method === 'line_items') {
249
+                                    $discountTotal = $invoice->lineItems()->sum('discount_total') / 100;
250
+                                } else {
251
+                                    // Calculate invoice-level discount
252
+                                    if ($invoice->discount_computation === AdjustmentComputation::Percentage) {
253
+                                        $discountTotal = $subtotal * ($invoice->discount_rate / 100);
254
+                                    } else {
255
+                                        $discountTotal = $invoice->discount_rate;
256
+                                    }
257
+                                }
258
+
251 259
                                 $grandTotal = $subtotal + $taxTotal - $discountTotal;
252 260
 
253 261
                                 $invoice->updateQuietly([
@@ -360,13 +368,7 @@ class InvoiceResource extends Resource
360 368
                                         return CurrencyConverter::formatToMoney($total);
361 369
                                     }),
362 370
                             ]),
363
-                        Forms\Components\Grid::make(6)
364
-                            ->schema([
365
-                                Forms\Components\ViewField::make('totals')
366
-                                    ->columnStart(5)
367
-                                    ->columnSpan(2)
368
-                                    ->view('filament.forms.components.invoice-totals'),
369
-                            ]),
371
+                        InvoiceTotals::make(),
370 372
                         Forms\Components\Textarea::make('terms')
371 373
                             ->columnSpanFull(),
372 374
                     ]),

+ 37
- 0
app/Filament/Forms/Components/InvoiceTotals.php Целия файл

@@ -0,0 +1,37 @@
1
+<?php
2
+
3
+namespace App\Filament\Forms\Components;
4
+
5
+use App\Enums\Accounting\AdjustmentComputation;
6
+use Filament\Forms\Components\Grid;
7
+use Filament\Forms\Components\Select;
8
+use Filament\Forms\Components\TextInput;
9
+use Filament\Forms\Get;
10
+
11
+class InvoiceTotals extends Grid
12
+{
13
+    protected string $view = 'filament.forms.components.invoice-totals';
14
+
15
+    protected function setUp(): void
16
+    {
17
+        parent::setUp();
18
+
19
+        $this->schema([
20
+            TextInput::make('discount_rate')
21
+                ->label('Discount Rate')
22
+                ->hiddenLabel()
23
+                ->live()
24
+                ->rate(computation: static fn (Get $get) => $get('discount_computation'), showAffix: false),
25
+            Select::make('discount_computation')
26
+                ->label('Discount Computation')
27
+                ->hiddenLabel()
28
+                ->options([
29
+                    'percentage' => '%',
30
+                    'fixed' => '$',
31
+                ])
32
+                ->default(AdjustmentComputation::Percentage)
33
+                ->selectablePlaceholder(false)
34
+                ->live(),
35
+        ]);
36
+    }
37
+}

+ 1
- 0
app/Models/Accounting/Invoice.php Целия файл

@@ -53,6 +53,7 @@ class Invoice extends Model
53 53
         'last_sent',
54 54
         'status',
55 55
         'currency_code',
56
+        'discount_method',
56 57
         'discount_computation',
57 58
         'discount_rate',
58 59
         'subtotal',

+ 15
- 11
app/Providers/MacroServiceProvider.php Целия файл

@@ -102,18 +102,22 @@ class MacroServiceProvider extends ServiceProvider
102 102
             return $this;
103 103
         });
104 104
 
105
-        TextInput::macro('rate', function (string | Closure | null $computation = null): static {
105
+        TextInput::macro('rate', function (string | Closure | null $computation = null, bool $showAffix = true): static {
106 106
             $this
107
-                ->prefix(static function (TextInput $component) use ($computation) {
108
-                    $computation = $component->evaluate($computation);
109
-
110
-                    return ratePrefix(computation: $computation);
111
-                })
112
-                ->suffix(static function (TextInput $component) use ($computation) {
113
-                    $computation = $component->evaluate($computation);
114
-
115
-                    return rateSuffix(computation: $computation);
116
-                })
107
+                ->when(
108
+                    $showAffix,
109
+                    fn (TextInput $component) => $component
110
+                        ->prefix(static function (TextInput $component) use ($computation) {
111
+                            $computation = $component->evaluate($computation);
112
+
113
+                            return ratePrefix(computation: $computation);
114
+                        })
115
+                        ->suffix(static function (TextInput $component) use ($computation) {
116
+                            $computation = $component->evaluate($computation);
117
+
118
+                            return rateSuffix(computation: $computation);
119
+                        })
120
+                )
117 121
                 ->mask(static function (TextInput $component) use ($computation) {
118 122
                     $computation = $component->evaluate($computation);
119 123
 

+ 2
- 2
app/View/Models/InvoiceTotalViewModel.php Целия файл

@@ -18,7 +18,7 @@ class InvoiceTotalViewModel
18 18
     {
19 19
         $lineItems = collect($this->data['lineItems'] ?? []);
20 20
 
21
-        $subtotal = $lineItems->sum(function ($item) {
21
+        $subtotal = $lineItems->sum(static function ($item) {
22 22
             $quantity = max((float) ($item['quantity'] ?? 0), 0);
23 23
             $unitPrice = max((float) ($item['unit_price'] ?? 0), 0);
24 24
 
@@ -58,7 +58,7 @@ class InvoiceTotalViewModel
58 58
             $discountComputation = $this->data['discount_computation'] ?? AdjustmentComputation::Percentage;
59 59
             $discountRate = (float) ($this->data['discount_rate'] ?? 0);
60 60
 
61
-            if ($discountComputation === AdjustmentComputation::Percentage) {
61
+            if (AdjustmentComputation::parse($discountComputation) === AdjustmentComputation::Percentage) {
62 62
                 $discountTotal = $subtotal * ($discountRate / 100);
63 63
             } else {
64 64
                 $discountTotal = $discountRate;

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

@@ -497,16 +497,16 @@
497 497
         },
498 498
         {
499 499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.334.2",
500
+            "version": "3.334.3",
501 501
             "source": {
502 502
                 "type": "git",
503 503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "b19afc076bb1cc2617bdef76efd41587596109e7"
504
+                "reference": "6576a9fcfc6ae7c76aed3c6fa4c3864060f72d04"
505 505
             },
506 506
             "dist": {
507 507
                 "type": "zip",
508
-                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/b19afc076bb1cc2617bdef76efd41587596109e7",
509
-                "reference": "b19afc076bb1cc2617bdef76efd41587596109e7",
508
+                "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/6576a9fcfc6ae7c76aed3c6fa4c3864060f72d04",
509
+                "reference": "6576a9fcfc6ae7c76aed3c6fa4c3864060f72d04",
510 510
                 "shasum": ""
511 511
             },
512 512
             "require": {
@@ -589,9 +589,9 @@
589 589
             "support": {
590 590
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
591 591
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
592
-                "source": "https://github.com/aws/aws-sdk-php/tree/3.334.2"
592
+                "source": "https://github.com/aws/aws-sdk-php/tree/3.334.3"
593 593
             },
594
-            "time": "2024-12-09T19:30:23+00:00"
594
+            "time": "2024-12-10T19:41:55+00:00"
595 595
         },
596 596
         {
597 597
             "name": "aws/aws-sdk-php-laravel",
@@ -2980,16 +2980,16 @@
2980 2980
         },
2981 2981
         {
2982 2982
             "name": "laravel/framework",
2983
-            "version": "v11.34.2",
2983
+            "version": "v11.35.0",
2984 2984
             "source": {
2985 2985
                 "type": "git",
2986 2986
                 "url": "https://github.com/laravel/framework.git",
2987
-                "reference": "865da6d73dd353f07a7bcbd778c55966a620121f"
2987
+                "reference": "f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc"
2988 2988
             },
2989 2989
             "dist": {
2990 2990
                 "type": "zip",
2991
-                "url": "https://api.github.com/repos/laravel/framework/zipball/865da6d73dd353f07a7bcbd778c55966a620121f",
2992
-                "reference": "865da6d73dd353f07a7bcbd778c55966a620121f",
2991
+                "url": "https://api.github.com/repos/laravel/framework/zipball/f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc",
2992
+                "reference": "f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc",
2993 2993
                 "shasum": ""
2994 2994
             },
2995 2995
             "require": {
@@ -3013,6 +3013,7 @@
3013 3013
                 "league/commonmark": "^2.2.1",
3014 3014
                 "league/flysystem": "^3.25.1",
3015 3015
                 "league/flysystem-local": "^3.25.1",
3016
+                "league/uri": "^7.5.1",
3016 3017
                 "monolog/monolog": "^3.0",
3017 3018
                 "nesbot/carbon": "^2.72.2|^3.4",
3018 3019
                 "nunomaduro/termwind": "^2.0",
@@ -3096,9 +3097,9 @@
3096 3097
                 "league/flysystem-read-only": "^3.25.1",
3097 3098
                 "league/flysystem-sftp-v3": "^3.25.1",
3098 3099
                 "mockery/mockery": "^1.6.10",
3099
-                "nyholm/psr7": "^1.2",
3100 3100
                 "orchestra/testbench-core": "^9.6",
3101 3101
                 "pda/pheanstalk": "^5.0.6",
3102
+                "php-http/discovery": "^1.15",
3102 3103
                 "phpstan/phpstan": "^1.11.5",
3103 3104
                 "phpunit/phpunit": "^10.5.35|^11.3.6",
3104 3105
                 "predis/predis": "^2.3",
@@ -3130,8 +3131,8 @@
3130 3131
                 "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
3131 3132
                 "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
3132 3133
                 "mockery/mockery": "Required to use mocking (^1.6).",
3133
-                "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
3134 3134
                 "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
3135
+                "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).",
3135 3136
                 "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).",
3136 3137
                 "predis/predis": "Required to use the predis connector (^2.3).",
3137 3138
                 "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
@@ -3152,6 +3153,7 @@
3152 3153
             },
3153 3154
             "autoload": {
3154 3155
                 "files": [
3156
+                    "src/Illuminate/Collections/functions.php",
3155 3157
                     "src/Illuminate/Collections/helpers.php",
3156 3158
                     "src/Illuminate/Events/functions.php",
3157 3159
                     "src/Illuminate/Filesystem/functions.php",
@@ -3189,7 +3191,7 @@
3189 3191
                 "issues": "https://github.com/laravel/framework/issues",
3190 3192
                 "source": "https://github.com/laravel/framework"
3191 3193
             },
3192
-            "time": "2024-11-27T15:43:57+00:00"
3194
+            "time": "2024-12-10T16:09:29+00:00"
3193 3195
         },
3194 3196
         {
3195 3197
             "name": "laravel/prompts",
@@ -3252,16 +3254,16 @@
3252 3254
         },
3253 3255
         {
3254 3256
             "name": "laravel/sanctum",
3255
-            "version": "v4.0.5",
3257
+            "version": "v4.0.6",
3256 3258
             "source": {
3257 3259
                 "type": "git",
3258 3260
                 "url": "https://github.com/laravel/sanctum.git",
3259
-                "reference": "fe361b9a63407a228f884eb78d7217f680b50140"
3261
+                "reference": "9e069e36d90b1e1f41886efa0fe9800a6b354694"
3260 3262
             },
3261 3263
             "dist": {
3262 3264
                 "type": "zip",
3263
-                "url": "https://api.github.com/repos/laravel/sanctum/zipball/fe361b9a63407a228f884eb78d7217f680b50140",
3264
-                "reference": "fe361b9a63407a228f884eb78d7217f680b50140",
3265
+                "url": "https://api.github.com/repos/laravel/sanctum/zipball/9e069e36d90b1e1f41886efa0fe9800a6b354694",
3266
+                "reference": "9e069e36d90b1e1f41886efa0fe9800a6b354694",
3265 3267
                 "shasum": ""
3266 3268
             },
3267 3269
             "require": {
@@ -3312,7 +3314,7 @@
3312 3314
                 "issues": "https://github.com/laravel/sanctum/issues",
3313 3315
                 "source": "https://github.com/laravel/sanctum"
3314 3316
             },
3315
-            "time": "2024-11-26T14:36:23+00:00"
3317
+            "time": "2024-11-26T21:18:33+00:00"
3316 3318
         },
3317 3319
         {
3318 3320
             "name": "laravel/serializable-closure",
@@ -3979,16 +3981,16 @@
3979 3981
         },
3980 3982
         {
3981 3983
             "name": "league/oauth1-client",
3982
-            "version": "v1.10.1",
3984
+            "version": "v1.11.0",
3983 3985
             "source": {
3984 3986
                 "type": "git",
3985 3987
                 "url": "https://github.com/thephpleague/oauth1-client.git",
3986
-                "reference": "d6365b901b5c287dd41f143033315e2f777e1167"
3988
+                "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055"
3987 3989
             },
3988 3990
             "dist": {
3989 3991
                 "type": "zip",
3990
-                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167",
3991
-                "reference": "d6365b901b5c287dd41f143033315e2f777e1167",
3992
+                "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055",
3993
+                "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055",
3992 3994
                 "shasum": ""
3993 3995
             },
3994 3996
             "require": {
@@ -4049,9 +4051,9 @@
4049 4051
             ],
4050 4052
             "support": {
4051 4053
                 "issues": "https://github.com/thephpleague/oauth1-client/issues",
4052
-                "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1"
4054
+                "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0"
4053 4055
             },
4054
-            "time": "2022-04-15T14:02:14+00:00"
4056
+            "time": "2024-12-10T19:59:05+00:00"
4055 4057
         },
4056 4058
         {
4057 4059
             "name": "league/uri",
@@ -5919,16 +5921,16 @@
5919 5921
         },
5920 5922
         {
5921 5923
             "name": "psy/psysh",
5922
-            "version": "v0.12.6",
5924
+            "version": "v0.12.7",
5923 5925
             "source": {
5924 5926
                 "type": "git",
5925 5927
                 "url": "https://github.com/bobthecow/psysh.git",
5926
-                "reference": "3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c"
5928
+                "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c"
5927 5929
             },
5928 5930
             "dist": {
5929 5931
                 "type": "zip",
5930
-                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c",
5931
-                "reference": "3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c",
5932
+                "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c",
5933
+                "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c",
5932 5934
                 "shasum": ""
5933 5935
             },
5934 5936
             "require": {
@@ -5992,9 +5994,9 @@
5992 5994
             ],
5993 5995
             "support": {
5994 5996
                 "issues": "https://github.com/bobthecow/psysh/issues",
5995
-                "source": "https://github.com/bobthecow/psysh/tree/v0.12.6"
5997
+                "source": "https://github.com/bobthecow/psysh/tree/v0.12.7"
5996 5998
             },
5997
-            "time": "2024-12-07T20:08:52+00:00"
5999
+            "time": "2024-12-10T01:58:33+00:00"
5998 6000
         },
5999 6001
         {
6000 6002
             "name": "ralouphie/getallheaders",
@@ -7869,8 +7871,8 @@
7869 7871
             "type": "library",
7870 7872
             "extra": {
7871 7873
                 "thanks": {
7872
-                    "name": "symfony/polyfill",
7873
-                    "url": "https://github.com/symfony/polyfill"
7874
+                    "url": "https://github.com/symfony/polyfill",
7875
+                    "name": "symfony/polyfill"
7874 7876
                 }
7875 7877
             },
7876 7878
             "autoload": {
@@ -8035,8 +8037,8 @@
8035 8037
             "type": "library",
8036 8038
             "extra": {
8037 8039
                 "thanks": {
8038
-                    "name": "symfony/polyfill",
8039
-                    "url": "https://github.com/symfony/polyfill"
8040
+                    "url": "https://github.com/symfony/polyfill",
8041
+                    "name": "symfony/polyfill"
8040 8042
                 }
8041 8043
             },
8042 8044
             "autoload": {
@@ -9239,16 +9241,16 @@
9239 9241
     "packages-dev": [
9240 9242
         {
9241 9243
             "name": "brianium/paratest",
9242
-            "version": "v7.6.1",
9244
+            "version": "v7.6.3",
9243 9245
             "source": {
9244 9246
                 "type": "git",
9245 9247
                 "url": "https://github.com/paratestphp/paratest.git",
9246
-                "reference": "9ac8eda68f17acda4dad4aa02ecdcc327d7e6675"
9248
+                "reference": "ae3c9f1aeda7daa374c904b35ece8f574f56d176"
9247 9249
             },
9248 9250
             "dist": {
9249 9251
                 "type": "zip",
9250
-                "url": "https://api.github.com/repos/paratestphp/paratest/zipball/9ac8eda68f17acda4dad4aa02ecdcc327d7e6675",
9251
-                "reference": "9ac8eda68f17acda4dad4aa02ecdcc327d7e6675",
9252
+                "url": "https://api.github.com/repos/paratestphp/paratest/zipball/ae3c9f1aeda7daa374c904b35ece8f574f56d176",
9253
+                "reference": "ae3c9f1aeda7daa374c904b35ece8f574f56d176",
9252 9254
                 "shasum": ""
9253 9255
             },
9254 9256
             "require": {
@@ -9262,21 +9264,21 @@
9262 9264
                 "phpunit/php-code-coverage": "^11.0.7",
9263 9265
                 "phpunit/php-file-iterator": "^5.1.0",
9264 9266
                 "phpunit/php-timer": "^7.0.1",
9265
-                "phpunit/phpunit": "^11.4.4",
9267
+                "phpunit/phpunit": "^11.5.0",
9266 9268
                 "sebastian/environment": "^7.2.0",
9267
-                "symfony/console": "^6.4.14 || ^7.1.7",
9268
-                "symfony/process": "^6.4.14 || ^7.1.7"
9269
+                "symfony/console": "^6.4.14 || ^7.2.0",
9270
+                "symfony/process": "^6.4.14 || ^7.2.0"
9269 9271
             },
9270 9272
             "require-dev": {
9271 9273
                 "doctrine/coding-standard": "^12.0.0",
9272 9274
                 "ext-pcov": "*",
9273 9275
                 "ext-posix": "*",
9274
-                "phpstan/phpstan": "^2",
9275
-                "phpstan/phpstan-deprecation-rules": "^2",
9276
-                "phpstan/phpstan-phpunit": "^2",
9276
+                "phpstan/phpstan": "^2.0.3",
9277
+                "phpstan/phpstan-deprecation-rules": "^2.0.1",
9278
+                "phpstan/phpstan-phpunit": "^2.0.1",
9277 9279
                 "phpstan/phpstan-strict-rules": "^2",
9278 9280
                 "squizlabs/php_codesniffer": "^3.11.1",
9279
-                "symfony/filesystem": "^6.4.13 || ^7.1.6"
9281
+                "symfony/filesystem": "^6.4.13 || ^7.2.0"
9280 9282
             },
9281 9283
             "bin": [
9282 9284
                 "bin/paratest",
@@ -9316,7 +9318,7 @@
9316 9318
             ],
9317 9319
             "support": {
9318 9320
                 "issues": "https://github.com/paratestphp/paratest/issues",
9319
-                "source": "https://github.com/paratestphp/paratest/tree/v7.6.1"
9321
+                "source": "https://github.com/paratestphp/paratest/tree/v7.6.3"
9320 9322
             },
9321 9323
             "funding": [
9322 9324
                 {
@@ -9328,7 +9330,7 @@
9328 9330
                     "type": "paypal"
9329 9331
                 }
9330 9332
             ],
9331
-            "time": "2024-12-05T10:55:39+00:00"
9333
+            "time": "2024-12-10T13:59:28+00:00"
9332 9334
         },
9333 9335
         {
9334 9336
             "name": "fakerphp/faker",
@@ -10006,38 +10008,38 @@
10006 10008
         },
10007 10009
         {
10008 10010
             "name": "pestphp/pest",
10009
-            "version": "v3.6.0",
10011
+            "version": "v3.7.0",
10010 10012
             "source": {
10011 10013
                 "type": "git",
10012 10014
                 "url": "https://github.com/pestphp/pest.git",
10013
-                "reference": "918a8fc16996849937e281482bd34f236881ce96"
10015
+                "reference": "9688b83a3d7d0acdda21c01b8aeb933ec9fcd556"
10014 10016
             },
10015 10017
             "dist": {
10016 10018
                 "type": "zip",
10017
-                "url": "https://api.github.com/repos/pestphp/pest/zipball/918a8fc16996849937e281482bd34f236881ce96",
10018
-                "reference": "918a8fc16996849937e281482bd34f236881ce96",
10019
+                "url": "https://api.github.com/repos/pestphp/pest/zipball/9688b83a3d7d0acdda21c01b8aeb933ec9fcd556",
10020
+                "reference": "9688b83a3d7d0acdda21c01b8aeb933ec9fcd556",
10019 10021
                 "shasum": ""
10020 10022
             },
10021 10023
             "require": {
10022
-                "brianium/paratest": "^7.6.0",
10024
+                "brianium/paratest": "^7.6.2",
10023 10025
                 "nunomaduro/collision": "^8.5.0",
10024 10026
                 "nunomaduro/termwind": "^2.3.0",
10025 10027
                 "pestphp/pest-plugin": "^3.0.0",
10026 10028
                 "pestphp/pest-plugin-arch": "^3.0.0",
10027 10029
                 "pestphp/pest-plugin-mutate": "^3.0.5",
10028 10030
                 "php": "^8.2.0",
10029
-                "phpunit/phpunit": "^11.4.4"
10031
+                "phpunit/phpunit": "^11.5.0"
10030 10032
             },
10031 10033
             "conflict": {
10032 10034
                 "filp/whoops": "<2.16.0",
10033
-                "phpunit/phpunit": ">11.4.4",
10035
+                "phpunit/phpunit": ">11.5.0",
10034 10036
                 "sebastian/exporter": "<6.0.0",
10035 10037
                 "webmozart/assert": "<1.11.0"
10036 10038
             },
10037 10039
             "require-dev": {
10038 10040
                 "pestphp/pest-dev-tools": "^3.3.0",
10039 10041
                 "pestphp/pest-plugin-type-coverage": "^3.2.0",
10040
-                "symfony/process": "^7.1.8"
10042
+                "symfony/process": "^7.2.0"
10041 10043
             },
10042 10044
             "bin": [
10043 10045
                 "bin/pest"
@@ -10102,7 +10104,7 @@
10102 10104
             ],
10103 10105
             "support": {
10104 10106
                 "issues": "https://github.com/pestphp/pest/issues",
10105
-                "source": "https://github.com/pestphp/pest/tree/v3.6.0"
10107
+                "source": "https://github.com/pestphp/pest/tree/v3.7.0"
10106 10108
             },
10107 10109
             "funding": [
10108 10110
                 {
@@ -10114,7 +10116,7 @@
10114 10116
                     "type": "github"
10115 10117
                 }
10116 10118
             ],
10117
-            "time": "2024-12-01T22:46:00+00:00"
10119
+            "time": "2024-12-10T11:54:49+00:00"
10118 10120
         },
10119 10121
         {
10120 10122
             "name": "pestphp/pest-plugin",
@@ -11117,16 +11119,16 @@
11117 11119
         },
11118 11120
         {
11119 11121
             "name": "phpunit/phpunit",
11120
-            "version": "11.4.4",
11122
+            "version": "11.5.0",
11121 11123
             "source": {
11122 11124
                 "type": "git",
11123 11125
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
11124
-                "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4"
11126
+                "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7"
11125 11127
             },
11126 11128
             "dist": {
11127 11129
                 "type": "zip",
11128
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4",
11129
-                "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4",
11130
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0569902506a6c0878930b87ea79ec3b50ea563f7",
11131
+                "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7",
11130 11132
                 "shasum": ""
11131 11133
             },
11132 11134
             "require": {
@@ -11150,11 +11152,12 @@
11150 11152
                 "sebastian/comparator": "^6.2.1",
11151 11153
                 "sebastian/diff": "^6.0.2",
11152 11154
                 "sebastian/environment": "^7.2.0",
11153
-                "sebastian/exporter": "^6.1.3",
11155
+                "sebastian/exporter": "^6.3.0",
11154 11156
                 "sebastian/global-state": "^7.0.2",
11155 11157
                 "sebastian/object-enumerator": "^6.0.1",
11156 11158
                 "sebastian/type": "^5.1.0",
11157
-                "sebastian/version": "^5.0.2"
11159
+                "sebastian/version": "^5.0.2",
11160
+                "staabm/side-effects-detector": "^1.0.5"
11158 11161
             },
11159 11162
             "suggest": {
11160 11163
                 "ext-soap": "To be able to generate mocks based on WSDL files"
@@ -11165,7 +11168,7 @@
11165 11168
             "type": "library",
11166 11169
             "extra": {
11167 11170
                 "branch-alias": {
11168
-                    "dev-main": "11.4-dev"
11171
+                    "dev-main": "11.5-dev"
11169 11172
                 }
11170 11173
             },
11171 11174
             "autoload": {
@@ -11197,7 +11200,7 @@
11197 11200
             "support": {
11198 11201
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
11199 11202
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
11200
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/11.4.4"
11203
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.0"
11201 11204
             },
11202 11205
             "funding": [
11203 11206
                 {
@@ -11213,7 +11216,7 @@
11213 11216
                     "type": "tidelift"
11214 11217
                 }
11215 11218
             ],
11216
-            "time": "2024-11-27T10:44:52+00:00"
11219
+            "time": "2024-12-06T05:57:38+00:00"
11217 11220
         },
11218 11221
         {
11219 11222
             "name": "pimple/pimple",
@@ -12852,6 +12855,58 @@
12852 12855
             ],
12853 12856
             "time": "2024-12-09T11:32:15+00:00"
12854 12857
         },
12858
+        {
12859
+            "name": "staabm/side-effects-detector",
12860
+            "version": "1.0.5",
12861
+            "source": {
12862
+                "type": "git",
12863
+                "url": "https://github.com/staabm/side-effects-detector.git",
12864
+                "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
12865
+            },
12866
+            "dist": {
12867
+                "type": "zip",
12868
+                "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
12869
+                "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
12870
+                "shasum": ""
12871
+            },
12872
+            "require": {
12873
+                "ext-tokenizer": "*",
12874
+                "php": "^7.4 || ^8.0"
12875
+            },
12876
+            "require-dev": {
12877
+                "phpstan/extension-installer": "^1.4.3",
12878
+                "phpstan/phpstan": "^1.12.6",
12879
+                "phpunit/phpunit": "^9.6.21",
12880
+                "symfony/var-dumper": "^5.4.43",
12881
+                "tomasvotruba/type-coverage": "1.0.0",
12882
+                "tomasvotruba/unused-public": "1.0.0"
12883
+            },
12884
+            "type": "library",
12885
+            "autoload": {
12886
+                "classmap": [
12887
+                    "lib/"
12888
+                ]
12889
+            },
12890
+            "notification-url": "https://packagist.org/downloads/",
12891
+            "license": [
12892
+                "MIT"
12893
+            ],
12894
+            "description": "A static analysis tool to detect side effects in PHP code",
12895
+            "keywords": [
12896
+                "static analysis"
12897
+            ],
12898
+            "support": {
12899
+                "issues": "https://github.com/staabm/side-effects-detector/issues",
12900
+                "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
12901
+            },
12902
+            "funding": [
12903
+                {
12904
+                    "url": "https://github.com/staabm",
12905
+                    "type": "github"
12906
+                }
12907
+            ],
12908
+            "time": "2024-10-20T05:08:20+00:00"
12909
+        },
12855 12910
         {
12856 12911
             "name": "symfony/polyfill-iconv",
12857 12912
             "version": "v1.31.0",

+ 2
- 1
database/migrations/2024_11_27_223015_create_invoices_table.php Целия файл

@@ -27,7 +27,8 @@ return new class extends Migration
27 27
             $table->timestamp('last_sent')->nullable();
28 28
             $table->string('status')->default('draft');
29 29
             $table->string('currency_code')->nullable();
30
-            $table->string('discount_computation')->nullable();
30
+            $table->string('discount_method')->default('line_items');
31
+            $table->string('discount_computation')->default('percentage');
31 32
             $table->integer('discount_rate')->default(0);
32 33
             $table->integer('subtotal')->default(0);
33 34
             $table->integer('tax_total')->default(0);

+ 3
- 3
package-lock.json Целия файл

@@ -1761,9 +1761,9 @@
1761 1761
             }
1762 1762
         },
1763 1763
         "node_modules/node-releases": {
1764
-            "version": "2.0.18",
1765
-            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
1766
-            "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
1764
+            "version": "2.0.19",
1765
+            "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
1766
+            "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
1767 1767
             "dev": true,
1768 1768
             "license": "MIT"
1769 1769
         },

+ 38
- 14
resources/views/filament/forms/components/invoice-totals.blade.php Целия файл

@@ -4,31 +4,55 @@
4 4
     $data = $this->form->getRawState();
5 5
     $viewModel = new \App\View\Models\InvoiceTotalViewModel($this->record, $data);
6 6
     extract($viewModel->buildViewData(), \EXTR_SKIP);
7
+
8
+    $isInvoiceLevelDiscount = $data['discount_method'] === 'invoice';
7 9
 @endphp
8 10
 
9 11
 <div class="totals-summary w-full pr-14">
10 12
     <table class="w-full text-right table-fixed">
13
+        <colgroup>
14
+            <col class="w-[20%]"> {{-- Items --}}
15
+            <col class="w-[30%]"> {{-- Description --}}
16
+            <col class="w-[10%]"> {{-- Quantity --}}
17
+            <col class="w-[10%]"> {{-- Price --}}
18
+            <col class="w-[20%]"> {{-- Taxes --}}
19
+            <col class="w-[10%]"> {{-- Amount --}}
20
+        </colgroup>
11 21
         <tbody>
12 22
             <tr>
13
-                <td class="w-2/3 text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Subtotal:</td>
14
-                <td class="w-1/3 text-sm pl-4 py-2 leading-6">{{ $subtotal }}</td>
15
-            </tr>
16
-            <tr>
17
-                <td class="w-2/3 text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Taxes:</td>
18
-                <td class="w-1/3 text-sm pl-4 py-2 leading-6">{{ $taxTotal }}</td>
23
+                <td colspan="4"></td>
24
+                <td class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Subtotal:</td>
25
+                <td class="text-sm pl-4 py-2 leading-6">{{ $subtotal }}</td>
19 26
             </tr>
20 27
             <tr>
21
-                <td class="w-2/3 text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Discounts:</td>
22
-                <td class="w-1/3 text-sm pl-4 py-2 leading-6">({{ $discountTotal }})</td>
28
+                <td colspan="4"></td>
29
+                <td class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Taxes:</td>
30
+                <td class="text-sm pl-4 py-2 leading-6">{{ $taxTotal }}</td>
23 31
             </tr>
32
+            @if($isInvoiceLevelDiscount)
33
+                <tr>
34
+                    <td colspan="4" class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white text-right">Discount:</td>
35
+                    <td class="text-sm px-4 py-2">
36
+                        <div class="flex justify-between space-x-2">
37
+                            @foreach($getChildComponentContainer()->getComponents() as $component)
38
+                                <div class="flex-1">{{ $component }}</div>
39
+                            @endforeach
40
+                        </div>
41
+                    </td>
42
+                    <td class="text-sm pl-4 py-2 leading-6">({{ $discountTotal }})</td>
43
+                </tr>
44
+            @else
45
+                <tr>
46
+                    <td colspan="4"></td>
47
+                    <td class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Discounts:</td>
48
+                    <td class="text-sm pl-4 py-2 leading-6">({{ $discountTotal }})</td>
49
+                </tr>
50
+            @endif
24 51
             <tr class="font-semibold">
25
-                <td class="w-2/3 text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Total:</td>
26
-                <td class="w-1/3 text-sm pl-4 py-2 leading-6">{{ $grandTotal }}</td>
52
+                <td colspan="4"></td>
53
+                <td class="text-sm px-4 py-2 font-medium leading-6 text-gray-950 dark:text-white">Total:</td>
54
+                <td class="text-sm pl-4 py-2 leading-6">{{ $grandTotal }}</td>
27 55
             </tr>
28 56
         </tbody>
29 57
     </table>
30 58
 </div>
31
-
32
-
33
-
34
-

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