invoice update
This commit is contained in:
122
app/Http/Controllers/Admin/AdminInvoiceController.php
Normal file
122
app/Http/Controllers/Admin/AdminInvoiceController.php
Normal 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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,7 +113,9 @@ class AdminOrderController extends Controller
|
|||||||
->with('error', 'Add at least one item before finishing.');
|
->with('error', 'Add at least one item before finishing.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate Order ID
|
// =======================
|
||||||
|
// GENERATE ORDER ID
|
||||||
|
// =======================
|
||||||
$year = date('y');
|
$year = date('y');
|
||||||
$prefix = "KNT-$year-";
|
$prefix = "KNT-$year-";
|
||||||
|
|
||||||
@@ -122,7 +124,9 @@ class AdminOrderController extends Controller
|
|||||||
|
|
||||||
$orderId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT);
|
$orderId = $prefix . str_pad($nextNumber, 8, '0', STR_PAD_LEFT);
|
||||||
|
|
||||||
|
// =======================
|
||||||
// TOTAL SUMS
|
// TOTAL SUMS
|
||||||
|
// =======================
|
||||||
$total_ctn = array_sum(array_column($items, 'ctn'));
|
$total_ctn = array_sum(array_column($items, 'ctn'));
|
||||||
$total_qty = array_sum(array_column($items, 'qty'));
|
$total_qty = array_sum(array_column($items, 'qty'));
|
||||||
$total_ttl_qty = array_sum(array_column($items, 'ttl_qty'));
|
$total_ttl_qty = array_sum(array_column($items, 'ttl_qty'));
|
||||||
@@ -132,7 +136,9 @@ class AdminOrderController extends Controller
|
|||||||
$total_kg = array_sum(array_column($items, 'kg'));
|
$total_kg = array_sum(array_column($items, 'kg'));
|
||||||
$total_ttl_kg = array_sum(array_column($items, 'ttl_kg'));
|
$total_ttl_kg = array_sum(array_column($items, 'ttl_kg'));
|
||||||
|
|
||||||
|
// =======================
|
||||||
// CREATE ORDER
|
// CREATE ORDER
|
||||||
|
// =======================
|
||||||
$order = Order::create([
|
$order = Order::create([
|
||||||
'order_id' => $orderId,
|
'order_id' => $orderId,
|
||||||
'mark_no' => $request->mark_no,
|
'mark_no' => $request->mark_no,
|
||||||
@@ -149,7 +155,7 @@ class AdminOrderController extends Controller
|
|||||||
'status' => 'pending',
|
'status' => 'pending',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// SAVE ALL SUB-ITEMS
|
// SAVE ORDER ITEMS
|
||||||
foreach ($items as $item) {
|
foreach ($items as $item) {
|
||||||
OrderItem::create([
|
OrderItem::create([
|
||||||
'order_id' => $order->id,
|
'order_id' => $order->id,
|
||||||
@@ -168,13 +174,88 @@ class AdminOrderController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// 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
|
// CLEAR TEMP DATA
|
||||||
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
|
session()->forget(['temp_order_items', 'mark_no', 'origin', 'destination']);
|
||||||
|
|
||||||
return redirect()->route('admin.orders.index')
|
return redirect()->route('admin.orders.index')
|
||||||
->with('success', 'Order saved successfully.');
|
->with('success', 'Order + Invoice created successfully.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// ORDER SHOW PAGE
|
// ORDER SHOW PAGE
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
77
app/Models/Invoice.php
Normal file
77
app/Models/Invoice.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
app/Models/InvoiceItem.php
Normal file
40
app/Models/InvoiceItem.php
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
"php": "^8.2",
|
"php": "^8.2",
|
||||||
"laravel/framework": "^12.0",
|
"laravel/framework": "^12.0",
|
||||||
"laravel/tinker": "^2.10.1",
|
"laravel/tinker": "^2.10.1",
|
||||||
|
"mpdf/mpdf": "^8.2",
|
||||||
"php-open-source-saver/jwt-auth": "2.8"
|
"php-open-source-saver/jwt-auth": "2.8"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
417
composer.lock
generated
417
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "dc4cd5d2076c08406e2d0bd4db2cd741",
|
"content-hash": "fa680e1e8b3550d710849bbd820e3626",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "brick/math",
|
"name": "brick/math",
|
||||||
@@ -2184,6 +2184,239 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-03-24T10:02:05+00:00"
|
"time": "2025-03-24T10:02:05+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "mpdf/mpdf",
|
||||||
|
"version": "v8.2.6",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mpdf/mpdf.git",
|
||||||
|
"reference": "dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/mpdf/mpdf/zipball/dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44",
|
||||||
|
"reference": "dd30e3b01061cf8dfe65e7041ab4cc46d8ebdd44",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-gd": "*",
|
||||||
|
"ext-mbstring": "*",
|
||||||
|
"mpdf/psr-http-message-shim": "^1.0 || ^2.0",
|
||||||
|
"mpdf/psr-log-aware-trait": "^2.0 || ^3.0",
|
||||||
|
"myclabs/deep-copy": "^1.7",
|
||||||
|
"paragonie/random_compat": "^1.4|^2.0|^9.99.99",
|
||||||
|
"php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0",
|
||||||
|
"psr/http-message": "^1.0 || ^2.0",
|
||||||
|
"psr/log": "^1.0 || ^2.0 || ^3.0",
|
||||||
|
"setasign/fpdi": "^2.1"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"mockery/mockery": "^1.3.0",
|
||||||
|
"mpdf/qrcode": "^1.1.0",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5.0",
|
||||||
|
"tracy/tracy": "~2.5",
|
||||||
|
"yoast/phpunit-polyfills": "^1.0"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-bcmath": "Needed for generation of some types of barcodes",
|
||||||
|
"ext-xml": "Needed mainly for SVG manipulation",
|
||||||
|
"ext-zlib": "Needed for compression of embedded resources, such as fonts"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/functions.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"Mpdf\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"GPL-2.0-only"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Matěj Humpál",
|
||||||
|
"role": "Developer, maintainer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Ian Back",
|
||||||
|
"role": "Developer (retired)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP library generating PDF files from UTF-8 encoded HTML",
|
||||||
|
"homepage": "https://mpdf.github.io",
|
||||||
|
"keywords": [
|
||||||
|
"pdf",
|
||||||
|
"php",
|
||||||
|
"utf-8"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"docs": "https://mpdf.github.io",
|
||||||
|
"issues": "https://github.com/mpdf/mpdf/issues",
|
||||||
|
"source": "https://github.com/mpdf/mpdf"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://www.paypal.me/mpdf",
|
||||||
|
"type": "custom"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-08-18T08:51:51+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mpdf/psr-http-message-shim",
|
||||||
|
"version": "v2.0.1",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mpdf/psr-http-message-shim.git",
|
||||||
|
"reference": "f25a0153d645e234f9db42e5433b16d9b113920f"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/mpdf/psr-http-message-shim/zipball/f25a0153d645e234f9db42e5433b16d9b113920f",
|
||||||
|
"reference": "f25a0153d645e234f9db42e5433b16d9b113920f",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"psr/http-message": "^2.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Mpdf\\PsrHttpMessageShim\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Dorison",
|
||||||
|
"email": "mark@chromatichq.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Kristofer Widholm",
|
||||||
|
"email": "kristofer@chromatichq.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Nigel Cunningham",
|
||||||
|
"email": "nigel.cunningham@technocrat.com.au"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Shim to allow support of different psr/message versions.",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/mpdf/psr-http-message-shim/issues",
|
||||||
|
"source": "https://github.com/mpdf/psr-http-message-shim/tree/v2.0.1"
|
||||||
|
},
|
||||||
|
"time": "2023-10-02T14:34:03+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mpdf/psr-log-aware-trait",
|
||||||
|
"version": "v3.0.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/mpdf/psr-log-aware-trait.git",
|
||||||
|
"reference": "a633da6065e946cc491e1c962850344bb0bf3e78"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/mpdf/psr-log-aware-trait/zipball/a633da6065e946cc491e1c962850344bb0bf3e78",
|
||||||
|
"reference": "a633da6065e946cc491e1c962850344bb0bf3e78",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"psr/log": "^3.0"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Mpdf\\PsrLogAwareTrait\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Mark Dorison",
|
||||||
|
"email": "mark@chromatichq.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Kristofer Widholm",
|
||||||
|
"email": "kristofer@chromatichq.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Trait to allow support of different psr/log versions.",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/mpdf/psr-log-aware-trait/issues",
|
||||||
|
"source": "https://github.com/mpdf/psr-log-aware-trait/tree/v3.0.0"
|
||||||
|
},
|
||||||
|
"time": "2023-05-03T06:19:36+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "myclabs/deep-copy",
|
||||||
|
"version": "1.13.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||||
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
|
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"doctrine/collections": "<1.6.8",
|
||||||
|
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"doctrine/collections": "^1.6.8",
|
||||||
|
"doctrine/common": "^2.13.3 || ^3.2.2",
|
||||||
|
"phpspec/prophecy": "^1.10",
|
||||||
|
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"files": [
|
||||||
|
"src/DeepCopy/deep_copy.php"
|
||||||
|
],
|
||||||
|
"psr-4": {
|
||||||
|
"DeepCopy\\": "src/DeepCopy/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"description": "Create deep copies (clones) of your objects",
|
||||||
|
"keywords": [
|
||||||
|
"clone",
|
||||||
|
"copy",
|
||||||
|
"duplicate",
|
||||||
|
"object",
|
||||||
|
"object graph"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||||
|
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-08-01T08:46:24+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "namshi/jose",
|
"name": "namshi/jose",
|
||||||
"version": "7.2.3",
|
"version": "7.2.3",
|
||||||
@@ -2655,6 +2888,56 @@
|
|||||||
],
|
],
|
||||||
"time": "2025-10-18T11:10:27+00:00"
|
"time": "2025-10-18T11:10:27+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "paragonie/random_compat",
|
||||||
|
"version": "v9.99.100",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/paragonie/random_compat.git",
|
||||||
|
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/paragonie/random_compat/zipball/996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||||
|
"reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">= 7"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "4.*|5.*",
|
||||||
|
"vimeo/psalm": "^1"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Paragon Initiative Enterprises",
|
||||||
|
"email": "security@paragonie.com",
|
||||||
|
"homepage": "https://paragonie.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7",
|
||||||
|
"keywords": [
|
||||||
|
"csprng",
|
||||||
|
"polyfill",
|
||||||
|
"pseudorandom",
|
||||||
|
"random"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"email": "info@paragonie.com",
|
||||||
|
"issues": "https://github.com/paragonie/random_compat/issues",
|
||||||
|
"source": "https://github.com/paragonie/random_compat"
|
||||||
|
},
|
||||||
|
"time": "2020-10-15T08:29:30+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "php-open-source-saver/jwt-auth",
|
"name": "php-open-source-saver/jwt-auth",
|
||||||
"version": "2.8.0",
|
"version": "2.8.0",
|
||||||
@@ -3515,6 +3798,78 @@
|
|||||||
},
|
},
|
||||||
"time": "2025-09-04T20:59:21+00:00"
|
"time": "2025-09-04T20:59:21+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setasign/fpdi",
|
||||||
|
"version": "v2.6.4",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/Setasign/FPDI.git",
|
||||||
|
"reference": "4b53852fde2734ec6a07e458a085db627c60eada"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/Setasign/FPDI/zipball/4b53852fde2734ec6a07e458a085db627c60eada",
|
||||||
|
"reference": "4b53852fde2734ec6a07e458a085db627c60eada",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ext-zlib": "*",
|
||||||
|
"php": "^7.1 || ^8.0"
|
||||||
|
},
|
||||||
|
"conflict": {
|
||||||
|
"setasign/tfpdf": "<1.31"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "^7",
|
||||||
|
"setasign/fpdf": "~1.8.6",
|
||||||
|
"setasign/tfpdf": "~1.33",
|
||||||
|
"squizlabs/php_codesniffer": "^3.5",
|
||||||
|
"tecnickcom/tcpdf": "^6.8"
|
||||||
|
},
|
||||||
|
"suggest": {
|
||||||
|
"setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured."
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"setasign\\Fpdi\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Jan Slabon",
|
||||||
|
"email": "jan.slabon@setasign.com",
|
||||||
|
"homepage": "https://www.setasign.com"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Maximilian Kresse",
|
||||||
|
"email": "maximilian.kresse@setasign.com",
|
||||||
|
"homepage": "https://www.setasign.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.",
|
||||||
|
"homepage": "https://www.setasign.com/fpdi",
|
||||||
|
"keywords": [
|
||||||
|
"fpdf",
|
||||||
|
"fpdi",
|
||||||
|
"pdf"
|
||||||
|
],
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/Setasign/FPDI/issues",
|
||||||
|
"source": "https://github.com/Setasign/FPDI/tree/v2.6.4"
|
||||||
|
},
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/setasign/fpdi",
|
||||||
|
"type": "tidelift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"time": "2025-08-05T09:57:14+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/clock",
|
"name": "symfony/clock",
|
||||||
"version": "v7.3.0",
|
"version": "v7.3.0",
|
||||||
@@ -6751,66 +7106,6 @@
|
|||||||
},
|
},
|
||||||
"time": "2024-05-16T03:13:13+00:00"
|
"time": "2024-05-16T03:13:13+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "myclabs/deep-copy",
|
|
||||||
"version": "1.13.4",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
|
||||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
|
||||||
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"php": "^7.1 || ^8.0"
|
|
||||||
},
|
|
||||||
"conflict": {
|
|
||||||
"doctrine/collections": "<1.6.8",
|
|
||||||
"doctrine/common": "<2.13.3 || >=3 <3.2.2"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"doctrine/collections": "^1.6.8",
|
|
||||||
"doctrine/common": "^2.13.3 || ^3.2.2",
|
|
||||||
"phpspec/prophecy": "^1.10",
|
|
||||||
"phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"autoload": {
|
|
||||||
"files": [
|
|
||||||
"src/DeepCopy/deep_copy.php"
|
|
||||||
],
|
|
||||||
"psr-4": {
|
|
||||||
"DeepCopy\\": "src/DeepCopy/"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"description": "Create deep copies (clones) of your objects",
|
|
||||||
"keywords": [
|
|
||||||
"clone",
|
|
||||||
"copy",
|
|
||||||
"duplicate",
|
|
||||||
"object",
|
|
||||||
"object graph"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
|
||||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
|
|
||||||
},
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
|
|
||||||
"type": "tidelift"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"time": "2025-08-01T08:46:24+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "nunomaduro/collision",
|
"name": "nunomaduro/collision",
|
||||||
"version": "v8.8.2",
|
"version": "v8.8.2",
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateInvoicesTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('invoices', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
// Links
|
||||||
|
$table->unsignedBigInteger('order_id')->index();
|
||||||
|
$table->unsignedBigInteger('customer_id')->nullable()->index(); // snapshot link if available
|
||||||
|
$table->string('mark_no')->nullable()->index();
|
||||||
|
|
||||||
|
// Invoice identity
|
||||||
|
$table->string('invoice_number')->unique();
|
||||||
|
$table->date('invoice_date')->nullable();
|
||||||
|
$table->date('due_date')->nullable();
|
||||||
|
|
||||||
|
// Payment / status
|
||||||
|
$table->string('payment_method')->nullable();
|
||||||
|
$table->string('reference_no')->nullable();
|
||||||
|
$table->enum('status', ['pending','paid','overdue'])->default('pending');
|
||||||
|
|
||||||
|
// Amounts
|
||||||
|
$table->decimal('final_amount', 14, 2)->default(0.00); // editable by user
|
||||||
|
$table->decimal('gst_percent', 5, 2)->default(0.00); // editable by user
|
||||||
|
$table->decimal('gst_amount', 14, 2)->default(0.00); // auto-calculated
|
||||||
|
$table->decimal('final_amount_with_gst', 14, 2)->default(0.00); // auto-calculated
|
||||||
|
|
||||||
|
// Customer snapshot (immutable fields)
|
||||||
|
$table->string('customer_name')->nullable();
|
||||||
|
$table->string('company_name')->nullable();
|
||||||
|
$table->string('customer_email')->nullable();
|
||||||
|
$table->string('customer_mobile')->nullable();
|
||||||
|
$table->text('customer_address')->nullable();
|
||||||
|
$table->string('pincode')->nullable();
|
||||||
|
|
||||||
|
// PDF / notes
|
||||||
|
$table->string('pdf_path')->nullable();
|
||||||
|
$table->text('notes')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// Foreign keys (optional — adjust table names/namespaces if yours are different)
|
||||||
|
$table->foreign('order_id')->references('id')->on('orders')->onDelete('cascade');
|
||||||
|
// customer_id may reference users table, keep nullable to avoid migration order issues
|
||||||
|
$table->foreign('customer_id')->references('id')->on('users')->onDelete('set null');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['order_id']);
|
||||||
|
$table->dropForeign(['customer_id']);
|
||||||
|
});
|
||||||
|
Schema::dropIfExists('invoices');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
class CreateInvoiceItemsTable extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('invoice_items', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->unsignedBigInteger('invoice_id')->index();
|
||||||
|
|
||||||
|
// Snapshot of order item fields (not editable)
|
||||||
|
$table->text('description')->nullable();
|
||||||
|
$table->integer('ctn')->default(0);
|
||||||
|
$table->integer('qty')->default(0);
|
||||||
|
$table->integer('ttl_qty')->default(0);
|
||||||
|
$table->string('unit')->nullable();
|
||||||
|
$table->decimal('price', 14, 2)->default(0.00);
|
||||||
|
$table->decimal('ttl_amount', 14, 2)->default(0.00);
|
||||||
|
|
||||||
|
$table->decimal('cbm', 12, 3)->default(0.000);
|
||||||
|
$table->decimal('ttl_cbm', 12, 3)->default(0.000);
|
||||||
|
|
||||||
|
$table->decimal('kg', 12, 3)->default(0.000);
|
||||||
|
$table->decimal('ttl_kg', 12, 3)->default(0.000);
|
||||||
|
|
||||||
|
$table->string('shop_no')->nullable();
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
|
||||||
|
// FK
|
||||||
|
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('invoice_items', function (Blueprint $table) {
|
||||||
|
$table->dropForeign(['invoice_id']);
|
||||||
|
});
|
||||||
|
Schema::dropIfExists('invoice_items');
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
public/invoices/invoice-INV-2025-000002.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000002.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000003.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000003.pdf
Normal file
Binary file not shown.
@@ -1,12 +1,110 @@
|
|||||||
@extends('admin.layouts.app')
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
@section('page-title', 'Dashboard')
|
@section('page-title', 'Invoice List')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow-sm">
|
||||||
<div class="card-body">
|
<div class="card-header bg-light">
|
||||||
<h4>Welcome to the Admin invoice</h4>
|
<h4 class="mb-0">All Invoices</h4>
|
||||||
<p>Here you can manage all system modules.</p>
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body table-responsive">
|
||||||
|
<table class="table table-bordered table-striped align-middle text-center">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Invoice Number</th>
|
||||||
|
<th>Customer</th>
|
||||||
|
<th>Final Amount (₹)</th>
|
||||||
|
<th>GST %</th>
|
||||||
|
<th>Total w/GST (₹)</th>
|
||||||
|
<th>Status</th>
|
||||||
|
<th>Invoice Date</th>
|
||||||
|
<th>Due Date</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
@foreach($invoices as $i => $invoice)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $i + 1 }}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<a href="#"
|
||||||
|
class="text-primary fw-bold open-invoice-popup"
|
||||||
|
data-id="{{ $invoice->id }}">
|
||||||
|
{{ $invoice->invoice_number }}
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{{ $invoice->customer_name }}</td>
|
||||||
|
|
||||||
|
<td>₹{{ number_format($invoice->final_amount, 2) }}</td>
|
||||||
|
<td>{{ $invoice->gst_percent }}%</td>
|
||||||
|
<td>₹{{ number_format($invoice->final_amount_with_gst, 2) }}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<span class="badge
|
||||||
|
@if($invoice->status=='paid') bg-success
|
||||||
|
@elseif($invoice->status=='overdue') bg-danger
|
||||||
|
@else bg-warning text-dark @endif">
|
||||||
|
{{ ucfirst($invoice->status) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>{{ $invoice->invoice_date }}</td>
|
||||||
|
<td>{{ $invoice->due_date }}</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
<a href="{{ route('admin.invoices.edit', $invoice->id) }}"
|
||||||
|
class="btn btn-sm btn-primary">
|
||||||
|
Edit
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{-- POPUP MODAL --}}
|
||||||
|
<div class="modal fade" id="invoiceModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title">Invoice Details</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body" id="invoiceModalBody">
|
||||||
|
<p class="text-center text-muted">Loading...</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('click', function(e) {
|
||||||
|
if (e.target.closest('.open-invoice-popup')) {
|
||||||
|
e.preventDefault();
|
||||||
|
const id = e.target.closest('.open-invoice-popup').dataset.id;
|
||||||
|
const modal = new bootstrap.Modal(document.getElementById('invoiceModal'));
|
||||||
|
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML =
|
||||||
|
"<p class='text-center text-muted'>Loading...</p>";
|
||||||
|
|
||||||
|
modal.show();
|
||||||
|
|
||||||
|
fetch(`/admin/invoices/${id}/popup`)
|
||||||
|
.then(res => res.text())
|
||||||
|
.then(html => {
|
||||||
|
document.getElementById('invoiceModalBody').innerHTML = html;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
89
resources/views/admin/invoice_edit.blade.php
Normal file
89
resources/views/admin/invoice_edit.blade.php
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
@extends('admin.layouts.app')
|
||||||
|
|
||||||
|
@section('page-title', 'Edit Invoice')
|
||||||
|
|
||||||
|
@section('content')
|
||||||
|
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header bg-light">
|
||||||
|
<h4>Edit Invoice</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<form action="{{ route('admin.invoices.update', $invoice->id) }}" method="POST">
|
||||||
|
@csrf
|
||||||
|
|
||||||
|
<div class="row g-3">
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Invoice Date</label>
|
||||||
|
<input type="date" class="form-control"
|
||||||
|
name="invoice_date"
|
||||||
|
value="{{ $invoice->invoice_date }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Due Date</label>
|
||||||
|
<input type="date" class="form-control"
|
||||||
|
name="due_date"
|
||||||
|
value="{{ $invoice->due_date }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Payment Method</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="payment_method"
|
||||||
|
value="{{ $invoice->payment_method }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Reference No</label>
|
||||||
|
<input type="text" class="form-control"
|
||||||
|
name="reference_no"
|
||||||
|
value="{{ $invoice->reference_no }}">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Final Amount (Editable)</label>
|
||||||
|
<input type="number" step="0.01" class="form-control"
|
||||||
|
name="final_amount"
|
||||||
|
value="{{ $invoice->final_amount }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">GST Percentage</label>
|
||||||
|
<input type="number" step="0.01" class="form-control"
|
||||||
|
name="gst_percent"
|
||||||
|
value="{{ $invoice->gst_percent }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Status</label>
|
||||||
|
<select class="form-select" name="status" required>
|
||||||
|
<option value="pending" @selected($invoice->status=='pending')>Pending</option>
|
||||||
|
<option value="paid" @selected($invoice->status=='paid')>Paid</option>
|
||||||
|
<option value="overdue" @selected($invoice->status=='overdue')>Overdue</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12">
|
||||||
|
<label class="form-label">Notes</label>
|
||||||
|
<textarea class="form-control" rows="3" name="notes">
|
||||||
|
{{ $invoice->notes }}
|
||||||
|
</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12 text-end mt-3">
|
||||||
|
<button type="submit" class="btn btn-success">
|
||||||
|
Update Invoice
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@endsection
|
||||||
@@ -176,7 +176,11 @@
|
|||||||
|
|
||||||
<a href="{{ route('admin.dashboard') }}" class="{{ request()->routeIs('admin.dashboard') ? 'active' : '' }}"><i class="bi bi-house"></i> Dashboard</a>
|
<a href="{{ route('admin.dashboard') }}" class="{{ request()->routeIs('admin.dashboard') ? 'active' : '' }}"><i class="bi bi-house"></i> Dashboard</a>
|
||||||
<a href="{{ route('admin.shipments') }}" class="{{ request()->routeIs('admin.shipments') ? 'active' : '' }}"><i class="bi bi-truck"></i> Shipments</a>
|
<a href="{{ route('admin.shipments') }}" class="{{ request()->routeIs('admin.shipments') ? 'active' : '' }}"><i class="bi bi-truck"></i> Shipments</a>
|
||||||
<a href="{{ route('admin.invoice') }}" class="{{ request()->routeIs('admin.invoice') ? 'active' : '' }}"><i class="bi bi-receipt"></i> Invoice</a>
|
<a href="{{ route('admin.invoices.index') }}"
|
||||||
|
class="{{ request()->routeIs('admin.invoices.index') ? 'active' : '' }}">
|
||||||
|
<i class="bi bi-receipt"></i> Invoice
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="{{ route('admin.customers') }}" class="{{ request()->routeIs('admin.customers') ? 'active' : '' }}"><i class="bi bi-people"></i> Customers</a>
|
<a href="{{ route('admin.customers') }}" class="{{ request()->routeIs('admin.customers') ? 'active' : '' }}"><i class="bi bi-people"></i> Customers</a>
|
||||||
<a href="{{ route('admin.reports') }}" class="{{ request()->routeIs('admin.reports') ? 'active' : '' }}"><i class="bi bi-graph-up"></i> Reports</a>
|
<a href="{{ route('admin.reports') }}" class="{{ request()->routeIs('admin.reports') ? 'active' : '' }}"><i class="bi bi-graph-up"></i> Reports</a>
|
||||||
<a href="{{ route('admin.chat_support') }}" class="{{ request()->routeIs('admin.chat_support') ? 'active' : '' }}"><i class="bi bi-chat-dots"></i> Chat Support</a>
|
<a href="{{ route('admin.chat_support') }}" class="{{ request()->routeIs('admin.chat_support') ? 'active' : '' }}"><i class="bi bi-chat-dots"></i> Chat Support</a>
|
||||||
|
|||||||
169
resources/views/admin/pdf/invoice.blade.php
Normal file
169
resources/views/admin/pdf/invoice.blade.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>{{ $invoice->invoice_number }}</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-box {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 2px solid #000;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
width: 120px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 3px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-box {
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
padding: 10px;
|
||||||
|
line-height: 1.6;
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #efefef;
|
||||||
|
padding: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
padding: 6px;
|
||||||
|
border: 1px solid #444;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.totals-box {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
background: #fafafa;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- HEADER -->
|
||||||
|
<div class="header-box">
|
||||||
|
|
||||||
|
<!-- LOGO -->
|
||||||
|
<img src="{{ public_path('images/kent_logo2.png') }}" class="logo">
|
||||||
|
|
||||||
|
<div class="title">KENT LOGISTICS</div>
|
||||||
|
<div class="subtitle">Official Invoice</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- INVOICE INFO -->
|
||||||
|
<div class="info-box">
|
||||||
|
<strong>Invoice No:</strong> {{ $invoice->invoice_number }} <br>
|
||||||
|
<strong>Invoice Date:</strong> {{ $invoice->invoice_date }} <br>
|
||||||
|
<strong>Due Date:</strong> {{ $invoice->due_date }} <br>
|
||||||
|
<strong>Status:</strong> {{ ucfirst($invoice->status) }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- CUSTOMER DETAILS -->
|
||||||
|
<div class="section-title">Customer Details</div>
|
||||||
|
<div class="info-box">
|
||||||
|
<strong>{{ $invoice->customer_name }}</strong><br>
|
||||||
|
{{ $invoice->company_name }} <br>
|
||||||
|
{{ $invoice->customer_mobile }} <br>
|
||||||
|
{{ $invoice->customer_email }} <br>
|
||||||
|
{{ $invoice->customer_address }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ITEMS TABLE -->
|
||||||
|
<div class="section-title">Invoice Items</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>CTN</th>
|
||||||
|
<th>QTY</th>
|
||||||
|
<th>TTL/QTY</th>
|
||||||
|
<th>Unit</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>TTL Amount</th>
|
||||||
|
<th>CBM</th>
|
||||||
|
<th>TTL CBM</th>
|
||||||
|
<th>KG</th>
|
||||||
|
<th>TTL KG</th>
|
||||||
|
<th>Shop No</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
@foreach($invoice->items as $i => $item)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $i + 1 }}</td>
|
||||||
|
<td>{{ $item->description }}</td>
|
||||||
|
<td>{{ $item->ctn }}</td>
|
||||||
|
<td>{{ $item->qty }}</td>
|
||||||
|
<td>{{ $item->ttl_qty }}</td>
|
||||||
|
<td>{{ $item->unit }}</td>
|
||||||
|
<td>{{ number_format($item->price, 2) }}</td>
|
||||||
|
<td>{{ number_format($item->ttl_amount, 2) }}</td>
|
||||||
|
<td>{{ $item->cbm }}</td>
|
||||||
|
<td>{{ $item->ttl_cbm }}</td>
|
||||||
|
<td>{{ $item->kg }}</td>
|
||||||
|
<td>{{ $item->ttl_kg }}</td>
|
||||||
|
<td>{{ $item->shop_no }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<!-- TOTALS -->
|
||||||
|
<div class="section-title">Totals</div>
|
||||||
|
|
||||||
|
<div class="totals-box">
|
||||||
|
<strong>Amount:</strong> ₹{{ number_format($invoice->final_amount, 2) }} <br>
|
||||||
|
<strong>GST ({{ $invoice->gst_percent }}%):</strong> ₹{{ number_format($invoice->gst_amount, 2) }} <br>
|
||||||
|
<strong>Total With GST:</strong> <strong>₹{{ number_format($invoice->final_amount_with_gst, 2) }}</strong>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
73
resources/views/admin/popup_invoice.blade.php
Normal file
73
resources/views/admin/popup_invoice.blade.php
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
<div>
|
||||||
|
<h4 class="fw-bold mb-2">
|
||||||
|
Invoice: {{ $invoice->invoice_number }}
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
<p><strong>Invoice Date:</strong> {{ $invoice->invoice_date }}</p>
|
||||||
|
<p><strong>Due Date:</strong> {{ $invoice->due_date }}</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Customer Details</h5>
|
||||||
|
<p>
|
||||||
|
<strong>{{ $invoice->customer_name }}</strong><br>
|
||||||
|
{{ $invoice->company_name }}<br>
|
||||||
|
{{ $invoice->customer_mobile }}<br>
|
||||||
|
{{ $invoice->customer_email }}<br>
|
||||||
|
{{ $invoice->customer_address }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Invoice Items</h5>
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-bordered align-middle text-center">
|
||||||
|
<thead class="table-light">
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>CTN</th>
|
||||||
|
<th>QTY</th>
|
||||||
|
<th>TTL/QTY</th>
|
||||||
|
<th>Unit</th>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>TTL Amount</th>
|
||||||
|
<th>CBM</th>
|
||||||
|
<th>TTL CBM</th>
|
||||||
|
<th>KG</th>
|
||||||
|
<th>TTL KG</th>
|
||||||
|
<th>Shop No</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
@foreach($invoice->items as $i => $item)
|
||||||
|
<tr>
|
||||||
|
<td>{{ $i+1 }}</td>
|
||||||
|
<td>{{ $item->description }}</td>
|
||||||
|
<td>{{ $item->ctn }}</td>
|
||||||
|
<td>{{ $item->qty }}</td>
|
||||||
|
<td>{{ $item->ttl_qty }}</td>
|
||||||
|
<td>{{ $item->unit }}</td>
|
||||||
|
<td>{{ number_format($item->price,2) }}</td>
|
||||||
|
<td>{{ number_format($item->ttl_amount,2) }}</td>
|
||||||
|
<td>{{ $item->cbm }}</td>
|
||||||
|
<td>{{ $item->ttl_cbm }}</td>
|
||||||
|
<td>{{ $item->kg }}</td>
|
||||||
|
<td>{{ $item->ttl_kg }}</td>
|
||||||
|
<td>{{ $item->shop_no }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<h5>Final Summary</h5>
|
||||||
|
<p><strong>Amount:</strong> ₹{{ number_format($invoice->final_amount,2) }}</p>
|
||||||
|
<p><strong>GST ({{ $invoice->gst_percent }}%):</strong> ₹{{ number_format($invoice->gst_amount,2) }}</p>
|
||||||
|
<p><strong>Total With GST:</strong> ₹{{ number_format($invoice->final_amount_with_gst,2) }}</p>
|
||||||
|
|
||||||
|
<p><strong>Status:</strong> {{ ucfirst($invoice->status) }}</p>
|
||||||
|
</div>
|
||||||
@@ -6,6 +6,7 @@ use App\Http\Controllers\Admin\UserRequestController;
|
|||||||
use App\Http\Controllers\Admin\AdminMarkListController;
|
use App\Http\Controllers\Admin\AdminMarkListController;
|
||||||
use App\Http\Controllers\Admin\AdminOrderController;
|
use App\Http\Controllers\Admin\AdminOrderController;
|
||||||
use App\Http\Controllers\Admin\ShipmentController;
|
use App\Http\Controllers\Admin\ShipmentController;
|
||||||
|
use App\Http\Controllers\Admin\AdminInvoiceController;
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
// Default Front Page
|
// Default Front Page
|
||||||
@@ -31,7 +32,7 @@ Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
|||||||
// Route::get('/dashboard', fn() => view('admin.dashboard'))->name('admin.dashboard');
|
// Route::get('/dashboard', fn() => view('admin.dashboard'))->name('admin.dashboard');
|
||||||
Route::get('/dashboard', [AdminOrderController::class, 'index'])->name('admin.dashboard');
|
Route::get('/dashboard', [AdminOrderController::class, 'index'])->name('admin.dashboard');
|
||||||
//Route::get('/shipments', fn() => view('admin.shipments'))->name('admin.shipments');
|
//Route::get('/shipments', fn() => view('admin.shipments'))->name('admin.shipments');
|
||||||
Route::get('/invoice', fn() => view('admin.invoice'))->name('admin.invoice');
|
//Route::get('/invoice', fn() => view('admin.invoice'))->name('admin.invoice');
|
||||||
Route::get('/customers', fn() => view('admin.customers'))->name('admin.customers');
|
Route::get('/customers', fn() => view('admin.customers'))->name('admin.customers');
|
||||||
Route::get('/reports', fn() => view('admin.reports'))->name('admin.reports');
|
Route::get('/reports', fn() => view('admin.reports'))->name('admin.reports');
|
||||||
Route::get('/chat-support', fn() => view('admin.chat_support'))->name('admin.chat_support');
|
Route::get('/chat-support', fn() => view('admin.chat_support'))->name('admin.chat_support');
|
||||||
@@ -95,4 +96,23 @@ Route::prefix('admin')->middleware('auth:admin')->group(function () {
|
|||||||
Route::get('/shipments/{id}', [ShipmentController::class, 'show'])
|
Route::get('/shipments/{id}', [ShipmentController::class, 'show'])
|
||||||
->name('admin.shipments.show');
|
->name('admin.shipments.show');
|
||||||
|
|
||||||
|
|
||||||
|
// Invoice list page
|
||||||
|
Route::get('/invoices', [AdminInvoiceController::class, 'index'])
|
||||||
|
->name('admin.invoices.index');
|
||||||
|
|
||||||
|
// Popup data
|
||||||
|
Route::get('/invoices/{id}/popup', [AdminInvoiceController::class, 'popup'])
|
||||||
|
->name('admin.invoices.popup');
|
||||||
|
|
||||||
|
// Edit invoice
|
||||||
|
Route::get('/invoices/{id}/edit', [AdminInvoiceController::class, 'edit'])
|
||||||
|
->name('admin.invoices.edit');
|
||||||
|
|
||||||
|
// Update invoice
|
||||||
|
Route::post('/invoices/{id}/update', [AdminInvoiceController::class, 'update'])
|
||||||
|
->name('admin.invoices.update');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user