with(['establishment:id,name', 'machine:id,uuid,name']); $this->scopeEstablishmentQuery($query); $this->applyEstablishmentFilter($request, $query); if ($request->filled('machine_type')) { $query->where('machine_type', $request->string('machine_type')); } if ($request->filled('day_type')) { $query->where('day_type', $request->string('day_type')); } if ($request->has('is_active') && $request->string('is_active')->toString() !== '') { $query->where('is_active', $request->boolean('is_active')); } if ($request->filled('search')) { $search = $request->string('search'); $query->where('label', 'like', "%{$search}%"); } $rules = $query ->tap(fn ($builder) => $this->applySort($request, $builder, [ 'establishment' => fn ($builder, string $direction) => $this->sortByRelatedEstablishment( $builder, 'pricing_rules', 'establishment_id', $direction, ), 'slot_start' => 'slot_start', 'machine_type' => 'machine_type', 'price' => 'price', 'priority' => 'priority', 'is_active' => 'is_active', ], 'priority', 'asc')) ->paginate($this->supervisorListPerPage($request)) ->withQueryString() ->through(fn (PricingRule $rule) => $this->formatRule($rule)); return Inertia::render('Supervisor/Pricing/Index', [ 'rules' => $rules, 'establishments' => $this->establishmentOptions(), 'machineTypes' => $this->machineTypeOptions(), 'dayTypes' => $this->dayTypeOptions(), 'filters' => [ 'establishment_id' => $request->filled('establishment_id') ? (int) $request->input('establishment_id') : null, 'machine_type' => $request->string('machine_type')->toString() ?: null, 'day_type' => $request->string('day_type')->toString() ?: null, 'is_active' => $request->has('is_active') && $request->string('is_active')->toString() !== '' ? ($request->boolean('is_active') ? '1' : '0') : null, 'search' => $request->string('search')->toString() ?: null, 'sort' => $request->string('sort')->toString() ?: 'priority', 'direction' => $request->string('direction')->toString() === 'desc' ? 'desc' : 'asc', 'per_page' => $this->supervisorListPerPage($request), ], ]); } public function store(Request $request): RedirectResponse { $validated = $this->validateRule($request); $this->assertEstablishmentAccessible((int) $validated['establishment_id']); PricingRule::query()->create($validated); return redirect()->route('supervisor.pricing.index') ->with('success', 'Règle tarifaire créée.'); } public function update(Request $request, int $id): RedirectResponse { $rule = $this->findAccessibleRule($id); $validated = $this->validateRule($request); $this->assertEstablishmentAccessible((int) $validated['establishment_id']); $rule->update($validated); return redirect()->route('supervisor.pricing.index') ->with('success', 'Règle tarifaire mise à jour.'); } public function destroy(int $id): RedirectResponse { $rule = $this->findAccessibleRule($id); $rule->delete(); return redirect()->route('supervisor.pricing.index') ->with('success', 'Règle tarifaire supprimée.'); } /** * @return array */ private function validateRule(Request $request): array { return $request->validate([ 'establishment_id' => ['required', 'integer', 'exists:establishments,id'], 'machine_id' => ['nullable', 'integer', 'exists:machines,id'], 'machine_type' => ['nullable', 'in:washer_small,washer_large,dryer_small,dryer_large'], 'day_type' => ['required', 'in:weekday,weekend,holiday,all'], 'slot_start' => ['required', 'date_format:H:i'], 'slot_end' => ['required', 'date_format:H:i', 'after:slot_start'], 'price' => ['required', 'numeric', 'min:0'], 'label' => ['nullable', 'string', 'max:100'], 'requires_app' => ['boolean'], 'priority' => ['integer', 'min:0'], 'is_active' => ['boolean'], ]); } private function findAccessibleRule(int $id): PricingRule { $query = PricingRule::query()->where('id', $id); $this->scopeEstablishmentQuery($query); return $query->firstOrFail(); } private function assertEstablishmentAccessible(int $establishmentId): void { $supervisor = $this->supervisor(); $exists = Establishment::query() ->where('id', $establishmentId) ->where('organization_id', $supervisor->organization_id) ->when($supervisor->establishment_id, fn ($q) => $q->where('id', $supervisor->establishment_id)) ->exists(); abort_unless($exists, 403); } /** * @return array */ private function formatRule(PricingRule $rule): array { return [ 'id' => $rule->id, 'establishment_id' => $rule->establishment_id, 'establishment_name' => $rule->establishment?->name, 'machine_id' => $rule->machine_id, 'machine_name' => $rule->machine?->name, 'machine_type' => $rule->machine_type, 'machine_type_label' => $rule->machine_type ? ($this->machineTypeOptions()[$rule->machine_type] ?? $rule->machine_type) : null, 'day_type' => $rule->day_type, 'day_type_label' => $this->dayTypeOptions()[$rule->day_type] ?? $rule->day_type, 'slot_start' => $rule->slot_start ? substr((string) $rule->slot_start, 0, 5) : null, 'slot_end' => $rule->slot_end ? substr((string) $rule->slot_end, 0, 5) : null, 'price' => (float) $rule->price, 'label' => $rule->label, 'requires_app' => $rule->requires_app, 'priority' => $rule->priority, 'is_active' => $rule->is_active, ]; } /** * @return array */ private function machineTypeOptions(): array { return [ 'washer_small' => 'Lave-linge petit', 'washer_large' => 'Lave-linge grand', 'dryer_small' => 'Sèche-linge petit', 'dryer_large' => 'Sèche-linge grand', ]; } /** * @return array */ private function dayTypeOptions(): array { return [ 'all' => 'Tous les jours', 'weekday' => 'Semaine', 'weekend' => 'Week-end', 'holiday' => 'Jours fériés', ]; } }