diff --git a/app/Concerns/ManagesLineItems.php b/app/Concerns/ManagesLineItems.php new file mode 100644 index 00000000..2bbb6668 --- /dev/null +++ b/app/Concerns/ManagesLineItems.php @@ -0,0 +1,119 @@ +lineItems->find($itemData['id']) + : $record->lineItems()->make(); + + $lineItem->fill([ + 'offering_id' => $itemData['offering_id'], + 'description' => $itemData['description'], + 'quantity' => $itemData['quantity'], + 'unit_price' => $itemData['unit_price'], + ]); + + if (! $lineItem->exists) { + $lineItem->documentable()->associate($record); + } + + $lineItem->save(); + + $this->handleLineItemAdjustments($lineItem, $itemData, $record->discount_method); + $this->updateLineItemTotals($lineItem, $record->discount_method); + } + } + + protected function deleteRemovedLineItems(Model $record, Collection $lineItems): void + { + $existingLineItemIds = $record->lineItems->pluck('id'); + $updatedLineItemIds = $lineItems->pluck('id')->filter(); + $lineItemsToDelete = $existingLineItemIds->diff($updatedLineItemIds); + + if ($lineItemsToDelete->isNotEmpty()) { + $record + ->lineItems() + ->whereIn('id', $lineItemsToDelete) + ->each(fn (DocumentLineItem $lineItem) => $lineItem->delete()); + } + } + + protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $itemData, DocumentDiscountMethod $discountMethod): void + { + $isBill = $lineItem->documentable instanceof Bill; + + $taxType = $isBill ? 'purchaseTaxes' : 'salesTaxes'; + $discountType = $isBill ? 'purchaseDiscounts' : 'salesDiscounts'; + + $adjustmentIds = collect($itemData[$taxType] ?? []) + ->merge($discountMethod->isPerLineItem() ? ($itemData[$discountType] ?? []) : []) + ->filter() + ->unique(); + + $lineItem->adjustments()->sync($adjustmentIds); + $lineItem->refresh(); + } + + protected function updateLineItemTotals(DocumentLineItem $lineItem, DocumentDiscountMethod $discountMethod): void + { + $lineItem->updateQuietly([ + 'tax_total' => $lineItem->calculateTaxTotal()->getAmount(), + 'discount_total' => $discountMethod->isPerLineItem() + ? $lineItem->calculateDiscountTotal()->getAmount() + : 0, + ]); + } + + protected function updateDocumentTotals(Model $record, array $data): array + { + $subtotalCents = $record->lineItems()->sum('subtotal'); + $taxTotalCents = $record->lineItems()->sum('tax_total'); + $discountTotalCents = $this->calculateDiscountTotal( + DocumentDiscountMethod::parse($data['discount_method']), + AdjustmentComputation::parse($data['discount_computation']), + $data['discount_rate'] ?? null, + $subtotalCents, + $record + ); + + $grandTotalCents = $subtotalCents + $taxTotalCents - $discountTotalCents; + + return [ + 'subtotal' => CurrencyConverter::convertCentsToFormatSimple($subtotalCents), + 'tax_total' => CurrencyConverter::convertCentsToFormatSimple($taxTotalCents), + 'discount_total' => CurrencyConverter::convertCentsToFormatSimple($discountTotalCents), + 'total' => CurrencyConverter::convertCentsToFormatSimple($grandTotalCents), + ]; + } + + protected function calculateDiscountTotal( + DocumentDiscountMethod $discountMethod, + ?AdjustmentComputation $discountComputation, + ?string $discountRate, + int $subtotalCents, + Model $record + ): int { + if ($discountMethod->isPerLineItem()) { + return $record->lineItems()->sum('discount_total'); + } + + if ($discountComputation?->isPercentage()) { + return (int) ($subtotalCents * ((float) $discountRate / 100)); + } + + return CurrencyConverter::convertToCents($discountRate); + } +} diff --git a/app/Enums/Accounting/AdjustmentComputation.php b/app/Enums/Accounting/AdjustmentComputation.php index 7a4b360d..c743e89f 100644 --- a/app/Enums/Accounting/AdjustmentComputation.php +++ b/app/Enums/Accounting/AdjustmentComputation.php @@ -2,10 +2,13 @@ namespace App\Enums\Accounting; +use App\Enums\Concerns\ParsesEnum; use Filament\Support\Contracts\HasLabel; enum AdjustmentComputation: string implements HasLabel { + use ParsesEnum; + case Percentage = 'percentage'; case Fixed = 'fixed'; @@ -13,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/Pages/CompanyDefault.php b/app/Filament/Company/Clusters/Settings/Pages/CompanyDefault.php index bb032349..bbce04b2 100644 --- a/app/Filament/Company/Clusters/Settings/Pages/CompanyDefault.php +++ b/app/Filament/Company/Clusters/Settings/Pages/CompanyDefault.php @@ -6,8 +6,6 @@ use App\Filament\Company\Clusters\Settings; use App\Models\Banking\BankAccount; use App\Models\Setting\CompanyDefault as CompanyDefaultModel; -use App\Models\Setting\Discount; -use App\Models\Setting\Tax; use Filament\Actions\Action; use Filament\Actions\ActionGroup; use Filament\Forms\Components\Component; @@ -108,7 +106,6 @@ public function form(Form $form): Form return $form ->schema([ $this->getGeneralSection(), - // $this->getModifiersSection(), ]) ->model($this->record) ->statePath('data') @@ -140,76 +137,6 @@ protected function getGeneralSection(): Component ])->columns(); } - protected function getModifiersSection(): Component - { - return Section::make('Taxes & Discounts') - ->schema([ - Select::make('sales_tax_id') - ->softRequired() - ->localizeLabel() - ->relationship('salesTax', 'name') - ->getOptionLabelFromRecordUsing(function (Tax $record) { - $currencyCode = $this->record->currency_code; - - $rate = rateFormat($record->rate, $record->computation->value, $currencyCode); - - $rateBadge = $this->renderBadgeOptionLabel($rate); - - return "{$record->name} ⁓ {$rateBadge}"; - }) - ->allowHtml() - ->saveRelationshipsUsing(null) - ->searchable(), - Select::make('purchase_tax_id') - ->softRequired() - ->localizeLabel() - ->relationship('purchaseTax', 'name') - ->getOptionLabelFromRecordUsing(function (Tax $record) { - $currencyCode = $this->record->currency_code; - - $rate = rateFormat($record->rate, $record->computation->value, $currencyCode); - - $rateBadge = $this->renderBadgeOptionLabel($rate); - - return "{$record->name} ⁓ {$rateBadge}"; - }) - ->allowHtml() - ->saveRelationshipsUsing(null) - ->searchable(), - Select::make('sales_discount_id') - ->softRequired() - ->localizeLabel() - ->relationship('salesDiscount', 'name') - ->getOptionLabelFromRecordUsing(function (Discount $record) { - $currencyCode = $this->record->currency_code; - - $rate = rateFormat($record->rate, $record->computation->value, $currencyCode); - - $rateBadge = $this->renderBadgeOptionLabel($rate); - - return "{$record->name} ⁓ {$rateBadge}"; - }) - ->saveRelationshipsUsing(null) - ->allowHtml() - ->searchable(), - Select::make('purchase_discount_id') - ->softRequired() - ->localizeLabel() - ->relationship('purchaseDiscount', 'name') - ->getOptionLabelFromRecordUsing(function (Discount $record) { - $currencyCode = $this->record->currency_code; - $rate = rateFormat($record->rate, $record->computation->value, $currencyCode); - - $rateBadge = $this->renderBadgeOptionLabel($rate); - - return "{$record->name} ⁓ {$rateBadge}"; - }) - ->allowHtml() - ->saveRelationshipsUsing(null) - ->searchable(), - ])->columns(); - } - public function renderBadgeOptionLabel(string $label): string { return Blade::render('filament::components.badge', [ 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/Purchases/BillResource.php b/app/Filament/Company/Resources/Purchases/BillResource.php index 4a6f9852..e935e363 100644 --- a/app/Filament/Company/Resources/Purchases/BillResource.php +++ b/app/Filament/Company/Resources/Purchases/BillResource.php @@ -3,13 +3,14 @@ namespace App\Filament\Company\Resources\Purchases; use App\Enums\Accounting\BillStatus; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Enums\Accounting\PaymentMethod; use App\Filament\Company\Resources\Purchases\BillResource\Pages; +use App\Filament\Forms\Components\BillTotals; use App\Filament\Tables\Actions\ReplicateBulkAction; use App\Filament\Tables\Filters\DateRangeFilter; use App\Models\Accounting\Adjustment; use App\Models\Accounting\Bill; -use App\Models\Accounting\DocumentLineItem; use App\Models\Banking\BankAccount; use App\Models\Common\Offering; use App\Utilities\Currency\CurrencyConverter; @@ -26,7 +27,6 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Auth; class BillResource extends Resource @@ -71,129 +71,44 @@ public static function form(Form $form): Form return now()->addDays($company->defaultBill->payment_terms->getDays()); }) ->required(), + Forms\Components\Select::make('discount_method') + ->label('Discount Method') + ->options(DocumentDiscountMethod::class) + ->selectablePlaceholder(false) + ->default(DocumentDiscountMethod::PerLineItem) + ->afterStateUpdated(function ($state, Forms\Set $set) { + $discountMethod = DocumentDiscountMethod::parse($state); + + if ($discountMethod->isPerDocument()) { + $set('lineItems.*.purchaseDiscounts', []); + } + }) + ->live(), ])->grow(true), ])->from('md'), TableRepeater::make('lineItems') ->relationship() - ->saveRelationshipsUsing(function (TableRepeater $component, Forms\Contracts\HasForms $livewire, ?array $state) { - if (! is_array($state)) { - $state = []; - } - - $relationship = $component->getRelationship(); - - $existingRecords = $component->getCachedExistingRecords(); - - $recordsToDelete = []; - - foreach ($existingRecords->pluck($relationship->getRelated()->getKeyName()) as $keyToCheckForDeletion) { - if (array_key_exists("record-{$keyToCheckForDeletion}", $state)) { - continue; - } - - $recordsToDelete[] = $keyToCheckForDeletion; - $existingRecords->forget("record-{$keyToCheckForDeletion}"); + ->saveRelationshipsUsing(null) + ->dehydrated(true) + ->headers(function (Forms\Get $get) { + $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem(); + + $headers = [ + Header::make('Items')->width($hasDiscounts ? '15%' : '20%'), + Header::make('Description')->width($hasDiscounts ? '25%' : '30%'), // Increase when no discounts + Header::make('Quantity')->width('10%'), + Header::make('Price')->width('10%'), + Header::make('Taxes')->width($hasDiscounts ? '15%' : '20%'), // Increase when no discounts + ]; + + if ($hasDiscounts) { + $headers[] = Header::make('Discounts')->width('15%'); } - $relationship - ->whereKey($recordsToDelete) - ->get() - ->each(static fn (Model $record) => $record->delete()); - - $childComponentContainers = $component->getChildComponentContainers( - withHidden: $component->shouldSaveRelationshipsWhenHidden(), - ); - - $itemOrder = 1; - $orderColumn = $component->getOrderColumn(); - - $translatableContentDriver = $livewire->makeFilamentTranslatableContentDriver(); - - foreach ($childComponentContainers as $itemKey => $item) { - $itemData = $item->getState(shouldCallHooksBefore: false); - - if ($orderColumn) { - $itemData[$orderColumn] = $itemOrder; - - $itemOrder++; - } - - if ($record = ($existingRecords[$itemKey] ?? null)) { - $itemData = $component->mutateRelationshipDataBeforeSave($itemData, record: $record); - - if ($itemData === null) { - continue; - } - - $translatableContentDriver ? - $translatableContentDriver->updateRecord($record, $itemData) : - $record->fill($itemData)->save(); - - continue; - } - - $relatedModel = $component->getRelatedModel(); - - $itemData = $component->mutateRelationshipDataBeforeCreate($itemData); - - if ($itemData === null) { - continue; - } - - if ($translatableContentDriver) { - $record = $translatableContentDriver->makeRecord($relatedModel, $itemData); - } else { - $record = new $relatedModel; - $record->fill($itemData); - } + $headers[] = Header::make('Amount')->width('10%')->align('right'); - $record = $relationship->save($record); - $item->model($record)->saveRelationships(); - $existingRecords->push($record); - } - - $component->getRecord()->setRelation($component->getRelationshipName(), $existingRecords); - - /** @var Bill $bill */ - $bill = $component->getRecord(); - - // Recalculate totals for line items - $bill->lineItems()->each(function (DocumentLineItem $lineItem) { - $lineItem->updateQuietly([ - 'tax_total' => $lineItem->calculateTaxTotal()->getAmount(), - 'discount_total' => $lineItem->calculateDiscountTotal()->getAmount(), - ]); - }); - - $subtotal = $bill->lineItems()->sum('subtotal') / 100; - $taxTotal = $bill->lineItems()->sum('tax_total') / 100; - $discountTotal = $bill->lineItems()->sum('discount_total') / 100; - $grandTotal = $subtotal + $taxTotal - $discountTotal; - - $bill->updateQuietly([ - 'subtotal' => $subtotal, - 'tax_total' => $taxTotal, - 'discount_total' => $discountTotal, - 'total' => $grandTotal, - ]); - - $bill->refresh(); - - if (! $bill->initialTransaction) { - $bill->createInitialTransaction(); - } else { - $bill->updateInitialTransaction(); - } + return $headers; }) - ->headers([ - Header::make('Items')->width('15%'), - Header::make('Description')->width('25%'), - Header::make('Quantity')->width('10%'), - Header::make('Price')->width('10%'), - Header::make('Taxes')->width('15%'), - Header::make('Discounts')->width('15%'), - Header::make('Amount')->width('10%')->align('right'), - ]) ->schema([ Forms\Components\Select::make('offering_id') ->relationship('purchasableOffering', 'name') @@ -209,7 +124,11 @@ public static function form(Form $form): Form $set('description', $offeringRecord->description); $set('unit_price', $offeringRecord->price); $set('purchaseTaxes', $offeringRecord->purchaseTaxes->pluck('id')->toArray()); - $set('purchaseDiscounts', $offeringRecord->purchaseDiscounts->pluck('id')->toArray()); + + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); + if ($discountMethod->isPerLineItem()) { + $set('purchaseDiscounts', $offeringRecord->purchaseDiscounts->pluck('id')->toArray()); + } } }), Forms\Components\TextInput::make('description'), @@ -225,15 +144,24 @@ public static function form(Form $form): Form ->default(0), Forms\Components\Select::make('purchaseTaxes') ->relationship('purchaseTaxes', 'name') + ->saveRelationshipsUsing(null) + ->dehydrated(true) ->preload() ->multiple() ->live() ->searchable(), Forms\Components\Select::make('purchaseDiscounts') ->relationship('purchaseDiscounts', 'name') + ->saveRelationshipsUsing(null) + ->dehydrated(true) ->preload() ->multiple() ->live() + ->hidden(function (Forms\Get $get) { + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); + + return $discountMethod->isPerDocument(); + }) ->searchable(), Forms\Components\Placeholder::make('total') ->hiddenLabel() @@ -265,13 +193,7 @@ public static function form(Form $form): Form return CurrencyConverter::formatToMoney($total); }), ]), - Forms\Components\Grid::make(6) - ->schema([ - Forms\Components\ViewField::make('totals') - ->columnStart(5) - ->columnSpan(2) - ->view('filament.forms.components.bill-totals'), - ]), + BillTotals::make(), ]), ]); } @@ -281,6 +203,11 @@ public static function table(Table $table): Table return $table ->defaultSort('due_date') ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true) + ->searchable(), Tables\Columns\TextColumn::make('status') ->badge() ->searchable(), diff --git a/app/Filament/Company/Resources/Purchases/BillResource/Pages/CreateBill.php b/app/Filament/Company/Resources/Purchases/BillResource/Pages/CreateBill.php index 4e708d96..6128fde4 100644 --- a/app/Filament/Company/Resources/Purchases/BillResource/Pages/CreateBill.php +++ b/app/Filament/Company/Resources/Purchases/BillResource/Pages/CreateBill.php @@ -2,13 +2,17 @@ namespace App\Filament\Company\Resources\Purchases\BillResource\Pages; +use App\Concerns\ManagesLineItems; use App\Concerns\RedirectToListPage; use App\Filament\Company\Resources\Purchases\BillResource; +use App\Models\Accounting\Bill; use Filament\Resources\Pages\CreateRecord; use Filament\Support\Enums\MaxWidth; +use Illuminate\Database\Eloquent\Model; class CreateBill extends CreateRecord { + use ManagesLineItems; use RedirectToListPage; protected static string $resource = BillResource::class; @@ -17,4 +21,22 @@ public function getMaxContentWidth(): MaxWidth | string | null { return MaxWidth::Full; } + + protected function handleRecordCreation(array $data): Model + { + /** @var Bill $record */ + $record = parent::handleRecordCreation($data); + + $this->handleLineItems($record, collect($data['lineItems'] ?? [])); + + $totals = $this->updateDocumentTotals($record, $data); + + $record->updateQuietly($totals); + + if (! $record->initialTransaction) { + $record->createInitialTransaction(); + } + + return $record; + } } diff --git a/app/Filament/Company/Resources/Purchases/BillResource/Pages/EditBill.php b/app/Filament/Company/Resources/Purchases/BillResource/Pages/EditBill.php index 2f38dded..488e22b9 100644 --- a/app/Filament/Company/Resources/Purchases/BillResource/Pages/EditBill.php +++ b/app/Filament/Company/Resources/Purchases/BillResource/Pages/EditBill.php @@ -2,14 +2,18 @@ namespace App\Filament\Company\Resources\Purchases\BillResource\Pages; +use App\Concerns\ManagesLineItems; use App\Concerns\RedirectToListPage; use App\Filament\Company\Resources\Purchases\BillResource; +use App\Models\Accounting\Bill; use Filament\Actions; use Filament\Resources\Pages\EditRecord; use Filament\Support\Enums\MaxWidth; +use Illuminate\Database\Eloquent\Model; class EditBill extends EditRecord { + use ManagesLineItems; use RedirectToListPage; protected static string $resource = BillResource::class; @@ -25,4 +29,28 @@ public function getMaxContentWidth(): MaxWidth | string | null { return MaxWidth::Full; } + + protected function handleRecordUpdate(Model $record, array $data): Model + { + /** @var Bill $record */ + $lineItems = collect($data['lineItems'] ?? []); + + $this->deleteRemovedLineItems($record, $lineItems); + + $this->handleLineItems($record, $lineItems); + + $totals = $this->updateDocumentTotals($record, $data); + + $data = array_merge($data, $totals); + + $record = parent::handleRecordUpdate($record, $data); + + if (! $record->initialTransaction) { + $record->createInitialTransaction(); + } else { + $record->updateInitialTransaction(); + } + + return $record; + } } diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource.php b/app/Filament/Company/Resources/Sales/InvoiceResource.php index bf4646d6..7c6cac8d 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource.php @@ -3,15 +3,16 @@ 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; use App\Filament\Company\Resources\Sales\InvoiceResource\RelationManagers; use App\Filament\Company\Resources\Sales\InvoiceResource\Widgets; +use App\Filament\Forms\Components\InvoiceTotals; use App\Filament\Tables\Actions\ReplicateBulkAction; use App\Filament\Tables\Filters\DateRangeFilter; use App\Models\Accounting\Adjustment; -use App\Models\Accounting\DocumentLineItem; use App\Models\Accounting\Invoice; use App\Models\Banking\BankAccount; use App\Models\Common\Offering; @@ -30,7 +31,6 @@ use Filament\Tables\Table; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Collection; -use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\Auth; use Livewire\Features\SupportFileUploads\TemporaryUploadedFile; @@ -128,125 +128,44 @@ public static function form(Form $form): Form ->minDate(static function (Forms\Get $get) { return $get('date') ?? now(); }), + Forms\Components\Select::make('discount_method') + ->label('Discount Method') + ->options(DocumentDiscountMethod::class) + ->selectablePlaceholder(false) + ->default(DocumentDiscountMethod::PerLineItem) + ->afterStateUpdated(function ($state, Forms\Set $set) { + $discountMethod = DocumentDiscountMethod::parse($state); + + if ($discountMethod->isPerDocument()) { + $set('lineItems.*.salesDiscounts', []); + } + }) + ->live(), ])->grow(true), ])->from('md'), TableRepeater::make('lineItems') ->relationship() - ->saveRelationshipsUsing(function (TableRepeater $component, Forms\Contracts\HasForms $livewire, ?array $state) { - if (! is_array($state)) { - $state = []; - } - - $relationship = $component->getRelationship(); - - $existingRecords = $component->getCachedExistingRecords(); - - $recordsToDelete = []; - - foreach ($existingRecords->pluck($relationship->getRelated()->getKeyName()) as $keyToCheckForDeletion) { - if (array_key_exists("record-{$keyToCheckForDeletion}", $state)) { - continue; - } - - $recordsToDelete[] = $keyToCheckForDeletion; - $existingRecords->forget("record-{$keyToCheckForDeletion}"); - } - - $relationship - ->whereKey($recordsToDelete) - ->get() - ->each(static fn (Model $record) => $record->delete()); - - $childComponentContainers = $component->getChildComponentContainers( - withHidden: $component->shouldSaveRelationshipsWhenHidden(), - ); - - $itemOrder = 1; - $orderColumn = $component->getOrderColumn(); - - $translatableContentDriver = $livewire->makeFilamentTranslatableContentDriver(); - - foreach ($childComponentContainers as $itemKey => $item) { - $itemData = $item->getState(shouldCallHooksBefore: false); - - if ($orderColumn) { - $itemData[$orderColumn] = $itemOrder; - - $itemOrder++; - } - - if ($record = ($existingRecords[$itemKey] ?? null)) { - $itemData = $component->mutateRelationshipDataBeforeSave($itemData, record: $record); - - if ($itemData === null) { - continue; - } - - $translatableContentDriver ? - $translatableContentDriver->updateRecord($record, $itemData) : - $record->fill($itemData)->save(); - - continue; - } - - $relatedModel = $component->getRelatedModel(); - - $itemData = $component->mutateRelationshipDataBeforeCreate($itemData); - - if ($itemData === null) { - continue; - } - - if ($translatableContentDriver) { - $record = $translatableContentDriver->makeRecord($relatedModel, $itemData); - } else { - $record = new $relatedModel; - $record->fill($itemData); - } - - $record = $relationship->save($record); - $item->model($record)->saveRelationships(); - $existingRecords->push($record); + ->saveRelationshipsUsing(null) + ->dehydrated(true) + ->headers(function (Forms\Get $get) { + $hasDiscounts = DocumentDiscountMethod::parse($get('discount_method'))->isPerLineItem(); + + $headers = [ + Header::make('Items')->width($hasDiscounts ? '15%' : '20%'), + Header::make('Description')->width($hasDiscounts ? '25%' : '30%'), // Increase when no discounts + Header::make('Quantity')->width('10%'), + Header::make('Price')->width('10%'), + Header::make('Taxes')->width($hasDiscounts ? '15%' : '20%'), // Increase when no discounts + ]; + + if ($hasDiscounts) { + $headers[] = Header::make('Discounts')->width('15%'); } - $component->getRecord()->setRelation($component->getRelationshipName(), $existingRecords); - - /** @var Invoice $invoice */ - $invoice = $component->getRecord(); - - // Recalculate totals for line items - $invoice->lineItems()->each(function (DocumentLineItem $lineItem) { - $lineItem->updateQuietly([ - 'tax_total' => $lineItem->calculateTaxTotal()->getAmount(), - 'discount_total' => $lineItem->calculateDiscountTotal()->getAmount(), - ]); - }); - - $subtotal = $invoice->lineItems()->sum('subtotal') / 100; - $taxTotal = $invoice->lineItems()->sum('tax_total') / 100; - $discountTotal = $invoice->lineItems()->sum('discount_total') / 100; - $grandTotal = $subtotal + $taxTotal - $discountTotal; - - $invoice->updateQuietly([ - 'subtotal' => $subtotal, - 'tax_total' => $taxTotal, - 'discount_total' => $discountTotal, - 'total' => $grandTotal, - ]); + $headers[] = Header::make('Amount')->width('10%')->align('right'); - if ($invoice->approved_at && $invoice->approvalTransaction) { - $invoice->updateApprovalTransaction(); - } + return $headers; }) - ->headers([ - Header::make('Items')->width('15%'), - Header::make('Description')->width('25%'), - Header::make('Quantity')->width('10%'), - Header::make('Price')->width('10%'), - Header::make('Taxes')->width('15%'), - Header::make('Discounts')->width('15%'), - Header::make('Amount')->width('10%')->align('right'), - ]) ->schema([ Forms\Components\Select::make('offering_id') ->relationship('sellableOffering', 'name') @@ -262,7 +181,11 @@ public static function form(Form $form): Form $set('description', $offeringRecord->description); $set('unit_price', $offeringRecord->price); $set('salesTaxes', $offeringRecord->salesTaxes->pluck('id')->toArray()); - $set('salesDiscounts', $offeringRecord->salesDiscounts->pluck('id')->toArray()); + + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); + if ($discountMethod->isPerLineItem()) { + $set('salesDiscounts', $offeringRecord->salesDiscounts->pluck('id')->toArray()); + } } }), Forms\Components\TextInput::make('description'), @@ -278,15 +201,24 @@ public static function form(Form $form): Form ->default(0), Forms\Components\Select::make('salesTaxes') ->relationship('salesTaxes', 'name') + ->saveRelationshipsUsing(null) + ->dehydrated(true) ->preload() ->multiple() ->live() ->searchable(), Forms\Components\Select::make('salesDiscounts') ->relationship('salesDiscounts', 'name') + ->saveRelationshipsUsing(null) + ->dehydrated(true) ->preload() ->multiple() ->live() + ->hidden(function (Forms\Get $get) { + $discountMethod = DocumentDiscountMethod::parse($get('../../discount_method')); + + return $discountMethod->isPerDocument(); + }) ->searchable(), Forms\Components\Placeholder::make('total') ->hiddenLabel() @@ -318,13 +250,7 @@ public static function form(Form $form): Form return CurrencyConverter::formatToMoney($total); }), ]), - Forms\Components\Grid::make(6) - ->schema([ - Forms\Components\ViewField::make('totals') - ->columnStart(5) - ->columnSpan(2) - ->view('filament.forms.components.invoice-totals'), - ]), + InvoiceTotals::make(), Forms\Components\Textarea::make('terms') ->columnSpanFull(), ]), @@ -342,6 +268,11 @@ public static function table(Table $table): Table return $table ->defaultSort('due_date') ->columns([ + Tables\Columns\TextColumn::make('id') + ->label('ID') + ->sortable() + ->toggleable(isToggledHiddenByDefault: true) + ->searchable(), Tables\Columns\TextColumn::make('status') ->badge() ->searchable(), diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/CreateInvoice.php b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/CreateInvoice.php index 36dfb1c7..c58a969e 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/CreateInvoice.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/CreateInvoice.php @@ -2,13 +2,17 @@ namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages; +use App\Concerns\ManagesLineItems; use App\Concerns\RedirectToListPage; use App\Filament\Company\Resources\Sales\InvoiceResource; +use App\Models\Accounting\Invoice; use Filament\Resources\Pages\CreateRecord; use Filament\Support\Enums\MaxWidth; +use Illuminate\Database\Eloquent\Model; class CreateInvoice extends CreateRecord { + use ManagesLineItems; use RedirectToListPage; protected static string $resource = InvoiceResource::class; @@ -17,4 +21,18 @@ public function getMaxContentWidth(): MaxWidth | string | null { return MaxWidth::Full; } + + protected function handleRecordCreation(array $data): Model + { + /** @var Invoice $record */ + $record = parent::handleRecordCreation($data); + + $this->handleLineItems($record, collect($data['lineItems'] ?? [])); + + $totals = $this->updateDocumentTotals($record, $data); + + $record->updateQuietly($totals); + + return $record; + } } diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/EditInvoice.php b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/EditInvoice.php index ea926050..27db7329 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/EditInvoice.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/EditInvoice.php @@ -2,14 +2,18 @@ namespace App\Filament\Company\Resources\Sales\InvoiceResource\Pages; +use App\Concerns\ManagesLineItems; use App\Concerns\RedirectToListPage; use App\Filament\Company\Resources\Sales\InvoiceResource; +use App\Models\Accounting\Invoice; use Filament\Actions; use Filament\Resources\Pages\EditRecord; use Filament\Support\Enums\MaxWidth; +use Illuminate\Database\Eloquent\Model; class EditInvoice extends EditRecord { + use ManagesLineItems; use RedirectToListPage; protected static string $resource = InvoiceResource::class; @@ -25,4 +29,26 @@ public function getMaxContentWidth(): MaxWidth | string | null { return MaxWidth::Full; } + + protected function handleRecordUpdate(Model $record, array $data): Model + { + /** @var Invoice $record */ + $lineItems = collect($data['lineItems'] ?? []); + + $this->deleteRemovedLineItems($record, $lineItems); + + $this->handleLineItems($record, $lineItems); + + $totals = $this->updateDocumentTotals($record, $data); + + $data = array_merge($data, $totals); + + $record = parent::handleRecordUpdate($record, $data); + + if ($record->approved_at && $record->approvalTransaction) { + $record->updateApprovalTransaction(); + } + + return $record; + } } diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php index 7ceb1dc5..4d93c73d 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource/Pages/ViewInvoice.php @@ -6,22 +6,32 @@ use App\Filament\Company\Resources\Sales\InvoiceResource; use App\Models\Accounting\Invoice; use Filament\Actions; +use Filament\Infolists\Components\Grid; use Filament\Infolists\Components\Section; use Filament\Infolists\Components\TextEntry; +use Filament\Infolists\Components\ViewEntry; use Filament\Infolists\Infolist; use Filament\Resources\Pages\ViewRecord; use Filament\Support\Enums\FontWeight; use Filament\Support\Enums\IconPosition; use Filament\Support\Enums\IconSize; +use Filament\Support\Enums\MaxWidth; class ViewInvoice extends ViewRecord { + protected static string $view = 'filament.company.resources.sales.invoices.pages.view-invoice'; + protected static string $resource = InvoiceResource::class; protected $listeners = [ 'refresh' => '$refresh', ]; + public function getMaxContentWidth(): MaxWidth | string | null + { + return MaxWidth::SixExtraLarge; + } + protected function getHeaderActions(): array { return [ @@ -49,39 +59,47 @@ public function infolist(Infolist $infolist): Infolist Section::make('Invoice Details') ->columns(4) ->schema([ - TextEntry::make('invoice_number') - ->label('Invoice #'), - TextEntry::make('status') - ->badge(), - TextEntry::make('client.name') - ->label('Client') - ->color('primary') - ->weight(FontWeight::SemiBold) - ->url(static fn (Invoice $record) => ClientResource::getUrl('edit', ['record' => $record->client_id])), - TextEntry::make('total') - ->label('Total') - ->money(), - TextEntry::make('amount_due') - ->label('Amount Due') - ->money(), - TextEntry::make('date') - ->label('Date') - ->date(), - TextEntry::make('due_date') - ->label('Due') - ->asRelativeDay(), - TextEntry::make('approved_at') - ->label('Approved At') - ->placeholder('Not Approved') - ->date(), - TextEntry::make('last_sent') - ->label('Last Sent') - ->placeholder('Never') - ->date(), - TextEntry::make('paid_at') - ->label('Paid At') - ->placeholder('Not Paid') - ->date(), + Grid::make(1) + ->schema([ + TextEntry::make('invoice_number') + ->label('Invoice #'), + TextEntry::make('status') + ->badge(), + TextEntry::make('client.name') + ->label('Client') + ->color('primary') + ->weight(FontWeight::SemiBold) + ->url(static fn (Invoice $record) => ClientResource::getUrl('edit', ['record' => $record->client_id])), + TextEntry::make('amount_due') + ->label('Amount Due') + ->money(), + TextEntry::make('due_date') + ->label('Due') + ->asRelativeDay(), + TextEntry::make('approved_at') + ->label('Approved At') + ->placeholder('Not Approved') + ->date(), + TextEntry::make('last_sent') + ->label('Last Sent') + ->placeholder('Never') + ->date(), + TextEntry::make('paid_at') + ->label('Paid At') + ->placeholder('Not Paid') + ->date(), + ])->columnSpan(1), + Grid::make() + ->schema([ + ViewEntry::make('invoice-view') + ->label('View Invoice') + ->columnSpan(3) + ->view('filament.company.resources.sales.invoices.components.invoice-view') + ->viewData([ + 'invoice' => $this->record, + ]), + ]) + ->columnSpan(3), ]), ]); } diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php b/app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php index 54cef717..f077bf6d 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource/RelationManagers/PaymentsRelationManager.php @@ -28,6 +28,8 @@ class PaymentsRelationManager extends RelationManager protected static ?string $modelLabel = 'Payment'; + protected static bool $isLazy = false; + protected $listeners = [ 'refresh' => '$refresh', ]; diff --git a/app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php b/app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php index 5b2f6e19..6fdb6ead 100644 --- a/app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php +++ b/app/Filament/Company/Resources/Sales/InvoiceResource/Widgets/InvoiceOverview.php @@ -45,7 +45,9 @@ protected function getStats(): array $totalValidInvoiceCount = $validInvoices->count(); - $averageInvoiceTotal = $totalValidInvoiceCount > 0 ? $totalValidInvoiceAmount / $totalValidInvoiceCount : 0; + $averageInvoiceTotal = $totalValidInvoiceCount > 0 + ? (int) round($totalValidInvoiceAmount / $totalValidInvoiceCount) + : 0; $averagePaymentTime = $this->getPageTableQuery() ->whereNotNull('paid_at') diff --git a/app/Filament/Forms/Components/BillTotals.php b/app/Filament/Forms/Components/BillTotals.php new file mode 100644 index 00000000..06409f94 --- /dev/null +++ b/app/Filament/Forms/Components/BillTotals.php @@ -0,0 +1,37 @@ +schema([ + TextInput::make('discount_rate') + ->label('Discount Rate') + ->hiddenLabel() + ->live() + ->rate(computation: static fn (Get $get) => $get('discount_computation'), showAffix: false), + Select::make('discount_computation') + ->label('Discount Computation') + ->hiddenLabel() + ->options([ + 'percentage' => '%', + 'fixed' => '$', + ]) + ->default(AdjustmentComputation::Percentage) + ->selectablePlaceholder(false) + ->live(), + ]); + } +} diff --git a/app/Filament/Forms/Components/InvoiceTotals.php b/app/Filament/Forms/Components/InvoiceTotals.php new file mode 100644 index 00000000..23d6f759 --- /dev/null +++ b/app/Filament/Forms/Components/InvoiceTotals.php @@ -0,0 +1,37 @@ +schema([ + TextInput::make('discount_rate') + ->label('Discount Rate') + ->hiddenLabel() + ->live() + ->rate(computation: static fn (Get $get) => $get('discount_computation'), showAffix: false), + Select::make('discount_computation') + ->label('Discount Computation') + ->hiddenLabel() + ->options([ + 'percentage' => '%', + 'fixed' => '$', + ]) + ->default(AdjustmentComputation::Percentage) + ->selectablePlaceholder(false) + ->live(), + ]); + } +} diff --git a/app/Models/Accounting/Account.php b/app/Models/Accounting/Account.php index e803dd85..f33c9b57 100644 --- a/app/Models/Accounting/Account.php +++ b/app/Models/Accounting/Account.php @@ -133,6 +133,16 @@ public static function getAccountsPayableAccount(): self return self::where('name', 'Accounts Payable')->firstOrFail(); } + public static function getSalesDiscountAccount(): self + { + return self::where('name', 'Sales Discount')->firstOrFail(); + } + + public static function getPurchaseDiscountAccount(): self + { + return self::where('name', 'Purchase Discount')->firstOrFail(); + } + protected static function newFactory(): Factory { return AccountFactory::new(); diff --git a/app/Models/Accounting/Bill.php b/app/Models/Accounting/Bill.php index 23fe19b3..2f7eafa4 100644 --- a/app/Models/Accounting/Bill.php +++ b/app/Models/Accounting/Bill.php @@ -3,14 +3,18 @@ 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; use App\Models\Common\Vendor; use App\Observers\BillObserver; +use App\Utilities\Currency\CurrencyConverter; use Filament\Actions\MountableAction; use Filament\Actions\ReplicateAction; use Illuminate\Database\Eloquent\Attributes\ObservedBy; @@ -42,6 +46,9 @@ class Bill extends Model 'paid_at', 'status', 'currency_code', + 'discount_method', + 'discount_computation', + 'discount_rate', 'subtotal', 'tax_total', 'discount_total', @@ -57,6 +64,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, @@ -215,7 +225,11 @@ public function createInitialTransaction(?Carbon $postedAt = null): void 'description' => $baseDescription, ]); - foreach ($this->lineItems as $lineItem) { + $totalLineItemSubtotal = (int) $this->lineItems()->sum('subtotal'); + $billDiscountTotalCents = (int) $this->getRawOriginal('discount_total'); + $remainingDiscountCents = $billDiscountTotalCents; + + foreach ($this->lineItems as $index => $lineItem) { $lineItemDescription = "{$baseDescription} › {$lineItem->offering->name}"; $transaction->journalEntries()->create([ @@ -245,6 +259,29 @@ public function createInitialTransaction(?Carbon $postedAt = null): void ]); } } + + if ($this->discount_method->isPerDocument() && $totalLineItemSubtotal > 0) { + $lineItemSubtotalCents = (int) $lineItem->getRawOriginal('subtotal'); + + if ($index === $this->lineItems->count() - 1) { + $lineItemDiscount = $remainingDiscountCents; + } else { + $lineItemDiscount = (int) round( + ($lineItemSubtotalCents / $totalLineItemSubtotal) * $billDiscountTotalCents + ); + $remainingDiscountCents -= $lineItemDiscount; + } + + if ($lineItemDiscount > 0) { + $transaction->journalEntries()->create([ + 'company_id' => $this->company_id, + 'type' => JournalEntryType::Credit, + 'account_id' => Account::getPurchaseDiscountAccount()->id, + 'amount' => CurrencyConverter::convertCentsToFormatSimple($lineItemDiscount), + 'description' => "{$lineItemDescription} (Proportional Discount)", + ]); + } + } } } diff --git a/app/Models/Accounting/Invoice.php b/app/Models/Accounting/Invoice.php index ac3aa45a..c2d1b710 100644 --- a/app/Models/Accounting/Invoice.php +++ b/app/Models/Accounting/Invoice.php @@ -3,15 +3,19 @@ namespace App\Models\Accounting; use App\Casts\MoneyCast; +use App\Casts\RateCast; use App\Collections\Accounting\InvoiceCollection; 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; use App\Filament\Company\Resources\Sales\InvoiceResource; use App\Models\Common\Client; use App\Observers\InvoiceObserver; +use App\Utilities\Currency\CurrencyConverter; use Filament\Actions\Action; use Filament\Actions\MountableAction; use Filament\Actions\ReplicateAction; @@ -51,6 +55,9 @@ class Invoice extends Model 'last_sent', 'status', 'currency_code', + 'discount_method', + 'discount_computation', + 'discount_rate', 'subtotal', 'tax_total', 'discount_total', @@ -69,6 +76,9 @@ 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, 'tax_total' => MoneyCast::class, 'discount_total' => MoneyCast::class, @@ -260,7 +270,11 @@ public function createApprovalTransaction(): void 'description' => $baseDescription, ]); - foreach ($this->lineItems as $lineItem) { + $totalLineItemSubtotal = (int) $this->lineItems()->sum('subtotal'); + $invoiceDiscountTotalCents = (int) $this->getRawOriginal('discount_total'); + $remainingDiscountCents = $invoiceDiscountTotalCents; + + foreach ($this->lineItems as $index => $lineItem) { $lineItemDescription = "{$baseDescription} › {$lineItem->offering->name}"; $transaction->journalEntries()->create([ @@ -280,6 +294,29 @@ public function createApprovalTransaction(): void 'description' => $lineItemDescription, ]); } + + if ($this->discount_method->isPerDocument() && $totalLineItemSubtotal > 0) { + $lineItemSubtotalCents = (int) $lineItem->getRawOriginal('subtotal'); + + if ($index === $this->lineItems->count() - 1) { + $lineItemDiscount = $remainingDiscountCents; + } else { + $lineItemDiscount = (int) round( + ($lineItemSubtotalCents / $totalLineItemSubtotal) * $invoiceDiscountTotalCents + ); + $remainingDiscountCents -= $lineItemDiscount; + } + + if ($lineItemDiscount > 0) { + $transaction->journalEntries()->create([ + 'company_id' => $this->company_id, + 'type' => JournalEntryType::Debit, + 'account_id' => Account::getSalesDiscountAccount()->id, + 'amount' => CurrencyConverter::convertCentsToFormatSimple($lineItemDiscount), + 'description' => "{$lineItemDescription} (Proportional Discount)", + ]); + } + } } } diff --git a/app/Models/Setting/Discount.php b/app/Models/Setting/Discount.php deleted file mode 100644 index 2a4c4333..00000000 --- a/app/Models/Setting/Discount.php +++ /dev/null @@ -1,82 +0,0 @@ - RateCast::class, - 'computation' => DiscountComputation::class, - 'type' => DiscountType::class, - 'scope' => DiscountScope::class, - 'start_date' => 'datetime', - 'end_date' => 'datetime', - 'enabled' => 'boolean', - ]; - - protected ?string $evaluatedDefault = 'type'; - - public function account(): BelongsTo - { - return $this->belongsTo(Account::class, 'account_id'); - } - - public function defaultSalesDiscount(): HasOne - { - return $this->hasOne(CompanyDefault::class, 'sales_discount_id'); - } - - public function defaultPurchaseDiscount(): HasOne - { - return $this->hasOne(CompanyDefault::class, 'purchase_discount_id'); - } - - public function adjustmentables(): MorphTo - { - return $this->morphTo(); - } - - protected static function newFactory(): Factory - { - return DiscountFactory::new(); - } -} diff --git a/app/Models/Setting/Tax.php b/app/Models/Setting/Tax.php deleted file mode 100644 index 08fd4ce6..00000000 --- a/app/Models/Setting/Tax.php +++ /dev/null @@ -1,78 +0,0 @@ - RateCast::class, - 'computation' => TaxComputation::class, - 'type' => TaxType::class, - 'scope' => TaxScope::class, - 'enabled' => 'boolean', - ]; - - protected ?string $evaluatedDefault = 'type'; - - public function account(): BelongsTo - { - return $this->belongsTo(Account::class, 'account_id'); - } - - public function defaultSalesTax(): HasOne - { - return $this->hasOne(CompanyDefault::class, 'sales_tax_id'); - } - - public function defaultPurchaseTax(): HasOne - { - return $this->hasOne(CompanyDefault::class, 'purchase_tax_id'); - } - - public function adjustmentables(): MorphTo - { - return $this->morphTo(); - } - - protected static function newFactory(): Factory - { - return TaxFactory::new(); - } -} diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php index a18a72e9..cc23d803 100644 --- a/app/Providers/AuthServiceProvider.php +++ b/app/Providers/AuthServiceProvider.php @@ -36,8 +36,6 @@ protected function registerEnabledRecordPolicy(): void { $models = [ Setting\Currency::class, - Setting\Discount::class, - Setting\Tax::class, Banking\BankAccount::class, ]; diff --git a/app/Providers/MacroServiceProvider.php b/app/Providers/MacroServiceProvider.php index 782b5793..043144e0 100644 --- a/app/Providers/MacroServiceProvider.php +++ b/app/Providers/MacroServiceProvider.php @@ -19,7 +19,6 @@ use Filament\Tables\Columns\TextColumn; use Illuminate\Support\Carbon; use Illuminate\Support\ServiceProvider; -use Illuminate\Support\Str; class MacroServiceProvider extends ServiceProvider { @@ -103,18 +102,22 @@ public function boot(): void return $this; }); - TextInput::macro('rate', function (string | Closure | null $computation = null): static { - $this->extraAttributes(['wire:key' => Str::random()]) - ->prefix(static function (TextInput $component) use ($computation) { - $computation = $component->evaluate($computation); - - return ratePrefix(computation: $computation); - }) - ->suffix(static function (TextInput $component) use ($computation) { - $computation = $component->evaluate($computation); - - return rateSuffix(computation: $computation); - }) + TextInput::macro('rate', function (string | Closure | null $computation = null, bool $showAffix = true): static { + $this + ->when( + $showAffix, + fn (TextInput $component) => $component + ->prefix(static function (TextInput $component) use ($computation) { + $computation = $component->evaluate($computation); + + return ratePrefix(computation: $computation); + }) + ->suffix(static function (TextInput $component) use ($computation) { + $computation = $component->evaluate($computation); + + return rateSuffix(computation: $computation); + }) + ) ->mask(static function (TextInput $component) use ($computation) { $computation = $component->evaluate($computation); 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/BillTotalViewModel.php b/app/View/Models/BillTotalViewModel.php index cdd12678..fcfd0847 100644 --- a/app/View/Models/BillTotalViewModel.php +++ b/app/View/Models/BillTotalViewModel.php @@ -2,6 +2,8 @@ namespace App\View\Models; +use App\Enums\Accounting\AdjustmentComputation; +use App\Enums\Accounting\DocumentDiscountMethod; use App\Models\Accounting\Adjustment; use App\Models\Accounting\Bill; use App\Utilities\Currency\CurrencyConverter; @@ -17,7 +19,7 @@ public function buildViewData(): array { $lineItems = collect($this->data['lineItems'] ?? []); - $subtotal = $lineItems->sum(function ($item) { + $subtotal = $lineItems->sum(static function ($item) { $quantity = max((float) ($item['quantity'] ?? 0), 0); $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); @@ -37,18 +39,32 @@ public function buildViewData(): array return $carry + $taxAmount; }, 0); - $discountTotal = $lineItems->reduce(function ($carry, $item) { - $quantity = max((float) ($item['quantity'] ?? 0), 0); - $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); - $purchaseDiscounts = $item['purchaseDiscounts'] ?? []; - $lineTotal = $quantity * $unitPrice; + // Calculate discount based on method + $discountMethod = DocumentDiscountMethod::parse($this->data['discount_method']) ?? DocumentDiscountMethod::PerLineItem; - $discountAmount = Adjustment::whereIn('id', $purchaseDiscounts) - ->pluck('rate') - ->sum(fn ($rate) => $lineTotal * ($rate / 100)); + if ($discountMethod->isPerLineItem()) { + $discountTotal = $lineItems->reduce(function ($carry, $item) { + $quantity = max((float) ($item['quantity'] ?? 0), 0); + $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); + $purchaseDiscounts = $item['purchaseDiscounts'] ?? []; + $lineTotal = $quantity * $unitPrice; - return $carry + $discountAmount; - }, 0); + $discountAmount = Adjustment::whereIn('id', $purchaseDiscounts) + ->pluck('rate') + ->sum(fn ($rate) => $lineTotal * ($rate / 100)); + + return $carry + $discountAmount; + }, 0); + } else { + $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage; + $discountRate = (float) ($this->data['discount_rate'] ?? 0); + + if ($discountComputation->isPercentage()) { + $discountTotal = $subtotal * ($discountRate / 100); + } else { + $discountTotal = $discountRate; + } + } $grandTotal = $subtotal + ($taxTotal - $discountTotal); diff --git a/app/View/Models/InvoiceTotalViewModel.php b/app/View/Models/InvoiceTotalViewModel.php index 1b077d23..8f1baac3 100644 --- a/app/View/Models/InvoiceTotalViewModel.php +++ b/app/View/Models/InvoiceTotalViewModel.php @@ -2,6 +2,8 @@ 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; @@ -17,7 +19,7 @@ public function buildViewData(): array { $lineItems = collect($this->data['lineItems'] ?? []); - $subtotal = $lineItems->sum(function ($item) { + $subtotal = $lineItems->sum(static function ($item) { $quantity = max((float) ($item['quantity'] ?? 0), 0); $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); @@ -37,18 +39,32 @@ public function buildViewData(): array return $carry + $taxAmount; }, 0); - $discountTotal = $lineItems->reduce(function ($carry, $item) { - $quantity = max((float) ($item['quantity'] ?? 0), 0); - $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); - $salesDiscounts = $item['salesDiscounts'] ?? []; - $lineTotal = $quantity * $unitPrice; + // Calculate discount based on method + $discountMethod = DocumentDiscountMethod::parse($this->data['discount_method']) ?? DocumentDiscountMethod::PerLineItem; - $discountAmount = Adjustment::whereIn('id', $salesDiscounts) - ->pluck('rate') - ->sum(fn ($rate) => $lineTotal * ($rate / 100)); + if ($discountMethod->isPerLineItem()) { + $discountTotal = $lineItems->reduce(function ($carry, $item) { + $quantity = max((float) ($item['quantity'] ?? 0), 0); + $unitPrice = max((float) ($item['unit_price'] ?? 0), 0); + $salesDiscounts = $item['salesDiscounts'] ?? []; + $lineTotal = $quantity * $unitPrice; - return $carry + $discountAmount; - }, 0); + $discountAmount = Adjustment::whereIn('id', $salesDiscounts) + ->pluck('rate') + ->sum(fn ($rate) => $lineTotal * ($rate / 100)); + + return $carry + $discountAmount; + }, 0); + } else { + $discountComputation = AdjustmentComputation::parse($this->data['discount_computation']) ?? AdjustmentComputation::Percentage; + $discountRate = (float) ($this->data['discount_rate'] ?? 0); + + if ($discountComputation->isPercentage()) { + $discountTotal = $subtotal * ($discountRate / 100); + } else { + $discountTotal = $discountRate; + } + } $grandTotal = $subtotal + ($taxTotal - $discountTotal); 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 c5f32d05..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", @@ -256,12 +256,12 @@ "type": "library", "extra": { "laravel": { - "providers": [ - "Wallo\\Transmatic\\TransmaticServiceProvider" - ], "aliases": { "Transmatic": "Wallo\\Transmatic\\Facades\\Transmatic" - } + }, + "providers": [ + "Wallo\\Transmatic\\TransmaticServiceProvider" + ] } }, "autoload": { @@ -497,16 +497,16 @@ }, { "name": "aws/aws-sdk-php", - "version": "3.334.1", + "version": "3.334.6", "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "3938b3467f64a30fed7ee1762a6785f808a5ae4d" + "reference": "2b0be3aded849d3b7bb0b53ea3295c7cecdeeee7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/3938b3467f64a30fed7ee1762a6785f808a5ae4d", - "reference": "3938b3467f64a30fed7ee1762a6785f808a5ae4d", + "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.1" + "source": "https://github.com/aws/aws-sdk-php/tree/3.334.6" }, - "time": "2024-12-05T01:17:41+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": { @@ -695,17 +695,17 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - }, "laravel": { - "providers": [ - "Barryvdh\\Snappy\\ServiceProvider" - ], "aliases": { "PDF": "Barryvdh\\Snappy\\Facades\\SnappyPdf", "SnappyImage": "Barryvdh\\Snappy\\Facades\\SnappyImage" - } + }, + "providers": [ + "Barryvdh\\Snappy\\ServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "1.0-dev" } }, "autoload": { @@ -1317,29 +1317,27 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.3", + "version": "1.1.4", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" + "psr/log": "^1 || ^2 || ^3" }, "suggest": { "psr/log": "Allows logging deprecations via PSR-3 logger implementation" @@ -1347,7 +1345,7 @@ "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -1358,9 +1356,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "source": "https://github.com/doctrine/deprecations/tree/1.1.4" }, - "time": "2024-01-30T19:34:25+00:00" + "time": "2024-12-07T21:18:45+00:00" }, { "name": "doctrine/inflector", @@ -1664,7 +1662,7 @@ }, { "name": "filament/actions", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/actions.git", @@ -1717,16 +1715,16 @@ }, { "name": "filament/filament", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/panels.git", - "reference": "27b834f6f1213c547580443e28e5028dfe125bdd" + "reference": "9a327a54dec24e0b12c437ef799828f492cb882a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/panels/zipball/27b834f6f1213c547580443e28e5028dfe125bdd", - "reference": "27b834f6f1213c547580443e28e5028dfe125bdd", + "url": "https://api.github.com/repos/filamentphp/panels/zipball/9a327a54dec24e0b12c437ef799828f492cb882a", + "reference": "9a327a54dec24e0b12c437ef799828f492cb882a", "shasum": "" }, "require": { @@ -1778,20 +1776,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-05T08:56:42+00:00" + "time": "2024-12-11T09:05:49+00:00" }, { "name": "filament/forms", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/forms.git", - "reference": "c86af3606b8fd3f908b29a03e3056628e4cea57e" + "reference": "e6133bdc03de05dfe23eac386b49e51093338883" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/forms/zipball/c86af3606b8fd3f908b29a03e3056628e4cea57e", - "reference": "c86af3606b8fd3f908b29a03e3056628e4cea57e", + "url": "https://api.github.com/repos/filamentphp/forms/zipball/e6133bdc03de05dfe23eac386b49e51093338883", + "reference": "e6133bdc03de05dfe23eac386b49e51093338883", "shasum": "" }, "require": { @@ -1834,20 +1832,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-05T08:56:35+00:00" + "time": "2024-12-11T09:05:38+00:00" }, { "name": "filament/infolists", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/infolists.git", - "reference": "e655ac3900ab2109022aa0243cfb4126729ef431" + "reference": "0db994330e7fe21a9684256ea1fc34fee916a8d6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/infolists/zipball/e655ac3900ab2109022aa0243cfb4126729ef431", - "reference": "e655ac3900ab2109022aa0243cfb4126729ef431", + "url": "https://api.github.com/repos/filamentphp/infolists/zipball/0db994330e7fe21a9684256ea1fc34fee916a8d6", + "reference": "0db994330e7fe21a9684256ea1fc34fee916a8d6", "shasum": "" }, "require": { @@ -1885,11 +1883,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-11-29T09:30:56+00:00" + "time": "2024-12-11T09:05:38+00:00" }, { "name": "filament/notifications", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/notifications.git", @@ -1941,16 +1939,16 @@ }, { "name": "filament/support", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/support.git", - "reference": "437d4f3305458f29c32ef4de5ef1d9dbdc74c3fe" + "reference": "e8aed9684d5c58ff7dde9517c7f1af44d575d871" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/support/zipball/437d4f3305458f29c32ef4de5ef1d9dbdc74c3fe", - "reference": "437d4f3305458f29c32ef4de5ef1d9dbdc74c3fe", + "url": "https://api.github.com/repos/filamentphp/support/zipball/e8aed9684d5c58ff7dde9517c7f1af44d575d871", + "reference": "e8aed9684d5c58ff7dde9517c7f1af44d575d871", "shasum": "" }, "require": { @@ -1996,20 +1994,20 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-05T08:56:49+00:00" + "time": "2024-12-11T09:05:54+00:00" }, { "name": "filament/tables", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/tables.git", - "reference": "4a60fda65574f248e082f109345216a38567093a" + "reference": "acdec73ae82961654a52a22ed9f53207cfee2ef8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filamentphp/tables/zipball/4a60fda65574f248e082f109345216a38567093a", - "reference": "4a60fda65574f248e082f109345216a38567093a", + "url": "https://api.github.com/repos/filamentphp/tables/zipball/acdec73ae82961654a52a22ed9f53207cfee2ef8", + "reference": "acdec73ae82961654a52a22ed9f53207cfee2ef8", "shasum": "" }, "require": { @@ -2048,11 +2046,11 @@ "issues": "https://github.com/filamentphp/filament/issues", "source": "https://github.com/filamentphp/filament" }, - "time": "2024-12-05T08:56:53+00:00" + "time": "2024-12-11T09:05:54+00:00" }, { "name": "filament/widgets", - "version": "v3.2.128", + "version": "v3.2.129", "source": { "type": "git", "url": "https://github.com/filamentphp/widgets.git", @@ -2982,16 +2980,16 @@ }, { "name": "laravel/framework", - "version": "v11.34.2", + "version": "v11.35.1", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "865da6d73dd353f07a7bcbd778c55966a620121f" + "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/865da6d73dd353f07a7bcbd778c55966a620121f", - "reference": "865da6d73dd353f07a7bcbd778c55966a620121f", + "url": "https://api.github.com/repos/laravel/framework/zipball/dcfa130ede1a6fa4343dc113410963e791ad34fb", + "reference": "dcfa130ede1a6fa4343dc113410963e791ad34fb", "shasum": "" }, "require": { @@ -3015,6 +3013,7 @@ "league/commonmark": "^2.2.1", "league/flysystem": "^3.25.1", "league/flysystem-local": "^3.25.1", + "league/uri": "^7.5.1", "monolog/monolog": "^3.0", "nesbot/carbon": "^2.72.2|^3.4", "nunomaduro/termwind": "^2.0", @@ -3098,9 +3097,9 @@ "league/flysystem-read-only": "^3.25.1", "league/flysystem-sftp-v3": "^3.25.1", "mockery/mockery": "^1.6.10", - "nyholm/psr7": "^1.2", "orchestra/testbench-core": "^9.6", "pda/pheanstalk": "^5.0.6", + "php-http/discovery": "^1.15", "phpstan/phpstan": "^1.11.5", "phpunit/phpunit": "^10.5.35|^11.3.6", "predis/predis": "^2.3", @@ -3132,8 +3131,8 @@ "league/flysystem-read-only": "Required to use read-only disks (^3.25.1)", "league/flysystem-sftp-v3": "Required to use the Flysystem SFTP driver (^3.25.1).", "mockery/mockery": "Required to use mocking (^1.6).", - "nyholm/psr7": "Required to use PSR-7 bridging features (^1.2).", "pda/pheanstalk": "Required to use the beanstalk queue driver (^5.0).", + "php-http/discovery": "Required to use PSR-7 bridging features (^1.15).", "phpunit/phpunit": "Required to use assertions and run tests (^10.5|^11.0).", "predis/predis": "Required to use the predis connector (^2.3).", "psr/http-message": "Required to allow Storage::put to accept a StreamInterface (^1.0).", @@ -3154,6 +3153,7 @@ }, "autoload": { "files": [ + "src/Illuminate/Collections/functions.php", "src/Illuminate/Collections/helpers.php", "src/Illuminate/Events/functions.php", "src/Illuminate/Filesystem/functions.php", @@ -3191,7 +3191,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2024-11-27T15:43:57+00:00" + "time": "2024-12-12T18:25:58+00:00" }, { "name": "laravel/prompts", @@ -3254,16 +3254,16 @@ }, { "name": "laravel/sanctum", - "version": "v4.0.5", + "version": "v4.0.6", "source": { "type": "git", "url": "https://github.com/laravel/sanctum.git", - "reference": "fe361b9a63407a228f884eb78d7217f680b50140" + "reference": "9e069e36d90b1e1f41886efa0fe9800a6b354694" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/sanctum/zipball/fe361b9a63407a228f884eb78d7217f680b50140", - "reference": "fe361b9a63407a228f884eb78d7217f680b50140", + "url": "https://api.github.com/repos/laravel/sanctum/zipball/9e069e36d90b1e1f41886efa0fe9800a6b354694", + "reference": "9e069e36d90b1e1f41886efa0fe9800a6b354694", "shasum": "" }, "require": { @@ -3314,7 +3314,7 @@ "issues": "https://github.com/laravel/sanctum/issues", "source": "https://github.com/laravel/sanctum" }, - "time": "2024-11-26T14:36:23+00:00" + "time": "2024-11-26T21:18:33+00:00" }, { "name": "laravel/serializable-closure", @@ -3410,16 +3410,16 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - }, "laravel": { - "providers": [ - "Laravel\\Socialite\\SocialiteServiceProvider" - ], "aliases": { "Socialite": "Laravel\\Socialite\\Facades\\Socialite" - } + }, + "providers": [ + "Laravel\\Socialite\\SocialiteServiceProvider" + ] + }, + "branch-alias": { + "dev-master": "5.x-dev" } }, "autoload": { @@ -3706,16 +3706,16 @@ }, { "name": "league/csv", - "version": "9.18.0", + "version": "9.20.0", "source": { "type": "git", "url": "https://github.com/thephpleague/csv.git", - "reference": "b02d010e4055ae992247f6ffd1e7b103ef2a0790" + "reference": "553579df208641ada6ffb450b3a151e2fcfa4f31" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/csv/zipball/b02d010e4055ae992247f6ffd1e7b103ef2a0790", - "reference": "b02d010e4055ae992247f6ffd1e7b103ef2a0790", + "url": "https://api.github.com/repos/thephpleague/csv/zipball/553579df208641ada6ffb450b3a151e2fcfa4f31", + "reference": "553579df208641ada6ffb450b3a151e2fcfa4f31", "shasum": "" }, "require": { @@ -3727,12 +3727,12 @@ "ext-xdebug": "*", "friendsofphp/php-cs-fixer": "^3.64.0", "phpbench/phpbench": "^1.3.1", - "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan": "^1.12.11", "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-phpunit": "^1.4.1", "phpstan/phpstan-strict-rules": "^1.6.1", - "phpunit/phpunit": "^10.5.16 || ^11.4.1", - "symfony/var-dumper": "^6.4.8 || ^7.1.5" + "phpunit/phpunit": "^10.5.16 || ^11.4.3", + "symfony/var-dumper": "^6.4.8 || ^7.1.8" }, "suggest": { "ext-dom": "Required to use the XMLConverter and the HTMLConverter classes", @@ -3789,7 +3789,7 @@ "type": "github" } ], - "time": "2024-10-18T08:14:48+00:00" + "time": "2024-12-13T15:49:27+00:00" }, { "name": "league/flysystem", @@ -3981,16 +3981,16 @@ }, { "name": "league/oauth1-client", - "version": "v1.10.1", + "version": "v1.11.0", "source": { "type": "git", "url": "https://github.com/thephpleague/oauth1-client.git", - "reference": "d6365b901b5c287dd41f143033315e2f777e1167" + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/d6365b901b5c287dd41f143033315e2f777e1167", - "reference": "d6365b901b5c287dd41f143033315e2f777e1167", + "url": "https://api.github.com/repos/thephpleague/oauth1-client/zipball/f9c94b088837eb1aae1ad7c4f23eb65cc6993055", + "reference": "f9c94b088837eb1aae1ad7c4f23eb65cc6993055", "shasum": "" }, "require": { @@ -4051,26 +4051,26 @@ ], "support": { "issues": "https://github.com/thephpleague/oauth1-client/issues", - "source": "https://github.com/thephpleague/oauth1-client/tree/v1.10.1" + "source": "https://github.com/thephpleague/oauth1-client/tree/v1.11.0" }, - "time": "2022-04-15T14:02:14+00:00" + "time": "2024-12-10T19:59:05+00:00" }, { "name": "league/uri", - "version": "7.4.1", + "version": "7.5.1", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4" + "reference": "81fb5145d2644324614cc532b28efd0215bda430" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/bedb6e55eff0c933668addaa7efa1e1f2c417cc4", - "reference": "bedb6e55eff0c933668addaa7efa1e1f2c417cc4", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/81fb5145d2644324614cc532b28efd0215bda430", + "reference": "81fb5145d2644324614cc532b28efd0215bda430", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.3", + "league/uri-interfaces": "^7.5", "php": "^8.1" }, "conflict": { @@ -4135,7 +4135,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.4.1" + "source": "https://github.com/thephpleague/uri/tree/7.5.1" }, "funding": [ { @@ -4143,20 +4143,20 @@ "type": "github" } ], - "time": "2024-03-23T07:42:40+00:00" + "time": "2024-12-08T08:40:02+00:00" }, { "name": "league/uri-interfaces", - "version": "7.4.1", + "version": "7.5.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718" + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/8d43ef5c841032c87e2de015972c06f3865ef718", - "reference": "8d43ef5c841032c87e2de015972c06f3865ef718", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", + "reference": "08cfc6c4f3d811584fb09c37e2849e6a7f9b0742", "shasum": "" }, "require": { @@ -4219,7 +4219,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.4.1" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.5.0" }, "funding": [ { @@ -4227,7 +4227,7 @@ "type": "github" } ], - "time": "2024-03-23T07:42:40+00:00" + "time": "2024-12-08T08:18:47+00:00" }, { "name": "livewire/livewire", @@ -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", @@ -5921,16 +5921,16 @@ }, { "name": "psy/psysh", - "version": "v0.12.6", + "version": "v0.12.7", "source": { "type": "git", "url": "https://github.com/bobthecow/psysh.git", - "reference": "3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c" + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/bobthecow/psysh/zipball/3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c", - "reference": "3b5ea0efaa791cd1c65ecc493aec3e2aa55ff57c", + "url": "https://api.github.com/repos/bobthecow/psysh/zipball/d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", + "reference": "d73fa3c74918ef4522bb8a3bf9cab39161c4b57c", "shasum": "" }, "require": { @@ -5994,9 +5994,9 @@ ], "support": { "issues": "https://github.com/bobthecow/psysh/issues", - "source": "https://github.com/bobthecow/psysh/tree/v0.12.6" + "source": "https://github.com/bobthecow/psysh/tree/v0.12.7" }, - "time": "2024-12-07T20:08:52+00:00" + "time": "2024-12-10T01:58:33+00:00" }, { "name": "ralouphie/getallheaders", @@ -6303,16 +6303,16 @@ }, { "name": "spatie/color", - "version": "1.6.1", + "version": "1.6.2", "source": { "type": "git", "url": "https://github.com/spatie/color.git", - "reference": "4c540ffbef68a3df3d209718ae06deaab081e708" + "reference": "b4fac074a9e5999dcca12cbfab0f7c73e2684d6d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/color/zipball/4c540ffbef68a3df3d209718ae06deaab081e708", - "reference": "4c540ffbef68a3df3d209718ae06deaab081e708", + "url": "https://api.github.com/repos/spatie/color/zipball/b4fac074a9e5999dcca12cbfab0f7c73e2684d6d", + "reference": "b4fac074a9e5999dcca12cbfab0f7c73e2684d6d", "shasum": "" }, "require": { @@ -6350,7 +6350,7 @@ ], "support": { "issues": "https://github.com/spatie/color/issues", - "source": "https://github.com/spatie/color/tree/1.6.1" + "source": "https://github.com/spatie/color/tree/1.6.2" }, "funding": [ { @@ -6358,7 +6358,7 @@ "type": "github" } ], - "time": "2024-11-18T15:00:47+00:00" + "time": "2024-12-09T16:20:38+00:00" }, { "name": "spatie/invade", @@ -6421,16 +6421,16 @@ }, { "name": "spatie/laravel-package-tools", - "version": "1.16.6", + "version": "1.17.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-package-tools.git", - "reference": "1f26942dc1e5c49eacfced34fdbc29ed234bd7b3" + "reference": "9ab30fd24f677e5aa370ea4cf6b41c517d16cf85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/1f26942dc1e5c49eacfced34fdbc29ed234bd7b3", - "reference": "1f26942dc1e5c49eacfced34fdbc29ed234bd7b3", + "url": "https://api.github.com/repos/spatie/laravel-package-tools/zipball/9ab30fd24f677e5aa370ea4cf6b41c517d16cf85", + "reference": "9ab30fd24f677e5aa370ea4cf6b41c517d16cf85", "shasum": "" }, "require": { @@ -6439,10 +6439,10 @@ }, "require-dev": { "mockery/mockery": "^1.5", - "orchestra/testbench": "^7.7|^8.0", - "pestphp/pest": "^1.22", - "phpunit/phpunit": "^9.5.24", - "spatie/pest-plugin-test-time": "^1.1" + "orchestra/testbench": "^7.7|^8.0|^9.0", + "pestphp/pest": "^1.22|^2", + "phpunit/phpunit": "^9.5.24|^10.5", + "spatie/pest-plugin-test-time": "^1.1|^2.2" }, "type": "library", "autoload": { @@ -6469,7 +6469,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-package-tools/issues", - "source": "https://github.com/spatie/laravel-package-tools/tree/1.16.6" + "source": "https://github.com/spatie/laravel-package-tools/tree/1.17.0" }, "funding": [ { @@ -6477,7 +6477,7 @@ "type": "github" } ], - "time": "2024-11-18T15:02:02+00:00" + "time": "2024-12-09T16:29:14+00:00" }, { "name": "squirephp/model", @@ -6664,16 +6664,16 @@ }, { "name": "symfony/console", - "version": "v7.2.0", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf" + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", - "reference": "23c8aae6d764e2bae02d2a99f7532a7f6ed619cf", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", "shasum": "" }, "require": { @@ -6737,7 +6737,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.0" + "source": "https://github.com/symfony/console/tree/v7.2.1" }, "funding": [ { @@ -6753,7 +6753,7 @@ "type": "tidelift" } ], - "time": "2024-11-06T14:24:19+00:00" + "time": "2024-12-11T03:49:26+00:00" }, { "name": "symfony/css-selector", @@ -6889,16 +6889,16 @@ }, { "name": "symfony/error-handler", - "version": "v7.2.0", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/error-handler.git", - "reference": "672b3dd1ef8b87119b446d67c58c106c43f965fe" + "reference": "6150b89186573046167796fa5f3f76601d5145f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/672b3dd1ef8b87119b446d67c58c106c43f965fe", - "reference": "672b3dd1ef8b87119b446d67c58c106c43f965fe", + "url": "https://api.github.com/repos/symfony/error-handler/zipball/6150b89186573046167796fa5f3f76601d5145f8", + "reference": "6150b89186573046167796fa5f3f76601d5145f8", "shasum": "" }, "require": { @@ -6944,7 +6944,7 @@ "description": "Provides tools to manage errors and ease debugging PHP code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/error-handler/tree/v7.2.0" + "source": "https://github.com/symfony/error-handler/tree/v7.2.1" }, "funding": [ { @@ -6960,7 +6960,7 @@ "type": "tidelift" } ], - "time": "2024-11-05T15:35:02+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/event-dispatcher", @@ -7331,16 +7331,16 @@ }, { "name": "symfony/http-kernel", - "version": "v7.2.0", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/http-kernel.git", - "reference": "6b4722a25e0aed1ccb4914b9bcbd493cc4676b4d" + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/6b4722a25e0aed1ccb4914b9bcbd493cc4676b4d", - "reference": "6b4722a25e0aed1ccb4914b9bcbd493cc4676b4d", + "url": "https://api.github.com/repos/symfony/http-kernel/zipball/d8ae58eecae44c8e66833e76cc50a4ad3c002d97", + "reference": "d8ae58eecae44c8e66833e76cc50a4ad3c002d97", "shasum": "" }, "require": { @@ -7425,7 +7425,7 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v7.2.0" + "source": "https://github.com/symfony/http-kernel/tree/v7.2.1" }, "funding": [ { @@ -7441,7 +7441,7 @@ "type": "tidelift" } ], - "time": "2024-11-29T08:42:40+00:00" + "time": "2024-12-11T12:09:10+00:00" }, { "name": "symfony/intl", @@ -7608,16 +7608,16 @@ }, { "name": "symfony/mime", - "version": "v7.2.0", + "version": "v7.2.1", "source": { "type": "git", "url": "https://github.com/symfony/mime.git", - "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d" + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/cc84a4b81f62158c3846ac7ff10f696aae2b524d", - "reference": "cc84a4b81f62158c3846ac7ff10f696aae2b524d", + "url": "https://api.github.com/repos/symfony/mime/zipball/7f9617fcf15cb61be30f8b252695ed5e2bfac283", + "reference": "7f9617fcf15cb61be30f8b252695ed5e2bfac283", "shasum": "" }, "require": { @@ -7672,7 +7672,7 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v7.2.0" + "source": "https://github.com/symfony/mime/tree/v7.2.1" }, "funding": [ { @@ -7688,7 +7688,7 @@ "type": "tidelift" } ], - "time": "2024-11-23T09:19:39+00:00" + "time": "2024-12-07T08:50:44+00:00" }, { "name": "symfony/polyfill-ctype", @@ -7716,8 +7716,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -7792,8 +7792,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -7871,8 +7871,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -7953,8 +7953,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8037,8 +8037,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8111,8 +8111,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8191,8 +8191,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -8273,8 +8273,8 @@ "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -9241,16 +9241,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.6.1", + "version": "v7.7.0", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "9ac8eda68f17acda4dad4aa02ecdcc327d7e6675" + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/9ac8eda68f17acda4dad4aa02ecdcc327d7e6675", - "reference": "9ac8eda68f17acda4dad4aa02ecdcc327d7e6675", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/4fb3f73bc5a4c3146bac2850af7dc72435a32daf", + "reference": "4fb3f73bc5a4c3146bac2850af7dc72435a32daf", "shasum": "" }, "require": { @@ -9261,24 +9261,24 @@ "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.4.4", + "phpunit/phpunit": "^11.5.1", "sebastian/environment": "^7.2.0", - "symfony/console": "^6.4.14 || ^7.1.7", - "symfony/process": "^6.4.14 || ^7.1.7" + "symfony/console": "^6.4.14 || ^7.2.1", + "symfony/process": "^6.4.14 || ^7.2.0" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^2", - "phpstan/phpstan-deprecation-rules": "^2", - "phpstan/phpstan-phpunit": "^2", + "phpstan/phpstan": "^2.0.3", + "phpstan/phpstan-deprecation-rules": "^2.0.1", + "phpstan/phpstan-phpunit": "^2.0.1", "phpstan/phpstan-strict-rules": "^2", "squizlabs/php_codesniffer": "^3.11.1", - "symfony/filesystem": "^6.4.13 || ^7.1.6" + "symfony/filesystem": "^6.4.13 || ^7.2.0" }, "bin": [ "bin/paratest", @@ -9318,7 +9318,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.6.1" + "source": "https://github.com/paratestphp/paratest/tree/v7.7.0" }, "funding": [ { @@ -9330,7 +9330,7 @@ "type": "paypal" } ], - "time": "2024-12-05T10:55:39+00:00" + "time": "2024-12-11T14:50:44+00:00" }, { "name": "fakerphp/faker", @@ -10008,38 +10008,38 @@ }, { "name": "pestphp/pest", - "version": "v3.6.0", + "version": "v3.7.1", "source": { "type": "git", "url": "https://github.com/pestphp/pest.git", - "reference": "918a8fc16996849937e281482bd34f236881ce96" + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/918a8fc16996849937e281482bd34f236881ce96", - "reference": "918a8fc16996849937e281482bd34f236881ce96", + "url": "https://api.github.com/repos/pestphp/pest/zipball/bf3178473dcaa53b0458f21dfdb271306ea62512", + "reference": "bf3178473dcaa53b0458f21dfdb271306ea62512", "shasum": "" }, "require": { - "brianium/paratest": "^7.6.0", + "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.4.4" + "phpunit/phpunit": "^11.5.1" }, "conflict": { "filp/whoops": "<2.16.0", - "phpunit/phpunit": ">11.4.4", + "phpunit/phpunit": ">11.5.1", "sebastian/exporter": "<6.0.0", "webmozart/assert": "<1.11.0" }, "require-dev": { "pestphp/pest-dev-tools": "^3.3.0", "pestphp/pest-plugin-type-coverage": "^3.2.0", - "symfony/process": "^7.1.8" + "symfony/process": "^7.2.0" }, "bin": [ "bin/pest" @@ -10104,7 +10104,7 @@ ], "support": { "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v3.6.0" + "source": "https://github.com/pestphp/pest/tree/v3.7.1" }, "funding": [ { @@ -10116,7 +10116,7 @@ "type": "github" } ], - "time": "2024-12-01T22:46:00+00:00" + "time": "2024-12-12T11:52:01+00:00" }, { "name": "pestphp/pest-plugin", @@ -10736,76 +10736,18 @@ }, "time": "2024-10-13T11:29:49+00:00" }, - { - "name": "phpstan/phpstan", - "version": "1.12.12", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpstan.git", - "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", - "reference": "b5ae1b88f471d3fd4ba1aa0046234b5ca3776dd0", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - } - ], - "time": "2024-11-28T22:13:23+00:00" - }, { "name": "phpunit/php-code-coverage", - "version": "11.0.7", + "version": "11.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca" + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f7f08030e8811582cc459871d28d6f5a1a4d35ca", - "reference": "f7f08030e8811582cc459871d28d6f5a1a4d35ca", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/418c59fd080954f8c4aa5631d9502ecda2387118", + "reference": "418c59fd080954f8c4aa5631d9502ecda2387118", "shasum": "" }, "require": { @@ -10824,7 +10766,7 @@ "theseer/tokenizer": "^1.2.3" }, "require-dev": { - "phpunit/phpunit": "^11.4.1" + "phpunit/phpunit": "^11.5.0" }, "suggest": { "ext-pcov": "PHP extension that provides line coverage", @@ -10862,7 +10804,7 @@ "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.7" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.8" }, "funding": [ { @@ -10870,7 +10812,7 @@ "type": "github" } ], - "time": "2024-10-09T06:21:38+00:00" + "time": "2024-12-11T12:34:27+00:00" }, { "name": "phpunit/php-file-iterator", @@ -11119,16 +11061,16 @@ }, { "name": "phpunit/phpunit", - "version": "11.4.4", + "version": "11.5.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4" + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4", - "reference": "f9ba7bd3c9f3ff54ec379d7a1c2e3f13fe0bbde4", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2b94d4f2450b9869fa64a46fd8a6a41997aef56a", + "reference": "2b94d4f2450b9869fa64a46fd8a6a41997aef56a", "shasum": "" }, "require": { @@ -11152,11 +11094,12 @@ "sebastian/comparator": "^6.2.1", "sebastian/diff": "^6.0.2", "sebastian/environment": "^7.2.0", - "sebastian/exporter": "^6.1.3", + "sebastian/exporter": "^6.3.0", "sebastian/global-state": "^7.0.2", "sebastian/object-enumerator": "^6.0.1", "sebastian/type": "^5.1.0", - "sebastian/version": "^5.0.2" + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" }, "suggest": { "ext-soap": "To be able to generate mocks based on WSDL files" @@ -11167,7 +11110,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "11.4-dev" + "dev-main": "11.5-dev" } }, "autoload": { @@ -11199,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.4.4" + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.1" }, "funding": [ { @@ -11215,7 +11158,7 @@ "type": "tidelift" } ], - "time": "2024-11-27T10:44:52+00:00" + "time": "2024-12-11T10:52:48+00:00" }, { "name": "pimple/pimple", @@ -11270,65 +11213,6 @@ }, "time": "2021-10-28T11:13:42+00:00" }, - { - "name": "rector/rector", - "version": "1.2.10", - "source": { - "type": "git", - "url": "https://github.com/rectorphp/rector.git", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rectorphp/rector/zipball/40f9cf38c05296bd32f444121336a521a293fa61", - "reference": "40f9cf38c05296bd32f444121336a521a293fa61", - "shasum": "" - }, - "require": { - "php": "^7.2|^8.0", - "phpstan/phpstan": "^1.12.5" - }, - "conflict": { - "rector/rector-doctrine": "*", - "rector/rector-downgrade-php": "*", - "rector/rector-phpunit": "*", - "rector/rector-symfony": "*" - }, - "suggest": { - "ext-dom": "To manipulate phpunit.xml via the custom-rule command" - }, - "bin": [ - "bin/rector" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Instant Upgrade and Automated Refactoring of any PHP code", - "keywords": [ - "automation", - "dev", - "migration", - "refactoring" - ], - "support": { - "issues": "https://github.com/rectorphp/rector/issues", - "source": "https://github.com/rectorphp/rector/tree/1.2.10" - }, - "funding": [ - { - "url": "https://github.com/tomasvotruba", - "type": "github" - } - ], - "time": "2024-11-08T13:59:10+00:00" - }, { "name": "sebastian/cli-parser", "version": "3.0.2", @@ -11388,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": { @@ -11433,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": [ { @@ -11441,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", @@ -12317,16 +12201,16 @@ }, { "name": "spatie/error-solutions", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/spatie/error-solutions.git", - "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67" + "reference": "d239a65235a1eb128dfa0a4e4c4ef032ea11b541" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/error-solutions/zipball/ae7393122eda72eed7cc4f176d1e96ea444f2d67", - "reference": "ae7393122eda72eed7cc4f176d1e96ea444f2d67", + "url": "https://api.github.com/repos/spatie/error-solutions/zipball/d239a65235a1eb128dfa0a4e4c4ef032ea11b541", + "reference": "d239a65235a1eb128dfa0a4e4c4ef032ea11b541", "shasum": "" }, "require": { @@ -12379,7 +12263,7 @@ ], "support": { "issues": "https://github.com/spatie/error-solutions/issues", - "source": "https://github.com/spatie/error-solutions/tree/1.1.1" + "source": "https://github.com/spatie/error-solutions/tree/1.1.2" }, "funding": [ { @@ -12387,7 +12271,7 @@ "type": "github" } ], - "time": "2024-07-25T11:06:04+00:00" + "time": "2024-12-11T09:51:56+00:00" }, { "name": "spatie/flare-client-php", @@ -12634,40 +12518,41 @@ }, { "name": "spatie/laravel-ray", - "version": "1.37.1", + "version": "1.39.0", "source": { "type": "git", "url": "https://github.com/spatie/laravel-ray.git", - "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9" + "reference": "31b601f98590606d20e76b5dd68578dc1642cd2c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/c2bedfd1172648df2c80aaceb2541d70f1d9a5b9", - "reference": "c2bedfd1172648df2c80aaceb2541d70f1d9a5b9", + "url": "https://api.github.com/repos/spatie/laravel-ray/zipball/31b601f98590606d20e76b5dd68578dc1642cd2c", + "reference": "31b601f98590606d20e76b5dd68578dc1642cd2c", "shasum": "" }, "require": { + "composer-runtime-api": "^2.2", "ext-json": "*", - "illuminate/contracts": "^7.20|^8.19|^9.0|^10.0|^11.0", - "illuminate/database": "^7.20|^8.19|^9.0|^10.0|^11.0", - "illuminate/queue": "^7.20|^8.19|^9.0|^10.0|^11.0", - "illuminate/support": "^7.20|^8.19|^9.0|^10.0|^11.0", - "php": "^7.4|^8.0", - "rector/rector": "^0.19.2|^1.0", + "illuminate/contracts": "^7.20 || ^8.19 || ^9.0 || ^10.0 || ^11.0", + "illuminate/database": "^7.20 || ^8.19 || ^9.0 || ^10.0 || ^11.0", + "illuminate/queue": "^7.20 || ^8.19 || ^9.0 || ^10.0 || ^11.0", + "illuminate/support": "^7.20 || ^8.19 || ^9.0 || ^10.0 || ^11.0", + "php": "^7.4 || ^8.0", "spatie/backtrace": "^1.0", - "spatie/ray": "^1.41.1", - "symfony/stopwatch": "4.2|^5.1|^6.0|^7.0", - "zbateson/mail-mime-parser": "^1.3.1|^2.0|^3.0" + "spatie/ray": "^1.41.3", + "symfony/stopwatch": "4.2 || ^5.1 || ^6.0 || ^7.0", + "zbateson/mail-mime-parser": "^1.3.1 || ^2.0 || ^3.0" }, "require-dev": { "guzzlehttp/guzzle": "^7.3", - "laravel/framework": "^7.20|^8.19|^9.0|^10.0|^11.0", - "orchestra/testbench-core": "^5.0|^6.0|^7.0|^8.0|^9.0", - "pestphp/pest": "^1.22|^2.0", - "phpstan/phpstan": "^1.10.57", - "phpunit/phpunit": "^9.3|^10.1", - "spatie/pest-plugin-snapshots": "^1.1|^2.0", - "symfony/var-dumper": "^4.2|^5.1|^6.0|^7.0.3" + "laravel/framework": "^7.20 || ^8.19 || ^9.0 || ^10.0 || ^11.0", + "orchestra/testbench-core": "^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0", + "pestphp/pest": "^1.22 || ^2.0", + "phpstan/phpstan": "^1.10.57 || ^2.0.2", + "phpunit/phpunit": "^9.3 || ^10.1", + "rector/rector": "dev-main", + "spatie/pest-plugin-snapshots": "^1.1 || ^2.0", + "symfony/var-dumper": "^4.2 || ^5.1 || ^6.0 || ^7.0.3" }, "type": "library", "extra": { @@ -12705,7 +12590,7 @@ ], "support": { "issues": "https://github.com/spatie/laravel-ray/issues", - "source": "https://github.com/spatie/laravel-ray/tree/1.37.1" + "source": "https://github.com/spatie/laravel-ray/tree/1.39.0" }, "funding": [ { @@ -12717,7 +12602,7 @@ "type": "other" } ], - "time": "2024-07-12T12:35:17+00:00" + "time": "2024-12-11T09:34:41+00:00" }, { "name": "spatie/macroable", @@ -12771,16 +12656,16 @@ }, { "name": "spatie/ray", - "version": "1.41.3", + "version": "1.41.4", "source": { "type": "git", "url": "https://github.com/spatie/ray.git", - "reference": "e2ecbc17a493dab635f3cf026858b46f0ccbb053" + "reference": "c5dbda0548c1881b30549ccc0b6d485f7471aaa5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/ray/zipball/e2ecbc17a493dab635f3cf026858b46f0ccbb053", - "reference": "e2ecbc17a493dab635f3cf026858b46f0ccbb053", + "url": "https://api.github.com/repos/spatie/ray/zipball/c5dbda0548c1881b30549ccc0b6d485f7471aaa5", + "reference": "c5dbda0548c1881b30549ccc0b6d485f7471aaa5", "shasum": "" }, "require": { @@ -12840,7 +12725,7 @@ ], "support": { "issues": "https://github.com/spatie/ray/issues", - "source": "https://github.com/spatie/ray/tree/1.41.3" + "source": "https://github.com/spatie/ray/tree/1.41.4" }, "funding": [ { @@ -12852,7 +12737,59 @@ "type": "other" } ], - "time": "2024-12-02T12:33:18+00:00" + "time": "2024-12-09T11:32:15+00:00" + }, + { + "name": "staabm/side-effects-detector", + "version": "1.0.5", + "source": { + "type": "git", + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", + "shasum": "" + }, + "require": { + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "lib/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "A static analysis tool to detect side effects in PHP code", + "keywords": [ + "static analysis" + ], + "support": { + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" + }, + "funding": [ + { + "url": "https://github.com/staabm", + "type": "github" + } + ], + "time": "2024-10-20T05:08:20+00:00" }, { "name": "symfony/polyfill-iconv", 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 130aecb1..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,6 +27,9 @@ public function up(): void $table->timestamp('last_sent')->nullable(); $table->string('status')->default('draft'); $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/package-lock.json b/package-lock.json index 1afff4f6..b8705641 100644 --- a/package-lock.json +++ b/package-lock.json @@ -456,9 +456,9 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "dev": true, "license": "MIT", "dependencies": { @@ -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": [ { @@ -1218,9 +1218,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.71", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.71.tgz", - "integrity": "sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==", + "version": "1.5.73", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", + "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==", "dev": true, "license": "ISC" }, @@ -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": { @@ -1761,9 +1761,9 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true, "license": "MIT" }, @@ -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/components/company/invoice/container.blade.php b/resources/views/components/company/invoice/container.blade.php index d42df72d..f18d432d 100644 --- a/resources/views/components/company/invoice/container.blade.php +++ b/resources/views/components/company/invoice/container.blade.php @@ -1,5 +1,15 @@ +@props([ + 'preview' => false, +]) +
-
+
$preview === false, + 'w-[38.25rem] h-[49.5rem] overflow-hidden' => $preview === true, + ]) + > {{ $slot }}
diff --git a/resources/views/components/company/invoice/footer.blade.php b/resources/views/components/company/invoice/footer.blade.php index 61d07797..983c9f44 100644 --- a/resources/views/components/company/invoice/footer.blade.php +++ b/resources/views/components/company/invoice/footer.blade.php @@ -1,3 +1,3 @@ -