middleware('check.permission:dues.read')->only(['index', 'collectDue']); } public function index(Request $request) { $businessId = auth()->user()->business_id; $activeBranch = auth()->user()->active_branch; $query = Party::where('business_id', $businessId) ->with(['purchases_dues', 'sales_dues']); if ($request->filled('type')) { $query->where('type', $request->type); } if ($request->search) { $search = $request->search; $query->where(function ($q) use ($search, $activeBranch) { $q->where('type', 'like', "%$search%") ->orWhere('name', 'like', "%$search%") ->orWhere('phone', 'like', "%$search%") ->orWhere('email', 'like', "%$search%"); if (! $activeBranch) { $q->orWhere('due', 'like', "%$search%"); } }); } $totalQuery = clone $query; $parties = $query->latest()->paginate($request->per_page ?? 20)->appends($request->query()); if ($activeBranch) { $total_supplier_due = (clone $totalQuery) ->where('type', 'Supplier') ->get() ->sum(fn ($p) => $p->purchases_dues->sum('dueAmount')); $total_customer_due = (clone $totalQuery) ->where('type', '!=', 'Supplier') ->get() ->sum(fn ($p) => $p->sales_dues->sum('dueAmount')); } else { $total_supplier_due = (clone $totalQuery) ->where('type', 'Supplier') ->sum('due'); $total_customer_due = (clone $totalQuery) ->where('type', '!=', 'Supplier') ->sum('due'); } if ($activeBranch) { $filtered = $parties->getCollection() ->map(function ($party) use ($activeBranch) { $sale_dues = $party->type === 'Supplier' ? $party->purchases_dues->where('branch_id', $activeBranch->id) : $party->sales_dues->where('branch_id', $activeBranch->id); $party_due = $sale_dues->sum('dueAmount'); if ($party->branch_id === $activeBranch->id) { $openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0; $party->due = $openingBalanceDue + $party_due; } else { $party->due = $party_due; } return $party; }) ->filter(fn ($p) => $p->due > 0) ->values(); $parties->setCollection($filtered); } else { $parties->setCollection( $parties->getCollection()->filter(fn ($p) => $p->due > 0)->values() ); } if ($request->ajax()) { return response()->json([ 'data' => view('business::dues.datas', compact('parties'))->render(), 'total_supplier_due' => currency_format($total_supplier_due, currency: business_currency()), 'total_customer_due' => currency_format($total_customer_due, currency: business_currency()) ]); } return view('business::dues.index', compact( 'parties', 'total_supplier_due', 'total_customer_due' )); } public function collectDue(string $id) { $activeBranch = auth()->user()->active_branch; $party = Party::where('business_id', auth()->user()->business_id)->with(['sales_dues', 'purchases_dues'])->findOrFail($id); $payment_types = PaymentType::where('business_id', auth()->user()->business_id)->whereStatus(1)->latest()->get(); $due_amount = 0; if ($party->type == 'Supplier') { foreach ($party->purchases_dues as $sales_due) { if (! $activeBranch || $sales_due->branch_id === $activeBranch->id) { $due_amount += $sales_due->dueAmount; } } } else { foreach ($party->sales_dues as $sales_due) { if (! $activeBranch || $sales_due->branch_id === $activeBranch->id) { $due_amount += $sales_due->dueAmount; } } } if ($activeBranch) { if ($party->branch_id === $activeBranch->id && $party->opening_balance_type === 'due') { $party_opening_due = $party->opening_balance ?? 0; } else { $party_opening_due = 0; } } else { $party_opening_due = $party->due - $due_amount; } return view('business::dues.collect-due', compact('party', 'party_opening_due', 'payment_types')); } public function collectDueStore(Request $request) { $party = Party::find($request->party_id); $activeBranch = auth()->user()->active_branch; $request->validate([ 'paymentDate' => 'required|string', 'payDueAmount' => 'required|numeric', 'party_id' => 'required|exists:parties,id', 'invoiceNumber' => 'nullable|exists:'.($party->type == 'Supplier' ? 'purchases' : 'sales').',invoiceNumber', ]); $business = Business::findOrFail(auth()->user()->business_id); DB::beginTransaction(); try { $branch_id = $activeBranch?->id; $invoice = null; $payments = $request->payments ?? []; if (isset($payments['main'])) { $mainPayment = $payments['main']; $mainPayment['amount'] = $request->payDueAmount ?? 0; $payments = [$mainPayment]; } $payDueAmount = collect($payments) ->reject(fn ($p) => strtolower($p['type']) === 'cheque') ->sum('amount'); $invoice_due = 0; $opening_balance_due = 0; if ($activeBranch) { if ($party->type == 'Supplier') { $invoice_due = Purchase::where('party_id', $request->party_id) ->where('branch_id', $activeBranch->id) ->sum('dueAmount'); } else { $invoice_due = Sale::where('party_id', $request->party_id) ->where('branch_id', $activeBranch->id) ->sum('dueAmount'); } if ($party->branch_id === $activeBranch->id && $party->opening_balance_type === 'due') { $opening_balance_due = $party->opening_balance ?? 0; } } else { if ($party->type == 'Supplier') { $invoice_due = Purchase::where('party_id', $request->party_id)->sum('dueAmount'); } else { $invoice_due = Sale::where('party_id', $request->party_id)->sum('dueAmount'); } $opening_balance_due = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0; } $total_due = $invoice_due + $opening_balance_due; if ($payDueAmount > $total_due) { return response()->json([ 'message' => __('You can pay maximum '.$total_due.' due amount.'), ], 400); } if ($activeBranch && ! $request->invoiceNumber && $invoice_due > 0 && $opening_balance_due == 0) { return response()->json([ 'message' => __('You must select an invoice when login any branch.'), ], 400); } if ($request->invoiceNumber) { if ($party->type == 'Supplier') { $invoice = Purchase::where('invoiceNumber', $request->invoiceNumber) ->where('party_id', $request->party_id) ->where('branch_id', $activeBranch?->id) ->first(); } else { $invoice = Sale::where('invoiceNumber', $request->invoiceNumber) ->where('party_id', $request->party_id) ->where('branch_id', $activeBranch?->id) ->first(); } if (! isset($invoice)) { return response()->json([ 'message' => 'Invoice Not Found.', ], 404); } if ($invoice->dueAmount < $request->payDueAmount) { return response()->json([ 'message' => 'Invoice due is '.$invoice->dueAmount.'. You can not pay more then the invoice due amount.', ], 400); } $branch_id = $invoice->branch_id; } if (! $request->invoiceNumber && $opening_balance_due > 0) { $party->update([ 'opening_balance' => max(0, $party->opening_balance - $payDueAmount), ]); } $data = DueCollect::create($request->except('user_id', 'business_id', 'sale_id', 'purchase_id', 'totalDue', 'dueAmountAfterPay', 'paymentDate') + [ 'user_id' => auth()->id(), 'business_id' => $business->id, 'sale_id' => $party->type != 'Supplier' && isset($invoice) ? $invoice->id : null, 'purchase_id' => $party->type == 'Supplier' && isset($invoice) ? $invoice->id : null, 'totalDue' => $total_due, 'dueAmountAfterPay' => $total_due - $payDueAmount, 'paymentDate' => $request->paymentDate ? Carbon::parse($request->paymentDate)->setTimeFromTimeString(now()->format('H:i:s')) : now(), ]); if (isset($invoice)) { $invoice->update([ 'dueAmount' => $invoice->dueAmount - $payDueAmount, 'paidAmount' => $invoice->paidAmount + $payDueAmount, ]); } $party->update([ 'due' => $party->due - $payDueAmount, ]); if ($party->type == 'Supplier') { updateBalance($payDueAmount, 'decrement', $branch_id); $platform = 'due_pay'; } else { updateBalance($payDueAmount, 'increment', $branch_id); $platform = 'due_collect'; } event(new MultiPaymentProcessed( $payments, $data->id, $platform, $payDueAmount, $party->id, )); sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business->id); if (moduleCheck('MarketingAddon')) { $this->dueTransactionMessage($business, $party, $data); } DB::commit(); return response()->json([ 'message' => __('Collect Due saved successfully'), 'redirect' => route('business.dues.index'), 'secondary_redirect_url' => route('business.collect.dues.invoice', $data->id), ]); } catch (\Exception $e) { DB::rollBack(); return response()->json(['message' => $e->getMessage()], 404); } } public function getInvoice($party_id, DueInvoiceService $service) { $data = $service->getInvoiceData($party_id); return view('business::dues.invoice', $data); } public function generatePDF($id) { $due_collect = DueCollect::with( 'user:id,name,role', 'party:id,name,email,phone,type', 'payment_type:id,name', 'business:id,companyName,address,phoneNumber,email,vat_name,vat_no,meta', 'transactions.paymentType:id,name' ) ->where('business_id', auth()->user()->business_id) ->where('party_id', $id) ->latest() ->firstOrFail(); $party = Party::with('dueCollect.business')->findOrFail($id); $bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first(); return PdfService::render( 'business::dues.pdf', [ 'due_collect' => $due_collect, 'party' => $party, 'transactionTypes' => transaction_types($due_collect->transactions), 'bank_detail' => $bank_detail, ], 'dues.pdf' ); } public function sendMail(Request $request, $id) { $due_collect = DueCollect::with('user:id,name,role', 'party:id,name,email,phone,type', 'payment_type:id,name', 'business:id,companyName,address,phoneNumber,email,vat_name,vat_no,meta') ->where('business_id', auth()->user()->business_id) ->where('party_id', $id) ->latest() ->first(); $transactionTypes = $due_collect->transactions ->map(function ($transaction) { if ($transaction->transaction_type === 'bank_payment' && !empty($transaction->paymentType?->name)) { return $transaction->paymentType->name; } return $transaction->transaction_type ? ucfirst(explode('_', $transaction->transaction_type)[0]) : ''; }) ->unique() ->implode(', '); $party = Party::with('dueCollect.business')->find($id); $bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first(); $pdf = Pdf::loadView('business::dues.pdf', compact('due_collect', 'party', 'transactionTypes', 'bank_detail')); // Send email with PDF attachment Mail::raw('Please find attached your Due Collext invoice.', function ($message) use ($pdf) { $message->to(auth()->user()->email) ->subject('Sales Invoice') ->attachData($pdf->output(), 'collect-due.pdf', [ 'mime' => 'application/pdf', ]); }); return response()->json([ 'message' => __('Email Sent Successfully.'), 'redirect' => route('business.dues.index'), ]); } public function partyDue(Request $request) { $user = auth()->user(); $business_id = $user->business_id; $activeBranch = $user->active_branch; $party_type = $request->type; $query = Party::where('business_id', $business_id); // Filter by party type if (in_array($party_type, ['Retailer', 'Dealer', 'Wholesaler', 'Supplier'])) { $query->where('type', $party_type); } // Apply search $query->when($request->search, function ($q) use ($request) { $q->where(function ($q) use ($request) { $q->where('email', 'like', '%'.$request->search.'%') ->orWhere('name', 'like', '%'.$request->search.'%') ->orWhere('phone', 'like', '%'.$request->search.'%') ->orWhere('due', 'like', '%'.$request->search.'%'); }); }); // Paginate parties $parties = $query->latest()->paginate($request->per_page ?? 20)->appends($request->query()); if ($activeBranch) { $parties->setCollection( $parties->getCollection() ->transform(function ($party) use ($activeBranch) { $sale_dues = $party->type === 'Supplier' ? $party->purchases_dues->where('branch_id', $activeBranch->id) : $party->sales_dues->where('branch_id', $activeBranch->id); $party_due = $sale_dues->sum('dueAmount'); if ($party->branch_id === $activeBranch->id) { $openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0; $party->due = $openingBalanceDue + $party_due; } else { $party->due = $party_due; } return $party; }) ->filter(fn ($party) => $party->due > 0) ->values() ); } else { // Non-active branch: ensure only parties with due > 0 $parties->setCollection( $parties->getCollection() ->filter(fn ($party) => $party->due > 0) ->values() ); } if ($request->ajax()) { return response()->json([ 'data' => view('business::dues.party.datas', compact('parties'))->render(), ]); } return view('business::dues.party.index', compact('parties', 'party_type')); } public function walk_dues(Request $request) { $walk_in_customers = Sale::where('business_id', auth()->user()->business_id) ->with('dueCollect') ->whereNull('party_id') ->where('dueAmount', '>', 0) ->when($request->search, function ($query) use ($request) { $query->where(function ($q) use ($request) { $q->where('invoiceNumber', 'like', '%' . $request->search . '%') ->orwhere('dueAmount', 'like', '%' . $request->search . '%'); }); }) ->latest() ->paginate($request->per_page ?? 20)->appends($request->query()); if ($request->ajax()) { return response()->json([ 'data' => view('business::walk-dues.datas', compact('walk_in_customers'))->render() ]); } return view('business::walk-dues.index', compact('walk_in_customers')); } public function walk_dues_filter(Request $request) { $walk_in_customers = Sale::where('business_id', auth()->user()->business_id) ->with('dueCollect') ->whereNull('party_id') ->where('dueAmount', '>', 0) ->when($request->search, function ($query) use ($request) { $query->where(function ($q) use ($request) { $q->where('invoiceNumber', 'like', '%' . $request->search . '%') ->orwhere('dueAmount', 'like', '%' . $request->search . '%'); }); }) ->latest() ->paginate($request->per_page ?? 20); if ($request->ajax()) { return response()->json([ 'data' => view('business::walk-dues.datas', compact('walk_in_customers'))->render() ]); } return redirect(url()->previous()); } public function walkDuesGetInvoice(string $id) { $due_collect = DueCollect::with('business:id,companyName,address,phoneNumber,email,vat_name,vat_no,meta', 'user:id,name,role', 'payment_type:id,name') ->where('business_id', auth()->user()->business_id) ->whereNull('party_id') ->where('id', $id) // 🔥 FIX HERE ->firstOrFail(); $transactionTypes = $due_collect->transactions ->map(function ($transaction) { if ($transaction->transaction_type === 'bank_payment' && !empty($transaction->paymentType?->name)) { return $transaction->paymentType->name; } return $transaction->transaction_type ? ucfirst(explode('_', $transaction->transaction_type)[0]) : ''; }) ->unique() ->implode(', '); // $walk_in_customer = Sale::with('dueCollect.business')->find($id); $walk_in_customer = Sale::with('dueCollect.business')->find($due_collect->sale_id); $bank_detail = PaymentType::where('business_id', auth()->user()->business_id)->where('show_in_invoice', 1)->latest()->first(); return view('business::walk-dues.invoice', compact('due_collect', 'walk_in_customer', 'transactionTypes', 'bank_detail')); } public function collectWalkDue(string $id) { $business_id = auth()->user()->business_id; $payment_types = PaymentType::where('business_id', $business_id)->whereStatus(1)->latest()->get(); $walk_due = Sale::with('dueCollect')->where('business_id', $business_id)->whereNull('party_id')->where('dueAmount', '>', 0)->findOrFail($id); $is_walk_in = true; return view('business::walk-dues.collect-due', compact('walk_due', 'payment_types', 'is_walk_in')); } public function collectWalkDueStore(Request $request) { $business_id = auth()->user()->business_id; $request->validate([ 'paymentDate' => 'required|string', 'payDueAmount' => 'required|numeric', 'invoiceNumber' => 'required|string|exists:sales,invoiceNumber', // Only for walk-in customers (Sales invoices) ]); DB::beginTransaction(); try { $branch_id = null; $invoice = Sale::where('business_id', $business_id)->where('invoiceNumber', $request->invoiceNumber)->whereNull('party_id')->first(); if (!$invoice) { return response()->json([ 'message' => 'Invoice Not Found.' ], 404); } if ($invoice->dueAmount < $request->payDueAmount) { return response()->json([ 'message' => 'Invoice due is ' . $invoice->dueAmount . '. You cannot pay more than the invoice due amount.' ], 400); } if (!auth()->user()->active_branch) { if (isset($invoice) && isset($invoice->branch_id)) { $branch_id = $invoice->branch_id; } } $payments = $request->payments ?? []; // Normalize main payment if (isset($payments['main'])) { $main = $payments['main']; $main['amount'] = $request->payDueAmount; $payments = [$main]; } $payDueAmount = collect($payments) ->reject(fn($p) => strtolower($p['type']) === 'cheque') ->sum('amount'); $data = DueCollect::create([ 'user_id' => auth()->id(), 'business_id' => $business_id, 'sale_id' => $invoice->id, 'invoiceNumber' => $request->invoiceNumber, 'totalDue' => $invoice->dueAmount, 'dueAmountAfterPay' => $invoice->dueAmount - $payDueAmount, 'payDueAmount' => $payDueAmount, 'paymentDate' => $request->paymentDate, ]); $invoice->update([ 'dueAmount' => $invoice->dueAmount - $payDueAmount ]); updateBalance($payDueAmount, 'increment', $branch_id); // MultiPaymentProcessed event event(new MultiPaymentProcessed( $payments, $data->id, 'due_collect', $payDueAmount, )); sendNotifyToUser($data->id, route('business.dues.index', ['id' => $data->id]), __('Due Collection has been created.'), $business_id); DB::commit(); return response()->json([ 'message' => __('Collect Due saved successfully'), 'redirect' => route('business.walk-dues.index'), 'secondary_redirect_url' => route('business.collect.walk-dues.invoice', $data->id), ]); } catch (\Exception $e) { DB::rollBack(); return response()->json(['message' => 'Something went wrong!'], 404); } } public function viewDuePayment($id) { $businessId = auth()->user()->business_id; $partyExists = Party::where('business_id', $businessId) ->where('id', $id) ->exists(); if ($partyExists) { $transactions = Transaction::with('paymentType:id,name') ->where('business_id', $businessId) ->whereIn('platform', ['due_collect', 'due_pay']) ->whereHas('dueCollect', function ($q) use ($id) { $q->where('party_id', $id); }) ->latest() ->get() ->map(function ($transaction) { return [ 'id' => $transaction->id, 'date' => formatted_date($transaction->date), 'receipt_no' => $transaction->invoice_no ?? '-', 'amount' => currency_format($transaction->amount, currency: business_currency()), 'payment_type' => $transaction->transaction_type === 'bank_payment' ? ($transaction->paymentType->name ?? '') : ucfirst(explode('_', $transaction->transaction_type)[0] ?? ''), ]; }); return response()->json([ 'data' => $transactions, ]); } $sale = Sale::where('business_id', $businessId) ->whereNull('party_id') ->where('id', $id) ->first(); if ($sale) { $transactions = Transaction::with('paymentType:id,name') ->where('business_id', $businessId) ->whereIn('platform', ['due_collect', 'due_pay']) ->whereHas('dueCollect', function ($q) use ($sale) { $q->where('sale_id', $sale->id); }) ->latest() ->get() ->map(function ($transaction) { return [ 'id' => $transaction->id, 'date' => formatted_date($transaction->date), 'receipt_no' => $transaction->invoice_no ?? '-', 'amount' => currency_format($transaction->amount, currency: business_currency()), 'payment_type' => $transaction->transaction_type === 'bank_payment' ? ($transaction->paymentType->name ?? '') : ucfirst(explode('_', $transaction->transaction_type)[0] ?? ''), ]; }); return response()->json([ 'data' => $transactions, ]); } } public function exportExcel() { return Excel::download(new ExportDueList, 'due-list.xlsx'); } public function exportCsv() { return Excel::download(new ExportDueList, 'due-list.csv'); } public function exportPdf() { $businessId = auth()->user()->business_id; $activeBranch = auth()->user()->active_branch; $query = Party::where('business_id', $businessId) ->with(['purchases_dues', 'sales_dues']); if (request('type')) { $query->where('type', request('type')); } if (request('search')) { $search = request('search'); $query->where(function ($q) use ($search, $activeBranch) { $q->where('type', 'like', "%$search%") ->orWhere('name', 'like', "%$search%") ->orWhere('phone', 'like', "%$search%") ->orWhere('email', 'like', "%$search%"); if (!$activeBranch) { $q->orWhere('due', 'like', "%$search%"); } }); } $parties = $query->latest()->limit(request('per_page') ?? 20)->get(); if ($activeBranch) { $parties = $parties->map(function ($party) use ($activeBranch) { $sale_dues = $party->type === 'Supplier' ? $party->purchases_dues->where('branch_id', $activeBranch->id) : $party->sales_dues->where('branch_id', $activeBranch->id); $party_due = $sale_dues->sum('dueAmount'); if ($party->branch_id === $activeBranch->id) { $openingBalanceDue = $party->opening_balance_type === 'due' ? ($party->opening_balance ?? 0) : 0; $party->due = $openingBalanceDue + $party_due; } else { $party->due = $party_due; } return $party; })->filter(fn($p) => $p->due > 0)->values(); } else { $parties = $parties->filter(fn($p) => $p->due > 0)->values(); } return PdfService::render('business::dues.pdf-export', compact('parties'),'due-list.pdf'); } public function walkDueExportExcel() { return Excel::download(new ExportGuestDue, 'guest-due-list.xlsx'); } public function walkDueExportCsv() { return Excel::download(new ExportGuestDue, 'guest-due-list.csv'); } public function walkDueExportPdf(Request $request) { $walk_in_customers = Sale::where('business_id', auth()->user()->business_id) ->with('dueCollect') ->whereNull('party_id') ->where('dueAmount', '>', 0) ->when(request('search'), function ($query) { $query->where(function ($q) { $q->where('invoiceNumber', 'like', '%' . request('search') . '%') ->orwhere('dueAmount', 'like', '%' . request('search') . '%'); }); }) ->latest() ->limit($request->per_page ?? 20)->get(); return PdfService::render('business::walk-dues.pdf', compact('walk_in_customers'),'guest-due-list.pdf'); } }