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

CustomerResource.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. <?php
  2. namespace App\Filament\Resources;
  3. use App\Actions\OptionAction\CreateCurrency;
  4. use App\Filament\Resources\CustomerResource\Pages;
  5. use App\Filament\Resources\CustomerResource\RelationManagers;
  6. use App\Models\Setting\Currency;
  7. use App\Services\CurrencyService;
  8. use Wallo\FilamentSelectify\Components\ButtonGroup;
  9. use App\Models\Contact;
  10. use Filament\Forms;
  11. use Filament\Resources\Form;
  12. use Filament\Resources\Resource;
  13. use Filament\Resources\Table;
  14. use Filament\Tables;
  15. use Illuminate\Database\Eloquent\Builder;
  16. use Illuminate\Support\Facades\Auth;
  17. use Illuminate\Support\Facades\DB;
  18. class CustomerResource extends Resource
  19. {
  20. protected static ?string $model = Contact::class;
  21. protected static ?string $navigationIcon = 'heroicon-o-user-group';
  22. protected static ?string $navigationGroup = 'Sales';
  23. protected static ?string $navigationLabel = 'Customers';
  24. protected static ?string $modelLabel = 'customer';
  25. public static function getEloquentQuery(): Builder
  26. {
  27. return parent::getEloquentQuery()
  28. ->customer()
  29. ->where('company_id', Auth::user()->currentCompany->id);
  30. }
  31. public static function form(Form $form): Form
  32. {
  33. return $form
  34. ->schema([
  35. Forms\Components\Section::make('General')
  36. ->schema([
  37. Forms\Components\Grid::make(3)
  38. ->schema([
  39. ButtonGroup::make('entity')
  40. ->label('Entity')
  41. ->options([
  42. 'individual' => 'Individual',
  43. 'company' => 'Company',
  44. ])
  45. ->gridDirection('column')
  46. ->default('individual')
  47. ->columnSpan(1),
  48. Forms\Components\Grid::make()
  49. ->schema([
  50. Forms\Components\TextInput::make('name')
  51. ->label('Name')
  52. ->maxLength(100)
  53. ->required(),
  54. Forms\Components\TextInput::make('email')
  55. ->label('Email')
  56. ->email()
  57. ->nullable(),
  58. Forms\Components\TextInput::make('phone')
  59. ->label('Phone')
  60. ->tel()
  61. ->maxLength(20),
  62. Forms\Components\TextInput::make('website')
  63. ->label('Website')
  64. ->maxLength(100)
  65. ->url()
  66. ->nullable(),
  67. Forms\Components\TextInput::make('reference')
  68. ->label('Reference')
  69. ->maxLength(100)
  70. ->columnSpan(2)
  71. ->nullable(),
  72. ])->columnSpan(2),
  73. ]),
  74. ])->columns(),
  75. Forms\Components\Section::make('Billing')
  76. ->schema([
  77. Forms\Components\TextInput::make('tax_number')
  78. ->label('Tax Number')
  79. ->maxLength(100)
  80. ->nullable(),
  81. Forms\Components\Select::make('currency_code')
  82. ->label('Currency')
  83. ->relationship('currency', 'name', static fn (Builder $query) => $query->where('company_id', Auth::user()->currentCompany->id))
  84. ->preload()
  85. ->default(Currency::getDefaultCurrency())
  86. ->searchable()
  87. ->reactive()
  88. ->required()
  89. ->createOptionForm([
  90. Forms\Components\Select::make('currency.code')
  91. ->label('Code')
  92. ->searchable()
  93. ->options(Currency::getCurrencyCodes())
  94. ->reactive()
  95. ->afterStateUpdated(static function (callable $set, $state) {
  96. if ($state === null) {
  97. return;
  98. }
  99. $code = $state;
  100. $currencyConfig = config("money.{$code}", []);
  101. $currencyService = app(CurrencyService::class);
  102. $defaultCurrency = Currency::getDefaultCurrency();
  103. $rate = 1;
  104. if ($defaultCurrency !== null) {
  105. $rate = $currencyService->getCachedExchangeRate($defaultCurrency, $code);
  106. }
  107. $set('currency.name', $currencyConfig['name'] ?? '');
  108. $set('currency.rate', $rate);
  109. })
  110. ->required(),
  111. Forms\Components\TextInput::make('currency.name')
  112. ->label('Name')
  113. ->maxLength(100)
  114. ->required(),
  115. Forms\Components\TextInput::make('currency.rate')
  116. ->label('Rate')
  117. ->numeric()
  118. ->required(),
  119. ])->createOptionAction(static function (Forms\Components\Actions\Action $action) {
  120. return $action
  121. ->label('Add Currency')
  122. ->modalHeading('Add Currency')
  123. ->modalButton('Add')
  124. ->action(static function (array $data) {
  125. return DB::transaction(static function () use ($data) {
  126. $code = $data['currency']['code'];
  127. $name = $data['currency']['name'];
  128. $rate = $data['currency']['rate'];
  129. return (new CreateCurrency())->create($code, $name, $rate);
  130. });
  131. });
  132. }),
  133. ])->columns(),
  134. Forms\Components\Section::make('Address')
  135. ->schema([
  136. Forms\Components\TextInput::make('address')
  137. ->label('Address')
  138. ->maxLength(100)
  139. ->columnSpanFull()
  140. ->nullable(),
  141. Forms\Components\Select::make('country')
  142. ->label('Country')
  143. ->searchable()
  144. ->reactive()
  145. ->options(Contact::getCountryOptions())
  146. ->nullable(),
  147. Forms\Components\Select::make('doesnt_exist') // TODO: Remove this when we have a better way to handle the searchable select when disabled
  148. ->label('Province/State')
  149. ->disabled()
  150. ->hidden(static fn (callable $get) => $get('country') !== null),
  151. Forms\Components\Select::make('state')
  152. ->label('Province/State')
  153. ->hidden(static fn (callable $get) => $get('country') === null)
  154. ->options(static function (callable $get) {
  155. $country = $get('country');
  156. if (! $country) {
  157. return [];
  158. }
  159. return Contact::getRegionOptions($country);
  160. })
  161. ->searchable()
  162. ->nullable(),
  163. Forms\Components\TextInput::make('city')
  164. ->label('Town/City')
  165. ->maxLength(100)
  166. ->nullable(),
  167. Forms\Components\TextInput::make('zip_code')
  168. ->label('Postal/Zip Code')
  169. ->maxLength(100)
  170. ->nullable(),
  171. ])->columns(),
  172. ]);
  173. }
  174. public static function table(Table $table): Table
  175. {
  176. return $table
  177. ->columns([
  178. Tables\Columns\TextColumn::make('name')
  179. ->label('Name')
  180. ->weight('semibold')
  181. ->description(static fn (Contact $record) => $record->tax_number ?: 'N/A')
  182. ->searchable()
  183. ->sortable(),
  184. Tables\Columns\TextColumn::make('email')
  185. ->label('Email')
  186. ->formatStateUsing(static fn (Contact $record) => $record->email ?: 'N/A')
  187. ->description(static fn (Contact $record) => $record->phone ?: 'N/A')
  188. ->searchable()
  189. ->sortable(),
  190. Tables\Columns\TextColumn::make('country')
  191. ->label('Country')
  192. ->searchable()
  193. ->formatStateUsing(static fn (Contact $record) => $record->country ?: 'N/A')
  194. ->description(static fn (Contact $record) => $record->currency->name ?: 'N/A')
  195. ->sortable(),
  196. ])
  197. ->filters([
  198. //
  199. ])
  200. ->actions([
  201. Tables\Actions\EditAction::make(),
  202. ])
  203. ->bulkActions([
  204. Tables\Actions\DeleteBulkAction::make(),
  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\ListCustomers::route('/'),
  217. 'create' => Pages\CreateCustomer::route('/create'),
  218. 'edit' => Pages\EditCustomer::route('/{record}/edit'),
  219. ];
  220. }
  221. }