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:
@@ -19,11 +19,12 @@ class PosSaleService
|
||||
public function __construct(
|
||||
private MerchantGatewayService $gateway,
|
||||
private PosTimelineService $timeline,
|
||||
private PosLoyaltyService $loyalty,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param list<array{product_id?: ?int, name: string, unit_price_minor: int, quantity: int}> $lines
|
||||
* @param array{customer_name?: ?string, customer_email?: ?string, customer_phone?: ?string, crm_customer_id?: ?int, location_id?: ?int, currency?: string} $meta
|
||||
* @param array{customer_name?: ?string, customer_email?: ?string, customer_phone?: ?string, crm_customer_id?: ?int, location_id?: ?int, currency?: string, loyalty_redeem_points?: int} $meta
|
||||
*/
|
||||
public function createSale(User $merchant, array $lines, array $meta = []): PosSale
|
||||
{
|
||||
@@ -50,6 +51,9 @@ class PosSaleService
|
||||
'customer_phone' => $meta['customer_phone'] ?? null,
|
||||
'crm_customer_id' => $meta['crm_customer_id'] ?? null,
|
||||
'subtotal_minor' => $subtotal,
|
||||
'loyalty_discount_minor' => 0,
|
||||
'loyalty_points_redeemed' => 0,
|
||||
'loyalty_points_earned' => 0,
|
||||
'total_minor' => $subtotal,
|
||||
'currency' => $currency,
|
||||
]);
|
||||
@@ -66,6 +70,28 @@ class PosSaleService
|
||||
]);
|
||||
}
|
||||
|
||||
$sale = $sale->fresh('lines');
|
||||
$redeemPoints = (int) ($meta['loyalty_redeem_points'] ?? 0);
|
||||
if ($redeemPoints > 0 && $sale->crm_customer_id) {
|
||||
$sale = $this->applyLoyaltyRedeem($sale, $redeemPoints);
|
||||
}
|
||||
|
||||
return $sale;
|
||||
}
|
||||
|
||||
public function applyLoyaltyRedeem(PosSale $sale, int $redeemPoints): PosSale
|
||||
{
|
||||
$result = $this->loyalty->redeemForSale($sale, $redeemPoints);
|
||||
$discount = (int) $result['discount_minor'];
|
||||
$points = (int) $result['points'];
|
||||
|
||||
$sale->forceFill([
|
||||
'loyalty_discount_minor' => $discount,
|
||||
'loyalty_points_redeemed' => $points,
|
||||
'loyalty_external_ref' => $result['external_ref'] ?: $sale->loyalty_external_ref,
|
||||
'total_minor' => max(0, (int) $sale->subtotal_minor - $discount),
|
||||
])->save();
|
||||
|
||||
return $sale->fresh('lines');
|
||||
}
|
||||
|
||||
@@ -451,6 +477,8 @@ class PosSaleService
|
||||
throw new RuntimeException('Only pending sales can be cancelled.');
|
||||
}
|
||||
|
||||
$this->loyalty->reverseForSale($sale);
|
||||
|
||||
$sale->forceFill(['status' => PosSale::STATUS_CANCELLED])->save();
|
||||
$this->closeTicket($sale);
|
||||
}
|
||||
@@ -553,9 +581,10 @@ class PosSaleService
|
||||
$this->closeTicket($sale);
|
||||
|
||||
$sale = $sale->fresh('lines');
|
||||
$this->loyalty->earnForSale($sale);
|
||||
$this->timeline->pushPaidSale($sale);
|
||||
|
||||
return $sale;
|
||||
return $sale->fresh('lines');
|
||||
}
|
||||
|
||||
public function completePayCheckout(string $paymentReference): PosSale
|
||||
@@ -579,9 +608,10 @@ class PosSaleService
|
||||
$this->closeTicket($sale);
|
||||
|
||||
$sale = $sale->fresh('lines');
|
||||
$this->loyalty->earnForSale($sale);
|
||||
$this->timeline->pushPaidSale($sale);
|
||||
|
||||
return $sale;
|
||||
return $sale->fresh('lines');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user