Files
kulakpos_web/Modules/Business/App/Http/Controllers/AcnooDueController.php
eko54r 05fd3230b8
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 5m14s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 17s
Build, Push and Deploy / deploy-staging (push) Successful in 41s
Build, Push and Deploy / deploy-production (push) Has been skipped
update marketing
2026-05-14 11:55:22 +07:00

789 lines
30 KiB
PHP

<?php
namespace Modules\Business\App\Http\Controllers;
use App\Services\Invoice\DueInvoiceService;
use Carbon\Carbon;
use App\Models\Sale;
use App\Models\Party;
use App\Models\Business;
use App\Models\Purchase;
use App\Models\DueCollect;
use App\Models\PaymentType;
use App\Models\Transaction;
use App\Services\PdfService;
use Illuminate\Http\Request;
use Barryvdh\DomPDF\Facade\Pdf;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Mail;
use App\Events\MultiPaymentProcessed;
use Maatwebsite\Excel\Facades\Excel;
use Modules\Business\App\Exports\ExportDueList;
use Modules\Business\App\Exports\ExportGuestDue;
class AcnooDueController extends Controller
{
public function __construct()
{
$this->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');
}
}