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

CustomerResource.php 11KB

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