1+ <?php
2+
3+ namespace App \Filament \Resources ;
4+
5+ use App \Domain \Checkout \Models \Checkout \Checkout ;
6+ use App \Domain \Checkout \Models \Checkout \States \Active ;
7+ use App \Domain \Checkout \Models \Checkout \States \Cancelled ;
8+ use App \Domain \Checkout \Models \Checkout \States \Finished ;
9+ use App \Filament \Resources \CheckoutResource \Pages ;
10+ use App \Filament \Resources \CheckoutResource \RelationManagers ;
11+ use Filament \Forms ;
12+ use Filament \Forms \Form ;
13+ use Filament \Resources \Resource ;
14+ use Filament \Tables ;
15+ use Filament \Tables \Table ;
16+ use Illuminate \Database \Eloquent \Builder ;
17+ use Illuminate \Database \Eloquent \Model ;
18+ use Illuminate \Support \HtmlString ;
19+
20+ class CheckoutResource extends Resource
21+ {
22+ protected static ?string $ model = Checkout::class;
23+
24+ protected static ?string $ navigationIcon = 'heroicon-o-shopping-cart ' ;
25+
26+ protected static ?string $ navigationGroup = 'Sales ' ;
27+
28+ protected static ?int $ navigationSort = 10 ;
29+
30+ public static function form (Form $ form ): Form
31+ {
32+ return $ form
33+ ->schema ([
34+ Forms \Components \Section::make ('Checkout Information ' )
35+ ->schema ([
36+ Forms \Components \TextInput::make ('remote_id ' )
37+ ->label ('Remote ID ' )
38+ ->disabled (),
39+
40+ Forms \Components \Select::make ('user_id ' )
41+ ->label ('Customer ' )
42+ ->relationship ('user ' , 'name ' )
43+ ->disabled ()
44+ ->searchable (),
45+
46+ Forms \Components \Select::make ('cashier_id ' )
47+ ->label ('Cashier ' )
48+ ->relationship ('cashier ' , 'name ' )
49+ ->disabled (),
50+
51+ Forms \Components \Select::make ('machine_id ' )
52+ ->label ('Machine ' )
53+ ->relationship ('machine ' , 'name ' )
54+ ->disabled (),
55+
56+ Forms \Components \TextInput::make ('status ' )
57+ ->disabled (),
58+
59+ Forms \Components \TextInput::make ('payment_method ' )
60+ ->disabled (),
61+ ])
62+ ->columns (2 ),
63+
64+ Forms \Components \Section::make ('Financial Details ' )
65+ ->schema ([
66+ Forms \Components \TextInput::make ('subtotal ' )
67+ ->prefix ('€ ' )
68+ ->numeric ()
69+ ->disabled (),
70+
71+ Forms \Components \TextInput::make ('tax ' )
72+ ->prefix ('€ ' )
73+ ->numeric ()
74+ ->disabled (),
75+
76+ Forms \Components \TextInput::make ('total ' )
77+ ->prefix ('€ ' )
78+ ->numeric ()
79+ ->disabled (),
80+ ])
81+ ->columns (3 ),
82+
83+ Forms \Components \Section::make ('TSE Information ' )
84+ ->schema ([
85+ Forms \Components \DateTimePicker::make ('tse_start_timestamp ' )
86+ ->label ('TSE Start ' )
87+ ->disabled (),
88+
89+ Forms \Components \DateTimePicker::make ('tse_end_timestamp ' )
90+ ->label ('TSE End ' )
91+ ->disabled (),
92+
93+ Forms \Components \TextInput::make ('tse_signature ' )
94+ ->label ('TSE Signature ' )
95+ ->disabled ()
96+ ->columnSpanFull (),
97+ ])
98+ ->columns (2 )
99+ ->collapsed (),
100+
101+ Forms \Components \Section::make ('Timestamps ' )
102+ ->schema ([
103+ Forms \Components \DateTimePicker::make ('created_at ' )
104+ ->disabled (),
105+
106+ Forms \Components \DateTimePicker::make ('updated_at ' )
107+ ->disabled (),
108+ ])
109+ ->columns (2 )
110+ ->collapsed (),
111+ ]);
112+ }
113+
114+ public static function table (Table $ table ): Table
115+ {
116+ return $ table
117+ ->columns ([
118+ Tables \Columns \TextColumn::make ('id ' )
119+ ->label ('ID ' )
120+ ->searchable ()
121+ ->sortable (),
122+
123+ Tables \Columns \TextColumn::make ('user.name ' )
124+ ->label ('Customer ' )
125+ ->searchable ()
126+ ->sortable ()
127+ ->url (fn ($ record ) => $ record ->user ? UserResource::getUrl ('index ' ) . '?tableSearch= ' . urlencode ($ record ->user ->name ) : null ),
128+
129+ Tables \Columns \TextColumn::make ('cashier.name ' )
130+ ->label ('Cashier ' )
131+ ->searchable ()
132+ ->sortable ()
133+ ->default ('- ' ),
134+
135+ Tables \Columns \TextColumn::make ('machine.name ' )
136+ ->label ('Machine ' )
137+ ->searchable ()
138+ ->sortable ()
139+ ->toggleable (),
140+
141+ Tables \Columns \TextColumn::make ('status ' )
142+ ->badge ()
143+ ->formatStateUsing (fn ($ state ) => class_basename ($ state ))
144+ ->color (fn ($ state ) => match (true ) {
145+ $ state instanceof Finished => 'success ' ,
146+ $ state instanceof Active => 'warning ' ,
147+ $ state instanceof Cancelled => 'danger ' ,
148+ default => 'gray ' ,
149+ }),
150+
151+ Tables \Columns \TextColumn::make ('payment_method ' )
152+ ->badge ()
153+ ->color (fn ($ state ) => match ($ state ) {
154+ 'cash ' => 'success ' ,
155+ 'card ' => 'info ' ,
156+ default => 'gray ' ,
157+ }),
158+
159+ Tables \Columns \TextColumn::make ('total ' )
160+ ->money ('EUR ' , divideBy: 100 )
161+ ->sortable ()
162+ ->summarize (Tables \Columns \Summarizers \Sum::make ()
163+ ->money ('EUR ' , divideBy: 100 )
164+ ->label ('Total ' )),
165+
166+ Tables \Columns \TextColumn::make ('items_count ' )
167+ ->counts ('items ' )
168+ ->label ('Items ' ),
169+
170+ Tables \Columns \TextColumn::make ('created_at ' )
171+ ->dateTime ()
172+ ->sortable ()
173+ ->toggleable (),
174+ ])
175+ ->defaultSort ('created_at ' , 'desc ' )
176+ ->filters ([
177+ Tables \Filters \SelectFilter::make ('status ' )
178+ ->options ([
179+ Active::class => 'Active ' ,
180+ Finished::class => 'Finished ' ,
181+ Cancelled::class => 'Cancelled ' ,
182+ ])
183+ ->multiple (),
184+
185+ Tables \Filters \SelectFilter::make ('payment_method ' )
186+ ->options ([
187+ 'cash ' => 'Cash ' ,
188+ 'card ' => 'Card ' ,
189+ ]),
190+
191+ Tables \Filters \SelectFilter::make ('machine_id ' )
192+ ->label ('Machine ' )
193+ ->relationship ('machine ' , 'name ' ),
194+
195+ Tables \Filters \Filter::make ('created_at ' )
196+ ->form ([
197+ Forms \Components \DatePicker::make ('created_from ' ),
198+ Forms \Components \DatePicker::make ('created_until ' ),
199+ ])
200+ ->query (function (Builder $ query , array $ data ): Builder {
201+ return $ query
202+ ->when (
203+ $ data ['created_from ' ],
204+ fn (Builder $ query , $ date ): Builder => $ query ->whereDate ('created_at ' , '>= ' , $ date ),
205+ )
206+ ->when (
207+ $ data ['created_until ' ],
208+ fn (Builder $ query , $ date ): Builder => $ query ->whereDate ('created_at ' , '<= ' , $ date ),
209+ );
210+ })
211+ ])
212+ ->actions ([
213+ Tables \Actions \ViewAction::make (),
214+ Tables \Actions \Action::make ('receipt ' )
215+ ->label ('Receipt ' )
216+ ->icon ('heroicon-o-document-text ' )
217+ ->color ('gray ' )
218+ ->url (fn (Checkout $ record ): string => route ('pos.checkout.receipt ' , $ record ))
219+ ->openUrlInNewTab (),
220+ Tables \Actions \Action::make ('print ' )
221+ ->label ('Print ' )
222+ ->icon ('heroicon-o-printer ' )
223+ ->color ('info ' )
224+ ->action (function (Checkout $ record ) {
225+ // Generate receipt PDF
226+ \App \Jobs \CreateReceiptFromCheckoutJob::dispatchSync ($ record );
227+
228+ // Find active receipt printer
229+ $ receiptPrinter = \App \Domain \Printing \Models \Printer::where ('is_active ' , true )
230+ ->where ('type ' , 'receipt ' )
231+ ->first ();
232+
233+ if (!$ receiptPrinter ) {
234+ \Filament \Notifications \Notification::make ()
235+ ->title ('No receipt printer found ' )
236+ ->body ('Please configure an active receipt printer first. ' )
237+ ->danger ()
238+ ->send ();
239+ return ;
240+ }
241+
242+ // Create print job
243+ $ record ->printJobs ()->create ([
244+ 'printer_id ' => $ receiptPrinter ->id ,
245+ 'type ' => 'receipt ' ,
246+ 'file ' => 'checkouts/ ' .$ record ->id .'.pdf ' ,
247+ 'status ' => \App \Enum \PrintJobStatusEnum::Pending,
248+ ]);
249+
250+ \Filament \Notifications \Notification::make ()
251+ ->title ('Receipt added to print queue ' )
252+ ->body ("Receipt for checkout # {$ record ->id } has been queued for printing. " )
253+ ->success ()
254+ ->send ();
255+ })
256+ ->requiresConfirmation ()
257+ ->modalHeading ('Print Receipt ' )
258+ ->modalDescription ('This will add the receipt to the print queue. ' ),
259+ ])
260+ ->bulkActions ([
261+ // No bulk actions for checkouts
262+ ]);
263+ }
264+
265+ public static function getRelations (): array
266+ {
267+ return [
268+ RelationManagers \ItemsRelationManager::class,
269+ ];
270+ }
271+
272+ public static function getPages (): array
273+ {
274+ return [
275+ 'index ' => Pages \ListCheckouts::route ('/ ' ),
276+ 'view ' => Pages \ViewCheckout::route ('/{record} ' ),
277+ ];
278+ }
279+
280+ public static function canCreate (): bool
281+ {
282+ return false ; // Checkouts are created through POS only
283+ }
284+
285+ public static function canEdit (Model $ record ): bool
286+ {
287+ return false ; // Checkouts should not be edited
288+ }
289+
290+ public static function canDelete (Model $ record ): bool
291+ {
292+ return false ; // Checkouts should not be deleted
293+ }
294+ }
0 commit comments