Files
ladill-pos/app/Models/PosSale.php
T
isaacclad 468346b183
Deploy Ladill POS / deploy (push) Has been cancelled
Wire POS register loyalty earn and redeem through CRM.
Redeem at charge, earn on payment, reverse on cancel, and surface
balances on the till, customer display, sales, and receipts.
2026-07-15 15:46:28 +00:00

140 lines
3.3 KiB
PHP

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class PosSale extends Model
{
public const STATUS_PENDING = 'pending';
public const STATUS_PAID = 'paid';
public const STATUS_FAILED = 'failed';
public const STATUS_CANCELLED = 'cancelled';
public const METHOD_PAY = 'pay';
public const METHOD_CASH = 'cash';
public const ORDER_COUNTER = 'counter';
public const ORDER_DINE_IN = 'dine_in';
public const ORDER_TAKEAWAY = 'takeaway';
public const KITCHEN_NONE = 'none';
public const KITCHEN_ACTIVE = 'active';
public const KITCHEN_SERVED = 'served';
protected $fillable = [
'owner_ref',
'location_id',
'table_id',
'reference',
'external_ref',
'status',
'payment_method',
'order_type',
'kitchen_status',
'covers',
'notes',
'pay_order_id',
'payment_reference',
'customer_name',
'customer_email',
'customer_phone',
'crm_customer_id',
'subtotal_minor',
'loyalty_discount_minor',
'loyalty_points_redeemed',
'loyalty_points_earned',
'loyalty_external_ref',
'total_minor',
'currency',
'paid_at',
'opened_at',
'kitchen_sent_at',
'closed_at',
];
protected function casts(): array
{
return [
'subtotal_minor' => 'integer',
'loyalty_discount_minor' => 'integer',
'loyalty_points_redeemed' => 'integer',
'loyalty_points_earned' => 'integer',
'total_minor' => 'integer',
'pay_order_id' => 'integer',
'crm_customer_id' => 'integer',
'covers' => 'integer',
'paid_at' => 'datetime',
'opened_at' => 'datetime',
'kitchen_sent_at' => 'datetime',
'closed_at' => 'datetime',
];
}
public function location(): BelongsTo
{
return $this->belongsTo(PosLocation::class, 'location_id');
}
public function table(): BelongsTo
{
return $this->belongsTo(PosTable::class, 'table_id');
}
public function payments(): HasMany
{
return $this->hasMany(PosPayment::class);
}
public function paidMinor(): int
{
return (int) $this->payments()->where('status', PosPayment::STATUS_PAID)->sum('amount_minor');
}
public function balanceMinor(): int
{
return max(0, (int) $this->total_minor - $this->paidMinor());
}
public function isOpen(): bool
{
return $this->status === self::STATUS_PENDING;
}
public function isDineIn(): bool
{
return $this->order_type === self::ORDER_DINE_IN;
}
public function scopeOpenTickets(Builder $query): Builder
{
return $query->where('status', self::STATUS_PENDING);
}
public function lines(): HasMany
{
return $this->hasMany(PosSaleLine::class)->orderBy('position');
}
public function isPaid(): bool
{
return $this->status === self::STATUS_PAID;
}
public function scopeOwned(Builder $query, string $ownerRef): Builder
{
return $query->where('owner_ref', $ownerRef);
}
}