Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewdwallo committed Dec 15, 2024
1 parent 4926a07 commit 6cec455
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 164 deletions.
20 changes: 13 additions & 7 deletions app/Concerns/ManagesLineItems.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@

use App\Enums\Accounting\AdjustmentComputation;
use App\Enums\Accounting\DocumentDiscountMethod;
use App\Models\Accounting\Bill;
use App\Models\Accounting\DocumentLineItem;
use App\Models\Accounting\Invoice;
use App\Utilities\Currency\CurrencyConverter;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;

trait ManagesLineItems
{
protected function handleLineItems(Invoice $record, Collection $lineItems): void
protected function handleLineItems(Model $record, Collection $lineItems): void
{
foreach ($lineItems as $itemData) {
$lineItem = isset($itemData['id'])
Expand All @@ -36,7 +37,7 @@ protected function handleLineItems(Invoice $record, Collection $lineItems): void
}
}

protected function deleteRemovedLineItems(Invoice $record, Collection $lineItems): void
protected function deleteRemovedLineItems(Model $record, Collection $lineItems): void
{
$existingLineItemIds = $record->lineItems->pluck('id');
$updatedLineItemIds = $lineItems->pluck('id')->filter();
Expand All @@ -52,8 +53,13 @@ protected function deleteRemovedLineItems(Invoice $record, Collection $lineItems

protected function handleLineItemAdjustments(DocumentLineItem $lineItem, array $itemData, DocumentDiscountMethod $discountMethod): void
{
$adjustmentIds = collect($itemData['salesTaxes'] ?? [])
->merge($discountMethod->isPerLineItem() ? ($itemData['salesDiscounts'] ?? []) : [])
$isBill = $lineItem->documentable instanceof Bill;

$taxType = $isBill ? 'purchaseTaxes' : 'salesTaxes';
$discountType = $isBill ? 'purchaseDiscounts' : 'salesDiscounts';

$adjustmentIds = collect($itemData[$taxType] ?? [])
->merge($discountMethod->isPerLineItem() ? ($itemData[$discountType] ?? []) : [])
->filter()
->unique();

Expand All @@ -71,7 +77,7 @@ protected function updateLineItemTotals(DocumentLineItem $lineItem, DocumentDisc
]);
}

protected function updateInvoiceTotals(Invoice $record, array $data): array
protected function updateDocumentTotals(Model $record, array $data): array
{
$subtotalCents = $record->lineItems()->sum('subtotal');
$taxTotalCents = $record->lineItems()->sum('tax_total');
Expand All @@ -98,7 +104,7 @@ protected function calculateDiscountTotal(
?AdjustmentComputation $discountComputation,
?string $discountRate,
int $subtotalCents,
Invoice $record
Model $record
): int {
if ($discountMethod->isPerLineItem()) {
return $record->lineItems()->sum('discount_total');
Expand Down
177 changes: 52 additions & 125 deletions app/Filament/Company/Resources/Purchases/BillResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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')
Expand All @@ -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'),
Expand All @@ -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()
Expand Down Expand Up @@ -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(),
]),
]);
}
Expand All @@ -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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
}
}
Loading

0 comments on commit 6cec455

Please sign in to comment.