diff --git a/app/Enums/CashTransactionType.php b/app/Enums/CashTransactionType.php index 4d9bec2..661e9e1 100644 --- a/app/Enums/CashTransactionType.php +++ b/app/Enums/CashTransactionType.php @@ -10,6 +10,7 @@ enum CashTransactionType: string case DEPOSIT = 'deposit'; case EXPENSE = 'expense'; + case TRANSACTION = 'transaction'; case WITHDRAWAL = 'withdrawal'; case EMPLOYEE_ADVANCE = 'employee_advance'; case SALARY = 'salary'; @@ -19,6 +20,7 @@ public function label(): string return match ($this) { self::DEPOSIT => 'Deposit', self::EXPENSE => 'Pengeluaran', + self::TRANSACTION => 'Transaksi', self::WITHDRAWAL => 'Withdrawal', self::EMPLOYEE_ADVANCE => 'Kasbon', self::SALARY => 'Gaji', diff --git a/app/Models/CashTransaction.php b/app/Models/CashTransaction.php index f86dc78..c6d7588 100644 --- a/app/Models/CashTransaction.php +++ b/app/Models/CashTransaction.php @@ -75,6 +75,12 @@ protected function expenseType(Builder $query): void $query->where('type', CashTransactionType::EXPENSE); } + #[Scope] + protected function transactionType(Builder $query): void + { + $query->where('type', CashTransactionType::TRANSACTION); + } + #[Scope] protected function transfer(Builder $query): void { diff --git a/app/Services/Admin/Manage/TransactionService.php b/app/Services/Admin/Manage/TransactionService.php index 2eeae1d..a87c1c1 100644 --- a/app/Services/Admin/Manage/TransactionService.php +++ b/app/Services/Admin/Manage/TransactionService.php @@ -2,6 +2,7 @@ namespace App\Services\Admin\Manage; +use App\Enums\CashTransactionType; use App\Enums\OrderChannel; use App\Enums\OrderStatus; use App\Enums\PaymentType; @@ -14,6 +15,7 @@ use App\Models\ProductVariant; use App\Models\User; use App\Services\Concerns\HasStockAdjustment; +use App\Services\Concerns\HandlesCashTransactions; use App\Services\Concerns\RegistersMedia; use App\Services\NotificationService; use App\Services\S3PresignedService; @@ -23,7 +25,7 @@ class TransactionService { - use HasStockAdjustment, RegistersMedia; + use HasStockAdjustment, HandlesCashTransactions, RegistersMedia; private const SELLING_PRICE_MAP = [ PriceType::DISTRIBUTOR->value => PriceType::DISTRIBUTOR, @@ -233,7 +235,14 @@ public function store(array $data): Order $this->applyStock($data['items'], $stockType, -1); - if ($paymentType !== PaymentType::CASH->value) { + if ($paymentType === PaymentType::CASH->value) { + $cashTransaction = $this->creditCash( + amount: $totalAmount, + description: 'Pembayaran tunai: '.$order->order_number, + type: CashTransactionType::TRANSACTION, + ); + $order->update(['cash_transaction_id' => $cashTransaction->id]); + } else { $this->syncPhoto($order, $data); } @@ -253,6 +262,7 @@ public function update(Order $order, array $data): Order $order = DB::transaction(function () use ($order, $data) { $order->load('orderItems'); + $oldPaymentType = $order->payment_type; $oldStockType = $order->orderItems->first()?->stock_quality?->value ?? ProductStockQuality::GOOD->value; $order->orderItems->each(function (OrderItem $item) use ($oldStockType) { @@ -284,13 +294,15 @@ public function update(Order $order, array $data): Order } DB::table('order_items')->insert($itemRows); + $newPaymentType = $data['payment_type'] ?? $order->payment_type->value; + $order->update([ 'customer_id' => $data['customer_id'] ?? null, 'marketing_id' => $data['marketing_id'] ?? null, 'channel' => $data['channel'] ?? $order->channel->value, 'price_type' => $priceType, 'status' => ($data['is_completed'] ?? false) ? OrderStatus::COMPLETED : OrderStatus::PENDING, - 'payment_type' => $data['payment_type'] ?? $order->payment_type->value, + 'payment_type' => $newPaymentType, 'tiktok_order_id' => $data['tiktok_order_id'] ?? null, 'shopee_order_id' => $data['shopee_order_id'] ?? null, 'subtotal' => $subtotal, @@ -303,11 +315,39 @@ public function update(Order $order, array $data): Order $this->applyStock($data['items'], $stockType, -1); - $paymentType = $data['payment_type'] ?? $order->payment_type->value; - if ($paymentType !== PaymentType::CASH->value) { - $this->syncPhoto($order, $data); - } else { + if ($oldPaymentType === PaymentType::CASH && $newPaymentType !== PaymentType::CASH->value) { + if ($order->cash_transaction_id) { + $order->cashTransaction()->delete(); + $order->update(['cash_transaction_id' => null]); + } + } elseif ($oldPaymentType !== PaymentType::CASH && $newPaymentType === PaymentType::CASH->value) { + $cashTransaction = $this->creditCash( + amount: $totalAmount, + description: 'Pembayaran tunai: '.$order->order_number, + type: CashTransactionType::TRANSACTION, + ); + $order->update(['cash_transaction_id' => $cashTransaction->id]); + } elseif ($oldPaymentType === PaymentType::CASH && $newPaymentType === PaymentType::CASH->value && $order->cash_transaction_id) { + $cashTransaction = $order->cashTransaction; + $oldAmount = $cashTransaction->amount; + $difference = $totalAmount - $oldAmount; + + if ($difference !== 0) { + $cashAccount = \App\Models\CashAccount::firstOrFail(); + $newBalance = $cashAccount->balance + $difference; + $cashAccount->update(['balance' => $newBalance]); + + $cashTransaction->update([ + 'amount' => $totalAmount, + 'balance_after' => $newBalance, + ]); + } + } + + if ($newPaymentType === PaymentType::CASH->value) { $order->clearMediaCollection('photos'); + } else { + $this->syncPhoto($order, $data); } return $order; @@ -336,6 +376,14 @@ public function destroy(Order $order): bool }); } + if ($order->payment_type === PaymentType::CASH && $order->cash_transaction_id) { + $this->debitCash( + amount: $order->total_amount, + description: 'Pembatalan transaksi: '.$order->order_number, + type: CashTransactionType::EXPENSE, + ); + } + $order->orderItems()->delete(); $order->delete(); @@ -365,6 +413,14 @@ public function updateStatus(Order $order, string $status): Order $order->orderItems->each(function (OrderItem $item) use ($stockType) { $this->adjustVariantStock($item->product_variant_id, $item->quantity, 1, $stockType); }); + + if ($order->payment_type === PaymentType::CASH && $order->cash_transaction_id) { + $this->debitCash( + amount: $order->total_amount, + description: 'Pembatalan transaksi: '.$order->order_number, + type: CashTransactionType::EXPENSE, + ); + } } return $order; diff --git a/resources/js/pages/admin/finance/cash-account/transaction-columns.tsx b/resources/js/pages/admin/finance/cash-account/transaction-columns.tsx index e825a27..a4d1f06 100644 --- a/resources/js/pages/admin/finance/cash-account/transaction-columns.tsx +++ b/resources/js/pages/admin/finance/cash-account/transaction-columns.tsx @@ -1,7 +1,7 @@ +import { RowActions } from '@/components/data-display'; +import { ImagePreviewButton } from '@/components/dialogs'; import type { ColumnDef } from '@tanstack/react-table'; import { Pencil, Trash2 } from 'lucide-react'; -import { ImagePreviewButton } from '@/components/dialogs'; -import { RowActions } from '@/components/data-display'; export type CashTransaction = { id: number; @@ -32,7 +32,8 @@ function getTypeLabel(type: string): string { withdrawal: 'Withdrawal', expense: 'Pengeluaran', employee_advance: 'Kasbon', - salary : 'Gaji' + salary: 'Gaji', + transaction: 'Transaksi' }; return labels[type] ?? type; @@ -77,13 +78,13 @@ export function createTransactionColumns( header: () => Jumlah, cell: ({ row }) => { const transaction = row.original; - const isDeposit = transaction.type === 'deposit'; + const isDeposit = transaction.type === 'deposit' || transaction.type === 'transaction'; return (