From 1548e178bcdc26a0e741ddbf996cd85e853e9eb6 Mon Sep 17 00:00:00 2001 From: Jp Date: Mon, 9 Feb 2026 22:16:10 +0800 Subject: [PATCH 1/3] feat(client): add journals relation and improve transaction ledger handling - Add journals relation to Client model via Branch - Update branch selection in JournalsRelationManager to use code instead of name - Improve tax, withholding, and cash account ledger queries by adding client_id filter and amount checks - Add missing import for GenerateVoucher command in ExpensesRelationManager - Label 'Normal Balance' column in AccountsRelationManager --- .../Transactions/CreateTransactionAction.php | 40 ++++++++++++------- .../AccountsRelationManager.php | 2 +- .../ExpensesRelationManager.php | 1 + .../JournalsRelationManager.php | 2 +- app/Models/Client.php | 5 +++ 5 files changed, 34 insertions(+), 16 deletions(-) diff --git a/app/Actions/Transactions/CreateTransactionAction.php b/app/Actions/Transactions/CreateTransactionAction.php index d6e6564..a51ac32 100644 --- a/app/Actions/Transactions/CreateTransactionAction.php +++ b/app/Actions/Transactions/CreateTransactionAction.php @@ -71,15 +71,21 @@ class CreateTransactionAction extends BaseAction { $accountName = $isExpense ? 'Input Tax' : 'Output Tax'; $type = $isExpense ? 'debit' : 'credit'; + $clientId = $payload->transactionable->branch->client_id; - $taxAccount = Account::query()->where('account', $accountName)->whereHas('balances', function ($balance) use ($payload) { - return $balance->where('branch_id', $payload->transactionable->branch_id); - })->first(); + $taxAccount = Account::query() + ->where('account', $accountName) + ->where('client_id', $clientId) + ->first(); - if ($taxAccount) { + $amount = $isExpense + ? ($payload->transactionable->input_tax ?? 0.00) + : ($payload->transactionable->output_tax ?? 0.00); + + if ($taxAccount && $amount > 0) { $ledgerPayload = new CreateLedgerDTO( branch_id: $payload->transactionable->branch_id, - amount: $payload->transactionable->input_tax ?? 0.00, // Assuming input_tax holds the tax amount for both? Or output_tax? + amount: $amount, transaction: $payload->transaction, account: $taxAccount, type: $type, @@ -93,15 +99,19 @@ class CreateTransactionAction extends BaseAction $isExpense = $payload->transactionable instanceof \App\Models\Expense; $accountName = $isExpense ? 'Payable Withholding Tax' : 'Creditable Withholding Tax'; $type = $isExpense ? 'credit' : 'debit'; + $clientId = $payload->transactionable->branch->client_id; - $withholdingAccount = Account::query()->where('account', $accountName)->whereHas('balances', function ($balance) use ($payload) { - return $balance->where('branch_id', $payload->transactionable->branch_id); - })->first(); + $withholdingAccount = Account::query() + ->where('account', $accountName) + ->where('client_id', $clientId) + ->first(); - if ($withholdingAccount) { + $amount = $payload->transaction->payable_withholding_tax ?? 0.00; + + if ($withholdingAccount && $amount > 0) { $ledgerPayload = new CreateLedgerDTO( branch_id: $payload->transactionable->branch_id, - amount: $payload->transaction->payable_withholding_tax ?? 0.00, + amount: $amount, transaction: $payload->transaction, account: $withholdingAccount, type: $type, @@ -116,12 +126,14 @@ class CreateTransactionAction extends BaseAction $type = $isExpense ? 'credit' : 'debit'; $wht = $isExpense ? ($payload->transaction->payable_withholding_tax ?? 0) : ($payload->transaction->creditable_withholding_tax ?? 0); $amount = ($payload->transaction->gross_amount ?? 0) - $wht; + $clientId = $payload->transactionable->branch->client_id; - $cashAccount = Account::query()->where('account', 'Cash')->whereHas('balances', function ($balance) use ($payload) { - return $balance->where('branch_id', $payload->transactionable->branch_id); - })->first(); + $cashAccount = Account::query() + ->where('account', 'Cash') + ->where('client_id', $clientId) + ->first(); - if ($cashAccount) { + if ($cashAccount && $amount > 0) { $ledgerPayload = new CreateLedgerDTO( branch_id: $payload->transactionable->branch_id, amount: $amount, diff --git a/app/Filament/Resources/ClientResource/RelationManagers/AccountsRelationManager.php b/app/Filament/Resources/ClientResource/RelationManagers/AccountsRelationManager.php index f21a029..548121d 100644 --- a/app/Filament/Resources/ClientResource/RelationManagers/AccountsRelationManager.php +++ b/app/Filament/Resources/ClientResource/RelationManagers/AccountsRelationManager.php @@ -37,7 +37,7 @@ class AccountsRelationManager extends RelationManager ->columns([ Tables\Columns\TextColumn::make('account')->description(fn (Account $record): string => $record->description ?? ''), Tables\Columns\TextColumn::make('accountType.type'), - Tables\Columns\TextColumn::make('accountType.normal_balance'), + Tables\Columns\TextColumn::make('accountType.normal_balance')->label('Normal Balance'), ]) ->filters([ // diff --git a/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php b/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php index afc2b84..26f8a57 100644 --- a/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php +++ b/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php @@ -3,6 +3,7 @@ namespace App\Filament\Resources\ClientResource\RelationManagers; use App\Actions\Transactions\CreateTransactionAction; +use App\Commands\Expenses\GenerateVoucher; use App\DataObjects\CreateTransactionDTO; use App\Models\Account; use App\Models\Branch; diff --git a/app/Filament/Resources/ClientResource/RelationManagers/JournalsRelationManager.php b/app/Filament/Resources/ClientResource/RelationManagers/JournalsRelationManager.php index f7494eb..56e045f 100644 --- a/app/Filament/Resources/ClientResource/RelationManagers/JournalsRelationManager.php +++ b/app/Filament/Resources/ClientResource/RelationManagers/JournalsRelationManager.php @@ -35,7 +35,7 @@ class JournalsRelationManager extends RelationManager ->schema([ Select::make('branch_id') ->label('Branch') - ->options(fn () => Branch::where('client_id', $this->getOwnerRecord()->id)->pluck('name', 'id')) + ->options(fn () => Branch::where('client_id', $this->getOwnerRecord()->id)->pluck('code', 'id')) ->required() ->default(fn () => Branch::where('client_id', $this->getOwnerRecord()->id)->first()?->id), diff --git a/app/Models/Client.php b/app/Models/Client.php index b031183..2d25bb2 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -73,4 +73,9 @@ class Client extends Model { return $this->hasManyThrough(Expense::class, Branch::class); } + + public function journals(): HasManyThrough + { + return $this->hasManyThrough(Journal::class, Branch::class); + } } -- 2.49.1 From 0e4da559d65575bf57556b150fa91d90c5a634c2 Mon Sep 17 00:00:00 2001 From: Jp Date: Mon, 9 Feb 2026 22:25:08 +0800 Subject: [PATCH 2/3] fix: cascade delete related transactions and ledgers for sales and expenses Ensure data integrity by cleaning up dependent records when deleting Sale or Expense models. This prevents orphaned transactions and ledgers in the database. --- app/Models/Expense.php | 14 ++++++++++++++ app/Models/Sale.php | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/app/Models/Expense.php b/app/Models/Expense.php index b41f733..248a04b 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -25,6 +25,20 @@ class Expense extends Model return $this->morphMany(Transaction::class, 'transactionable'); } + protected static function booted() + { + static::deleting(function ($expense) { + $expense->transactions->each(function ($transaction) { + // Delete associated ledgers first to trigger any ledger deletion logic if exists + $transaction->ledgers->each(function ($ledger) { + $ledger->balances()->delete(); // Delete balances associated with ledger + $ledger->delete(); + }); + $transaction->delete(); + }); + }); + } + public function branch(): BelongsTo { return $this->belongsTo(Branch::class); diff --git a/app/Models/Sale.php b/app/Models/Sale.php index 97707d8..bf59284 100644 --- a/app/Models/Sale.php +++ b/app/Models/Sale.php @@ -25,6 +25,20 @@ class Sale extends Model return $this->morphMany(Transaction::class, 'transactionable'); } + protected static function booted() + { + static::deleting(function ($sale) { + $sale->transactions->each(function ($transaction) { + // Delete associated ledgers first to trigger any ledger deletion logic if exists + $transaction->ledgers->each(function ($ledger) { + $ledger->balances()->delete(); // Delete balances associated with ledger + $ledger->delete(); + }); + $transaction->delete(); + }); + }); + } + public function branch(): BelongsTo { return $this->belongsTo(Branch::class); -- 2.49.1 From 0131193b8d93b9a2c1c6fb1f755425112afd8317 Mon Sep 17 00:00:00 2001 From: Jp Date: Tue, 10 Feb 2026 15:05:36 +0800 Subject: [PATCH 3/3] refactor: streamline sales and expenses management in client resource - Move create/edit logic from relation managers to dedicated resource pages - Add transaction handling with proper rollback in sale create/update - Fix expense transaction creation by using correct array access - Set default client from query parameter in sale/expense forms - Exclude 'type' field from balance creation to prevent errors --- app/Actions/Balances/CreateBalanceAction.php | 2 +- .../ExpensesRelationManager.php | 207 +--------------- .../RelationManagers/SalesRelationManager.php | 228 +----------------- app/Filament/Resources/ExpenseResource.php | 1 + .../ExpenseResource/Pages/CreateExpense.php | 4 +- app/Filament/Resources/SaleResource.php | 1 + .../SaleResource/Pages/CreateSale.php | 43 ++++ .../Resources/SaleResource/Pages/EditSale.php | 63 +++++ 8 files changed, 123 insertions(+), 426 deletions(-) diff --git a/app/Actions/Balances/CreateBalanceAction.php b/app/Actions/Balances/CreateBalanceAction.php index eecbcb9..58159c5 100644 --- a/app/Actions/Balances/CreateBalanceAction.php +++ b/app/Actions/Balances/CreateBalanceAction.php @@ -15,7 +15,7 @@ class CreateBalanceAction extends BaseAction public function __invoke(Data $payload, Closure $next) { - $this->createBalanceCommand->execute($payload->balanceDTO->except('amount')->toArray()); + $this->createBalanceCommand->execute($payload->balanceDTO->except('amount', 'type')->toArray()); return $next($payload); } diff --git a/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php b/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php index 26f8a57..9aee350 100644 --- a/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php +++ b/app/Filament/Resources/ClientResource/RelationManagers/ExpensesRelationManager.php @@ -2,31 +2,13 @@ namespace App\Filament\Resources\ClientResource\RelationManagers; -use App\Actions\Transactions\CreateTransactionAction; -use App\Commands\Expenses\GenerateVoucher; -use App\DataObjects\CreateTransactionDTO; -use App\Models\Account; -use App\Models\Branch; -use App\Models\Client; -use Awcodes\TableRepeater\Components\TableRepeater; -use Awcodes\TableRepeater\Header; -use Filament\Forms; -use Filament\Forms\Components\DatePicker; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; +use App\Filament\Resources\ExpenseResource; +use App\Models\Expense; use Filament\Forms\Form; -use Filament\Forms\Get; -use Filament\Forms\Set; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\SoftDeletingScope; -use Illuminate\Support\Arr; -use Illuminate\Support\Collection; -use Illuminate\Support\Facades\Pipeline; class ExpensesRelationManager extends RelationManager { @@ -37,155 +19,7 @@ class ExpensesRelationManager extends RelationManager public function form(Form $form): Form { return $form - ->schema([ - Select::make('branch_id') - ->relationship('branch', 'code', fn (Builder $query) => $query->where('client_id', $this->getOwnerRecord()->id)) - ->afterStateUpdated(function ($set, $get) { - $set('voucher_number', static::getVoucherNumber($get)); - $set('transactions.*.branch_id', $get('branch_id')); - }) - ->required() - ->live(), - TextInput::make('supplier')->label('Supplier Name')->required(), - TextInput::make('reference_number')->label('Reference Number'), - TextInput::make('voucher_number')->label('Voucher Number') - ->default(fn ($get) => static::getVoucherNumber($get)) - ->readOnly(), - DatePicker::make('happened_on')->label('Date') - ->required() - ->afterStateUpdated(function ($set, $get) { - $set('transactions.*.happened_on', $get('happened_on')); - }) - ->live() - ->native(false), - - TableRepeater::make('transactions') - ->headers(fn (Get $get): array => static::getTransactionTableHeader($get)) - ->relationship('transactions') - ->schema(fn (Get $get): array => $this->getTransactionTableFormSchema($get)) - ->visible(fn (Get $get) => $get('branch_id') != null) - ->columnSpan('full'), - ]); - } - - public static function getVoucherNumber(Get $get): string - { - $branch = Branch::find($get('branch_id')); - - if ($branch) { - return GenerateVoucher::execute($branch); - } - - return ''; - } - - private static function getTransactionTableHeader(Get $get): array - { - return [ - Header::make('Charge Account'), - Header::make('Description'), - Header::make('Gross Amount'), - Header::make('Exempt'), - Header::make('Zero Rated'), - Header::make('Vatable Amount'), - Header::make('Input Tax'), - Header::make('Withholding Tax'), - Header::make('Net Amount'), - ]; - } - - private function getTransactionTableFormSchema(Get $get): array - { - return [ - Select::make('account_id')->options(fn ($get) => $this->getAccountOptions($get)), - TextInput::make('description')->label('Description'), - Hidden::make('branch_id')->default(fn (Get $get) => $get('../../branch_id')), - TextInput::make('gross_amount') - ->numeric() - ->live(false, 500) - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('exempt') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('zero_rated') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('vatable_amount') - ->numeric() - ->nullable() - ->live() - ->readOnly() - ->default(0), - Hidden::make('happened_on')->default(fn (Get $get) => $get('../../happened_on')), - TextInput::make('input_tax') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('payable_withholding_tax') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('net_amount')->numeric()->default(0), - ]; - } - - private function getAccountOptions(Get $get): Collection - { - $query = Account::query(); - - $query->where([ - 'client_id' => $this->getOwnerRecord()->id, - ]); - - if ($get('../../branch_id')) { - $query->whereHas('balances', function ($query) use ($get) { - return $query->where('branch_id', $get('../../branch_id')); - }); - } - - $query->whereHas('accountType', function ($query) { - return $query->where('type', 'Expenses'); - }); - - return $query->get()->pluck('account', 'id'); - } - - private function setDefaultFormValues(Get $get, Set $set, ?string $old, ?string $state): void - { - $exempt = (float) $get('exempt'); - $withHoldingTax = (float) $get('payable_withholding_tax'); - $vatableSales = $get('gross_amount'); - $vatableAmount = 0; - if ($vatableSales) { - $vatableAmount = $vatableSales / 1.12; - } - - $inputTax = $vatableAmount * 0.12; - - $netAmount = (int) $vatableSales - $get('payable_withholding_tax'); - - if ($this->getOwnerRecord()->vatable) { - $netAmount = ($vatableAmount + $exempt) - $withHoldingTax; - } - - $set('input_tax', number_format($inputTax, 2, '.', '')); - $set('vatable_amount', number_format($vatableAmount, 2, '.', '')); - $set('net_amount', number_format($netAmount, 2, '.', '')); + ->schema([]); } public function table(Table $table): Table @@ -204,40 +38,11 @@ class ExpensesRelationManager extends RelationManager ]) ->headerActions([ Tables\Actions\CreateAction::make() - ->using(function (array $data, string $model) { - $transactions = $data['transactions'] ?? []; - $data = Arr::except($data, ['transactions']); - - $record = $model::create($data); - - try { - $branch = $record->branch; - - foreach ($transactions as $transaction) { - - $tData = [ - 'branch_id' => $branch->id, - 'happened_on' => $record->happened_on, - ...$transaction, - ]; - - $payload = new CreateTransactionDTO(data: $tData, transactionable: $record); - - Pipeline::send(passable: $payload)->through( - [ - CreateTransactionAction::class, - ] - )->thenReturn(); - } - } catch (\Exception $exception) { - throw new \Exception('Failed to save transactions : '.$exception->getMessage()); - } - - return $record; - }), + ->url(fn () => ExpenseResource::getUrl('create', ['client_id' => $this->getOwnerRecord()->id])), ]) ->actions([ - Tables\Actions\EditAction::make(), + Tables\Actions\EditAction::make() + ->url(fn (Expense $record) => ExpenseResource::getUrl('edit', ['record' => $record])), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ diff --git a/app/Filament/Resources/ClientResource/RelationManagers/SalesRelationManager.php b/app/Filament/Resources/ClientResource/RelationManagers/SalesRelationManager.php index d505203..dc5276a 100644 --- a/app/Filament/Resources/ClientResource/RelationManagers/SalesRelationManager.php +++ b/app/Filament/Resources/ClientResource/RelationManagers/SalesRelationManager.php @@ -2,31 +2,14 @@ namespace App\Filament\Resources\ClientResource\RelationManagers; -use App\Actions\Transactions\CreateTransactionAction; -use App\DataObjects\CreateTransactionDTO; -use Illuminate\Support\Facades\Pipeline; -use App\Models\Account; -use App\Models\Branch; -use App\Models\Client; -use Awcodes\TableRepeater\Components\TableRepeater; -use Awcodes\TableRepeater\Header; +use App\Filament\Resources\SaleResource; +use App\Models\Sale; use Filament\Forms; -use Filament\Forms\Components\Checkbox; -use Filament\Forms\Components\DatePicker; -use Filament\Forms\Components\Hidden; -use Filament\Forms\Components\Select; -use Filament\Forms\Components\TextInput; use Filament\Forms\Form; -use Filament\Forms\Get; -use Filament\Forms\Set; use Filament\Resources\RelationManagers\RelationManager; use Filament\Tables; use Filament\Tables\Columns\TextColumn; use Filament\Tables\Table; -use Illuminate\Database\Eloquent\Builder; -use Illuminate\Database\Eloquent\SoftDeletingScope; -use Illuminate\Support\Arr; -use Illuminate\Support\Collection; class SalesRelationManager extends RelationManager { @@ -36,178 +19,7 @@ class SalesRelationManager extends RelationManager public function form(Form $form): Form { - return $form - ->schema([ - Select::make('branch_id') - ->relationship('branch', 'code', fn (Builder $query) => $query->where('client_id', $this->getOwnerRecord()->id)) - ->required() - ->afterStateUpdated(function ($set, $get) { - $set('current_series', static::getSeries($get)); - $set('transactions.*.branch_id', $get('branch_id')); - }) - ->live(), - TextInput::make('current_series') - ->label('Series') - ->disabled(), - DatePicker::make('happened_on')->label('Date') - ->required() - ->afterStateUpdated(function ($set, $get) { - $set('transactions.*.happened_on', $get('happened_on')); - }) - ->native(false), - Checkbox::make('with_discount')->label('With Discount?')->default(false)->live(), - - TableRepeater::make('transactions') - ->headers(fn (Get $get): array => static::getTransactionTableHeader($get)) - ->relationship('transactions') - ->schema(fn (Get $get): array => $this->getTransactionTableFormSchema($get)) - ->visible(fn (Get $get) => $get('branch_id') != null) - ->columnSpan('full'), - ]); - } - - public static function getSeries(Get $get): string - { - $branch = Branch::find($get('branch_id')); - - if ($branch) { - $currentSeries = $branch->current_series; - - return str_pad($currentSeries + 1, 6, '0', STR_PAD_LEFT); - } - - return ''; - - } - - private static function getTransactionTableHeader(Get $get): array - { - if ($get('with_discount')) { - return [ - Header::make('Charge Account'), - Header::make('Description'), - Header::make('Gross Amount'), - Header::make('Exempt'), - Header::make('Vatable Amount'), - Header::make('Output Tax'), - Header::make('Withholding Tax'), - Header::make('Discount'), - Header::make('Net Amount'), - ]; - } - - return [ - Header::make('Charge Account'), - Header::make('Description'), - Header::make('Gross Amount'), - Header::make('Exempt'), - Header::make('Vatable Amount'), - Header::make('Output Tax'), - Header::make('Withholding Tax'), - Header::make('Net Amount'), - ]; - } - - private function getTransactionTableFormSchema(Get $get): array - { - return [ - Select::make('account_id')->options(fn ($get) => $this->getAccountOptions($get)), - TextInput::make('description')->label('Description'), - Hidden::make('branch_id')->default(fn (Get $get) => $get('../../branch_id')), - TextInput::make('gross_amount') - ->numeric() - ->live(false, 500) - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('exempt') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('vatable_amount') - ->numeric() - ->nullable() - ->live() - ->readOnly() - ->default(0), - Hidden::make('happened_on')->default(fn (Get $get) => $get('../../happened_on')), - Hidden::make('with_discount')->default(fn (Get $get) => $get('../../with_discount')), - TextInput::make('output_tax') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('payable_withholding_tax') - ->numeric() - ->live() - ->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) { - - $this->setDefaultFormValues($get, $set, $old, $state); - })->default(0), - TextInput::make('discount') - ->numeric() - ->readOnly() - ->visible(fn (Get $get) => $get('../../with_discount')) - ->live(), - TextInput::make('net_amount')->numeric()->default(0), - ]; - } - - private function getAccountOptions($get) - { - $query = Account::query(); - - $query->where([ - 'client_id' => $this->getOwnerRecord()->id, - ]); - - if ($get('../../branch_id')) { - $query->whereHas('balances', function ($query) use ($get) { - return $query->where('branch_id', $get('../../branch_id')); - }); - } - - $query->whereHas('accountType', function ($query) { - return $query->where('type', 'Revenue'); - }); - - return $query->get()->pluck('account', 'id'); - } - - private function setDefaultFormValues(Get $get, Set $set, ?string $old, ?string $state) - { - $exempt = (float) $get('exempt'); - $withHoldingTax = (float) $get('payable_withholding_tax'); - $vatableSales = $get('gross_amount'); - $vatableAmount = 0; - if ($vatableSales) { - $vatableAmount = $vatableSales / 1.12; - } - - $discount = $exempt * .20; - $outputTax = $vatableAmount * 0.12; - - //default net amount - $netAmount = (int) $vatableSales - $get('payable_withholding_tax'); - - //net amount if vatable - if ($this->getOwnerRecord()->vatable) { - $netAmount = ($vatableAmount + $exempt) - $withHoldingTax; - } - - //if discounted - if ($get('../../with_discount')) { - $netAmount = $netAmount - $discount; - } - - $set('output_tax', number_format($outputTax, 2, '.', '')); - $set('discount', number_format($discount, 2, '.', '')); - $set('vatable_amount', number_format($vatableAmount, 2, '.', '')); - $set('net_amount', number_format($netAmount, 2, '.', '')); + return $form->schema([]); } public function table(Table $table): Table @@ -231,39 +43,11 @@ class SalesRelationManager extends RelationManager ]) ->headerActions([ Tables\Actions\CreateAction::make() - ->using(function (array $data, string $model) { - $transactions = $data['transactions'] ?? []; - $data = Arr::except($data, ['transactions']); - - $record = $model::create($data); - - try { - $branch = $record->branch; - - foreach ($transactions as $transaction) { - $tData = [ - 'branch_id' => $branch->id, - 'happened_on' => $record->happened_on, - ...$transaction, - ]; - - $payload = new CreateTransactionDTO(data: $tData, transactionable: $record); - - Pipeline::send(passable: $payload)->through( - [ - CreateTransactionAction::class, - ] - )->thenReturn(); - } - } catch (\Exception $exception) { - throw new \Exception('Failed to save transactions : '.$exception->getMessage()); - } - - return $record; - }), + ->url(fn () => SaleResource::getUrl('create', ['client_id' => $this->getOwnerRecord()->id])), ]) ->actions([ - Tables\Actions\EditAction::make(), + Tables\Actions\EditAction::make() + ->url(fn (Sale $record) => SaleResource::getUrl('edit', ['record' => $record])), Tables\Actions\DeleteAction::make(), ]) ->bulkActions([ diff --git a/app/Filament/Resources/ExpenseResource.php b/app/Filament/Resources/ExpenseResource.php index 84c31af..741a620 100644 --- a/app/Filament/Resources/ExpenseResource.php +++ b/app/Filament/Resources/ExpenseResource.php @@ -43,6 +43,7 @@ class ExpenseResource extends Resource { return [ Select::make('client') + ->default(request()->query('client_id')) ->options(Client::query()->get()->pluck('company', 'id')) ->afterStateUpdated(function ($set, $get) { $set('branch_id', ''); diff --git a/app/Filament/Resources/ExpenseResource/Pages/CreateExpense.php b/app/Filament/Resources/ExpenseResource/Pages/CreateExpense.php index 27fcca8..b1104ac 100644 --- a/app/Filament/Resources/ExpenseResource/Pages/CreateExpense.php +++ b/app/Filament/Resources/ExpenseResource/Pages/CreateExpense.php @@ -28,7 +28,7 @@ class CreateExpense extends CreateRecord protected function afterCreate(): void { - $transactions = Arr::only($this->form->getState(), ['transactions']); + $transactions = $this->form->getState()['transactions'] ?? []; try { $branch = $this->getRecord()->branch; @@ -38,7 +38,7 @@ class CreateExpense extends CreateRecord $data = [ 'branch_id' => $branch->id, 'happened_on' => $this->getRecord()->happened_on, - ...Arr::first($transaction), + ...$transaction, ]; $payload = new CreateTransactionDTO(data: $data, transactionable: $this->getRecord()); diff --git a/app/Filament/Resources/SaleResource.php b/app/Filament/Resources/SaleResource.php index 1a1723f..99ab935 100644 --- a/app/Filament/Resources/SaleResource.php +++ b/app/Filament/Resources/SaleResource.php @@ -35,6 +35,7 @@ class SaleResource extends Resource return $form ->schema([ Select::make('client') + ->default(request()->query('client_id')) ->options(Client::query()->get()->pluck('company', 'id')) ->afterStateUpdated(function ($set, $get) { $set('branch_id', ''); diff --git a/app/Filament/Resources/SaleResource/Pages/CreateSale.php b/app/Filament/Resources/SaleResource/Pages/CreateSale.php index a44cfd5..4f7189b 100644 --- a/app/Filament/Resources/SaleResource/Pages/CreateSale.php +++ b/app/Filament/Resources/SaleResource/Pages/CreateSale.php @@ -2,10 +2,16 @@ namespace App\Filament\Resources\SaleResource\Pages; +use App\Actions\Transactions\CreateTransactionAction; +use App\DataObjects\CreateTransactionDTO; use App\Filament\Resources\SaleResource; use App\Models\Branch; +use App\Models\Sale; use Filament\Resources\Pages\CreateRecord; +use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Arr; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Pipeline; class CreateSale extends CreateRecord { @@ -16,11 +22,48 @@ class CreateSale extends CreateRecord return $this->getFormDataMutation($data); } + protected function handleRecordCreation(array $data): Model + { + $transactions = $this->data['transactions'] ?? []; + return $this->processCreate($data, $transactions); + } + public function getFormDataMutation(array $data): array { return Arr::except($data, ['client', 'transactions', 'with_discount']); } + public function processCreate(array $data, array $transactions): Model + { + try { + DB::beginTransaction(); + $record = Sale::create($data); + $branch = $record->branch; + + foreach ($transactions as $transaction) { + $tData = [ + 'branch_id' => $branch->id, + 'happened_on' => $record->happened_on, + ...$transaction, + ]; + + $payload = new CreateTransactionDTO(data: $tData, transactionable: $record); + + Pipeline::send(passable: $payload)->through( + [ + CreateTransactionAction::class, + ] + )->thenReturn(); + } + DB::commit(); + } catch (\Exception $exception) { + DB::rollBack(); + throw new \Exception('Failed to save transactions : '.$exception->getMessage()); + } + + return $record; + } + protected function afterCreate(): void { $branch = Branch::find($this->data['branch_id']); diff --git a/app/Filament/Resources/SaleResource/Pages/EditSale.php b/app/Filament/Resources/SaleResource/Pages/EditSale.php index 6e2f9e2..e5b59ff 100644 --- a/app/Filament/Resources/SaleResource/Pages/EditSale.php +++ b/app/Filament/Resources/SaleResource/Pages/EditSale.php @@ -2,9 +2,15 @@ namespace App\Filament\Resources\SaleResource\Pages; +use App\Actions\Transactions\CreateTransactionAction; +use App\DataObjects\CreateTransactionDTO; use App\Filament\Resources\SaleResource; use Filament\Actions; use Filament\Resources\Pages\EditRecord; +use Illuminate\Database\Eloquent\Model; +use Illuminate\Support\Arr; +use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Pipeline; class EditSale extends EditRecord { @@ -16,4 +22,61 @@ class EditSale extends EditRecord Actions\DeleteAction::make(), ]; } + + protected function mutateFormDataBeforeSave(array $data): array + { + return $this->getFormDataMutation($data); + } + + protected function handleRecordUpdate(Model $record, array $data): Model + { + $transactions = $this->data['transactions'] ?? []; + return $this->processUpdate($record, $data, $transactions); + } + + public function getFormDataMutation(array $data): array + { + return Arr::except($data, ['client', 'transactions', 'with_discount']); + } + + public function processUpdate(Model $record, array $data, array $transactions): Model + { + try { + DB::beginTransaction(); + $record->update($data); + $branch = $record->branch; + + // Delete existing transactions and their related ledgers/balances + $record->transactions->each(function ($transaction) { + $transaction->ledgers->each(function ($ledger) { + $ledger->balances()->delete(); + $ledger->delete(); + }); + $transaction->delete(); + }); + + // Create new transactions + foreach ($transactions as $transaction) { + $tData = [ + 'branch_id' => $branch->id, + 'happened_on' => $record->happened_on, + ...$transaction, + ]; + + $payload = new CreateTransactionDTO(data: $tData, transactionable: $record); + + Pipeline::send(passable: $payload)->through( + [ + CreateTransactionAction::class, + ] + )->thenReturn(); + } + DB::commit(); + } catch (\Exception $exception) { + DB::rollBack(); + throw new \Exception('Failed to save transactions : '.$exception->getMessage()); + } + + return $record; + } } -- 2.49.1