您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

CurrencyResource.php 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. <?php
  2. namespace App\Filament\Resources;
  3. use App\Filament\Resources\CurrencyResource\Pages;
  4. use App\Filament\Resources\CurrencyResource\RelationManagers;
  5. use App\Models\Banking\Account;
  6. use App\Models\Setting\Currency;
  7. use App\Services\CurrencyService;
  8. use Closure;
  9. use Exception;
  10. use Filament\Forms;
  11. use Filament\Notifications\Notification;
  12. use Filament\Resources\Form;
  13. use Filament\Resources\Resource;
  14. use Filament\Resources\Table;
  15. use Filament\Tables;
  16. use Illuminate\Database\Eloquent\Builder;
  17. use Illuminate\Support\Collection;
  18. use Illuminate\Support\Facades\Auth;
  19. use Wallo\FilamentSelectify\Components\ToggleButton;
  20. class CurrencyResource extends Resource
  21. {
  22. protected static ?string $model = Currency::class;
  23. protected static ?string $navigationIcon = 'heroicon-o-currency-dollar';
  24. protected static ?string $navigationGroup = 'Settings';
  25. public static function getEloquentQuery(): Builder
  26. {
  27. return parent::getEloquentQuery()
  28. ->where('company_id', Auth::user()->currentCompany->id);
  29. }
  30. public static function form(Form $form): Form
  31. {
  32. return $form
  33. ->schema([
  34. Forms\Components\Section::make('General')
  35. ->description('Upon selecting a currency code, the corresponding values based on real-world currencies will auto-populate. The default currency is used for all transactions and reports and cannot be deleted. Currency precision determines the number of decimal places to display when formatting currency amounts. The currency rate is set to 1 for the default currency and is utilized as the basis for setting exchange rates for all other currencies. Alterations to default values are allowed but manage such changes wisely as any confusion or discrepancies are your responsibility.')
  36. ->schema([
  37. Forms\Components\Select::make('code')
  38. ->label('Code')
  39. ->options(Currency::getCurrencyCodes())
  40. ->searchable()
  41. ->placeholder('Select a currency code...')
  42. ->reactive()
  43. ->hidden(static fn (Closure $get): bool => $get('enabled'))
  44. ->afterStateUpdated(static function (Closure $set, $state) {
  45. if ($state === null) {
  46. return;
  47. }
  48. $code = $state;
  49. $currencyConfig = config("money.{$code}", []);
  50. $currencyService = app(CurrencyService::class);
  51. $defaultCurrency = Currency::getDefaultCurrency();
  52. $rate = 1;
  53. if ($defaultCurrency !== null) {
  54. $rate = $currencyService->getCachedExchangeRate($defaultCurrency, $code);
  55. }
  56. $set('name', $currencyConfig['name'] ?? '');
  57. $set('rate', $rate);
  58. $set('precision', $currencyConfig['precision'] ?? '');
  59. $set('symbol', $currencyConfig['symbol'] ?? '');
  60. $set('symbol_first', $currencyConfig['symbol_first'] ?? '');
  61. $set('decimal_mark', $currencyConfig['decimal_mark'] ?? '');
  62. $set('thousands_separator', $currencyConfig['thousands_separator'] ?? '');
  63. })
  64. ->required(),
  65. Forms\Components\TextInput::make('code')
  66. ->label('Code')
  67. ->hidden(static fn (Closure $get): bool => !$get('enabled'))
  68. ->disabled(static fn (Closure $get): bool => $get('enabled'))
  69. ->required(),
  70. Forms\Components\TextInput::make('name')
  71. ->translateLabel()
  72. ->maxLength(100)
  73. ->required(),
  74. Forms\Components\TextInput::make('rate')
  75. ->label('Rate')
  76. ->dehydrateStateUsing(static fn (Closure $get, $state) => $get('enabled') ? '1' : $state)
  77. ->numeric()
  78. ->reactive()
  79. ->disabled(static fn (Closure $get): bool => $get('enabled'))
  80. ->required(),
  81. Forms\Components\Select::make('precision')
  82. ->label('Precision')
  83. ->searchable()
  84. ->placeholder('Select the currency precision...')
  85. ->options(['0', '1', '2', '3', '4'])
  86. ->required(),
  87. Forms\Components\TextInput::make('symbol')
  88. ->label('Symbol')
  89. ->maxLength(5)
  90. ->required(),
  91. Forms\Components\Select::make('symbol_first')
  92. ->label('Symbol Position')
  93. ->searchable()
  94. ->boolean('Before Amount', 'After Amount', 'Select the currency symbol position...')
  95. ->required(),
  96. Forms\Components\TextInput::make('decimal_mark')
  97. ->label('Decimal Separator')
  98. ->maxLength(1)
  99. ->required(),
  100. Forms\Components\TextInput::make('thousands_separator')
  101. ->label('Thousands Separator')
  102. ->maxLength(1)
  103. ->required(),
  104. ToggleButton::make('enabled')
  105. ->label('Default Currency')
  106. ->reactive()
  107. ->offColor('danger')
  108. ->onColor('primary')
  109. ->afterStateUpdated(static function (Closure $set, Closure $get, $state) {
  110. $enabled = $state;
  111. $code = $get('code');
  112. $currencyService = app(CurrencyService::class);
  113. if ($enabled) {
  114. $rate = 1;
  115. } else {
  116. $defaultCurrency = Currency::getDefaultCurrency();
  117. $rate = $defaultCurrency ? $currencyService->getCachedExchangeRate($defaultCurrency, $code) : 1;
  118. }
  119. $set('rate', $rate);
  120. }),
  121. ])->columns(),
  122. ]);
  123. }
  124. /**
  125. * @throws Exception
  126. */
  127. public static function table(Table $table): Table
  128. {
  129. return $table
  130. ->columns([
  131. Tables\Columns\TextColumn::make('name')
  132. ->label('Name')
  133. ->weight('semibold')
  134. ->icon(static fn (Currency $record) => $record->enabled ? 'heroicon-o-lock-closed' : null)
  135. ->tooltip(static fn (Currency $record) => $record->enabled ? 'Default Currency' : null)
  136. ->iconPosition('after')
  137. ->searchable()
  138. ->sortable(),
  139. Tables\Columns\TextColumn::make('code')
  140. ->label('Code')
  141. ->searchable()
  142. ->sortable(),
  143. Tables\Columns\TextColumn::make('symbol')
  144. ->label('Symbol')
  145. ->searchable()
  146. ->sortable(),
  147. Tables\Columns\TextColumn::make('rate')
  148. ->label('Rate')
  149. ->searchable()
  150. ->sortable(),
  151. ])
  152. ->filters([
  153. //
  154. ])
  155. ->actions([
  156. Tables\Actions\EditAction::make(),
  157. Tables\Actions\DeleteAction::make()
  158. ->before(static function (Tables\Actions\DeleteAction $action, Currency $record) {
  159. $defaultCurrency = $record->enabled;
  160. $accountUsesCurrency = Account::where('currency_code', $record->code)->exists();
  161. if ($defaultCurrency) {
  162. Notification::make()
  163. ->danger()
  164. ->title('Action Denied')
  165. ->body(__('The :name currency is currently set as the default currency and cannot be deleted. Please set a different currency as your default before attempting to delete this one.', ['name' => $record->name]))
  166. ->persistent()
  167. ->send();
  168. $action->cancel();
  169. } elseif ($accountUsesCurrency) {
  170. Notification::make()
  171. ->danger()
  172. ->title('Action Denied')
  173. ->body(__('The :name currency is currently in use by one or more accounts and cannot be deleted. Please remove this currency from all accounts before attempting to delete it.', ['name' => $record->name]))
  174. ->persistent()
  175. ->send();
  176. $action->cancel();
  177. }
  178. }),
  179. ])
  180. ->bulkActions([
  181. Tables\Actions\DeleteBulkAction::make()
  182. ->before(static function (Tables\Actions\DeleteBulkAction $action, Collection $records) {
  183. foreach ($records as $record) {
  184. $defaultCurrency = $record->enabled;
  185. $accountUsesCurrency = Account::where('currency_code', $record->code)->exists();
  186. if ($defaultCurrency) {
  187. Notification::make()
  188. ->danger()
  189. ->title('Action Denied')
  190. ->body(__('The :name currency is currently set as the default currency and cannot be deleted. Please set a different currency as your default before attempting to delete this one.', ['name' => $record->name]))
  191. ->persistent()
  192. ->send();
  193. $action->cancel();
  194. } elseif ($accountUsesCurrency) {
  195. Notification::make()
  196. ->danger()
  197. ->title('Action Denied')
  198. ->body(__('The :name currency is currently in use by one or more accounts and cannot be deleted. Please remove this currency from all accounts before attempting to delete it.', ['name' => $record->name]))
  199. ->persistent()
  200. ->send();
  201. $action->cancel();
  202. }
  203. }
  204. }),
  205. ]);
  206. }
  207. public static function getRelations(): array
  208. {
  209. return [
  210. //
  211. ];
  212. }
  213. public static function getPages(): array
  214. {
  215. return [
  216. 'index' => Pages\ListCurrencies::route('/'),
  217. 'create' => Pages\CreateCurrency::route('/create'),
  218. 'edit' => Pages\EditCurrency::route('/{record}/edit'),
  219. ];
  220. }
  221. }