Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

OfferingResource.php 8.6KB

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