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.

AccountChart.php 6.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. <?php
  2. namespace App\Filament\Company\Pages\Accounting;
  3. use App\Enums\Accounting\AccountCategory;
  4. use App\Models\Accounting\Account;
  5. use App\Models\Accounting\Account as ChartModel;
  6. use App\Models\Accounting\AccountSubtype;
  7. use App\Services\AccountService;
  8. use App\Utilities\Accounting\AccountCode;
  9. use App\Utilities\Currency\CurrencyAccessor;
  10. use Filament\Actions\Action;
  11. use Filament\Actions\CreateAction;
  12. use Filament\Actions\EditAction;
  13. use Filament\Forms\Components\Select;
  14. use Filament\Forms\Components\Textarea;
  15. use Filament\Forms\Components\TextInput;
  16. use Filament\Forms\Form;
  17. use Filament\Forms\Get;
  18. use Filament\Forms\Set;
  19. use Filament\Pages\Page;
  20. use Filament\Support\Enums\MaxWidth;
  21. use Illuminate\Support\Collection;
  22. use Livewire\Attributes\Computed;
  23. use Livewire\Attributes\Url;
  24. class AccountChart extends Page
  25. {
  26. protected static ?string $navigationIcon = 'heroicon-o-clipboard-document-list';
  27. protected static ?string $title = 'Chart of Accounts';
  28. protected static ?string $navigationGroup = 'Accounting';
  29. protected static ?string $slug = 'accounting/chart';
  30. protected static string $view = 'filament.company.pages.accounting.chart';
  31. public ?ChartModel $chart = null;
  32. #[Url]
  33. public ?string $activeTab = null;
  34. protected AccountService $accountService;
  35. public function boot(AccountService $accountService): void
  36. {
  37. $this->accountService = $accountService;
  38. }
  39. public function mount(): void
  40. {
  41. $this->activeTab = $this->activeTab ?? AccountCategory::Asset->value;
  42. }
  43. public function getAccountBalance(Account $account): ?string
  44. {
  45. $company = $account->company;
  46. $startDate = $company->locale->fiscalYearStartDate();
  47. $endDate = $company->locale->fiscalYearEndDate();
  48. return $this->accountService->getEndingBalance($account, $startDate, $endDate)?->formatted();
  49. }
  50. protected function configureAction(Action $action): void
  51. {
  52. $action
  53. ->modalWidth(MaxWidth::TwoExtraLarge)
  54. ->stickyModalHeader()
  55. ->stickyModalFooter();
  56. }
  57. #[Computed]
  58. public function categories(): Collection
  59. {
  60. return AccountSubtype::withCount('accounts')
  61. ->get()
  62. ->groupBy('category');
  63. }
  64. public function editChartAction(): Action
  65. {
  66. return EditAction::make()
  67. ->iconButton()
  68. ->record($this->chart)
  69. ->name('editChart')
  70. ->label('Edit account')
  71. ->modalHeading('Edit Account')
  72. ->icon('heroicon-m-pencil-square')
  73. ->mountUsing(function (array $arguments, Form $form) {
  74. $chartId = $arguments['chart'];
  75. $this->chart = ChartModel::find($chartId);
  76. $form
  77. ->fill($this->chart->toArray())
  78. ->operation('edit')
  79. ->model($this->chart); // This is needed for form relationships to work (maybe a bug in Filament regarding passed arguments related to timing)
  80. })
  81. ->form($this->getChartForm());
  82. }
  83. public function createChartAction(): Action
  84. {
  85. return CreateAction::make()
  86. ->link()
  87. ->name('createChart')
  88. ->form($this->getChartForm())
  89. ->model(ChartModel::class)
  90. ->label('Add a new account')
  91. ->icon('heroicon-o-plus-circle')
  92. ->mountUsing(function (array $arguments, Form $form) {
  93. $subtypeId = $arguments['subtype'];
  94. $this->chart = new ChartModel([
  95. 'subtype_id' => $subtypeId,
  96. ]);
  97. if ($subtypeId) {
  98. $companyId = auth()->user()->currentCompany->id;
  99. $generatedCode = AccountCode::generate($companyId, $subtypeId);
  100. $this->chart->code = $generatedCode;
  101. }
  102. $form->fill($this->chart->toArray())
  103. ->operation('create');
  104. });
  105. }
  106. private function getChartForm(bool $useActiveTab = true): array
  107. {
  108. return [
  109. Select::make('subtype_id')
  110. ->label('Type')
  111. ->required()
  112. ->live()
  113. ->disabled(static fn (string $operation, ?ChartModel $record) => $operation === 'edit' && $record?->default === true)
  114. ->options($this->getChartSubtypeOptions($useActiveTab))
  115. ->afterStateUpdated(static function (?string $state, Set $set): void {
  116. if ($state) {
  117. $companyId = auth()->user()->currentCompany->id;
  118. $generatedCode = AccountCode::generate($companyId, $state);
  119. $set('code', $generatedCode);
  120. }
  121. }),
  122. TextInput::make('code')
  123. ->label('Code')
  124. ->required()
  125. ->validationAttribute('account code')
  126. ->unique(table: ChartModel::class, column: 'code', ignoreRecord: true)
  127. ->validateAccountCode(static fn (Get $get) => $get('subtype_id')),
  128. TextInput::make('name')
  129. ->label('Name')
  130. ->required(),
  131. Select::make('currency_code')
  132. ->localizeLabel('Currency')
  133. ->relationship('currency', 'name')
  134. ->default(CurrencyAccessor::getDefaultCurrency())
  135. ->preload()
  136. ->searchable()
  137. ->visible(function (Get $get): bool {
  138. return filled($get('subtype_id')) && AccountSubtype::find($get('subtype_id'))->multi_currency;
  139. })
  140. ->live(),
  141. Textarea::make('description')
  142. ->label('Description')
  143. ->autosize(),
  144. ];
  145. }
  146. private function getChartSubtypeOptions($useActiveTab = true): array
  147. {
  148. $subtypes = $useActiveTab ?
  149. AccountSubtype::where('category', $this->activeTab)->get() :
  150. AccountSubtype::all();
  151. return $subtypes->groupBy(fn (AccountSubtype $subtype) => $subtype->type->getLabel())
  152. ->map(fn (Collection $subtypes, string $type) => $subtypes->mapWithKeys(static fn (AccountSubtype $subtype) => [$subtype->id => $subtype->name]))
  153. ->toArray();
  154. }
  155. protected function getHeaderActions(): array
  156. {
  157. return [
  158. CreateAction::make()
  159. ->button()
  160. ->label('Add New Account')
  161. ->model(ChartModel::class)
  162. ->form($this->getChartForm(false)),
  163. ];
  164. }
  165. public function getCategoryLabel($categoryValue): string
  166. {
  167. return AccountCategory::from($categoryValue)->getLabel();
  168. }
  169. }