You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TransactionResource.php 7.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. <?php
  2. namespace App\Filament\Company\Resources\Accounting;
  3. use App\Enums\Accounting\AccountCategory;
  4. use App\Enums\DateFormat;
  5. use App\Filament\Company\Resources\Accounting\TransactionResource\Pages;
  6. use App\Models\Accounting\Account;
  7. use App\Models\Accounting\Transaction;
  8. use App\Models\Banking\BankAccount;
  9. use App\Models\Setting\Localization;
  10. use Filament\Forms;
  11. use Filament\Forms\Form;
  12. use Filament\Resources\Resource;
  13. use Filament\Support\Enums\FontWeight;
  14. use Filament\Support\Enums\MaxWidth;
  15. use Filament\Tables;
  16. use Filament\Tables\Table;
  17. use Illuminate\Support\Carbon;
  18. use Illuminate\Support\Collection;
  19. class TransactionResource extends Resource
  20. {
  21. protected static ?string $model = Transaction::class;
  22. protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
  23. public static function form(Form $form): Form
  24. {
  25. return $form
  26. ->schema([
  27. Forms\Components\DatePicker::make('posted_at')
  28. ->label('Date')
  29. ->required()
  30. ->displayFormat('Y-m-d')
  31. ->default(now()->format('Y-m-d')),
  32. Forms\Components\TextInput::make('description')
  33. ->label('Description'),
  34. Forms\Components\Select::make('bank_account_id')
  35. ->label('Account')
  36. ->options(fn () => static::getBankAccountOptions())
  37. ->live()
  38. ->searchable()
  39. ->preload()
  40. ->required(),
  41. Forms\Components\Select::make('method')
  42. ->label('Type')
  43. ->live()
  44. ->options([
  45. 'deposit' => 'Deposit',
  46. 'withdrawal' => 'Withdrawal',
  47. ])
  48. ->default('deposit')
  49. ->afterStateUpdated(static function (Forms\Set $set, $state) {
  50. if ($state === 'deposit') {
  51. $account = Account::where('category', AccountCategory::Revenue)
  52. ->where('name', 'Uncategorized Income')->first();
  53. if ($account->exists()) {
  54. $set('account_id', $account->id);
  55. }
  56. } else {
  57. $account = Account::where('category', AccountCategory::Expense)
  58. ->where('name', 'Uncategorized Expense')->first();
  59. if ($account->exists()) {
  60. $set('account_id', $account->id);
  61. }
  62. }
  63. })
  64. ->required(),
  65. Forms\Components\TextInput::make('amount')
  66. ->label('Amount')
  67. ->money(static function (Forms\Get $get) {
  68. $bankAccount = $get('bank_account_id');
  69. $bankAccount = BankAccount::find($bankAccount);
  70. $account = $bankAccount->account ?? null;
  71. if ($account) {
  72. return $account->currency_code;
  73. }
  74. return 'USD';
  75. })
  76. ->required(),
  77. Forms\Components\Select::make('account_id')
  78. ->label('Category')
  79. ->options(static fn (Forms\Get $get) => static::getAccountOptions($get('method')))
  80. ->searchable()
  81. ->preload()
  82. ->required(),
  83. Forms\Components\Textarea::make('notes')
  84. ->label('Notes')
  85. ->autosize()
  86. ->rows(10)
  87. ->columnSpanFull(),
  88. ]);
  89. }
  90. public static function table(Table $table): Table
  91. {
  92. return $table
  93. ->columns([
  94. Tables\Columns\TextColumn::make('posted_at')
  95. ->label('Date')
  96. ->sortable()
  97. ->formatStateUsing(static function ($state) {
  98. $dateFormat = Localization::firstOrFail()->date_format->value ?? DateFormat::DEFAULT;
  99. return Carbon::parse($state)->translatedFormat($dateFormat);
  100. }),
  101. Tables\Columns\TextColumn::make('bankAccount.account.name')
  102. ->label('Account')
  103. ->sortable(),
  104. Tables\Columns\TextColumn::make('description')
  105. ->limit(50)
  106. ->label('Description'),
  107. Tables\Columns\TextColumn::make('account.name')
  108. ->label('Category'),
  109. Tables\Columns\TextColumn::make('amount')
  110. ->label('Amount')
  111. ->sortable()
  112. ->weight(FontWeight::Medium)
  113. ->color(static fn (Transaction $record) => $record->type === 'expense' ? 'danger' : null)
  114. ->currency(static fn (Transaction $record) => $record->bankAccount->account->currency_code ?? 'USD', true),
  115. ])
  116. ->defaultSort('posted_at', 'desc')
  117. ->filters([
  118. //
  119. ])
  120. ->actions([
  121. Tables\Actions\EditAction::make()
  122. ->modalWidth(MaxWidth::ThreeExtraLarge)
  123. ->stickyModalHeader()
  124. ->stickyModalFooter()
  125. ->mutateFormDataUsing(static function (array $data): array {
  126. $method = $data['method'];
  127. if ($method === 'deposit') {
  128. $data['type'] = 'income';
  129. } else {
  130. $data['type'] = 'expense';
  131. }
  132. return $data;
  133. }),
  134. ])
  135. ->bulkActions([
  136. Tables\Actions\BulkActionGroup::make([
  137. Tables\Actions\DeleteBulkAction::make(),
  138. ]),
  139. ]);
  140. }
  141. public static function getRelations(): array
  142. {
  143. return [
  144. //
  145. ];
  146. }
  147. public static function getPages(): array
  148. {
  149. return [
  150. 'index' => Pages\ManageTransaction::route('/'),
  151. ];
  152. }
  153. public static function getBankAccountOptions(): array
  154. {
  155. $bankAccounts = BankAccount::with('account.subtype')->get();
  156. return $bankAccounts->groupBy('account.subtype.name')
  157. ->map(fn (Collection $bankAccounts) => $bankAccounts->pluck('account.name', 'id'))
  158. ->toArray();
  159. }
  160. public static function getAccountOptions(mixed $method)
  161. {
  162. $excludedCategory = match ($method) {
  163. 'deposit' => AccountCategory::Expense,
  164. 'withdrawal' => AccountCategory::Revenue,
  165. };
  166. $accounts = Account::whereNot('category', $excludedCategory)->get();
  167. return $accounts->groupBy(fn (Account $account) => $account->category->getLabel())
  168. ->map(fn (Collection $accounts, string $category) => $accounts->mapWithKeys(fn (Account $account) => [$account->id => $account->name]))
  169. ->toArray();
  170. }
  171. }