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

OfferingResource.php 8.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. <?php
  2. namespace App\Filament\Company\Resources\Common;
  3. use App\Enums\Accounting\AccountCategory;
  4. use App\Enums\Accounting\AccountType;
  5. use App\Enums\Accounting\AdjustmentCategory;
  6. use App\Enums\Accounting\AdjustmentType;
  7. use App\Enums\Common\OfferingType;
  8. use App\Filament\Company\Resources\Common\OfferingResource\Pages;
  9. use App\Filament\Forms\Components\Banner;
  10. use App\Filament\Forms\Components\CreateAccountSelect;
  11. use App\Filament\Forms\Components\CreateAdjustmentSelect;
  12. use App\Models\Common\Offering;
  13. use App\Utilities\Currency\CurrencyAccessor;
  14. use Filament\Forms;
  15. use Filament\Forms\Form;
  16. use Filament\Resources\Resource;
  17. use Filament\Tables;
  18. use Filament\Tables\Table;
  19. use Illuminate\Database\Eloquent\Builder;
  20. use Illuminate\Support\HtmlString;
  21. use Illuminate\Support\Str;
  22. use JaOcero\RadioDeck\Forms\Components\RadioDeck;
  23. class OfferingResource extends Resource
  24. {
  25. protected static ?string $model = Offering::class;
  26. protected static ?string $navigationIcon = 'heroicon-o-square-3-stack-3d';
  27. public static function form(Form $form): Form
  28. {
  29. return $form
  30. ->schema([
  31. Banner::make('inactiveAdjustments')
  32. ->label('Inactive adjustments')
  33. ->warning()
  34. ->icon('heroicon-o-exclamation-triangle')
  35. ->visible(fn (?Offering $record) => $record?->hasInactiveAdjustments())
  36. ->columnSpanFull()
  37. ->description(function (Offering $record) {
  38. $inactiveAdjustments = collect();
  39. foreach ($record->adjustments as $adjustment) {
  40. if ($adjustment->isInactive() && $inactiveAdjustments->doesntContain($adjustment->name)) {
  41. $inactiveAdjustments->push($adjustment->name);
  42. }
  43. }
  44. $adjustmentsList = $inactiveAdjustments->map(static function ($name) {
  45. return "<span class='font-medium'>{$name}</span>";
  46. })->join(', ');
  47. $output = "<p class='text-sm'>This offering contains inactive adjustments that need to be addressed: {$adjustmentsList}</p>";
  48. return new HtmlString($output);
  49. }),
  50. Forms\Components\Section::make('General')
  51. ->schema([
  52. RadioDeck::make('type')
  53. ->options(OfferingType::class)
  54. ->default(OfferingType::Product)
  55. ->icons(OfferingType::class)
  56. ->color('primary')
  57. ->columns()
  58. ->required(),
  59. Forms\Components\TextInput::make('name')
  60. ->autofocus()
  61. ->required()
  62. ->columnStart(1)
  63. ->maxLength(255),
  64. Forms\Components\TextInput::make('price')
  65. ->required()
  66. ->money(),
  67. Forms\Components\Textarea::make('description')
  68. ->label('Description')
  69. ->columnSpan(2)
  70. ->rows(3),
  71. Forms\Components\CheckboxList::make('attributes')
  72. ->options([
  73. 'Sellable' => 'Sellable',
  74. 'Purchasable' => 'Purchasable',
  75. ])
  76. ->hiddenLabel()
  77. ->required()
  78. ->live()
  79. ->bulkToggleable()
  80. ->validationMessages([
  81. 'required' => 'The offering must be either sellable or purchasable.',
  82. ]),
  83. ])->columns(),
  84. // Sellable Section
  85. Forms\Components\Section::make('Sale Information')
  86. ->schema([
  87. CreateAccountSelect::make('income_account_id')
  88. ->label('Income account')
  89. ->category(AccountCategory::Revenue)
  90. ->type(AccountType::OperatingRevenue)
  91. ->required()
  92. ->validationMessages([
  93. 'required' => 'The income account is required for sellable offerings.',
  94. ]),
  95. CreateAdjustmentSelect::make('salesTaxes')
  96. ->label('Sales tax')
  97. ->category(AdjustmentCategory::Tax)
  98. ->type(AdjustmentType::Sales)
  99. ->multiple(),
  100. CreateAdjustmentSelect::make('salesDiscounts')
  101. ->label('Sales discount')
  102. ->category(AdjustmentCategory::Discount)
  103. ->type(AdjustmentType::Sales)
  104. ->multiple(),
  105. ])
  106. ->columns()
  107. ->visible(static fn (Forms\Get $get) => in_array('Sellable', $get('attributes') ?? [])),
  108. // Purchasable Section
  109. Forms\Components\Section::make('Purchase Information')
  110. ->schema([
  111. CreateAccountSelect::make('expense_account_id')
  112. ->label('Expense account')
  113. ->category(AccountCategory::Expense)
  114. ->type(AccountType::OperatingExpense)
  115. ->required()
  116. ->validationMessages([
  117. 'required' => 'The expense account is required for purchasable offerings.',
  118. ]),
  119. CreateAdjustmentSelect::make('purchaseTaxes')
  120. ->label('Purchase tax')
  121. ->category(AdjustmentCategory::Tax)
  122. ->type(AdjustmentType::Purchase)
  123. ->multiple(),
  124. CreateAdjustmentSelect::make('purchaseDiscounts')
  125. ->label('Purchase discount')
  126. ->category(AdjustmentCategory::Discount)
  127. ->type(AdjustmentType::Purchase)
  128. ->multiple(),
  129. ])
  130. ->columns()
  131. ->visible(static fn (Forms\Get $get) => in_array('Purchasable', $get('attributes') ?? [])),
  132. ])->columns();
  133. }
  134. public static function table(Table $table): Table
  135. {
  136. return $table
  137. ->modifyQueryUsing(function (Builder $query) {
  138. $query->selectRaw("
  139. *,
  140. CONCAT_WS(' & ',
  141. CASE WHEN sellable THEN 'Sellable' END,
  142. CASE WHEN purchasable THEN 'Purchasable' END
  143. ) AS attributes
  144. ");
  145. })
  146. ->columns([
  147. Tables\Columns\TextColumn::make('name')
  148. ->label('Name'),
  149. Tables\Columns\TextColumn::make('attributes')
  150. ->label('Attributes')
  151. ->badge(),
  152. Tables\Columns\TextColumn::make('type')
  153. ->searchable(),
  154. Tables\Columns\TextColumn::make('price')
  155. ->currency(CurrencyAccessor::getDefaultCurrency(), true)
  156. ->sortable()
  157. ->description(function (Offering $record) {
  158. $adjustments = $record->adjustments()
  159. ->pluck('name')
  160. ->join(', ');
  161. if (empty($adjustments)) {
  162. return null;
  163. }
  164. $adjustmentsList = Str::of($adjustments)->limit(40);
  165. return "+ {$adjustmentsList}";
  166. }),
  167. ])
  168. ->filters([
  169. //
  170. ])
  171. ->actions([
  172. Tables\Actions\EditAction::make(),
  173. ])
  174. ->bulkActions([
  175. Tables\Actions\BulkActionGroup::make([
  176. Tables\Actions\DeleteBulkAction::make(),
  177. ]),
  178. ]);
  179. }
  180. public static function getRelations(): array
  181. {
  182. return [
  183. //
  184. ];
  185. }
  186. public static function getPages(): array
  187. {
  188. return [
  189. 'index' => Pages\ListOfferings::route('/'),
  190. 'create' => Pages\CreateOffering::route('/create'),
  191. 'edit' => Pages\EditOffering::route('/{record}/edit'),
  192. ];
  193. }
  194. }