invoice update

This commit is contained in:
Abhishek Mali
2025-11-17 10:33:11 +05:30
parent cfd128cf9b
commit df89031d36
16 changed files with 1330 additions and 139 deletions

View File

@@ -0,0 +1,122 @@
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use Mpdf\Mpdf;
class AdminInvoiceController extends Controller
{
// -------------------------------------------------------------
// INVOICE LIST PAGE
// -------------------------------------------------------------
public function index()
{
$invoices = Invoice::latest()->get();
return view('admin.invoice', compact('invoices'));
}
// -------------------------------------------------------------
// POPUP VIEW (AJAX)
// -------------------------------------------------------------
public function popup($id)
{
$invoice = Invoice::with('items')->findOrFail($id);
return view('admin.popup_invoice', compact('invoice'));
}
// -------------------------------------------------------------
// EDIT INVOICE PAGE
// -------------------------------------------------------------
public function edit($id)
{
$invoice = Invoice::findOrFail($id);
return view('admin.invoice_edit', compact('invoice'));
}
// -------------------------------------------------------------
// UPDATE INVOICE
// -------------------------------------------------------------
public function update(Request $request, $id)
{
$invoice = Invoice::findOrFail($id);
// Validate editable fields
$data = $request->validate([
'invoice_date' => 'required|date',
'due_date' => 'required|date|after_or_equal:invoice_date',
'payment_method' => 'nullable|string',
'reference_no' => 'nullable|string|max:255',
'final_amount' => 'required|numeric|min:0',
'gst_percent' => 'required|numeric|min:0|max:28',
'status' => 'required|in:pending,paid,overdue',
'notes' => 'nullable|string',
]);
// Auto-calc
$gst_amount = ($data['final_amount'] * $data['gst_percent']) / 100;
$final_amount_with_gst = $data['final_amount'] + $gst_amount;
$data['gst_amount'] = $gst_amount;
$data['final_amount_with_gst'] = $final_amount_with_gst;
// Update DB
$invoice->update($data);
// Generate PDF
$this->generateInvoicePDF($invoice);
return redirect()
->route('admin.invoices.index')
->with('success', 'Invoice updated & PDF generated successfully.');
}
// -------------------------------------------------------------
// PDF GENERATION USING mPDF
// -------------------------------------------------------------
public function generateInvoicePDF($invoice)
{
// PDF Name
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
// Save directly in /public/invoices
$folder = public_path('invoices/');
// Create folder if not exists
if (!file_exists($folder)) {
mkdir($folder, 0777, true);
}
// Full path
$filePath = $folder . $fileName;
// Delete old file
if (file_exists($filePath)) {
unlink($filePath);
}
// Initialize mPDF
$mpdf = new Mpdf([
'mode' => 'utf-8',
'format' => 'A4',
'default_font' => 'sans-serif'
]);
// Load HTML view
$html = view('admin.pdf.invoice', compact('invoice'))->render();
// Generate
$mpdf->WriteHTML($html);
// Save to public/invoices
$mpdf->Output($filePath, 'F');
// Save path in DB
$invoice->update([
'pdf_path' => 'invoices/' . $fileName
]);
}
}

View File

@@ -99,82 +99,163 @@ class AdminOrderController extends Controller
// -------------------------------------------------------------------------
public function finishOrder(Request $request)
{
$request->validate([
'mark_no' => 'required',
'origin' => 'required',
'destination' => 'required',
]);
{
$request->validate([
'mark_no' => 'required',
'origin' => 'required',
'destination' => 'required',
]);
$items = session('temp_order_items', []);
$items = session('temp_order_items', []);
if (empty($items)) {
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
->with('error', 'Add at least one item before finishing.');
}
// Generate Order ID
$year = date('y');
$prefix = "KNT-$year-";
$lastOrder = Order::latest('id')->first();
$nextNumber = $lastOrder ? intval(substr($lastOrder->order_id, -8)) + 1 : 1;
$orderId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT);
// TOTAL SUMS
$total_ctn = array_sum(array_column($items, 'ctn'));
$total_qty = array_sum(array_column($items, 'qty'));
$total_ttl_qty = array_sum(array_column($items, 'ttl_qty'));
$total_amount = array_sum(array_column($items, 'ttl_amount'));
$total_cbm = array_sum(array_column($items, 'cbm'));
$total_ttl_cbm = array_sum(array_column($items, 'ttl_cbm'));
$total_kg = array_sum(array_column($items, 'kg'));
$total_ttl_kg = array_sum(array_column($items, 'ttl_kg'));
// CREATE ORDER
$order = Order::create([
'order_id' => $orderId,
'mark_no' => $request->mark_no,
'origin' => $request->origin,
'destination' => $request->destination,
'ctn' => $total_ctn,
'qty' => $total_qty,
'ttl_qty' => $total_ttl_qty,
'ttl_amount' => $total_amount,
'cbm' => $total_cbm,
'ttl_cbm' => $total_ttl_cbm,
'kg' => $total_kg,
'ttl_kg' => $total_ttl_kg,
'status' => 'pending',
]);
// SAVE ALL SUB-ITEMS
foreach ($items as $item) {
OrderItem::create([
'order_id' => $order->id,
'description' => $item['description'],
'ctn' => $item['ctn'],
'qty' => $item['qty'],
'ttl_qty' => $item['ttl_qty'],
'unit' => $item['unit'],
'price' => $item['price'],
'ttl_amount' => $item['ttl_amount'],
'cbm' => $item['cbm'],
'ttl_cbm' => $item['ttl_cbm'],
'kg' => $item['kg'],
'ttl_kg' => $item['ttl_kg'],
'shop_no' => $item['shop_no'],
]);
}
// CLEAR TEMP DATA
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
return redirect()->route('admin.orders.index')
->with('success', 'Order saved successfully.');
if (empty($items)) {
return redirect()->to(route('admin.orders.index') . '#createOrderForm')
->with('error', 'Add at least one item before finishing.');
}
// =======================
// GENERATE ORDER ID
// =======================
$year = date('y');
$prefix = "KNT-$year-";
$lastOrder = Order::latest('id')->first();
$nextNumber = $lastOrder ? intval(substr($lastOrder->order_id, -8)) + 1 : 1;
$orderId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT);
// =======================
// TOTAL SUMS
// =======================
$total_ctn = array_sum(array_column($items, 'ctn'));
$total_qty = array_sum(array_column($items, 'qty'));
$total_ttl_qty = array_sum(array_column($items, 'ttl_qty'));
$total_amount = array_sum(array_column($items, 'ttl_amount'));
$total_cbm = array_sum(array_column($items, 'cbm'));
$total_ttl_cbm = array_sum(array_column($items, 'ttl_cbm'));
$total_kg = array_sum(array_column($items, 'kg'));
$total_ttl_kg = array_sum(array_column($items, 'ttl_kg'));
// =======================
// CREATE ORDER
// =======================
$order = Order::create([
'order_id' => $orderId,
'mark_no' => $request->mark_no,
'origin' => $request->origin,
'destination' => $request->destination,
'ctn' => $total_ctn,
'qty' => $total_qty,
'ttl_qty' => $total_ttl_qty,
'ttl_amount' => $total_amount,
'cbm' => $total_cbm,
'ttl_cbm' => $total_ttl_cbm,
'kg' => $total_kg,
'ttl_kg' => $total_ttl_kg,
'status' => 'pending',
]);
// SAVE ORDER ITEMS
foreach ($items as $item) {
OrderItem::create([
'order_id' => $order->id,
'description' => $item['description'],
'ctn' => $item['ctn'],
'qty' => $item['qty'],
'ttl_qty' => $item['ttl_qty'],
'unit' => $item['unit'],
'price' => $item['price'],
'ttl_amount' => $item['ttl_amount'],
'cbm' => $item['cbm'],
'ttl_cbm' => $item['ttl_cbm'],
'kg' => $item['kg'],
'ttl_kg' => $item['ttl_kg'],
'shop_no' => $item['shop_no'],
]);
}
// =======================
// INVOICE CREATION START
// =======================
// 1. Auto-generate invoice number
$lastInvoice = \App\Models\Invoice::latest()->first();
$nextInvoice = $lastInvoice ? $lastInvoice->id + 1 : 1;
$invoiceNumber = 'INV-' . date('Y') . '-' . str_pad($nextInvoice, 6, '0', STR_PAD_LEFT);
// 2. Fetch customer (using mark list → customer_id)
$markList = MarkList::where('mark_no', $order->mark_no)->first();
$customer = null;
if ($markList && $markList->customer_id) {
$customer = \App\Models\User::where('customer_id', $markList->customer_id)->first();
}
// 3. Create Invoice Record
$invoice = \App\Models\Invoice::create([
'order_id' => $order->id,
'customer_id' => $customer->id ?? null,
'mark_no' => $order->mark_no,
'invoice_number' => $invoiceNumber,
'invoice_date' => now(),
'due_date' => now()->addDays(10),
'payment_method' => null,
'reference_no' => null,
'status' => 'pending',
'final_amount' => $total_amount,
'gst_percent' => 0,
'gst_amount' => 0,
'final_amount_with_gst' => $total_amount,
// snapshot customer fields
'customer_name' => $customer->customer_name ?? null,
'company_name' => $customer->company_name ?? null,
'customer_email' => $customer->email ?? null,
'customer_mobile' => $customer->mobile_no ?? null,
'customer_address' => $customer->address ?? null,
'pincode' => $customer->pincode ?? null,
'notes' => null,
]);
// 4. Clone order items into invoice_items
foreach ($order->items as $item) {
\App\Models\InvoiceItem::create([
'invoice_id' => $invoice->id,
'description' => $item->description,
'ctn' => $item->ctn,
'qty' => $item->qty,
'ttl_qty' => $item->ttl_qty,
'unit' => $item->unit,
'price' => $item->price,
'ttl_amount' => $item->ttl_amount,
'cbm' => $item->cbm,
'ttl_cbm' => $item->ttl_cbm,
'kg' => $item->kg,
'ttl_kg' => $item->ttl_kg,
'shop_no' => $item->shop_no,
]);
}
// 5. TODO: PDF generation (I will add this later)
$invoice->pdf_path = null; // placeholder for now
$invoice->save();
// =======================
// END INVOICE CREATION
// =======================
// CLEAR TEMP DATA
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
return redirect()->route('admin.orders.index')
->with('success', 'Order + Invoice created successfully.');
}
// -------------------------------------------------------------------------
// ORDER SHOW PAGE
// -------------------------------------------------------------------------

77
app/Models/Invoice.php Normal file
View File

@@ -0,0 +1,77 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Invoice extends Model
{
use HasFactory;
protected $fillable = [
'order_id',
'customer_id',
'mark_no',
'invoice_number',
'invoice_date',
'due_date',
'payment_method',
'reference_no',
'status',
'final_amount',
'gst_percent',
'gst_amount',
'final_amount_with_gst',
'customer_name',
'company_name',
'customer_email',
'customer_mobile',
'customer_address',
'pincode',
'pdf_path',
'notes',
];
/****************************
* Relationships
****************************/
public function items()
{
return $this->hasMany(InvoiceItem::class)->orderBy('id', 'ASC');
}
public function order()
{
return $this->belongsTo(Order::class);
}
public function customer()
{
return $this->belongsTo(User::class, 'customer_id');
}
/****************************
* Helper Functions
****************************/
// Auto calculate GST fields (you can call this in controller before saving)
public function calculateTotals()
{
$gst = ($this->final_amount * $this->gst_percent) / 100;
$this->gst_amount = $gst;
$this->final_amount_with_gst = $this->final_amount + $gst;
}
// Check overdue status condition
public function isOverdue()
{
return $this->status === 'pending' && now()->gt($this->due_date);
}
}

View File

@@ -0,0 +1,40 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class InvoiceItem extends Model
{
use HasFactory;
protected $fillable = [
'invoice_id',
'description',
'ctn',
'qty',
'ttl_qty',
'unit',
'price',
'ttl_amount',
'cbm',
'ttl_cbm',
'kg',
'ttl_kg',
'shop_no',
];
/****************************
* Relationships
****************************/
public function invoice()
{
return $this->belongsTo(Invoice::class);
}
}