Wire POS register loyalty earn and redeem through CRM.
Deploy Ladill POS / deploy (push) Has been cancelled
Deploy Ladill POS / deploy (push) Has been cancelled
Redeem at charge, earn on payment, reverse on cancel, and surface balances on the till, customer display, sales, and receipts.
This commit is contained in:
@@ -10,6 +10,7 @@ use App\Models\PosSale;
|
||||
use App\Services\Pos\CustomerDisplayService;
|
||||
use App\Services\Pos\PosBarcodeLookupService;
|
||||
use App\Services\Pos\PosLocationService;
|
||||
use App\Services\Pos\PosLoyaltyService;
|
||||
use App\Services\Pos\PosSaleService;
|
||||
use App\Services\Crm\CrmClient;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -27,6 +28,7 @@ class RegisterController extends Controller
|
||||
private PosLocationService $locations,
|
||||
private PosBarcodeLookupService $barcodes,
|
||||
private CustomerDisplayService $customerDisplays,
|
||||
private PosLoyaltyService $loyalty,
|
||||
) {}
|
||||
|
||||
public function index(Request $request): View
|
||||
@@ -34,12 +36,16 @@ class RegisterController extends Controller
|
||||
$owner = $this->ownerRef($request);
|
||||
$location = $this->location($request);
|
||||
$display = $this->customerDisplays->ensureForLocation($location);
|
||||
$loyaltyProgram = $this->loyalty->program($owner);
|
||||
|
||||
return view('pos.register', [
|
||||
'products' => $this->registerProducts($owner, $location),
|
||||
'location' => $location,
|
||||
'crmCustomers' => $this->crmCustomers($owner),
|
||||
'barcodeLookupUrl' => route('pos.register.lookup'),
|
||||
'loyaltyUrl' => route('pos.register.loyalty'),
|
||||
'loyaltyEnabled' => (bool) ($loyaltyProgram['enabled'] ?? false),
|
||||
'loyaltyProgram' => $loyaltyProgram,
|
||||
'customerDisplayUrl' => $this->customerDisplays->displayUrl($display),
|
||||
'customerDisplayPushUrl' => route('pos.customer-display.push'),
|
||||
'customerDisplayActionsUrl' => route('pos.customer-display.actions'),
|
||||
@@ -47,6 +53,33 @@ class RegisterController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/** Live loyalty quote for the register (CRM-backed). */
|
||||
public function loyalty(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
'crm_customer_id' => ['required', 'integer'],
|
||||
'subtotal_minor' => ['nullable', 'integer', 'min:0'],
|
||||
'redeem_points' => ['nullable', 'integer', 'min:0'],
|
||||
]);
|
||||
|
||||
$owner = $this->ownerRef($request);
|
||||
$subtotal = (int) ($data['subtotal_minor'] ?? 0);
|
||||
$redeem = (int) ($data['redeem_points'] ?? 0);
|
||||
|
||||
$snapshot = $this->loyalty->snapshot($owner, (int) $data['crm_customer_id'], $subtotal);
|
||||
if (! $snapshot) {
|
||||
return response()->json(['enabled' => false, 'message' => 'Loyalty unavailable.'], 200);
|
||||
}
|
||||
|
||||
$quote = $this->loyalty->quote($owner, (int) $data['crm_customer_id'], max(1, $subtotal), $redeem);
|
||||
|
||||
return response()->json([
|
||||
'enabled' => (bool) ($snapshot['program']['enabled'] ?? false),
|
||||
'snapshot' => $snapshot,
|
||||
'quote' => $quote,
|
||||
]);
|
||||
}
|
||||
|
||||
public function lookup(Request $request): JsonResponse
|
||||
{
|
||||
$data = $request->validate([
|
||||
@@ -146,6 +179,7 @@ class RegisterController extends Controller
|
||||
'customer_email' => ['nullable', 'email', 'max:255'],
|
||||
'customer_phone' => ['nullable', 'string', 'max:40'],
|
||||
'crm_customer_id' => ['nullable', 'integer'],
|
||||
'loyalty_redeem_points' => ['nullable', 'integer', 'min:0'],
|
||||
'payment_method' => ['required', 'in:pay,cash'],
|
||||
]);
|
||||
|
||||
@@ -166,11 +200,25 @@ class RegisterController extends Controller
|
||||
'customer_email' => $data['customer_email'] ?? null,
|
||||
'customer_phone' => $data['customer_phone'] ?? null,
|
||||
'crm_customer_id' => $data['crm_customer_id'] ?? null,
|
||||
'loyalty_redeem_points' => (int) ($data['loyalty_redeem_points'] ?? 0),
|
||||
]);
|
||||
|
||||
if ($data['payment_method'] === 'cash') {
|
||||
$this->sales->recordCashPayment($sale);
|
||||
$this->customerDisplays->pushReceipt($location, $sale->fresh(['lines']));
|
||||
$sale = $sale->fresh(['lines']);
|
||||
$loyaltyPayload = null;
|
||||
if ($sale->crm_customer_id) {
|
||||
$snap = $this->loyalty->snapshot($merchant->public_id, (int) $sale->crm_customer_id, (int) $sale->total_minor);
|
||||
$loyaltyPayload = $this->loyalty->displayPayload(
|
||||
$snap,
|
||||
(int) $sale->loyalty_points_redeemed,
|
||||
(int) $sale->loyalty_points_earned,
|
||||
);
|
||||
}
|
||||
$this->customerDisplays->pushReceipt($location, $sale, array_filter([
|
||||
'loyalty' => $loyaltyPayload,
|
||||
'discount_minor' => (int) $sale->loyalty_discount_minor,
|
||||
]));
|
||||
|
||||
if ($location->printer_auto_print) {
|
||||
return redirect()
|
||||
|
||||
Reference in New Issue
Block a user