Email digital receipts, add promo settings, and apply tips at charge.
Deploy Ladill POS / deploy (push) Successful in 31s

Send real receipt emails from the customer display and sale page, store
branch promo content for the idle screen, and fold selected tips into totals.
This commit is contained in:
isaacclad
2026-07-15 16:10:20 +00:00
parent 468346b183
commit 600aedb59d
20 changed files with 675 additions and 19 deletions
@@ -4,15 +4,21 @@ namespace App\Http\Controllers\Public;
use App\Http\Controllers\Controller;
use App\Models\PosCustomerDisplay;
use App\Models\PosSale;
use App\Services\Pos\CustomerDisplayService;
use App\Services\Pos\ReceiptEmailService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
use RuntimeException;
use Symfony\Component\HttpFoundation\StreamedResponse;
class CustomerDisplayController extends Controller
{
public function __construct(private CustomerDisplayService $displays) {}
public function __construct(
private CustomerDisplayService $displays,
private ReceiptEmailService $receiptEmail,
) {}
public function show(string $token): View
{
@@ -122,6 +128,9 @@ class CustomerDisplayController extends Controller
if ($data['type'] === 'receipt_email') {
$action['email'] = $data['email'] ?? null;
$emailResult = $this->emailReceiptForDisplay($display, (string) ($data['email'] ?? ''));
$action['email_sent'] = $emailResult['ok'];
$action['email_message'] = $emailResult['message'];
}
if ($data['type'] === 'payment_option') {
@@ -133,9 +142,43 @@ class CustomerDisplayController extends Controller
return response()->json([
'ok' => true,
'state' => $this->displays->publicState($display->fresh()),
'email_sent' => $action['email_sent'] ?? null,
'message' => $action['email_message'] ?? null,
]);
}
/** @return array{ok: bool, message: string} */
private function emailReceiptForDisplay(PosCustomerDisplay $display, string $email): array
{
$payload = is_array($display->payload) ? $display->payload : [];
$saleId = (int) ($payload['sale_id'] ?? $payload['receipt']['sale_id'] ?? 0);
$reference = (string) ($payload['sale_reference'] ?? $payload['receipt']['reference'] ?? '');
$sale = null;
if ($saleId > 0) {
$sale = PosSale::query()
->where('id', $saleId)
->where('location_id', $display->location_id)
->first();
}
if (! $sale && $reference !== '') {
$sale = PosSale::query()
->where('reference', $reference)
->where('location_id', $display->location_id)
->first();
}
if (! $sale) {
return ['ok' => false, 'message' => 'No receipt is ready to email yet.'];
}
try {
return $this->receiptEmail->send($sale, $email);
} catch (RuntimeException $e) {
return ['ok' => false, 'message' => $e->getMessage()];
}
}
private function find(string $token): PosCustomerDisplay
{
return PosCustomerDisplay::query()