mein-sterntours/app/Http/Controllers/OfferController.php

184 lines
6.1 KiB
PHP

<?php
namespace App\Http\Controllers;
use App\Models\Offer;
use App\Models\OfferVersion;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class OfferController extends Controller
{
public function __construct()
{
$this->middleware(['admin', '2fa']);
}
// ── Permissions: offers-r = Routing (Lesen), fein: offers-w, offers-send, offers-accept
public function hasOfferPermission(string $key): bool
{
$user = auth()->user();
if (! $user) {
return false;
}
return $user->isPermission($key);
}
public function assertOfferPermission(string $key): void
{
if (! $this->hasOfferPermission($key)) {
abort(Response::HTTP_FORBIDDEN, 'Fehlende Berechtigung: ' . $key);
}
}
public function index($step = false)
{
return view('offer.index', [
'step' => $step,
]);
}
public function getOffers()
{
$query = Offer::query()
->with(['createdBy', 'contact'])
->select('offers.*');
return \DataTables::eloquent($query)
->addColumn('action_edit', function (Offer $offer) {
return '<a href="' . e(route('offer_detail', [$offer->id])) . '" class="btn icon-btn btn-sm btn-primary" title="Öffnen">'
. '<span class="fa fa-edit"></span></a>';
})
->addColumn('id', function (Offer $offer) {
return '<a data-order="' . (int) $offer->id . '" href="'
. e(route('offer_detail', [$offer->id])) . '" data-id="' . (int) $offer->id . '">'
. (int) $offer->id . '</a>';
})
->addColumn('offer_number', function (Offer $offer) {
return e($offer->offer_number);
})
->addColumn('contact_name', function (Offer $offer) {
$c = $offer->contact;
if (! $c) {
return '';
}
return e(trim(($c->firstname ?? '') . ' ' . ($c->name ?? '')));
})
->addColumn('status_badge', function (Offer $offer) {
return '<span class="badge badge-secondary">' . e($offer->status) . '</span>';
})
->addColumn('created_name', function (Offer $offer) {
return e($offer->createdBy->name ?? '');
})
->addColumn('created_at_fmt', function (Offer $offer) {
if (! $offer->created_at) {
return '';
}
return e($offer->created_at->format(\Util::formatDateTimeDB()));
})
->orderColumn('id', 'offers.id $1')
->orderColumn('offer_number', 'offers.offer_number $1')
->rawColumns(['action_edit', 'id', 'status_badge'])
->make(true);
}
public function detail($id)
{
if ($id === 'new') {
abort(404, 'Anlage über Modal folgt in B2.');
}
$offer = Offer::query()
->with(['currentVersion.items', 'contact', 'inquiry', 'createdBy'])
->findOrFail($id);
return view('offer.detail', [
'offer' => $offer,
'id' => $offer->id,
]);
}
public function store(Request $request, $id)
{
$this->assertOfferPermission('offers-w');
if (! $request->has('action')) {
abort(403, 'keine Action');
}
// B3: Angebots-Editor, Autosave, …
\Session::flash('alert-info', 'Speichern (Stub A6) — B3 liefert die Logik.');
return redirect()->to(route('offer_detail', [$id]) . (string) $request->get('fragment', ''));
}
public function loadModal(Request $request)
{
$data = $request->all();
$html = '';
if (($data['action'] ?? null) === 'modal-new-offer') {
$html = view('offer.modal_new_offer_stub', ['data' => $data])->render();
}
if ($request->ajax() || $request->wantsJson()) {
return response()->json(['html' => $html, 'response' => $data]);
}
return response($html);
}
public function action(Request $request, $action, $id = null)
{
$wActions = [
'create', 'update', 'supersede', 'duplicate', 'delete-file',
];
$sendActions = ['send', 'resend'];
$acceptActions = ['markAccepted', 'markDeclined', 'withdraw', 'mark_accepted', 'mark_declined'];
if (in_array($action, $wActions, true)) {
$this->assertOfferPermission('offers-w');
}
if (in_array($action, $sendActions, true)) {
$this->assertOfferPermission('offers-send');
}
if (in_array($action, $acceptActions, true)) {
$this->assertOfferPermission('offers-accept');
}
$any = array_merge($wActions, $sendActions, $acceptActions);
if (! in_array($action, $any, true)) {
$this->assertOfferPermission('offers-w');
}
if ($id === null) {
abort(404);
}
if ($id !== 'new') {
$offer = Offer::find($id);
if (! $offer) {
abort(404);
}
} else {
$offer = null;
}
\Session::flash('alert-info', 'Aktion «' . e($action) . '» (Stub A6) — siehe B2/B3.');
if ($offer) {
return redirect()->route('offer_detail', [$offer->id]);
}
return redirect()->route('offers');
}
public function delete($id, $del = null)
{
$this->assertOfferPermission('offers-w');
\Session::flash('alert-info', 'Löschen (Stub) — B9 liefert Soft-Delete-Logik.');
return redirect()->route('offers');
}
/**
* PDF-Generierung: Stub (Ticket B5 liefert den Stream).
*/
public function pdf($versionId, $do = null)
{
$version = OfferVersion::query()->with('offer')->findOrFail($versionId);
return response('PDF-Generator folgt in B5 (OfferVersion #' . (int) $version->id . ').', 200, [
'Content-Type' => 'text/plain; charset=UTF-8',
]);
}
}