Andrew Wallo 10 months ago
parent
commit
4a3f7250c9

+ 3
- 0
app/Enums/Accounting/AdjustmentComputation.php View File

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

+ 25
- 23
app/Filament/Company/Resources/Sales/InvoiceResource.php View File

9
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
9
 use App\Filament\Company\Resources\Sales\InvoiceResource\Pages;
10
 use App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
10
 use App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers;
11
 use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets;
11
 use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets;
12
+use App\Filament\Forms\Components\InvoiceTotals;
12
 use App\Filament\Tables\Actions\ReplicateBulkAction;
13
 use App\Filament\Tables\Actions\ReplicateBulkAction;
13
 use App\Filament\Tables\Filters\DateRangeFilter;
14
 use App\Filament\Tables\Filters\DateRangeFilter;
14
 use App\Models\Accounting\Adjustment;
15
 use App\Models\Accounting\Adjustment;
137
                                     ])
138
                                     ])
138
                                     ->selectablePlaceholder(false)
139
                                     ->selectablePlaceholder(false)
139
                                     ->default('line_items')
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
                                     ->live(),
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
                             ])->grow(true),
149
                             ])->grow(true),
155
                         ])->from('md'),
150
                         ])->from('md'),
156
                         TableRepeater::make('lineItems')
151
                         TableRepeater::make('lineItems')
238
                                 $invoice = $component->getRecord();
233
                                 $invoice = $component->getRecord();
239
 
234
 
240
                                 // Recalculate totals for line items
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
                                     $lineItem->updateQuietly([
238
                                     $lineItem->updateQuietly([
243
                                         'tax_total' => $lineItem->calculateTaxTotal()->getAmount(),
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
                                 $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
246
                                 $subtotal = $invoice->lineItems()->sum('subtotal') / 100;
249
                                 $taxTotal = $invoice->lineItems()->sum('tax_total') / 100;
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
                                 $grandTotal = $subtotal + $taxTotal - $discountTotal;
259
                                 $grandTotal = $subtotal + $taxTotal - $discountTotal;
252
 
260
 
253
                                 $invoice->updateQuietly([
261
                                 $invoice->updateQuietly([
360
                                         return CurrencyConverter::formatToMoney($total);
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
                         Forms\Components\Textarea::make('terms')
372
                         Forms\Components\Textarea::make('terms')
371
                             ->columnSpanFull(),
373
                             ->columnSpanFull(),
372
                     ]),
374
                     ]),

+ 37
- 0
app/Filament/Forms/Components/InvoiceTotals.php View File

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 View File

53
         'last_sent',
53
         'last_sent',
54
         'status',
54
         'status',
55
         'currency_code',
55
         'currency_code',
56
+        'discount_method',
56
         'discount_computation',
57
         'discount_computation',
57
         'discount_rate',
58
         'discount_rate',
58
         'subtotal',
59
         'subtotal',

+ 15
- 11
app/Providers/MacroServiceProvider.php View File

102
             return $this;
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
             $this
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
                 ->mask(static function (TextInput $component) use ($computation) {
121
                 ->mask(static function (TextInput $component) use ($computation) {
118
                     $computation = $component->evaluate($computation);
122
                     $computation = $component->evaluate($computation);
119
 
123
 

+ 2
- 2
app/View/Models/InvoiceTotalViewModel.php View File

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

+ 121
- 66
composer.lock View File

497
         },
497
         },
498
         {
498
         {
499
             "name": "aws/aws-sdk-php",
499
             "name": "aws/aws-sdk-php",
500
-            "version": "3.334.2",
500
+            "version": "3.334.3",
501
             "source": {
501
             "source": {
502
                 "type": "git",
502
                 "type": "git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
503
                 "url": "https://github.com/aws/aws-sdk-php.git",
504
-                "reference": "b19afc076bb1cc2617bdef76efd41587596109e7"
504
+                "reference": "6576a9fcfc6ae7c76aed3c6fa4c3864060f72d04"
505
             },
505
             },
506
             "dist": {
506
             "dist": {
507
                 "type": "zip",
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
                 "shasum": ""
510
                 "shasum": ""
511
             },
511
             },
512
             "require": {
512
             "require": {
589
             "support": {
589
             "support": {
590
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
590
                 "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80",
591
                 "issues": "https://github.com/aws/aws-sdk-php/issues",
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
             "name": "aws/aws-sdk-php-laravel",
597
             "name": "aws/aws-sdk-php-laravel",
2980
         },
2980
         },
2981
         {
2981
         {
2982
             "name": "laravel/framework",
2982
             "name": "laravel/framework",
2983
-            "version": "v11.34.2",
2983
+            "version": "v11.35.0",
2984
             "source": {
2984
             "source": {
2985
                 "type": "git",
2985
                 "type": "git",
2986
                 "url": "https://github.com/laravel/framework.git",
2986
                 "url": "https://github.com/laravel/framework.git",
2987
-                "reference": "865da6d73dd353f07a7bcbd778c55966a620121f"
2987
+                "reference": "f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc"
2988
             },
2988
             },
2989
             "dist": {
2989
             "dist": {
2990
                 "type": "zip",
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
                 "shasum": ""
2993
                 "shasum": ""
2994
             },
2994
             },
2995
             "require": {
2995
             "require": {
3013
                 "league/commonmark": "^2.2.1",
3013
                 "league/commonmark": "^2.2.1",
3014
                 "league/flysystem": "^3.25.1",
3014
                 "league/flysystem": "^3.25.1",
3015
                 "league/flysystem-local": "^3.25.1",
3015
                 "league/flysystem-local": "^3.25.1",
3016
+                "league/uri": "^7.5.1",
3016
                 "monolog/monolog": "^3.0",
3017
                 "monolog/monolog": "^3.0",
3017
                 "nesbot/carbon": "^2.72.2|^3.4",
3018
                 "nesbot/carbon": "^2.72.2|^3.4",
3018
                 "nunomaduro/termwind": "^2.0",
3019
                 "nunomaduro/termwind": "^2.0",
3096
                 "league/flysystem-read-only": "^3.25.1",
3097
                 "league/flysystem-read-only": "^3.25.1",
3097
                 "league/flysystem-sftp-v3": "^3.25.1",
3098
                 "league/flysystem-sftp-v3": "^3.25.1",
3098
                 "mockery/mockery": "^1.6.10",
3099
                 "mockery/mockery": "^1.6.10",
3099
-                "nyholm/psr7": "^1.2",
3100
                 "orchestra/testbench-core": "^9.6",
3100
                 "orchestra/testbench-core": "^9.6",
3101
                 "pda/pheanstalk": "^5.0.6",
3101
                 "pda/pheanstalk": "^5.0.6",
3102
+                "php-http/discovery": "^1.15",
3102
                 "phpstan/phpstan": "^1.11.5",
3103
                 "phpstan/phpstan": "^1.11.5",
3103
                 "phpunit/phpunit": "^10.5.35|^11.3.6",
3104
                 "phpunit/phpunit": "^10.5.35|^11.3.6",
3104
                 "predis/predis": "^2.3",
3105
                 "predis/predis": "^2.3",
3130
                 "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
3131
                 "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)",
3131
                 "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
3132
                 "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).",
3132
                 "mockery/mockery": "Required to use mocking (^1.6).",
3133
                 "mockery/mockery": "Required to use mocking (^1.6).",
3133
-                "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).",
3134
                 "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).",
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
                 "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).",
3136
                 "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).",
3136
                 "predis/predis": "Required to use the predis connector (^2.3).",
3137
                 "predis/predis": "Required to use the predis connector (^2.3).",
3137
                 "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
3138
                 "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).",
3152
             },
3153
             },
3153
             "autoload": {
3154
             "autoload": {
3154
                 "files": [
3155
                 "files": [
3156
+                    "src/Illuminate/Collections/functions.php",
3155
                     "src/Illuminate/Collections/helpers.php",
3157
                     "src/Illuminate/Collections/helpers.php",
3156
                     "src/Illuminate/Events/functions.php",
3158
                     "src/Illuminate/Events/functions.php",
3157
                     "src/Illuminate/Filesystem/functions.php",
3159
                     "src/Illuminate/Filesystem/functions.php",
3189
                 "issues": "https://github.com/laravel/framework/issues",
3191
                 "issues": "https://github.com/laravel/framework/issues",
3190
                 "source": "https://github.com/laravel/framework"
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
             "name": "laravel/prompts",
3197
             "name": "laravel/prompts",
3252
         },
3254
         },
3253
         {
3255
         {
3254
             "name": "laravel/sanctum",
3256
             "name": "laravel/sanctum",
3255
-            "version": "v4.0.5",
3257
+            "version": "v4.0.6",
3256
             "source": {
3258
             "source": {
3257
                 "type": "git",
3259
                 "type": "git",
3258
                 "url": "https://github.com/laravel/sanctum.git",
3260
                 "url": "https://github.com/laravel/sanctum.git",
3259
-                "reference": "fe361b9a63407a228f884eb78d7217f680b50140"
3261
+                "reference": "9e069e36d90b1e1f41886efa0fe9800a6b354694"
3260
             },
3262
             },
3261
             "dist": {
3263
             "dist": {
3262
                 "type": "zip",
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
                 "shasum": ""
3267
                 "shasum": ""
3266
             },
3268
             },
3267
             "require": {
3269
             "require": {
3312
                 "issues": "https://github.com/laravel/sanctum/issues",
3314
                 "issues": "https://github.com/laravel/sanctum/issues",
3313
                 "source": "https://github.com/laravel/sanctum"
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
             "name": "laravel/serializable-closure",
3320
             "name": "laravel/serializable-closure",
3979
         },
3981
         },
3980
         {
3982
         {
3981
             "name": "league/oauth1-client",
3983
             "name": "league/oauth1-client",
3982
-            "version": "v1.10.1",
3984
+            "version": "v1.11.0",
3983
             "source": {
3985
             "source": {
3984
                 "type": "git",
3986
                 "type": "git",
3985
                 "url": "https://github.com/thephpleague/oauth1-client.git",
3987
                 "url": "https://github.com/thephpleague/oauth1-client.git",
3986
-                "reference": "d6365b901b5c287dd41f143033315e2f777e1167"
3988
+                "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055"
3987
             },
3989
             },
3988
             "dist": {
3990
             "dist": {
3989
                 "type": "zip",
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
                 "shasum": ""
3994
                 "shasum": ""
3993
             },
3995
             },
3994
             "require": {
3996
             "require": {
4049
             ],
4051
             ],
4050
             "support": {
4052
             "support": {
4051
                 "issues": "https://github.com/thephpleague/oauth1-client/issues",
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
             "name": "league/uri",
4059
             "name": "league/uri",
5919
         },
5921
         },
5920
         {
5922
         {
5921
             "name": "psy/psysh",
5923
             "name": "psy/psysh",
5922
-            "version": "v0.12.6",
5924
+            "version": "v0.12.7",
5923
             "source": {
5925
             "source": {
5924
                 "type": "git",
5926
                 "type": "git",
5925
                 "url": "https://github.com/bobthecow/psysh.git",
5927
                 "url": "https://github.com/bobthecow/psysh.git",
5926
-                "reference": "3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c"
5928
+                "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c"
5927
             },
5929
             },
5928
             "dist": {
5930
             "dist": {
5929
                 "type": "zip",
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
                 "shasum": ""
5934
                 "shasum": ""
5933
             },
5935
             },
5934
             "require": {
5936
             "require": {
5992
             ],
5994
             ],
5993
             "support": {
5995
             "support": {
5994
                 "issues": "https://github.com/bobthecow/psysh/issues",
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
             "name": "ralouphie/getallheaders",
6002
             "name": "ralouphie/getallheaders",
7869
             "type": "library",
7871
             "type": "library",
7870
             "extra": {
7872
             "extra": {
7871
                 "thanks": {
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
             "autoload": {
7878
             "autoload": {
8035
             "type": "library",
8037
             "type": "library",
8036
             "extra": {
8038
             "extra": {
8037
                 "thanks": {
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
             "autoload": {
8044
             "autoload": {
9239
     "packages-dev": [
9241
     "packages-dev": [
9240
         {
9242
         {
9241
             "name": "brianium/paratest",
9243
             "name": "brianium/paratest",
9242
-            "version": "v7.6.1",
9244
+            "version": "v7.6.3",
9243
             "source": {
9245
             "source": {
9244
                 "type": "git",
9246
                 "type": "git",
9245
                 "url": "https://github.com/paratestphp/paratest.git",
9247
                 "url": "https://github.com/paratestphp/paratest.git",
9246
-                "reference": "9ac8eda68f17acda4dad4aa02ecdcc327d7e6675"
9248
+                "reference": "ae3c9f1aeda7daa374c904b35ece8f574f56d176"
9247
             },
9249
             },
9248
             "dist": {
9250
             "dist": {
9249
                 "type": "zip",
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
                 "shasum": ""
9254
                 "shasum": ""
9253
             },
9255
             },
9254
             "require": {
9256
             "require": {
9262
                 "phpunit/php-code-coverage": "^11.0.7",
9264
                 "phpunit/php-code-coverage": "^11.0.7",
9263
                 "phpunit/php-file-iterator": "^5.1.0",
9265
                 "phpunit/php-file-iterator": "^5.1.0",
9264
                 "phpunit/php-timer": "^7.0.1",
9266
                 "phpunit/php-timer": "^7.0.1",
9265
-                "phpunit/phpunit": "^11.4.4",
9267
+                "phpunit/phpunit": "^11.5.0",
9266
                 "sebastian/environment": "^7.2.0",
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
             "require-dev": {
9272
             "require-dev": {
9271
                 "doctrine/coding-standard": "^12.0.0",
9273
                 "doctrine/coding-standard": "^12.0.0",
9272
                 "ext-pcov": "*",
9274
                 "ext-pcov": "*",
9273
                 "ext-posix": "*",
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
                 "phpstan/phpstan-strict-rules": "^2",
9279
                 "phpstan/phpstan-strict-rules": "^2",
9278
                 "squizlabs/php_codesniffer": "^3.11.1",
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
             "bin": [
9283
             "bin": [
9282
                 "bin/paratest",
9284
                 "bin/paratest",
9316
             ],
9318
             ],
9317
             "support": {
9319
             "support": {
9318
                 "issues": "https://github.com/paratestphp/paratest/issues",
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
             "funding": [
9323
             "funding": [
9322
                 {
9324
                 {
9328
                     "type": "paypal"
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
             "name": "fakerphp/faker",
9336
             "name": "fakerphp/faker",
10006
         },
10008
         },
10007
         {
10009
         {
10008
             "name": "pestphp/pest",
10010
             "name": "pestphp/pest",
10009
-            "version": "v3.6.0",
10011
+            "version": "v3.7.0",
10010
             "source": {
10012
             "source": {
10011
                 "type": "git",
10013
                 "type": "git",
10012
                 "url": "https://github.com/pestphp/pest.git",
10014
                 "url": "https://github.com/pestphp/pest.git",
10013
-                "reference": "918a8fc16996849937e281482bd34f236881ce96"
10015
+                "reference": "9688b83a3d7d0acdda21c01b8aeb933ec9fcd556"
10014
             },
10016
             },
10015
             "dist": {
10017
             "dist": {
10016
                 "type": "zip",
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
                 "shasum": ""
10021
                 "shasum": ""
10020
             },
10022
             },
10021
             "require": {
10023
             "require": {
10022
-                "brianium/paratest": "^7.6.0",
10024
+                "brianium/paratest": "^7.6.2",
10023
                 "nunomaduro/collision": "^8.5.0",
10025
                 "nunomaduro/collision": "^8.5.0",
10024
                 "nunomaduro/termwind": "^2.3.0",
10026
                 "nunomaduro/termwind": "^2.3.0",
10025
                 "pestphp/pest-plugin": "^3.0.0",
10027
                 "pestphp/pest-plugin": "^3.0.0",
10026
                 "pestphp/pest-plugin-arch": "^3.0.0",
10028
                 "pestphp/pest-plugin-arch": "^3.0.0",
10027
                 "pestphp/pest-plugin-mutate": "^3.0.5",
10029
                 "pestphp/pest-plugin-mutate": "^3.0.5",
10028
                 "php": "^8.2.0",
10030
                 "php": "^8.2.0",
10029
-                "phpunit/phpunit": "^11.4.4"
10031
+                "phpunit/phpunit": "^11.5.0"
10030
             },
10032
             },
10031
             "conflict": {
10033
             "conflict": {
10032
                 "filp/whoops": "<2.16.0",
10034
                 "filp/whoops": "<2.16.0",
10033
-                "phpunit/phpunit": ">11.4.4",
10035
+                "phpunit/phpunit": ">11.5.0",
10034
                 "sebastian/exporter": "<6.0.0",
10036
                 "sebastian/exporter": "<6.0.0",
10035
                 "webmozart/assert": "<1.11.0"
10037
                 "webmozart/assert": "<1.11.0"
10036
             },
10038
             },
10037
             "require-dev": {
10039
             "require-dev": {
10038
                 "pestphp/pest-dev-tools": "^3.3.0",
10040
                 "pestphp/pest-dev-tools": "^3.3.0",
10039
                 "pestphp/pest-plugin-type-coverage": "^3.2.0",
10041
                 "pestphp/pest-plugin-type-coverage": "^3.2.0",
10040
-                "symfony/process": "^7.1.8"
10042
+                "symfony/process": "^7.2.0"
10041
             },
10043
             },
10042
             "bin": [
10044
             "bin": [
10043
                 "bin/pest"
10045
                 "bin/pest"
10102
             ],
10104
             ],
10103
             "support": {
10105
             "support": {
10104
                 "issues": "https://github.com/pestphp/pest/issues",
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
             "funding": [
10109
             "funding": [
10108
                 {
10110
                 {
10114
                     "type": "github"
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
             "name": "pestphp/pest-plugin",
10122
             "name": "pestphp/pest-plugin",
11117
         },
11119
         },
11118
         {
11120
         {
11119
             "name": "phpunit/phpunit",
11121
             "name": "phpunit/phpunit",
11120
-            "version": "11.4.4",
11122
+            "version": "11.5.0",
11121
             "source": {
11123
             "source": {
11122
                 "type": "git",
11124
                 "type": "git",
11123
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
11125
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
11124
-                "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4"
11126
+                "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7"
11125
             },
11127
             },
11126
             "dist": {
11128
             "dist": {
11127
                 "type": "zip",
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
                 "shasum": ""
11132
                 "shasum": ""
11131
             },
11133
             },
11132
             "require": {
11134
             "require": {
11150
                 "sebastian/comparator": "^6.2.1",
11152
                 "sebastian/comparator": "^6.2.1",
11151
                 "sebastian/diff": "^6.0.2",
11153
                 "sebastian/diff": "^6.0.2",
11152
                 "sebastian/environment": "^7.2.0",
11154
                 "sebastian/environment": "^7.2.0",
11153
-                "sebastian/exporter": "^6.1.3",
11155
+                "sebastian/exporter": "^6.3.0",
11154
                 "sebastian/global-state": "^7.0.2",
11156
                 "sebastian/global-state": "^7.0.2",
11155
                 "sebastian/object-enumerator": "^6.0.1",
11157
                 "sebastian/object-enumerator": "^6.0.1",
11156
                 "sebastian/type": "^5.1.0",
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
             "suggest": {
11162
             "suggest": {
11160
                 "ext-soap": "To be able to generate mocks based on WSDL files"
11163
                 "ext-soap": "To be able to generate mocks based on WSDL files"
11165
             "type": "library",
11168
             "type": "library",
11166
             "extra": {
11169
             "extra": {
11167
                 "branch-alias": {
11170
                 "branch-alias": {
11168
-                    "dev-main": "11.4-dev"
11171
+                    "dev-main": "11.5-dev"
11169
                 }
11172
                 }
11170
             },
11173
             },
11171
             "autoload": {
11174
             "autoload": {
11197
             "support": {
11200
             "support": {
11198
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
11201
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
11199
                 "security": "https://github.com/sebastianbergmann/phpunit/security/policy",
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
             "funding": [
11205
             "funding": [
11203
                 {
11206
                 {
11213
                     "type": "tidelift"
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
             "name": "pimple/pimple",
11222
             "name": "pimple/pimple",
12852
             ],
12855
             ],
12853
             "time": "2024-12-09T11:32:15+00:00"
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
             "name": "symfony/polyfill-iconv",
12911
             "name": "symfony/polyfill-iconv",
12857
             "version": "v1.31.0",
12912
             "version": "v1.31.0",

+ 2
- 1
database/migrations/2024_11_27_223015_create_invoices_table.php View File

27
             $table->timestamp('last_sent')->nullable();
27
             $table->timestamp('last_sent')->nullable();
28
             $table->string('status')->default('draft');
28
             $table->string('status')->default('draft');
29
             $table->string('currency_code')->nullable();
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
             $table->integer('discount_rate')->default(0);
32
             $table->integer('discount_rate')->default(0);
32
             $table->integer('subtotal')->default(0);
33
             $table->integer('subtotal')->default(0);
33
             $table->integer('tax_total')->default(0);
34
             $table->integer('tax_total')->default(0);

+ 3
- 3
package-lock.json View File

1761
             }
1761
             }
1762
         },
1762
         },
1763
         "node_modules/node-releases": {
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
             "dev": true,
1767
             "dev": true,
1768
             "license": "MIT"
1768
             "license": "MIT"
1769
         },
1769
         },

+ 38
- 14
resources/views/filament/forms/components/invoice-totals.blade.php View File

4
     $data = $this->form->getRawState();
4
     $data = $this->form->getRawState();
5
     $viewModel = new \App\View\Models\InvoiceTotalViewModel($this->record, $data);
5
     $viewModel = new \App\View\Models\InvoiceTotalViewModel($this->record, $data);
6
     extract($viewModel->buildViewData(), \EXTR_SKIP);
6
     extract($viewModel->buildViewData(), \EXTR_SKIP);
7
+
8
+    $isInvoiceLevelDiscount = $data['discount_method'] === 'invoice';
7
 @endphp
9
 @endphp
8
 
10
 
9
 <div class="totals-summary w-full pr-14">
11
 <div class="totals-summary w-full pr-14">
10
     <table class="w-full text-right table-fixed">
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
         <tbody>
21
         <tbody>
12
             <tr>
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
             </tr>
26
             </tr>
20
             <tr>
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
             </tr>
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
             <tr class="font-semibold">
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
             </tr>
55
             </tr>
28
         </tbody>
56
         </tbody>
29
     </table>
57
     </table>
30
 </div>
58
 </div>
31
-
32
-
33
-
34
-

Loading…
Cancel
Save