$request->all()]); if ($request->ajax()) { $query = Purchase::with(['party','items']); // Filtering if ($request->date_range) { [$start, $end] = explode(' to ', $request->date_range); $query->whereBetween('invoice_date', [$start, $end]); } if ($request->party_id) { $query->where('party_id', $request->party_id); } if ($request->invoice_no) { $query->where('invoice_no', 'like', '%' . $request->invoice_no . '%'); } if ($request->status) { $query->where('status', $request->status); } $data = datatables()->of($query->latest()) ->addColumn('party', fn($row) => $row->party->company) ->addColumn('items', fn($row) => $row->items()->count()) ->addColumn('actions', function ($row) { return ' '; }) ->rawColumns(['actions']) ->make(true); Log::info('PurchaseManagementController@index returning AJAX data'); return $data; } $parties = CustomerVendor::all(); $items = StockItem::all(); Log::info('PurchaseManagementController@index returning view', [ 'parties_count' => $parties->count(), 'items_count' => $items->count() ]); $purchaseOrders = PurchaseOrder::with(['quotation','quotation.items'])->where('status', 'approved')->get(); // dd($purchaseOrders); $nextInvoiceNo = nextNumber('UEPL/P/INV', 'purchases', 'invoice_no'); $currentDate = date('Y-m-d'); return view('superadmin.purchasemanagement.index', compact('parties', 'items', 'purchaseOrders', 'nextInvoiceNo', 'currentDate')); } public function show($id) { Log::info('PurchaseManagementController@show called', ['id' => $id]); $purchase = Purchase::with(['party', 'items.item', 'files'])->findOrFail($id); Log::info('PurchaseManagementController@show loaded purchase', ['purchase_id' => $purchase->id]); return response()->json($purchase); } public function edit($id) { Log::info('PurchaseManagementController@edit called', ['id' => $id]); $purchase = Purchase::with(['items', 'files'])->findOrFail($id); Log::info('PurchaseManagementController@edit loaded purchase', ['purchase_id' => $purchase->id]); return response()->json($purchase); } // Update purchase public function update(Request $request, $id) { // Step 1: Find the existing purchase $purchase = Purchase::findOrFail($id); // Step 2: Validate the incoming request $validatedData = $request->validate([ 'purchase_order_id' => 'required|integer', 'purchase_order_date' => 'required|date', 'purchase_no' => 'required|string|exists:purchases,purchase_id', 'vendor_email' => 'required|email', 'invoice_no' => 'required|string|exists:purchases,invoice_no', 'invoice_date' => 'required|date', 'purchase_id' => 'required|integer|exists:customer_vendors,id', // likely party/vendor ID 'vendor_reference_no' => 'nullable|string', 'payment_terms' => 'nullable|integer', 'company_gstn' => 'required|string', 'company_address' => 'required|string', 'company_phone' => 'required|string', 'description' => 'nullable|string', 'delivery_terms' => 'nullable|string', 'items' => 'required|array|min:1', 'items.*.sl_no' => 'required|integer', 'items.*.item_id' => 'required|integer', 'items.*.description' => 'nullable|string', 'items.*.quantity' => 'required|numeric|min:0.01', 'items.*.uom' => 'required|string', 'items.*.rate' => 'required|numeric|min:0', 'items.*.tds' => 'nullable|numeric|min:0', 'items.*.discount' => 'nullable|numeric|min:0', 'items.*.value' => 'nullable|numeric', 'items.*.sgst' => 'nullable|numeric', 'items.*.cgst' => 'nullable|numeric', 'items.*.igst' => 'nullable|numeric', 'items.*.amount' => 'nullable|numeric', 'additional_charges' => 'nullable|numeric|min:0', 'total_tds' => 'nullable|numeric|min:0', 'sub_total' => 'required|numeric|min:0', 'total_amount' => 'required|numeric|min:0', 'paid_amount' => 'nullable|numeric|min:0', 'balance_amount' => 'nullable|numeric|min:0', 'payment_mode' => 'nullable|string', 'payment_reference' => 'nullable|string', 'payment_reason' => 'nullable|string', 'note' => 'nullable|string', ]); // Step 3: Update the purchase record $purchase->update([ 'purchase_id' => $validatedData['purchase_no'], // assuming purchase_no is stored as purchase_id 'purchase_date' => $validatedData['purchase_order_date'], 'invoice_no' => $validatedData['invoice_no'], 'invoice_date' => $validatedData['invoice_date'], 'party_id' => $validatedData['purchase_id'], // vendor/party ID 'sub_total' => $validatedData['sub_total'], 'additional_charges' => $validatedData['additional_charges'], 'tds' => $validatedData['total_tds'], 'total_amount' => $validatedData['total_amount'], 'paid_amount' => $validatedData['paid_amount'], 'balance_amount' => $validatedData['balance_amount'], 'status' => $validatedData['paid_amount'] >= $validatedData['total_amount'] ? 'paid' : 'unpaid', 'payment_mode' => $validatedData['payment_mode'], 'reference' => $validatedData['payment_reference'], 'notes' => $validatedData['note'], ]); // Step 4: Handle items - Sync logic (update existing, add new, remove deleted) $itemIdsFromRequest = collect($validatedData['items'])->map(function ($item) use ($purchase) { return isset($item['id']) ? $item['id'] : null; // if item already exists in DB, it should have an 'id' })->filter(); // only non-null IDs // Delete items not present in the request $purchase->items() ->whereNotIn('id', $itemIdsFromRequest) ->delete(); // Update or create items foreach ($validatedData['items'] as $item) { $itemData = [ 'item_id' => $item['item_id'], 'uom' => $item['uom'], 'quantity' => $item['quantity'], 'rate' => $item['rate'], 'discount' => $item['discount'] ?? 0, ]; if (isset($item['id'])) { // Existing item: update $purchase->items()->where('id', $item['id'])->update($itemData); } else { // New item: create $purchase->items()->create($itemData); } } // Step 5: Handle file uploads (if any) if ($request->hasFile('purchase_files')) { // Example: Add new files, or replace old ones // You may want to store them and attach to purchase // Storage::put('purchases/'.$purchase->id, $request->file('purchase_files')); } // Step 6: Return success response return redirect()->route('superadmin.purchase.index')->with('success', 'Purchase updated successfully'); } public function store(Request $request) { // Step 1: Validate the request data $validatedData = $request->validate([ 'purchase_order_id' => 'required|integer', 'purchase_order_date' => 'required|date', 'purchase_no' => 'required|string', 'vendor_email' => 'required|email', 'invoice_no' => 'required|string', 'invoice_date' => 'required|date', 'purchase_id' => 'required|integer', 'vendor_reference_no' => 'nullable|string', 'payment_terms' => 'nullable|integer', 'company_gstn' => 'required|string', 'company_address' => 'required|string', 'company_phone' => 'required|string', 'description' => 'nullable|string', 'delivery_terms' => 'nullable|string', 'items' => 'required|array', 'items.*.sl_no' => 'required|integer', 'items.*.item_id' => 'required|integer', 'items.*.description' => 'required|string', 'items.*.quantity' => 'required|numeric', 'items.*.uom' => 'required|string', 'items.*.rate' => 'required|numeric', 'items.*.tds' => 'nullable|numeric', 'items.*.discount' => 'nullable|numeric', 'items.*.value' => 'nullable|numeric', 'items.*.sgst' => 'nullable|numeric', 'items.*.cgst' => 'nullable|numeric', 'items.*.igst' => 'nullable|numeric', 'items.*.amount' => 'nullable|numeric', 'additional_charges' => 'nullable|numeric', 'total_tds' => 'nullable|numeric', 'sub_total' => 'required|numeric', 'total_amount' => 'required|numeric', 'paid_amount' => 'nullable|numeric', 'balance_amount' => 'nullable|numeric', 'payment_mode' => 'nullable|string', 'payment_reference' => 'nullable|string', 'payment_reason' => 'nullable|string', 'note' => 'nullable|string', ]); // Step 2: Create the purchase record $purchase = Purchase::create([ 'purchase_id' => $validatedData['purchase_no'], // Use purchase_no as purchase_id 'purchase_date' => $validatedData['purchase_order_date'], 'invoice_no' => $validatedData['invoice_no'], 'invoice_date' => $validatedData['invoice_date'], 'party_id' => $validatedData['purchase_id'], // Assuming purchase_id refers to party_id 'sub_total' => $validatedData['sub_total'], 'additional_charges' => $validatedData['additional_charges'], 'tds' => $validatedData['total_tds'], 'total_amount' => $validatedData['total_amount'], 'paid_amount' => $validatedData['paid_amount'], 'balance_amount' => $validatedData['balance_amount'], 'status' => 'unpaid', // Default status 'payment_mode' => $validatedData['payment_mode'], 'reference' => $validatedData['payment_reference'], 'notes' => $validatedData['note'], ]); // Step 3: Create purchase items records foreach ($validatedData['items'] as $item) { PurchaseItem::create([ 'purchase_id' => $purchase->id, 'item_id' => $item['item_id'], 'uom' => $item['uom'], 'quantity' => $item['quantity'], 'rate' => $item['rate'], 'discount' => $item['discount'], ]); } // Step 4: Handle files (if any) if ($request->hasFile('purchase_files')) { // Logic to handle file uploads // Example: Store files in storage and save paths in database } // Step 5: Return a response return redirect()->route('superadmin.purchase.index')->with('success', 'Purchase created successfully'); } public function getNextInvoiceNumber() { Log::info('PurchaseManagementController@getNextInvoiceNumber called'); $latest = Purchase::orderBy('id', 'desc')->first(); if (!$latest || !$latest->invoice_no) { $nextNumber = 1; } else { // Extract the last 4 digits and increment $lastNumber = (int)substr($latest->invoice_no, -4); $nextNumber = $lastNumber + 1; } $nextInvoiceNo = 'UEPL/P/INV/' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT); Log::info('PurchaseManagementController@getNextInvoiceNumber finished', ['invoice_no' => $nextInvoiceNo]); return response()->json(['invoice_no' => $nextInvoiceNo]); } public function export(Request $request) { Log::info('PurchaseManagementController@export called', ['request' => $request->all()]); // Same filter logic as your index() $query = \App\Models\Purchase::with(['party', 'items.item']); if ($request->date_range) { [$start, $end] = explode(' to ', $request->date_range); $query->whereBetween('invoice_date', [$start, $end]); } if ($request->party_id) { $query->where('party_id', $request->party_id); } if ($request->invoice_no) { $query->where('invoice_no', 'like', '%' . $request->invoice_no . '%'); } if ($request->status) { $query->where('status', $request->status); } $purchases = $query->orderBy('invoice_date', 'desc')->get(); // Create Spreadsheet $spreadsheet = new Spreadsheet(); $sheet = $spreadsheet->getActiveSheet(); // Set headers $sheet->fromArray([ ['Purchase Id', 'Purchase Date', 'Invoice No', 'Invoice Date', 'Party', 'Sub Total', 'Total Amount', 'Paid Amount', 'Balance', 'Status', 'Notes', 'Item Name', 'Quantity', 'UOM', 'Rate', 'Discount %'] ], NULL, 'A1'); $row = 2; foreach ($purchases as $purchase) { $first = true; foreach ($purchase->items as $item) { $sheet->fromArray([ $first ? $purchase->purchase_id : '', $first ? $purchase->purchase_date : '', $first ? $purchase->invoice_no : '', $first ? $purchase->invoice_date : '', $first ? ($purchase->party ? $purchase->party->company : '') : '', $first ? $purchase->sub_total : '', $first ? $purchase->total_amount : '', $first ? $purchase->paid_amount : '', $first ? $purchase->balance_amount : '', $first ? $purchase->status : '', $first ? $purchase->notes : '', $item->item ? $item->item->item_name : '', $item->quantity, $item->uom, $item->rate, $item->discount ], NULL, 'A' . $row); $row++; $first = false; } // If no items, still show the purchase if ($first) { $sheet->fromArray([ $purchase->purchase_id, $purchase->purchase_date, $purchase->invoice_no, $purchase->invoice_date, $purchase->party ? $purchase->party->company : '', $purchase->sub_total, $purchase->total_amount, $purchase->paid_amount, $purchase->balance_amount, $purchase->status, $purchase->notes, '', '', '', '', '' ], NULL, 'A' . $row); $row++; } } // Download response $writer = new Xlsx($spreadsheet); $filename = 'purchase_export_' . date('Ymd_His') . '.xlsx'; $temp_file = tempnam(sys_get_temp_dir(), $filename); $writer->save($temp_file); return response()->download($temp_file, $filename)->deleteFileAfterSend(true); } public function getNextCreditNoteNo() { $year = date('Y'); $latest = \App\Models\CreditNote::where('credit_note_no', 'like', "CN-{$year}-%") ->orderByDesc('id')->first(); if (!$latest) { $nextNumber = 1; } else { $lastNumber = (int)substr($latest->credit_note_no, -4); $nextNumber = $lastNumber + 1; } $nextCreditNo = 'CN-' . $year . '-' . str_pad($nextNumber, 4, '0', STR_PAD_LEFT); return response()->json(['credit_note_no' => $nextCreditNo]); } public function creditNoteStore(Request $request) { Log::info('PurchaseManagementController@creditNoteStore called', ['request' => $request->all()]); $data = $request->validate([ 'credit_note_no' => 'required|unique:credit_notes,credit_note_no', 'date' => 'required|date', 'invoice_no' => 'required|string', 'party_id' => 'required|exists:customer_vendors,id', 'amount' => 'required|numeric|min:0', 'reason' => 'required|string', 'description' => 'nullable|string', ]); $creditNote = \App\Models\CreditNote::create($data); return response()->json(['success' => true, 'credit_note_id' => $creditNote->id]); } public function payments(Request $request) { $query = Purchase::with('party'); // Filters if ($request->date_range) { [$start, $end] = explode(' to ', $request->date_range); $query->whereBetween('invoice_date', [$start, $end]); } if ($request->party_id) { $query->where('party_id', $request->party_id); } if ($request->min_amount) { $query->where('paid_amount', '>=', $request->min_amount); } if ($request->max_amount) { $query->where('paid_amount', '<=', $request->max_amount); } return datatables()->of($query->latest()) ->addColumn('party', fn($row) => $row->party ? $row->party->company : '') ->addColumn('purchase_id', fn($row) => $row->purchase_id) ->addColumn('purchase_date', fn($row) => $row->purchase_date) ->addColumn('invoice_no', fn($row) => $row->invoice_no) ->addColumn('invoice_date', fn($row) => $row->invoice_date) ->addColumn('paid_amount', fn($row) => $row->paid_amount) ->addColumn('payment_mode', fn($row) => $row->payment_mode) ->addColumn('reference', fn($row) => $row->reference) ->addColumn('actions', function ($row) { return ''; }) ->rawColumns(['actions']) ->make(true); } public function Vendorshow($id) { $purchase = Purchase::with(['party', 'items.item'])->findOrFail($id); return response()->json($purchase); } public function creditNotesData(Request $request) { $query = CreditNote::with('party'); // Filters (optional) if ($request->date_range) { [$start, $end] = explode(' to ', $request->date_range); $query->whereBetween('date', [$start, $end]); } //dd($start, $end); if ($request->party_id) { $query->where('party_id', $request->party_id); } if ($request->credit_note_no) { $query->where('credit_note_no', 'like', "%{$request->credit_note_no}%"); } // DataTables response return datatables()->eloquent($query)->addColumn('party', function ($row) { return $row->party ? $row->party->company : ''; })->addColumn('actions', function ($row) { return ''; })->rawColumns(['actions']) ->make(true); } public function showCreditNote($id) { $creditNote = \App\Models\CreditNote::with('party')->findOrFail($id); return response()->json($creditNote); } public function agingReport(Request $request) { // Validate and get filters $asOfDate = $request->filled('as_of_date') ? $request->as_of_date : date('Y-m-d'); $partyId = $request->filled('party_id') ? $request->party_id : null; // Query purchases with balance > 0 $query = \App\Models\Purchase::with('party') ->where('invoice_date', '<=', $asOfDate) ->where('balance_amount', '>', 0); if ($partyId) { $query->where('party_id', $partyId); } $purchases = $query->get(); // Group by party $report = []; foreach ($purchases as $purchase) { $days = \Carbon\Carbon::parse($purchase->invoice_date)->diffInDays($asOfDate, false); $party = $purchase->party ? $purchase->party->company : 'Unknown'; if (!isset($report[$party])) { $report[$party] = [ 'party' => $party, 'current' => 0, '1_30' => 0, '31_60' => 0, '61_90' => 0, 'over_90' => 0, 'total_due' => 0, ]; } if ($days <= 0) { $report[$party]['current'] += $purchase->balance_amount; } elseif ($days <= 30) { $report[$party]['1_30'] += $purchase->balance_amount; } elseif ($days <= 60) { $report[$party]['31_60'] += $purchase->balance_amount; } elseif ($days <= 90) { $report[$party]['61_90'] += $purchase->balance_amount; } else { $report[$party]['over_90'] += $purchase->balance_amount; } $report[$party]['total_due'] += $purchase->balance_amount; } // Convert to datatable-friendly array $report = array_values($report); return response()->json([ 'data' => $report ]); } public function destroy_old(Purchase $purchase) { $purchase->delete(); return response()->json(['success' => true, 'message' => 'Purchase deleted.']); } public function destroy(Purchase $purchase) { // Delete related items $purchase->items()->delete(); // Delete related files from storage and DB foreach ($purchase->files as $file) { if ($file->file_path && Storage::disk('public')->exists($file->file_path)) { Storage::disk('public')->delete($file->file_path); } $file->delete(); } // Delete the purchase record $purchase->delete(); return response()->json(['success' => true, 'message' => 'Purchase and related data deleted.']); } public function deleteFile(PurchaseFile $file) { if ($file->file_path && Storage::disk('public')->exists($file->file_path)) { Storage::disk('public')->delete($file->file_path); } $file->delete(); return response()->json(['success' => true, 'message' => 'File deleted successfully']); } }