From 4926a072f2e71c77b5fa04c2e2141cbc6c939b68 Mon Sep 17 00:00:00 2001 From: Andrew Wallo Date: Sat, 14 Dec 2024 20:09:20 -0500 Subject: [PATCH] wip --- app/Concerns/ManagesLineItems.php | 17 +- .../Accounting/AdjustmentComputation.php | 10 ++ .../Accounting/DocumentDiscountMethod.php | 32 ++++ .../Settings/Resources/AdjustmentResource.php | 4 +- .../Resources/Sales/InvoiceResource.php | 22 ++- app/Models/Accounting/Bill.php | 9 + app/Models/Accounting/Invoice.php | 4 +- app/Services/ReportService.php | 13 +- app/View/Models/InvoiceTotalViewModel.php | 9 +- composer.json | 2 +- composer.lock | 162 +++++++++--------- .../Accounting/AdjustmentFactory.php | 4 +- .../2024_11_27_221657_create_bills_table.php | 3 + ...024_11_27_223015_create_invoices_table.php | 2 +- package-lock.json | 32 ++-- .../forms/components/invoice-totals.blade.php | 15 +- 16 files changed, 205 insertions(+), 135 deletions(-) create mode 100644 app/Enums/Accounting/DocumentDiscountMethod.php diff --git a/app/Concerns/ManagesLineItems.php b/app/Concerns/ManagesLineItems.php index 9ec9785d..ae424ea3 100644 --- a/app/Concerns/ManagesLineItems.php +++ b/app/Concerns/ManagesLineItems.php @@ -3,6 +3,7 @@ namespace App\Concerns; use App\Enums\Accounting\AdjustmentComputation; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Models\Accounting\DocumentLineItem; use App\Models\Accounting\Invoice; use App\Utilities\Currency\CurrencyConverter; @@ -49,10 +50,10 @@ protected function deleteRemovedLineItems(Invoice $record, Collection $lineItems } } - protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $itemData, string $discountMethod): void + protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $itemData, DocumentDiscountMethod $discountMethod): void { $adjustmentIds = collect($itemData['salesTaxes'] ?? []) - ->merge($discountMethod === 'line_items' ? ($itemData['salesDiscounts'] ?? []) : []) + ->merge($discountMethod->isPerLineItem() ? ($itemData['salesDiscounts'] ?? []) : []) ->filter() ->unique(); @@ -60,11 +61,11 @@ protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $ $lineItem->refresh(); } - protected function updateLineItemTotals(DocumentLineItem $lineItem, string $discountMethod): void + protected function updateLineItemTotals(DocumentLineItem $lineItem, DocumentDiscountMethod $discountMethod): void { $lineItem->updateQuietly([ 'tax_total' => $lineItem->calculateTaxTotal()->getAmount(), - 'discount_total' => $discountMethod === 'line_items' + 'discount_total' => $discountMethod->isPerLineItem() ? $lineItem->calculateDiscountTotal()->getAmount() : 0, ]); @@ -75,7 +76,7 @@ protected function updateInvoiceTotals(Invoice $record, array $data): array $subtotalCents = $record->lineItems()->sum('subtotal'); $taxTotalCents = $record->lineItems()->sum('tax_total'); $discountTotalCents = $this->calculateDiscountTotal( - $data['discount_method'], + DocumentDiscountMethod::parse($data['discount_method']), AdjustmentComputation::parse($data['discount_computation']), $data['discount_rate'] ?? null, $subtotalCents, @@ -93,17 +94,17 @@ protected function updateInvoiceTotals(Invoice $record, array $data): array } protected function calculateDiscountTotal( - string $discountMethod, + DocumentDiscountMethod $discountMethod, ?AdjustmentComputation $discountComputation, ?string $discountRate, int $subtotalCents, Invoice $record ): int { - if ($discountMethod === 'line_items') { + if ($discountMethod->isPerLineItem()) { return $record->lineItems()->sum('discount_total'); } - if ($discountComputation === AdjustmentComputation::Percentage) { + if ($discountComputation?->isPercentage()) { return (int) ($subtotalCents * ((float) $discountRate / 100)); } diff --git a/app/Enums/Accounting/AdjustmentComputation.php b/app/Enums/Accounting/AdjustmentComputation.php index 2e451466..c743e89f 100644 --- a/app/Enums/Accounting/AdjustmentComputation.php +++ b/app/Enums/Accounting/AdjustmentComputation.php @@ -16,4 +16,14 @@ public function getLabel(): ?string { return translate($this->name); } + + public function isPercentage(): bool + { + return $this == self::Percentage; + } + + public function isFixed(): bool + { + return $this == self::Fixed; + } } diff --git a/app/Enums/Accounting/DocumentDiscountMethod.php b/app/Enums/Accounting/DocumentDiscountMethod.php new file mode 100644 index 00000000..44475151 --- /dev/null +++ b/app/Enums/Accounting/DocumentDiscountMethod.php @@ -0,0 +1,32 @@ + 'Per Line Item', + self::PerDocument => 'Per Document', + }; + } + + public function isPerLineItem(): bool + { + return $this == self::PerLineItem; + } + + public function isPerDocument(): bool + { + return $this == self::PerDocument; + } +} diff --git a/app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php b/app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php index 13332cd6..9676b1d4 100644 --- a/app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php +++ b/app/Filament/Company/Clusters/Settings/Resources/AdjustmentResource.php @@ -53,7 +53,7 @@ public static function form(Form $form): Form ToggleButton::make('recoverable') ->label('Recoverable') ->default(false) - ->visible(fn (Forms\Get $get) => AdjustmentCategory::parse($get('category')) === AdjustmentCategory::Tax && AdjustmentType::parse($get('type')) === AdjustmentType::Purchase), + ->visible(fn (Forms\Get $get) => AdjustmentCategory::parse($get('category'))->isTax() && AdjustmentType::parse($get('type'))->isPurchase()), ]) ->columns() ->visibleOn('create'), @@ -80,7 +80,7 @@ public static function form(Form $form): Form Forms\Components\DateTimePicker::make('end_date'), ]) ->columns() - ->visible(fn (Forms\Get $get) => AdjustmentCategory::parse($get('category')) === AdjustmentCategory::Discount), + ->visible(fn (Forms\Get $get) => AdjustmentCategory::parse($get('category'))->isDiscount()), ]); } diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource.php b/app/Filament/Company/Resources/Sales/InvoiceResource.php index 70f625d5..7c6cac8d 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource.php @@ -3,6 +3,7 @@ namespace App\Filament\Company\Resources\Sales; use App\Collections\Accounting\InvoiceCollection; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Enums\Accounting\InvoiceStatus; use App\Enums\Accounting\PaymentMethod; use App\Filament\Company\Resources\Sales\InvoiceResource\Pages; @@ -129,16 +130,13 @@ public static function form(Form $form): Form }), Forms\Components\Select::make('discount_method') ->label('Discount Method') - ->options([ - 'line_items' => 'Per Line Item Discounts', - 'invoice' => 'Invoice Level Discount', - ]) + ->options(DocumentDiscountMethod::class) ->selectablePlaceholder(false) - ->default('line_items') + ->default(DocumentDiscountMethod::PerLineItem) ->afterStateUpdated(function ($state, Forms\Set $set) { - $discountMethod = $state; + $discountMethod = DocumentDiscountMethod::parse($state); - if ($discountMethod === 'invoice') { + if ($discountMethod->isPerDocument()) { $set('lineItems.*.salesDiscounts', []); } }) @@ -150,7 +148,7 @@ public static function form(Form $form): Form ->saveRelationshipsUsing(null) ->dehydrated(true) ->headers(function (Forms\Get $get) { - $hasDiscounts = $get('discount_method') === 'line_items'; + $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem(); $headers = [ Header::make('Items')->width($hasDiscounts ? '15%' : '20%'), @@ -184,8 +182,8 @@ public static function form(Form $form): Form $set('unit_price', $offeringRecord->price); $set('salesTaxes', $offeringRecord->salesTaxes->pluck('id')->toArray()); - $discountMethod = $get('../../discount_method'); - if ($discountMethod === 'line_items') { + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); + if ($discountMethod->isPerLineItem()) { $set('salesDiscounts', $offeringRecord->salesDiscounts->pluck('id')->toArray()); } } @@ -217,9 +215,9 @@ public static function form(Form $form): Form ->multiple() ->live() ->hidden(function (Forms\Get $get) { - $discountMethod = $get('../../discount_method'); + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); - return $discountMethod === 'invoice'; + return $discountMethod->isPerDocument(); }) ->searchable(), Forms\Components\Placeholder::make('total') diff --git a/app/Models/Accounting/Bill.php b/app/Models/Accounting/Bill.php index 23fe19b3..465d9e14 100644 --- a/app/Models/Accounting/Bill.php +++ b/app/Models/Accounting/Bill.php @@ -3,9 +3,12 @@ namespace App\Models\Accounting; use App\Casts\MoneyCast; +use App\Casts\RateCast; use App\Concerns\Blamable; use App\Concerns\CompanyOwned; +use App\Enums\Accounting\AdjustmentComputation; use App\Enums\Accounting\BillStatus; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Enums\Accounting\JournalEntryType; use App\Enums\Accounting\TransactionType; use App\Filament\Company\Resources\Purchases\BillResource; @@ -42,6 +45,9 @@ class Bill extends Model 'paid_at', 'status', 'currency_code', + 'discount_method', + 'discount_computation', + 'discount_rate', 'subtotal', 'tax_total', 'discount_total', @@ -57,6 +63,9 @@ class Bill extends Model 'due_date' => 'date', 'paid_at' => 'datetime', 'status' => BillStatus::class, + 'discount_method' => DocumentDiscountMethod::class, + 'discount_computation' => AdjustmentComputation::class, + 'discount_rate' => RateCast::class, 'subtotal' => MoneyCast::class, 'tax_total' => MoneyCast::class, 'discount_total' => MoneyCast::class, diff --git a/app/Models/Accounting/Invoice.php b/app/Models/Accounting/Invoice.php index 4671a2cb..c2d1b710 100644 --- a/app/Models/Accounting/Invoice.php +++ b/app/Models/Accounting/Invoice.php @@ -8,6 +8,7 @@ use App\Concerns\Blamable; use App\Concerns\CompanyOwned; use App\Enums\Accounting\AdjustmentComputation; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Enums\Accounting\InvoiceStatus; use App\Enums\Accounting\JournalEntryType; use App\Enums\Accounting\TransactionType; @@ -75,6 +76,7 @@ class Invoice extends Model 'paid_at' => 'datetime', 'last_sent' => 'datetime', 'status' => InvoiceStatus::class, + 'discount_method' => DocumentDiscountMethod::class, 'discount_computation' => AdjustmentComputation::class, 'discount_rate' => RateCast::class, 'subtotal' => MoneyCast::class, @@ -293,7 +295,7 @@ public function createApprovalTransaction(): void ]); } - if ($this->discount_method === 'invoice' && $totalLineItemSubtotal > 0) { + if ($this->discount_method->isPerDocument() && $totalLineItemSubtotal > 0) { $lineItemSubtotalCents = (int) $lineItem->getRawOriginal('subtotal'); if ($index === $this->lineItems->count() - 1) { diff --git a/app/Services/ReportService.php b/app/Services/ReportService.php index fdabacf0..0f93bd53 100644 --- a/app/Services/ReportService.php +++ b/app/Services/ReportService.php @@ -177,6 +177,8 @@ public function buildAccountTransactionsReport(string $startDate, string $endDat $accountTransactions = []; $currentBalance = $account->starting_balance; + $periodDebitTotal = 0; + $periodCreditTotal = 0; $accountTransactions[] = new AccountTransactionDTO( id: null, @@ -192,6 +194,13 @@ public function buildAccountTransactionsReport(string $startDate, string $endDat foreach ($account->journalEntries as $journalEntry) { $transaction = $journalEntry->transaction; $signedAmount = $journalEntry->signed_amount; + $amount = $journalEntry->getRawOriginal('amount'); + + if ($journalEntry->type->isDebit()) { + $periodDebitTotal += $amount; + } else { + $periodCreditTotal += $amount; + } if ($account->category->isNormalDebitBalance()) { $currentBalance += $signedAmount; @@ -219,8 +228,8 @@ public function buildAccountTransactionsReport(string $startDate, string $endDat id: null, date: 'Totals and Ending Balance', description: '', - debit: money($account->total_debit, $defaultCurrency)->format(), - credit: money($account->total_credit, $defaultCurrency)->format(), + debit: money($periodDebitTotal, $defaultCurrency)->format(), + credit: money($periodCreditTotal, $defaultCurrency)->format(), balance: money($currentBalance, $defaultCurrency)->format(), type: null, tableAction: null diff --git a/app/View/Models/InvoiceTotalViewModel.php b/app/View/Models/InvoiceTotalViewModel.php index f919967b..8f1baac3 100644 --- a/app/View/Models/InvoiceTotalViewModel.php +++ b/app/View/Models/InvoiceTotalViewModel.php @@ -3,6 +3,7 @@ namespace App\View\Models; use App\Enums\Accounting\AdjustmentComputation; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Models\Accounting\Adjustment; use App\Models\Accounting\Invoice; use App\Utilities\Currency\CurrencyConverter; @@ -39,9 +40,9 @@ public function buildViewData(): array }, 0); // Calculate discount based on method - $discountMethod = $this->data['discount_method'] ?? 'line_items'; + $discountMethod = DocumentDiscountMethod::parse($this->data['discount_method']) ?? DocumentDiscountMethod::PerLineItem; - if ($discountMethod === 'line_items') { + if ($discountMethod->isPerLineItem()) { $discountTotal = $lineItems->reduce(function ($carry, $item) { $quantity = max((float) ($item['quantity'] ?? 0), 0); $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); @@ -55,10 +56,10 @@ public function buildViewData(): array return $carry + $discountAmount; }, 0); } else { - $discountComputation = $this->data['discount_computation'] ?? AdjustmentComputation::Percentage; + $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage; $discountRate = (float) ($this->data['discount_rate'] ?? 0); - if (AdjustmentComputation::parse($discountComputation) === AdjustmentComputation::Percentage) { + if ($discountComputation->isPercentage()) { $discountTotal = $subtotal * ($discountRate / 100); } else { $discountTotal = $discountRate; diff --git a/composer.json b/composer.json index 29513336..9c7eaf8f 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "andrewdwallo/transmatic": "^1.1", "awcodes/filament-table-repeater": "^3.0", "barryvdh/laravel-snappy": "^1.0", - "filament/filament": "^3.2.115", + "filament/filament": "v3.2.129", "guava/filament-clusters": "^1.1", "guzzlehttp/guzzle": "^7.8", "jaocero/radio-deck": "^1.2", diff --git a/composer.lock b/composer.lock index 03216c35..be6a5dc3 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6f7204f976352f049caf598df4454ace", + "content-hash": "7301f370a98e2573adf4e6b4e7ab4a8e", "packages": [ { "name": "akaunting/laravel-money", @@ -497,16 +497,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.334.4", + "version": "3.334.6", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "3261e515cfc1bae024bce72be3ea28708461c0a3" + "reference": "2b0be3aded849d3b7bb0b53ea3295c7cecdeeee7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3261e515cfc1bae024bce72be3ea28708461c0a3", - "reference": "3261e515cfc1bae024bce72be3ea28708461c0a3", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/2b0be3aded849d3b7bb0b53ea3295c7cecdeeee7", + "reference": "2b0be3aded849d3b7bb0b53ea3295c7cecdeeee7", "shasum": "" }, "require": { @@ -589,9 +589,9 @@ "support": { "forum": "https://forums.aws.amazon.com/forum.jspa?forumID=80", "issues": "https://github.com/aws/aws-sdk-php/issues", - "source": "https://github.com/aws/aws-sdk-php/tree/3.334.4" + "source": "https://github.com/aws/aws-sdk-php/tree/3.334.6" }, - "time": "2024-12-11T19:41:47+00:00" + "time": "2024-12-13T19:18:29+00:00" }, { "name": "aws/aws-sdk-php-laravel", @@ -624,12 +624,12 @@ "type": "library", "extra": { "laravel": { - "providers": [ - "Aws\\Laravel\\AwsServiceProvider" - ], "aliases": { "AWS": "Aws\\Laravel\\AwsFacade" - } + }, + "providers": [ + "Aws\\Laravel\\AwsServiceProvider" + ] } }, "autoload": { @@ -1662,7 +1662,7 @@ }, { "name": "filament/actions", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", @@ -1715,7 +1715,7 @@ }, { "name": "filament/filament", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", @@ -1780,7 +1780,7 @@ }, { "name": "filament/forms", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", @@ -1836,7 +1836,7 @@ }, { "name": "filament/infolists", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", @@ -1887,7 +1887,7 @@ }, { "name": "filament/notifications", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", @@ -1939,16 +1939,16 @@ }, { "name": "filament/support", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "1654851f04733f48faed7235b032b2c5842b5629" + "reference": "e8aed9684d5c58ff7dde9517c7f1af44d575d871" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/1654851f04733f48faed7235b032b2c5842b5629", - "reference": "1654851f04733f48faed7235b032b2c5842b5629", + "url": "https://api.github.com/repos/filamentphp/support/zipball/e8aed9684d5c58ff7dde9517c7f1af44d575d871", + "reference": "e8aed9684d5c58ff7dde9517c7f1af44d575d871", "shasum": "" }, "require": { @@ -1959,7 +1959,7 @@ "illuminate/support": "^10.45|^11.0", "illuminate/view": "^10.45|^11.0", "kirschbaum-development/eloquent-power-joins": "^3.0|^4.0", - "livewire/livewire": "^3.5", + "livewire/livewire": "3.5.12", "php": "^8.1", "ryangjchandler/blade-capture-directive": "^0.2|^0.3|^1.0", "spatie/color": "^1.5", @@ -1994,20 +1994,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-11T12:57:14+00:00" + "time": "2024-12-11T09:05:54+00:00" }, { "name": "filament/tables", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "d7dcaa4d5f04c7e6fb7b9d457083e6fa588ca600" + "reference": "acdec73ae82961654a52a22ed9f53207cfee2ef8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/d7dcaa4d5f04c7e6fb7b9d457083e6fa588ca600", - "reference": "d7dcaa4d5f04c7e6fb7b9d457083e6fa588ca600", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/acdec73ae82961654a52a22ed9f53207cfee2ef8", + "reference": "acdec73ae82961654a52a22ed9f53207cfee2ef8", "shasum": "" }, "require": { @@ -2046,11 +2046,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-11T12:57:12+00:00" + "time": "2024-12-11T09:05:54+00:00" }, { "name": "filament/widgets", - "version": "v3.2.130", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", @@ -2980,16 +2980,16 @@ }, { "name": "laravel/framework", - "version": "v11.35.0", + "version": "v11.35.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc" + "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc", - "reference": "f1a7aaa3c1235b7a95ccaa58db90e0cd9d8c3fcc", + "url": "https://api.github.com/repos/laravel/framework/zipball/dcfa130ede1a6fa4343dc113410963e791ad34fb", + "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb", "shasum": "" }, "require": { @@ -3191,7 +3191,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-12-10T16:09:29+00:00" + "time": "2024-12-12T18:25:58+00:00" }, { "name": "laravel/prompts", @@ -3706,16 +3706,16 @@ }, { "name": "league/csv", - "version": "9.19.0", + "version": "9.20.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "f81df48a012a9e86d077e74eaff666fd15bfab88" + "reference": "553579df208641ada6ffb450b3a151e2fcfa4f31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/f81df48a012a9e86d077e74eaff666fd15bfab88", - "reference": "f81df48a012a9e86d077e74eaff666fd15bfab88", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/553579df208641ada6ffb450b3a151e2fcfa4f31", + "reference": "553579df208641ada6ffb450b3a151e2fcfa4f31", "shasum": "" }, "require": { @@ -3789,7 +3789,7 @@ "type": "github" } ], - "time": "2024-12-08T08:09:35+00:00" + "time": "2024-12-13T15:49:27+00:00" }, { "name": "league/flysystem", @@ -4231,16 +4231,16 @@ }, { "name": "livewire/livewire", - "version": "v3.5.17", + "version": "v3.5.12", "source": { "type": "git", "url": "https://github.com/livewire/livewire.git", - "reference": "7bbf80d93db9b866776bf957ca6229364bca8d87" + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/livewire/livewire/zipball/7bbf80d93db9b866776bf957ca6229364bca8d87", - "reference": "7bbf80d93db9b866776bf957ca6229364bca8d87", + "url": "https://api.github.com/repos/livewire/livewire/zipball/3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", + "reference": "3c8d1f9d7d9098aaea663093ae168f2d5d2ae73d", "shasum": "" }, "require": { @@ -4295,7 +4295,7 @@ "description": "A front-end framework for Laravel.", "support": { "issues": "https://github.com/livewire/livewire/issues", - "source": "https://github.com/livewire/livewire/tree/v3.5.17" + "source": "https://github.com/livewire/livewire/tree/v3.5.12" }, "funding": [ { @@ -4303,7 +4303,7 @@ "type": "github" } ], - "time": "2024-12-06T13:41:21+00:00" + "time": "2024-10-15T19:35:06+00:00" }, { "name": "masterminds/html5", @@ -5350,16 +5350,16 @@ }, { "name": "phpseclib/phpseclib", - "version": "3.0.42", + "version": "3.0.43", "source": { "type": "git", "url": "https://github.com/phpseclib/phpseclib.git", - "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98" + "reference": "709ec107af3cb2f385b9617be72af8cf62441d02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/db92f1b1987b12b13f248fe76c3a52cadb67bb98", - "reference": "db92f1b1987b12b13f248fe76c3a52cadb67bb98", + "url": "https://api.github.com/repos/phpseclib/phpseclib/zipball/709ec107af3cb2f385b9617be72af8cf62441d02", + "reference": "709ec107af3cb2f385b9617be72af8cf62441d02", "shasum": "" }, "require": { @@ -5440,7 +5440,7 @@ ], "support": { "issues": "https://github.com/phpseclib/phpseclib/issues", - "source": "https://github.com/phpseclib/phpseclib/tree/3.0.42" + "source": "https://github.com/phpseclib/phpseclib/tree/3.0.43" }, "funding": [ { @@ -5456,7 +5456,7 @@ "type": "tidelift" } ], - "time": "2024-09-16T03:06:04+00:00" + "time": "2024-12-14T21:12:59+00:00" }, { "name": "psr/cache", @@ -9241,16 +9241,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.6.3", + "version": "v7.7.0", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "ae3c9f1aeda7daa374c904b35ece8f574f56d176" + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/ae3c9f1aeda7daa374c904b35ece8f574f56d176", - "reference": "ae3c9f1aeda7daa374c904b35ece8f574f56d176", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/4fb3f73bc5a4c3146bac2850af7dc72435a32daf", + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf", "shasum": "" }, "require": { @@ -9261,12 +9261,12 @@ "fidry/cpu-core-counter": "^1.2.0", "jean85/pretty-package-versions": "^2.1.0", "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^11.0.7", + "phpunit/php-code-coverage": "^11.0.8", "phpunit/php-file-iterator": "^5.1.0", "phpunit/php-timer": "^7.0.1", - "phpunit/phpunit": "^11.5.0", + "phpunit/phpunit": "^11.5.1", "sebastian/environment": "^7.2.0", - "symfony/console": "^6.4.14 || ^7.2.0", + "symfony/console": "^6.4.14 || ^7.2.1", "symfony/process": "^6.4.14 || ^7.2.0" }, "require-dev": { @@ -9318,7 +9318,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.6.3" + "source": "https://github.com/paratestphp/paratest/tree/v7.7.0" }, "funding": [ { @@ -9330,7 +9330,7 @@ "type": "paypal" } ], - "time": "2024-12-10T13:59:28+00:00" + "time": "2024-12-11T14:50:44+00:00" }, { "name": "fakerphp/faker", @@ -10008,31 +10008,31 @@ }, { "name": "pestphp/pest", - "version": "v3.7.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "9688b83a3d7d0acdda21c01b8aeb933ec9fcd556" + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/9688b83a3d7d0acdda21c01b8aeb933ec9fcd556", - "reference": "9688b83a3d7d0acdda21c01b8aeb933ec9fcd556", + "url": "https://api.github.com/repos/pestphp/pest/zipball/bf3178473dcaa53b0458f21dfdb271306ea62512", + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512", "shasum": "" }, "require": { - "brianium/paratest": "^7.6.2", + "brianium/paratest": "^7.7.0", "nunomaduro/collision": "^8.5.0", "nunomaduro/termwind": "^2.3.0", "pestphp/pest-plugin": "^3.0.0", "pestphp/pest-plugin-arch": "^3.0.0", "pestphp/pest-plugin-mutate": "^3.0.5", "php": "^8.2.0", - "phpunit/phpunit": "^11.5.0" + "phpunit/phpunit": "^11.5.1" }, "conflict": { "filp/whoops": "<2.16.0", - "phpunit/phpunit": ">11.5.0", + "phpunit/phpunit": ">11.5.1", "sebastian/exporter": "<6.0.0", "webmozart/assert": "<1.11.0" }, @@ -10104,7 +10104,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v3.7.0" + "source": "https://github.com/pestphp/pest/tree/v3.7.1" }, "funding": [ { @@ -10116,7 +10116,7 @@ "type": "github" } ], - "time": "2024-12-10T11:54:49+00:00" + "time": "2024-12-12T11:52:01+00:00" }, { "name": "pestphp/pest-plugin", @@ -11061,16 +11061,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.5.0", + "version": "11.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7" + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/0569902506a6c0878930b87ea79ec3b50ea563f7", - "reference": "0569902506a6c0878930b87ea79ec3b50ea563f7", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2b94d4f2450b9869fa64a46fd8a6a41997aef56a", + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a", "shasum": "" }, "require": { @@ -11142,7 +11142,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.0" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.1" }, "funding": [ { @@ -11158,7 +11158,7 @@ "type": "tidelift" } ], - "time": "2024-12-06T05:57:38+00:00" + "time": "2024-12-11T10:52:48+00:00" }, { "name": "pimple/pimple", @@ -11272,23 +11272,23 @@ }, { "name": "sebastian/code-unit", - "version": "3.0.1", + "version": "3.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "6bb7d09d6623567178cf54126afa9c2310114268" + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/6bb7d09d6623567178cf54126afa9c2310114268", - "reference": "6bb7d09d6623567178cf54126afa9c2310114268", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", + "reference": "ee88b0cdbe74cf8dd3b54940ff17643c0d6543ca", "shasum": "" }, "require": { "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^11.0" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { @@ -11317,7 +11317,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/code-unit/issues", "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.1" + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.2" }, "funding": [ { @@ -11325,7 +11325,7 @@ "type": "github" } ], - "time": "2024-07-03T04:44:28+00:00" + "time": "2024-12-12T09:59:06+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", diff --git a/database/factories/Accounting/AdjustmentFactory.php b/database/factories/Accounting/AdjustmentFactory.php index c8fc34ea..ff29c691 100644 --- a/database/factories/Accounting/AdjustmentFactory.php +++ b/database/factories/Accounting/AdjustmentFactory.php @@ -30,9 +30,11 @@ public function definition(): array { $startDate = $this->faker->dateTimeBetween('now', '+1 year'); $endDate = $this->faker->dateTimeBetween($startDate, Carbon::parse($startDate)->addYear()); + + /** @var AdjustmentComputation $computation */ $computation = $this->faker->randomElement(AdjustmentComputation::class); - $rate = $computation === AdjustmentComputation::Fixed + $rate = $computation->isFixed() ? $this->faker->numberBetween(5, 100) * 100 // $5 - $100 for fixed amounts : $this->faker->numberBetween(3, 25) * 10000; // 3% - 25% for percentages diff --git a/database/migrations/2024_11_27_221657_create_bills_table.php b/database/migrations/2024_11_27_221657_create_bills_table.php index e2d97f6a..c563c8fc 100644 --- a/database/migrations/2024_11_27_221657_create_bills_table.php +++ b/database/migrations/2024_11_27_221657_create_bills_table.php @@ -22,6 +22,9 @@ public function up(): void $table->timestamp('paid_at')->nullable(); $table->string('status')->default('unpaid'); $table->string('currency_code')->nullable(); + $table->string('discount_method')->default('per_line_item'); + $table->string('discount_computation')->default('percentage'); + $table->integer('discount_rate')->default(0); $table->integer('subtotal')->default(0); $table->integer('tax_total')->default(0); $table->integer('discount_total')->default(0); diff --git a/database/migrations/2024_11_27_223015_create_invoices_table.php b/database/migrations/2024_11_27_223015_create_invoices_table.php index 85fcf7a4..cf1194e1 100644 --- a/database/migrations/2024_11_27_223015_create_invoices_table.php +++ b/database/migrations/2024_11_27_223015_create_invoices_table.php @@ -27,7 +27,7 @@ public function up(): void $table->timestamp('last_sent')->nullable(); $table->string('status')->default('draft'); $table->string('currency_code')->nullable(); - $table->string('discount_method')->default('line_items'); + $table->string('discount_method')->default('per_line_item'); $table->string('discount_computation')->default('percentage'); $table->integer('discount_rate')->default(0); $table->integer('subtotal')->default(0); diff --git a/package-lock.json b/package-lock.json index 3d9f8829..b8705641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1014,9 +1014,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", + "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", "dev": true, "funding": [ { @@ -1034,9 +1034,9 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -1057,9 +1057,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001687", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001687.tgz", - "integrity": "sha512-0S/FDhf4ZiqrTUiQ39dKeUjYRjkv7lOZU1Dgif2rIqrTzX/1wV2hfKu9TOm1IHkdSijfLswxTFzl/cvir+SLSQ==", + "version": "1.0.30001688", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", + "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", "dev": true, "funding": [ { @@ -1487,9 +1487,9 @@ } }, "node_modules/is-core-module": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", - "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.0.tgz", + "integrity": "sha512-urTSINYfAYgcbLb0yDQ6egFm6h3Mo1DcF9EkyXSRjjzdHbsulg01qhwWuXdOoUBuTkbQ80KDboXa0vFJ+BDH+g==", "dev": true, "license": "MIT", "dependencies": { @@ -2192,13 +2192,13 @@ } }, "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "version": "1.22.9", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", + "integrity": "sha512-QxrmX1DzraFIi9PxdG5VkRfRwIgjwyud+z/iBwfRRrVmHc+P9Q7u2lSSpQ6bjr2gy5lrqIiU9vb6iAeGf2400A==", "dev": true, "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, diff --git a/resources/views/filament/forms/components/invoice-totals.blade.php b/resources/views/filament/forms/components/invoice-totals.blade.php index 4fe5faa5..82630382 100644 --- a/resources/views/filament/forms/components/invoice-totals.blade.php +++ b/resources/views/filament/forms/components/invoice-totals.blade.php @@ -1,11 +1,14 @@ -@use('App\Utilities\Currency\CurrencyAccessor') - @php + use App\Enums\Accounting\DocumentDiscountMethod; + use App\Utilities\Currency\CurrencyAccessor; + use App\View\Models\InvoiceTotalViewModel; + $data = $this->form->getRawState(); - $viewModel = new \App\View\Models\InvoiceTotalViewModel($this->record, $data); - extract($viewModel->buildViewData(), \EXTR_SKIP); + $viewModel = new InvoiceTotalViewModel($this->record, $data); + extract($viewModel->buildViewData(), EXTR_SKIP); - $isInvoiceLevelDiscount = $data['discount_method'] === 'invoice'; + $discountMethod = DocumentDiscountMethod::parse($data['discount_method']); + $isPerDocumentDiscount = $discountMethod->isPerDocument(); @endphp
@@ -29,7 +32,7 @@ Taxes: {{ $taxTotal }} - @if($isInvoiceLevelDiscount) + @if($isPerDocumentDiscount) Discount: