Allow cancelling and deleting pending POS sales
Deploy Ladill POS / deploy (push) Successful in 37s

On a sale's detail page, unpaid sales (retail or restaurant) can now be
cancelled or deleted:
- Cancel (pending only) marks it cancelled and frees its table / clears it from
  the kitchen (PosSaleService::cancelSale).
- Delete removes the sale and cascades its lines, modifiers and payments, freeing
  the table first (deleteSale). Paid sales are protected — they can't be deleted.
Status badge now distinguishes cancelled/failed. Covered by PosRestaurantTest.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
isaacclad
2026-06-25 21:52:51 +00:00
co-authored by Claude Opus 4.8
parent 5d8e185223
commit f64a28cb21
5 changed files with 112 additions and 1 deletions
@@ -44,6 +44,32 @@ class SaleController extends Controller
return view('pos.sales.show', compact('sale', 'invoiceUrl'));
}
public function cancel(Request $request, PosSale $sale): RedirectResponse
{
$this->authorizeOwner($request, $sale);
try {
$this->sales->cancelSale($sale);
} catch (RuntimeException $e) {
return back()->with('error', $e->getMessage());
}
return redirect()->route('pos.sales.show', $sale)->with('success', 'Sale cancelled.');
}
public function destroy(Request $request, PosSale $sale): RedirectResponse
{
$this->authorizeOwner($request, $sale);
try {
$this->sales->deleteSale($sale);
} catch (RuntimeException $e) {
return back()->with('error', $e->getMessage());
}
return redirect()->route('pos.sales.index')->with('success', 'Sale deleted.');
}
public function callback(Request $request, PosSale $sale): RedirectResponse
{
$reference = trim((string) $request->query('reference', $sale->payment_reference ?? ''));