feat: updates on expenses
This commit is contained in:
22
app/Actions/Balances/CreateBalanceAction.php
Normal file
22
app/Actions/Balances/CreateBalanceAction.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace App\Actions\Balances;
|
||||
|
||||
use App\Actions\BaseAction;
|
||||
use App\Commands\Balances\CreateBalanceCommand;
|
||||
use Closure;
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class CreateBalanceAction extends BaseAction
|
||||
{
|
||||
public function __construct(
|
||||
private CreateBalanceCommand $createBalanceCommand
|
||||
) {}
|
||||
|
||||
public function __invoke(Data $payload, Closure $next)
|
||||
{
|
||||
$this->createBalanceCommand->execute($payload->balanceDTO->except('amount')->toArray());
|
||||
|
||||
return $next($payload);
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ namespace App\Actions\Ledgers;
|
||||
|
||||
use App\Actions\BaseAction;
|
||||
use App\Commands\Ledgers\CreateLedgerCommand;
|
||||
use App\DataObjects\CreateBalanceDTO;
|
||||
use App\DataObjects\CreateLedgerDTO;
|
||||
use Closure;
|
||||
use Spatie\LaravelData\Data;
|
||||
@@ -16,7 +17,15 @@ class CreateLedgerAction extends BaseAction
|
||||
|
||||
public function __invoke(CreateLedgerDTO|Data $payload, Closure $next)
|
||||
{
|
||||
$this->createLedgerCommand->execute($payload->data);
|
||||
$ledger = $this->createLedgerCommand->execute($payload->data);
|
||||
|
||||
$payload->balanceDTO = new CreateBalanceDTO(
|
||||
amount: $payload->amount,
|
||||
is_starting: false,
|
||||
ledger_id: $ledger->id,
|
||||
account_id: $ledger->account_id,
|
||||
branch_id: $ledger->branch_id,
|
||||
);
|
||||
|
||||
return $next($payload);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Transactions;
|
||||
|
||||
use App\Actions\Balances\CreateBalanceAction;
|
||||
use App\Actions\BaseAction;
|
||||
use App\Actions\Ledgers\CreateLedgerAction;
|
||||
use App\DataObjects\CreateLedgerDTO;
|
||||
@@ -21,6 +22,8 @@ class CreateTransactionAction extends BaseAction
|
||||
|
||||
$this->withHoldingAccountLedger($payload);
|
||||
|
||||
$this->cashAccountLedger($payload);
|
||||
|
||||
return $next($payload);
|
||||
}
|
||||
|
||||
@@ -56,6 +59,7 @@ class CreateTransactionAction extends BaseAction
|
||||
{
|
||||
return Pipeline::send(passable: $ledgerPayload)->through([
|
||||
CreateLedgerAction::class,
|
||||
CreateBalanceAction::class,
|
||||
])->thenReturn();
|
||||
}
|
||||
|
||||
@@ -82,10 +86,27 @@ class CreateTransactionAction extends BaseAction
|
||||
|
||||
$ledgerPayload = new CreateLedgerDTO(
|
||||
branch_id: $payload->transactionable->branch_id,
|
||||
amount: $payload->transaction->payable_withholding_tax,
|
||||
amount: $payload->transaction->payable_withholding_tax ?? 0.00,
|
||||
transaction: $payload->transaction,
|
||||
account: $withholdingAccount,
|
||||
);
|
||||
$this->ledgerPipe($ledgerPayload);
|
||||
}
|
||||
|
||||
public function cashAccountLedger($payload): void
|
||||
{
|
||||
$cashAccount = Account::query()->where('account', 'Cash')->whereHas('balances', function ($balance) use ($payload) {
|
||||
return $balance->where('branch_id', $payload->transactionable->branch_id);
|
||||
})->first();
|
||||
|
||||
$amount = $payload->transaction->gross_amount - $payload->transaction->creditable_withholding_tax;
|
||||
|
||||
$ledgerPayload = new CreateLedgerDTO(
|
||||
branch_id: $payload->transactionable->branch_id,
|
||||
amount: $amount ?? 0.00,
|
||||
transaction: $payload->transaction,
|
||||
account: $cashAccount,
|
||||
);
|
||||
$this->ledgerPipe($ledgerPayload);
|
||||
}
|
||||
}
|
||||
|
||||
21
app/Commands/Balances/CreateBalanceCommand.php
Normal file
21
app/Commands/Balances/CreateBalanceCommand.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Commands\Balances;
|
||||
|
||||
use App\Commands\Command;
|
||||
use App\Models\Balance;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class CreateBalanceCommand implements Command
|
||||
{
|
||||
public function execute(array $data): mixed
|
||||
{
|
||||
return DB::transaction(
|
||||
callback: fn () => Balance::query()->updateOrCreate([
|
||||
'id' => $data['id'],
|
||||
'account_id' => $data['account_id'],
|
||||
], $data),
|
||||
attempts: 2
|
||||
);
|
||||
}
|
||||
}
|
||||
30
app/DataObjects/CreateBalanceDTO.php
Normal file
30
app/DataObjects/CreateBalanceDTO.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\DataObjects;
|
||||
|
||||
use App\Models\Account;
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class CreateBalanceDTO extends Data
|
||||
{
|
||||
public int $balance;
|
||||
|
||||
public function __construct(
|
||||
public float $amount,
|
||||
public bool $is_starting,
|
||||
public ?int $id = null,
|
||||
public ?int $ledger_id = null,
|
||||
public ?int $account_id = null,
|
||||
public ?int $branch_id = null
|
||||
) {
|
||||
$account = Account::query()->where('id', $this->account_id)->first();
|
||||
|
||||
$currentBalance = $account ? $account->current_balance : 0;
|
||||
|
||||
if ($account->normal_balance == 'credit') {
|
||||
$this->balance = $currentBalance - $this->amount;
|
||||
} else {
|
||||
$this->balance = $currentBalance + $this->amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ class CreateLedgerDTO extends Data
|
||||
public ?Model $ledger = null,
|
||||
public ?Model $transaction = null,
|
||||
public ?Model $account = null,
|
||||
public ?CreateBalanceDTO $balanceDTO = null,
|
||||
) {
|
||||
$this->transaction_id = $this->transaction->id;
|
||||
$this->account_id = $this->account->id;
|
||||
|
||||
@@ -15,15 +15,19 @@ 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\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Support\Collection;
|
||||
use JetBrains\PhpStorm\NoReturn;
|
||||
|
||||
class ExpenseResource extends Resource
|
||||
{
|
||||
protected static ?string $model = Expense::class;
|
||||
|
||||
protected static bool $isVatable;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
public static function form(Form $form): Form
|
||||
@@ -36,29 +40,147 @@ class ExpenseResource extends Resource
|
||||
{
|
||||
return [
|
||||
Select::make('client')
|
||||
->options(Client::query()->get()->pluck('company', 'id'))->live(),
|
||||
Select::make('branch_id')->options(fn ($get) => Branch::query()->where('client_id', $get('client'))->get()->pluck('code', 'id'))
|
||||
->afterStateUpdated(fn ($state, $set) => $set('voucher_number', GenerateVoucher::execute(Branch::find($state))))->live(),
|
||||
->options(Client::query()->get()->pluck('company', 'id'))
|
||||
->afterStateUpdated(function ($set, $get) {
|
||||
$set('branch_id', '');
|
||||
$set('voucher_number', static::getVoucherNumber($get));
|
||||
})
|
||||
->live(),
|
||||
Select::make('branch_id')
|
||||
->relationship('branch', 'code')
|
||||
->options(fn ($get) => Branch::query()->where('client_id', $get('client'))->get()->pluck('code', 'id'))
|
||||
->afterStateUpdated(fn ($set, $get) => $set('voucher_number', static::getVoucherNumber($get)))
|
||||
->live(),
|
||||
TextInput::make('supplier')->label('Supplier Name'),
|
||||
TextInput::make('reference_number')->label('Reference Number'),
|
||||
TextInput::make('voucher_number')->label('Voucher Number'),
|
||||
TextInput::make('voucher_number')->label('Voucher Number')
|
||||
->default(fn ($get) => static::getVoucherNumber($get))
|
||||
->readOnly(),
|
||||
DatePicker::make('happened_on')->label('Date')->native(false),
|
||||
|
||||
TableRepeater::make('transactions')
|
||||
->headers([
|
||||
->headers(fn (Get $get): array => static::getTransactionTableHeader($get))
|
||||
->relationship('transactions')
|
||||
->schema(fn (Get $get): array => static::getTransactionTableFormSchema($get))
|
||||
|
||||
->columnSpan('full'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getVoucherNumber(Get $get): string
|
||||
{
|
||||
$branch = Branch::find($get('branch_id'));
|
||||
|
||||
if ($branch) {
|
||||
return GenerateVoucher::execute($branch);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public static function getTransactionTableHeader(Get $get): array
|
||||
{
|
||||
|
||||
if (! static::getIsVatable($get)) {
|
||||
return [
|
||||
Header::make('Charge Account'),
|
||||
Header::make('Description'),
|
||||
Header::make('Gross Amount'),
|
||||
Header::make('Withholding Tax'),
|
||||
Header::make('Net Amount'),
|
||||
])
|
||||
->schema([
|
||||
];
|
||||
}
|
||||
|
||||
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'),
|
||||
];
|
||||
}
|
||||
|
||||
public static function getIsVatable(Get $get): bool
|
||||
{
|
||||
$client = Client::find($get('client'));
|
||||
|
||||
return $client && $client->vatable;
|
||||
|
||||
}
|
||||
|
||||
public static function isVatable(bool $value): void
|
||||
{
|
||||
static::$isVatable = $value;
|
||||
dump('sett');
|
||||
}
|
||||
|
||||
public static function getTransactionTableFormSchema(Get $get): array
|
||||
{
|
||||
if (! static::getIsVatable($get)) {
|
||||
return [
|
||||
Select::make('account_id')->options(fn ($get) => static::getAccountOptions($get)),
|
||||
TextInput::make('description')->label('Description'),
|
||||
TextInput::make('gross_amount'),
|
||||
TextInput::make('payable_withholding_tax'),
|
||||
TextInput::make('net_amount'),
|
||||
])->columnSpan('full'),
|
||||
TextInput::make('gross_amount')->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('payable_withholding_tax')->numeric()->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('net_amount')->numeric()->default(0),
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
Select::make('account_id')->options(fn ($get) => static::getAccountOptions($get)),
|
||||
TextInput::make('description')->label('Description'),
|
||||
TextInput::make('gross_amount')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('exempt')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('zero_rated')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('vatable_amount')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('input_tax')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('payable_withholding_tax')
|
||||
->numeric()
|
||||
->live()
|
||||
->afterStateUpdated(function (Get $get, Set $set, ?string $old, ?string $state) {
|
||||
|
||||
static::setDefaultFormValues($get, $set, $old, $state);
|
||||
})->default(0),
|
||||
TextInput::make('net_amount')->numeric()->default(0),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -83,6 +205,28 @@ class ExpenseResource extends Resource
|
||||
return $query->get()->pluck('account', 'id');
|
||||
}
|
||||
|
||||
#[NoReturn]
|
||||
public static 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 = $vatableSales / 1.12;
|
||||
|
||||
$inputTax = $vatableAmount * 0.12;
|
||||
|
||||
$netAmount = $get('gross_amount') - $get('payable_withholding_tax');
|
||||
|
||||
if (static::getIsVatable($get)) {
|
||||
$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));
|
||||
}
|
||||
|
||||
public static function table(Table $table): Table
|
||||
{
|
||||
return $table
|
||||
|
||||
@@ -16,4 +16,12 @@ class EditExpense extends EditRecord
|
||||
Actions\DeleteAction::make(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function mutateFormDataBeforeFill(array $data): array
|
||||
{
|
||||
return [
|
||||
'client' => $this->getRecord()->branch->client->id,
|
||||
...$this->getRecord()->toArray(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class Client extends Model
|
||||
|
||||
public function getVatableAttribute(): bool
|
||||
{
|
||||
return $this->type->type == 'Vatable' ? true : false;
|
||||
return $this->type->type == 'Vatable';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user