diff --git a/app/Livewire/Datatable/Studio/Manage/OrdersTable.php b/app/Livewire/Datatable/Studio/Manage/OrdersTable.php
index df6e0fa..144f926 100644
--- a/app/Livewire/Datatable/Studio/Manage/OrdersTable.php
+++ b/app/Livewire/Datatable/Studio/Manage/OrdersTable.php
@@ -23,6 +23,8 @@ public function columns(): array
return [
Column::make('Pegawai', 'user.employee.full_name')->searchable(),
+ Column::make('Voucher', 'voucher.name')->searchable(),
+
Column::make('Nomor Invoice')
->label(function ($row) {
return <<with(['user', 'user.employee', 'customer']);
+ ->with(['user', 'user.employee', 'customer', 'voucher']);
}
}
diff --git a/app/Livewire/Forms/Studio/Manage/OrderForm.php b/app/Livewire/Forms/Studio/Manage/OrderForm.php
index 657f9d3..91be13e 100644
--- a/app/Livewire/Forms/Studio/Manage/OrderForm.php
+++ b/app/Livewire/Forms/Studio/Manage/OrderForm.php
@@ -8,7 +8,10 @@
use App\Enums\PaymentStatus;
use App\Models\Order;
use App\Models\Payment;
+use App\Models\User;
+use App\Models\Voucher;
use App\Rules\UnsignedInteger;
+use App\Traits\Order\WithDiscount;
use App\Traits\Order\WithOrderItem;
use App\Traits\Order\WithUpdateStock;
use App\Traits\WithMediaHandler;
@@ -18,7 +21,7 @@
class OrderForm extends Form
{
- use WithMediaHandler, WithOrderItem, WithUpdateStock;
+ use WithDiscount, WithMediaHandler, WithOrderItem, WithUpdateStock;
public ?Order $order = null;
@@ -50,6 +53,8 @@ class OrderForm extends Form
public string $member_id = '';
+ public string $voucher_id = '';
+
public function rules(): array
{
return [
@@ -59,6 +64,7 @@ public function rules(): array
'payment_method' => ['required', Rule::in(PaymentMethod::values())],
'discount' => ['nullable', new UnsignedInteger],
'member_id' => ['nullable', Rule::exists('customers', 'id')],
+ 'voucher_id' => ['nullable', Rule::exists('vouchers', 'id')],
];
}
@@ -68,6 +74,7 @@ public function validationAttributes(): array
'payment_method' => 'metode pembayaran',
'discount' => 'diskon',
'member_id' => 'member',
+ 'voucher_id' => 'voucher',
];
}
@@ -87,17 +94,32 @@ public function store()
// Calculate subtotal before any global discount
$subtotal = $items->sum(fn ($item) => $item->unit_price * $item->quantity);
- $discount = replaceCurrency($this->discount);
+ $manualDiscount = replaceCurrency($this->discount);
- // Apply global discount to get final total
- $total = $subtotal - $discount;
+ // Apply manual discount
+ $afterManual = $subtotal - $manualDiscount;
+
+ $voucherDiscount = $this->calculateDiscount($afterManual);
+
+ if (is_array($voucherDiscount)) {
+ return $voucherDiscount;
+ }
+
+ // Calculate total discount
+ $discount = $voucherDiscount + $manualDiscount;
+
+ // Calculate total (subtotal - manual discount - voucher discount)
+ $total = $afterManual - $voucherDiscount;
+
+ $member = User::whereHas('customer', fn ($q) => $q->where('id', $this->member_id))->first();
// Wrap the entire operation in a database transaction
// Ensures atomicity — if any step fails, all changes are rolled back
- DB::transaction(function () use ($invoiceNumber, $cogs, $subtotal, $discount, $total, $items) {
+ DB::transaction(function () use ($invoiceNumber, $cogs, $subtotal, $discount, $total, $items, $member) {
$order = Order::create([
'outlet_id' => $this->outlet_id,
'user_id' => auth()->id(),
+ 'voucher_id' => $this->voucher_id == '' ? null : $this->voucher_id,
'customer_id' => $this->member_id == '' ? null : $this->member_id,
'invoice_number' => $invoiceNumber,
'cogs' => $cogs,
@@ -126,6 +148,33 @@ public function store()
'amount' => $total,
'status' => PaymentStatus::PAID->value,
]);
+
+ if ($this->voucher_id) {
+ $userVoucher = DB::table('user_voucher')
+ ->where('user_id', $member->id)
+ ->where('voucher_id', $this->voucher_id)
+ ->lockForUpdate()
+ ->first();
+
+ if ($userVoucher) {
+ if ($userVoucher->quantity > 1) {
+ DB::table('user_voucher')
+ ->where('id', $userVoucher->id)
+ ->update([
+ 'quantity' => $userVoucher->quantity - 1,
+ 'updated_at' => now(),
+ ]);
+ } else {
+ DB::table('user_voucher')
+ ->where('id', $userVoucher->id)
+ ->delete();
+ }
+ }
+ }
});
+
+ return [
+ 'status' => true,
+ ];
}
}
diff --git a/app/Livewire/Studio/Manage/Order/Create.php b/app/Livewire/Studio/Manage/Order/Create.php
index d4a3da5..3438683 100644
--- a/app/Livewire/Studio/Manage/Order/Create.php
+++ b/app/Livewire/Studio/Manage/Order/Create.php
@@ -43,6 +43,8 @@ class Create extends Component
public $items;
+ public array $vouchers = [];
+
public function mount()
{
$this->outlets = auth()->user()->outlets->pluck('name', 'id')->toArray();
@@ -75,7 +77,13 @@ public function save()
return;
}
- $this->form->store();
+ $result = $this->form->store();
+
+ if (! $result['status']) {
+ $this->toast($result['message'], 'Gagal', 'danger');
+
+ return;
+ }
$this->dispatch('refreshDatatable');
diff --git a/app/Traits/Order/WithDiscount.php b/app/Traits/Order/WithDiscount.php
index fffe509..506ee94 100644
--- a/app/Traits/Order/WithDiscount.php
+++ b/app/Traits/Order/WithDiscount.php
@@ -2,6 +2,9 @@
namespace App\Traits\Order;
+use App\Enums\VoucherType;
+use App\Models\Voucher;
+
trait WithDiscount
{
public function updatedFormDiscount(string $value)
@@ -11,4 +14,39 @@ public function updatedFormDiscount(string $value)
$this->total = $total - $discount;
}
+
+ public function calculateDiscount(int $subtotal)
+ {
+ $voucher = Voucher::find($this->voucher_id);
+ $discount = 0;
+
+ if ($voucher) {
+ // Check minimum purchase
+ if ($voucher->min_purchase && $subtotal < $voucher->min_purchase) {
+ $this->discount = 0;
+
+ return [
+ 'status' => false,
+ 'message' => 'Minimum pembelian harus sebesar '.currency($voucher->min_purchase, 'Rp').' untuk menggunakan voucher ini.',
+ ];
+ }
+
+ // Calculate discount based on voucher type
+ if ($voucher->type === VoucherType::PERCENTAGE) {
+ $discount = ($subtotal * $voucher->discount_amount) / 100;
+
+ // Apply maximum discount limit
+ if ($voucher->max_discount && $discount > $voucher->max_discount) {
+ $discount = $voucher->max_discount;
+ }
+ } else {
+ $discount = $voucher->discount_amount;
+ }
+
+ // Ensure discount does not exceed subtotal
+ $discount = min($discount, $subtotal);
+ }
+
+ return $discount;
+ }
}
diff --git a/app/Traits/Order/WithMember.php b/app/Traits/Order/WithMember.php
index 3ac40a9..2c77b58 100644
--- a/app/Traits/Order/WithMember.php
+++ b/app/Traits/Order/WithMember.php
@@ -4,6 +4,7 @@
use App\Enums\UserStatus;
use App\Models\Customer;
+use App\Models\Voucher;
use Livewire\Attributes\Computed;
trait WithMember
@@ -27,4 +28,36 @@ public function members()
])
->toArray();
}
+
+ public function updatedFormMemberId(Customer $customer)
+ {
+ $customer->load('user');
+
+ $memberId = $customer->user?->id;
+
+ $this->vouchers = Voucher::whereHas('users', function ($q) use ($memberId) {
+ $q->where('user_id', $memberId);
+ })
+ ->where(function ($q) {
+ $q->where(function ($q) {
+ $q->whereNull('start_date')
+ ->orWhere('start_date', '<=', now());
+ });
+ $q->where(function ($q) {
+ $q->whereNull('end_date')
+ ->orWhere('end_date', '>=', now());
+ });
+ })
+ ->select('id', 'name', 'type', 'discount_amount', 'max_discount')
+ ->orderByRaw("
+ CASE
+ WHEN type = 'percentage' THEN
+ LEAST(discount_amount / 100 * COALESCE(max_discount, 99999999), COALESCE(max_discount, 99999999))
+ ELSE
+ discount_amount
+ END DESC
+ ")
+ ->pluck('name', 'id')
+ ->toArray();
+ }
}
diff --git a/resources/views/livewire/studio/manage/order/form.blade.php b/resources/views/livewire/studio/manage/order/form.blade.php
index 5f15f03..644edea 100644
--- a/resources/views/livewire/studio/manage/order/form.blade.php
+++ b/resources/views/livewire/studio/manage/order/form.blade.php
@@ -290,6 +290,23 @@ class="text-xs text-gray-400">{{ currency($item->quantity, '') }}
+
+ @if (!empty($vouchers))
+
+ Voucher
+ Voucher diurutkan berdasarkan dengan diskon yang paling besar.
+
+
+ @foreach ($vouchers as $key => $name)
+
+ {{ $name }}
+
+ @endforeach
+
+
+
+ @endif