gst
This commit is contained in:
@@ -7,6 +7,11 @@ use Illuminate\Http\Request;
|
|||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
use App\Models\InvoiceItem;
|
use App\Models\InvoiceItem;
|
||||||
use Mpdf\Mpdf;
|
use Mpdf\Mpdf;
|
||||||
|
use App\Models\InvoiceInstallment;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class AdminInvoiceController extends Controller
|
class AdminInvoiceController extends Controller
|
||||||
{
|
{
|
||||||
@@ -15,108 +20,242 @@ class AdminInvoiceController extends Controller
|
|||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$invoices = Invoice::latest()->get();
|
$invoices = Invoice::with(['order.shipments'])->latest()->get();
|
||||||
return view('admin.invoice', compact('invoices'));
|
return view('admin.invoice', compact('invoices'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// POPUP VIEW (AJAX)
|
// POPUP VIEW (AJAX)
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function popup($id)
|
public function popup($id)
|
||||||
{
|
{
|
||||||
$invoice = Invoice::with('items')->findOrFail($id);
|
$invoice = Invoice::with(['items', 'order'])->findOrFail($id);
|
||||||
return view('admin.popup_invoice', compact('invoice'));
|
|
||||||
|
// Find actual Shipment record
|
||||||
|
$shipment = \App\Models\Shipment::whereHas('items', function ($q) use ($invoice) {
|
||||||
|
$q->where('order_id', $invoice->order_id);
|
||||||
|
})
|
||||||
|
->first();
|
||||||
|
|
||||||
|
return view('admin.popup_invoice', compact('invoice', 'shipment'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// EDIT INVOICE PAGE
|
// EDIT INVOICE PAGE
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$invoice = Invoice::findOrFail($id);
|
$invoice = Invoice::with(['order.shipments'])->findOrFail($id);
|
||||||
return view('admin.invoice_edit', compact('invoice'));
|
|
||||||
|
$shipment = $invoice->order?->shipments?->first();
|
||||||
|
|
||||||
|
return view('admin.invoice_edit', compact('invoice', 'shipment'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// UPDATE INVOICE
|
// UPDATE INVOICE
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function update(Request $request, $id)
|
|
||||||
{
|
|
||||||
$invoice = Invoice::findOrFail($id);
|
|
||||||
|
|
||||||
// Validate editable fields
|
public function update(Request $request, $id)
|
||||||
$data = $request->validate([
|
{
|
||||||
'invoice_date' => 'required|date',
|
Log::info("🟡 Invoice Update Request Received", [
|
||||||
'due_date' => 'required|date|after_or_equal:invoice_date',
|
'invoice_id' => $id,
|
||||||
'payment_method' => 'nullable|string',
|
'request' => $request->all()
|
||||||
'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
|
$invoice = Invoice::findOrFail($id);
|
||||||
$gst_amount = ($data['final_amount'] * $data['gst_percent']) / 100;
|
|
||||||
$final_amount_with_gst = $data['final_amount'] + $gst_amount;
|
|
||||||
|
|
||||||
$data['gst_amount'] = $gst_amount;
|
$data = $request->validate([
|
||||||
$data['final_amount_with_gst'] = $final_amount_with_gst;
|
'invoice_date' => 'required|date',
|
||||||
|
'due_date' => 'required|date|after_or_equal:invoice_date',
|
||||||
|
'final_amount' => 'required|numeric|min:0',
|
||||||
|
'tax_type' => 'required|in:gst,igst',
|
||||||
|
'tax_percent' => 'required|numeric|min:0|max:28',
|
||||||
|
'status' => 'required|in:pending,paid,overdue',
|
||||||
|
'notes' => 'nullable|string',
|
||||||
|
]);
|
||||||
|
|
||||||
// Update DB
|
Log::info("✅ Validated Invoice Update Data", $data);
|
||||||
$invoice->update($data);
|
|
||||||
|
|
||||||
// Generate PDF
|
$finalAmount = floatval($data['final_amount']);
|
||||||
$this->generateInvoicePDF($invoice);
|
$taxPercent = floatval($data['tax_percent']);
|
||||||
|
$taxAmount = 0;
|
||||||
|
|
||||||
return redirect()
|
if ($data['tax_type'] === 'gst') {
|
||||||
->route('admin.invoices.index')
|
Log::info("🟢 GST Selected", compact('taxPercent'));
|
||||||
->with('success', 'Invoice updated & PDF generated successfully.');
|
|
||||||
|
$data['cgst_percent'] = $taxPercent / 2;
|
||||||
|
$data['sgst_percent'] = $taxPercent / 2;
|
||||||
|
$data['igst_percent'] = 0;
|
||||||
|
} else {
|
||||||
|
Log::info("🔵 IGST Selected", compact('taxPercent'));
|
||||||
|
|
||||||
|
$data['cgst_percent'] = 0;
|
||||||
|
$data['sgst_percent'] = 0;
|
||||||
|
$data['igst_percent'] = $taxPercent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$taxAmount = ($finalAmount * $taxPercent) / 100;
|
||||||
|
|
||||||
|
$data['gst_amount'] = $taxAmount;
|
||||||
|
$data['final_amount_with_gst'] = $finalAmount + $taxAmount;
|
||||||
|
|
||||||
|
// ✅ store original % for UI reference
|
||||||
|
$data['gst_percent'] = $taxPercent;
|
||||||
|
|
||||||
|
Log::info("📌 Final Calculated Invoice Values", [
|
||||||
|
'invoice_id' => $invoice->id,
|
||||||
|
'final_amount' => $finalAmount,
|
||||||
|
'gst_amount' => $data['gst_amount'],
|
||||||
|
'final_amount_with_gst' => $data['final_amount_with_gst'],
|
||||||
|
'tax_type' => $data['tax_type'],
|
||||||
|
'cgst_percent' => $data['cgst_percent'],
|
||||||
|
'sgst_percent' => $data['sgst_percent'],
|
||||||
|
'igst_percent' => $data['igst_percent'],
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoice->update($data);
|
||||||
|
|
||||||
|
Log::info("✅ Invoice Updated Successfully", [
|
||||||
|
'invoice_id' => $invoice->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
// regenerate PDF
|
||||||
|
$this->generateInvoicePDF($invoice);
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('admin.invoices.index')
|
||||||
|
->with('success', 'Invoice updated & PDF generated successfully.');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
// PDF GENERATION USING mPDF
|
// PDF GENERATION USING mPDF
|
||||||
// -------------------------------------------------------------
|
// -------------------------------------------------------------
|
||||||
public function generateInvoicePDF($invoice)
|
public function generateInvoicePDF($invoice)
|
||||||
{
|
{
|
||||||
// PDF Name
|
// Load relationship including shipment
|
||||||
|
$invoice->load(['items', 'order.shipments']);
|
||||||
|
|
||||||
|
// Fetch shipment
|
||||||
|
$shipment = $invoice->order?->shipments?->first();
|
||||||
|
|
||||||
|
// PDF filename
|
||||||
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
$fileName = 'invoice-' . $invoice->invoice_number . '.pdf';
|
||||||
|
|
||||||
// Save directly in /public/invoices
|
// Directory path
|
||||||
$folder = public_path('invoices/');
|
$folder = public_path('invoices/');
|
||||||
|
|
||||||
// Create folder if not exists
|
|
||||||
if (!file_exists($folder)) {
|
if (!file_exists($folder)) {
|
||||||
mkdir($folder, 0777, true);
|
mkdir($folder, 0777, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Full path
|
|
||||||
$filePath = $folder . $fileName;
|
$filePath = $folder . $fileName;
|
||||||
|
|
||||||
// Delete old file
|
// Delete old file if exists
|
||||||
if (file_exists($filePath)) {
|
if (file_exists($filePath)) {
|
||||||
unlink($filePath);
|
unlink($filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize mPDF
|
// Create mPDF instance
|
||||||
$mpdf = new Mpdf([
|
$mpdf = new Mpdf([
|
||||||
'mode' => 'utf-8',
|
'mode' => 'utf-8',
|
||||||
'format' => 'A4',
|
'format' => 'A4',
|
||||||
'default_font' => 'sans-serif'
|
'default_font' => 'sans-serif',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Load HTML view
|
// Load HTML view
|
||||||
$html = view('admin.pdf.invoice', compact('invoice'))->render();
|
$html = view('admin.pdf.invoice', [
|
||||||
|
'invoice' => $invoice,
|
||||||
|
'shipment' => $shipment
|
||||||
|
])->render();
|
||||||
|
|
||||||
// Generate
|
// Write HTML to PDF
|
||||||
$mpdf->WriteHTML($html);
|
$mpdf->WriteHTML($html);
|
||||||
|
|
||||||
// Save to public/invoices
|
// Save PDF
|
||||||
$mpdf->Output($filePath, 'F');
|
$mpdf->Output($filePath, 'F');
|
||||||
|
|
||||||
// Save path in DB
|
// Update DB path
|
||||||
$invoice->update([
|
$invoice->update([
|
||||||
'pdf_path' => 'invoices/' . $fileName
|
'pdf_path' => 'invoices/' . $fileName
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public function storeInstallment(Request $request, $invoice_id)
|
||||||
|
{
|
||||||
|
$request->validate([
|
||||||
|
'installment_date' => 'required|date',
|
||||||
|
'payment_method' => 'required|string',
|
||||||
|
'reference_no' => 'nullable|string',
|
||||||
|
'amount' => 'required|numeric|min:1',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$invoice = Invoice::findOrFail($invoice_id);
|
||||||
|
|
||||||
|
$paidTotal = $invoice->installments()->sum('amount');
|
||||||
|
$remaining = $invoice->final_amount - $paidTotal;
|
||||||
|
|
||||||
|
if ($request->amount > $remaining) {
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'error',
|
||||||
|
'message' => 'Installment amount exceeds remaining balance.'
|
||||||
|
], 422);
|
||||||
|
}
|
||||||
|
|
||||||
|
$installment = InvoiceInstallment::create([
|
||||||
|
'invoice_id' => $invoice_id,
|
||||||
|
'installment_date' => $request->installment_date,
|
||||||
|
'payment_method' => $request->payment_method,
|
||||||
|
'reference_no' => $request->reference_no,
|
||||||
|
'amount' => $request->amount,
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Update invoice status to paid if fully cleared
|
||||||
|
$newPaid = $paidTotal + $request->amount;
|
||||||
|
|
||||||
|
if ($newPaid >= $invoice->final_amount) {
|
||||||
|
$invoice->update(['status' => 'paid']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Installment added successfully.',
|
||||||
|
'installment' => $installment,
|
||||||
|
'totalPaid' => $newPaid,
|
||||||
|
'remaining' => max(0, $invoice->final_amount - $newPaid),
|
||||||
|
'isCompleted' => $newPaid >= $invoice->final_amount
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteInstallment($id)
|
||||||
|
{
|
||||||
|
$installment = InvoiceInstallment::findOrFail($id);
|
||||||
|
$invoice = $installment->invoice;
|
||||||
|
|
||||||
|
// delete installment
|
||||||
|
$installment->delete();
|
||||||
|
|
||||||
|
// recalc totals
|
||||||
|
$paidTotal = $invoice->installments()->sum('amount');
|
||||||
|
$remaining = $invoice->final_amount - $paidTotal;
|
||||||
|
|
||||||
|
// auto update invoice status
|
||||||
|
if ($remaining > 0 && $invoice->status === "paid") {
|
||||||
|
$invoice->update(['status' => 'pending']);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json([
|
||||||
|
'status' => 'success',
|
||||||
|
'message' => 'Installment deleted.',
|
||||||
|
'totalPaid' => $paidTotal,
|
||||||
|
'remaining' => $remaining,
|
||||||
|
'isZero' => $paidTotal == 0
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,34 +9,41 @@ class Invoice extends Model
|
|||||||
{
|
{
|
||||||
use HasFactory;
|
use HasFactory;
|
||||||
|
|
||||||
protected $fillable = [
|
protected $fillable = [
|
||||||
'order_id',
|
'order_id',
|
||||||
'customer_id',
|
'customer_id',
|
||||||
'mark_no',
|
'mark_no',
|
||||||
|
|
||||||
'invoice_number',
|
'invoice_number',
|
||||||
'invoice_date',
|
'invoice_date',
|
||||||
'due_date',
|
'due_date',
|
||||||
|
|
||||||
'payment_method',
|
'payment_method',
|
||||||
'reference_no',
|
'reference_no',
|
||||||
'status',
|
'status',
|
||||||
|
|
||||||
'final_amount',
|
'final_amount', // without tax
|
||||||
'gst_percent',
|
|
||||||
'gst_amount',
|
|
||||||
'final_amount_with_gst',
|
|
||||||
|
|
||||||
'customer_name',
|
'tax_type', // gst / igst
|
||||||
'company_name',
|
'gst_percent', // only used for gst UI input
|
||||||
'customer_email',
|
'cgst_percent',
|
||||||
'customer_mobile',
|
'sgst_percent',
|
||||||
'customer_address',
|
'igst_percent',
|
||||||
'pincode',
|
|
||||||
|
'gst_amount', // total tax amount
|
||||||
|
'final_amount_with_gst',
|
||||||
|
|
||||||
|
'customer_name',
|
||||||
|
'company_name',
|
||||||
|
'customer_email',
|
||||||
|
'customer_mobile',
|
||||||
|
'customer_address',
|
||||||
|
'pincode',
|
||||||
|
|
||||||
|
'pdf_path',
|
||||||
|
'notes',
|
||||||
|
];
|
||||||
|
|
||||||
'pdf_path',
|
|
||||||
'notes',
|
|
||||||
];
|
|
||||||
|
|
||||||
/****************************
|
/****************************
|
||||||
* Relationships
|
* Relationships
|
||||||
@@ -74,4 +81,16 @@ class Invoice extends Model
|
|||||||
{
|
{
|
||||||
return $this->status === 'pending' && now()->gt($this->due_date);
|
return $this->status === 'pending' && now()->gt($this->due_date);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getShipment()
|
||||||
|
{
|
||||||
|
return $this->order?->shipments?->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function installments()
|
||||||
|
{
|
||||||
|
return $this->hasMany(InvoiceInstallment::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
app/Models/InvoiceInstallment.php
Normal file
24
app/Models/InvoiceInstallment.php
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class InvoiceInstallment extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'invoice_id',
|
||||||
|
'installment_date',
|
||||||
|
'payment_method',
|
||||||
|
'reference_no',
|
||||||
|
'amount',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function invoice()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Invoice::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,4 +46,15 @@ class Order extends Model
|
|||||||
->withTimestamps();
|
->withTimestamps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shipmentItems()
|
||||||
|
{
|
||||||
|
return $this->hasMany(\App\Models\ShipmentItem::class, 'order_id', 'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shipments()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(\App\Models\Shipment::class, 'shipment_items', 'order_id', 'shipment_id');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
|
|
||||||
|
// GST type — gst or igst
|
||||||
|
$table->enum('tax_type', ['gst', 'igst'])
|
||||||
|
->default('gst')
|
||||||
|
->after('final_amount');
|
||||||
|
|
||||||
|
// Old gst_percent becomes optional
|
||||||
|
$table->decimal('gst_percent', 5, 2)
|
||||||
|
->nullable()
|
||||||
|
->change();
|
||||||
|
|
||||||
|
// Split GST %
|
||||||
|
$table->decimal('cgst_percent', 5, 2)
|
||||||
|
->nullable()
|
||||||
|
->after('gst_percent');
|
||||||
|
|
||||||
|
$table->decimal('sgst_percent', 5, 2)
|
||||||
|
->nullable()
|
||||||
|
->after('cgst_percent');
|
||||||
|
|
||||||
|
// IGST %
|
||||||
|
$table->decimal('igst_percent', 5, 2)
|
||||||
|
->nullable()
|
||||||
|
->after('sgst_percent');
|
||||||
|
|
||||||
|
// Tax amount recalculation is the same
|
||||||
|
// gst_amount and final_amount_with_gst already exist
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('invoices', function (Blueprint $table) {
|
||||||
|
$table->dropColumn([
|
||||||
|
'tax_type',
|
||||||
|
'cgst_percent',
|
||||||
|
'sgst_percent',
|
||||||
|
'igst_percent',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('invoice_installments', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
|
||||||
|
$table->unsignedBigInteger('invoice_id');
|
||||||
|
$table->foreign('invoice_id')->references('id')->on('invoices')->onDelete('cascade');
|
||||||
|
|
||||||
|
$table->date('installment_date');
|
||||||
|
$table->string('payment_method')->nullable(); // cash, bank, UPI, cheque, etc
|
||||||
|
$table->string('reference_no')->nullable();
|
||||||
|
$table->decimal('amount', 10, 2);
|
||||||
|
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('invoice_installments');
|
||||||
|
}
|
||||||
|
};
|
||||||
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000009.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000009.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000010.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000010.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000011.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000011.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000012.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000012.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000013.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000013.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000014.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000014.pdf
Normal file
Binary file not shown.
BIN
public/invoices/invoice-INV-2025-000022.pdf
Normal file
BIN
public/invoices/invoice-INV-2025-000022.pdf
Normal file
Binary file not shown.
@@ -3,430 +3,369 @@
|
|||||||
@section('page-title', 'Edit Invoice')
|
@section('page-title', 'Edit Invoice')
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
/* World-Class UI Design System - No Blur Effects */
|
/* --------------------------------------------------
|
||||||
|
GLOBAL VARIABLES
|
||||||
|
-------------------------------------------------- */
|
||||||
:root {
|
:root {
|
||||||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
--primary-gradient: linear-gradient(135deg,#667eea,#764ba2);
|
||||||
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
--success-gradient: linear-gradient(135deg,#10b981,#059669);
|
||||||
--glass-bg: #ffffff;
|
--glass-bg: #fff;
|
||||||
--glass-border: rgba(255, 255, 255, 0.2);
|
--shadow-soft: 0 8px 32px rgba(0,0,0,0.1);
|
||||||
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.1);
|
--shadow-medium: 0 12px 48px rgba(0,0,0,0.15);
|
||||||
--shadow-medium: 0 12px 48px rgba(0, 0, 0, 0.15);
|
--shadow-strong: 0 20px 60px rgba(0,0,0,0.2);
|
||||||
--shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enhanced Card - No Blur */
|
/* --------------------------------------------------
|
||||||
|
CARD DESIGN
|
||||||
|
-------------------------------------------------- */
|
||||||
.card {
|
.card {
|
||||||
background: var(--glass-bg);
|
background: var(--glass-bg);
|
||||||
border-radius: 24px;
|
border-radius: 12px;
|
||||||
box-shadow: var(--shadow-strong);
|
|
||||||
border: 1px solid #e4e6ef;
|
border: 1px solid #e4e6ef;
|
||||||
animation: cardEntrance 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94);
|
box-shadow: var(--shadow-strong);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
animation: fadeUp 0.8s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card::before {
|
.card::before {
|
||||||
content: '';
|
content:"";
|
||||||
position: absolute;
|
position:absolute; inset:0;
|
||||||
top: 0;
|
background:linear-gradient(45deg,
|
||||||
left: 0;
|
rgba(102,126,234,.03),
|
||||||
right: 0;
|
rgba(118,75,162,.03) 50%,
|
||||||
bottom: 0;
|
rgba(16,185,129,.03));
|
||||||
background: linear-gradient(45deg,
|
pointer-events:none;
|
||||||
rgba(102, 126, 234, 0.03) 0%,
|
|
||||||
rgba(118, 75, 162, 0.03) 50%,
|
|
||||||
rgba(16, 185, 129, 0.03) 100%);
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes cardEntrance {
|
@keyframes fadeUp {
|
||||||
0% {
|
0% {opacity:0; transform:translateY(30px) scale(.95);}
|
||||||
opacity: 0;
|
100% {opacity:1; transform:translateY(0) scale(1);}
|
||||||
transform: translateY(30px) scale(0.95);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Premium Card Header */
|
/* --------------------------------------------------
|
||||||
|
CARD HEADER
|
||||||
|
-------------------------------------------------- */
|
||||||
.card-header {
|
.card-header {
|
||||||
background: var(--primary-gradient);
|
background:var(--primary-gradient);
|
||||||
color: white;
|
color:#fff;
|
||||||
border-bottom: none;
|
padding:11px 19px;
|
||||||
padding: 28px 35px;
|
border:none;
|
||||||
position: relative;
|
position:relative;
|
||||||
overflow: hidden;
|
overflow:hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header::before {
|
.card-header::before {
|
||||||
content: '';
|
content:"";
|
||||||
position: absolute;
|
position:absolute;
|
||||||
top: -50%;
|
top:-50%; left:-50%;
|
||||||
left: -50%;
|
width:200%; height:200%;
|
||||||
width: 200%;
|
background:linear-gradient(45deg,transparent,
|
||||||
height: 200%;
|
rgba(255,255,255,.1) 50%,transparent);
|
||||||
background: linear-gradient(45deg,
|
animation: shimmer 6s infinite linear;
|
||||||
transparent 0%,
|
|
||||||
rgba(255, 255, 255, 0.1) 50%,
|
|
||||||
transparent 100%);
|
|
||||||
animation: headerShimmer 6s infinite linear;
|
|
||||||
transform: rotate(45deg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes headerShimmer {
|
@keyframes shimmer {
|
||||||
0% { transform: translateX(-100%) rotate(45deg); }
|
from {transform:translateX(-100%) rotate(45deg);}
|
||||||
100% { transform: translateX(100%) rotate(45deg); }
|
to {transform:translateX(100%) rotate(45deg);}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h4 {
|
.card-header h4 {
|
||||||
margin: 0;
|
margin:0; font-weight:800; font-size:1.6rem;
|
||||||
font-weight: 800;
|
display:flex; align-items:center; gap:12px;
|
||||||
font-size: 1.6rem;
|
text-shadow:0 2px 4px rgba(0,0,0,.1);
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header h4::before {
|
.card-header h4::before {
|
||||||
content: '✨';
|
content:"✨"; font-size:1.4rem;
|
||||||
font-size: 1.4rem;
|
|
||||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body {
|
/* --------------------------------------------------
|
||||||
padding: 35px;
|
BODY + FORM ELEMENTS
|
||||||
background: #f8fafc;
|
-------------------------------------------------- */
|
||||||
position: relative;
|
.card-body { padding:15px; background:#f8fafc; }
|
||||||
}
|
|
||||||
|
|
||||||
/* World-Class Form Elements - No Blur */
|
|
||||||
.form-label {
|
.form-label {
|
||||||
font-weight: 700;
|
font-weight:700; color:#1e293b;
|
||||||
color: #1e293b;
|
margin-bottom:10px; font-size:.95rem;
|
||||||
margin-bottom: 10px;
|
letter-spacing:.5px; text-transform:uppercase;
|
||||||
font-size: 0.95rem;
|
display:flex; align-items:center; gap:8px;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-label::before {
|
.form-label::before {
|
||||||
content: '';
|
content:""; width:4px; height:16px;
|
||||||
width: 4px;
|
background:var(--primary-gradient);
|
||||||
height: 16px;
|
border-radius:2px;
|
||||||
background: var(--primary-gradient);
|
|
||||||
border-radius: 2px;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control, .form-select {
|
.form-control, .form-select {
|
||||||
border: 2px solid #e2e8f0;
|
border: 2px solid #e2e8f0;
|
||||||
border-radius: 16px;
|
padding: 8px 15px;
|
||||||
padding: 16px 20px;
|
border-radius: 10px;
|
||||||
|
background: #fff;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
transition: .4s;
|
||||||
background: #ffffff;
|
box-shadow: 0 2px 12px rgba(0, 0, 0, .05);
|
||||||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
|
||||||
position: relative;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control:focus, .form-select:focus {
|
.form-control:focus, .form-select:focus {
|
||||||
border-color: #667eea;
|
border-color:#667eea;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 0 4px rgba(102, 126, 234, 0.15),
|
0 0 0 4px rgba(102,126,234,.15),
|
||||||
0 8px 24px rgba(102, 126, 234, 0.2),
|
0 8px 24px rgba(102,126,234,.2);
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.8);
|
transform:translateY(-2px) scale(1.02);
|
||||||
background: #ffffff;
|
|
||||||
transform: translateY(-2px) scale(1.02);
|
|
||||||
animation: inputGlow 2s infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes inputGlow {
|
|
||||||
0%, 100% { box-shadow: 0 0 0 4px rgba(102, 126, 234, 0.15), 0 8px 24px rgba(102, 126, 234, 0.2); }
|
|
||||||
50% { box-shadow: 0 0 0 6px rgba(102, 126, 234, 0.1), 0 12px 32px rgba(102, 126, 234, 0.25); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* hover */
|
||||||
.form-control:hover, .form-select:hover {
|
.form-control:hover, .form-select:hover {
|
||||||
border-color: #cbd5e1;
|
border-color:#cbd5e1;
|
||||||
transform: translateY(-2px);
|
transform:translateY(-2px);
|
||||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.08);
|
box-shadow:0 6px 20px rgba(0,0,0,.08);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Enhanced Grid System */
|
/* textarea */
|
||||||
.row.g-3 {
|
|
||||||
margin: -15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.row.g-3 > [class*="col-"] {
|
|
||||||
padding: 15px;
|
|
||||||
animation: formElementEntrance 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes formElementEntrance {
|
|
||||||
0% {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(25px) scale(0.9);
|
|
||||||
}
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0) scale(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Staggered Animation Delays */
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(1) { animation-delay: 0.1s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(2) { animation-delay: 0.2s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(3) { animation-delay: 0.3s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(4) { animation-delay: 0.4s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(5) { animation-delay: 0.5s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(6) { animation-delay: 0.6s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(7) { animation-delay: 0.7s; }
|
|
||||||
.row.g-3 > [class*="col-"]:nth-child(8) { animation-delay: 0.8s; }
|
|
||||||
|
|
||||||
/* Premium Textarea */
|
|
||||||
textarea.form-control {
|
textarea.form-control {
|
||||||
|
height: 65px;
|
||||||
resize: vertical;
|
resize: vertical;
|
||||||
min-height: 120px;
|
|
||||||
line-height: 1.6;
|
|
||||||
background: #ffffff;
|
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea.form-control:focus {
|
/* --------------------------------------------------
|
||||||
transform: translateY(-2px) scale(1.01);
|
BUTTON STYLING
|
||||||
box-shadow:
|
-------------------------------------------------- */
|
||||||
0 0 0 4px rgba(102, 126, 234, 0.15),
|
|
||||||
0 12px 32px rgba(102, 126, 234, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* World-Class Button Design - No Blur */
|
|
||||||
.btn-success {
|
.btn-success {
|
||||||
background: var(--success-gradient);
|
background: var(--success-gradient);
|
||||||
border: none;
|
border: none;
|
||||||
padding: 18px 45px;
|
padding: 10px 25px;
|
||||||
border-radius: 16px;
|
border-radius: 7px;
|
||||||
color: white;
|
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
font-size: 1.1rem;
|
font-size: 1.1rem;
|
||||||
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
color: #fff;
|
||||||
|
transition: .4s;
|
||||||
box-shadow: var(--shadow-medium);
|
box-shadow: var(--shadow-medium);
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success::before {
|
.btn-success::before {
|
||||||
content: '';
|
content:"";
|
||||||
position: absolute;
|
position:absolute; left:-100%; top:0;
|
||||||
top: 0;
|
width:100%; height:100%;
|
||||||
left: -100%;
|
background:linear-gradient(90deg,transparent,
|
||||||
width: 100%;
|
rgba(255,255,255,.4),transparent);
|
||||||
height: 100%;
|
transition:.6s;
|
||||||
background: linear-gradient(90deg,
|
|
||||||
transparent,
|
|
||||||
rgba(255, 255, 255, 0.4),
|
|
||||||
transparent);
|
|
||||||
transition: left 0.6s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success:hover::before {
|
|
||||||
left: 100%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success:hover {
|
.btn-success:hover {
|
||||||
transform: translateY(-4px) scale(1.05);
|
transform:translateY(-4px) scale(1.05);
|
||||||
box-shadow:
|
box-shadow:0 16px 40px rgba(16,185,129,.4);
|
||||||
0 16px 40px rgba(16, 185, 129, 0.4),
|
|
||||||
0 0 0 1px rgba(255, 255, 255, 0.1);
|
|
||||||
background: var(--success-gradient);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success:active {
|
.btn-success:hover::before { left:100%; }
|
||||||
transform: translateY(-1px) scale(1.02);
|
|
||||||
transition: all 0.1s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced Select Styling */
|
|
||||||
.form-select {
|
|
||||||
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%23667eea' stroke-linecap='round' stroke-linejoin='round' stroke-width='2.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e");
|
|
||||||
background-position: right 20px center;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 18px;
|
|
||||||
padding-right: 50px;
|
|
||||||
cursor: pointer;
|
|
||||||
appearance: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advanced Loading Animation */
|
|
||||||
.btn-success.loading {
|
.btn-success.loading {
|
||||||
pointer-events: none;
|
pointer-events:none; padding-right:60px;
|
||||||
padding-right: 60px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-success.loading::after {
|
.btn-success.loading::after {
|
||||||
content: '';
|
content:"";
|
||||||
position: absolute;
|
position:absolute; right:20px; top:50%;
|
||||||
right: 20px;
|
width:20px; height:20px; margin-top:-10px;
|
||||||
top: 50%;
|
border-radius:50%;
|
||||||
width: 20px;
|
border:2px solid transparent;
|
||||||
height: 20px;
|
border-top-color:#fff;
|
||||||
margin-top: -10px;
|
animation:spin 1s linear infinite;
|
||||||
border: 2px solid transparent;
|
|
||||||
border-top: 2px solid white;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {to{transform:rotate(360deg);}}
|
||||||
0% { transform: rotate(0deg); }
|
|
||||||
100% { transform: rotate(360deg); }
|
/* --------------------------------------------------
|
||||||
|
RESPONSIVE
|
||||||
|
-------------------------------------------------- */
|
||||||
|
@media(max-width:768px){
|
||||||
|
.card-body{padding:25px 20px;}
|
||||||
|
.card-header{padding:22px 25px;}
|
||||||
|
.card-header h4{font-size:1.4rem;}
|
||||||
|
.btn-success{width:100%; padding:16px 30px;}
|
||||||
|
.form-control,.form-select{padding:14px 16px;}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Premium Focus States */
|
/* Only animation reduction */
|
||||||
.form-control:focus-visible,
|
@media(prefers-reduced-motion:reduce){
|
||||||
.form-select:focus-visible,
|
*{animation:none!important; transition:none!important;}
|
||||||
.btn-success:focus-visible {
|
|
||||||
outline: 3px solid #667eea;
|
|
||||||
outline-offset: 2px;
|
|
||||||
border-radius: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Advanced Responsive Design */
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.card-body {
|
|
||||||
padding: 25px 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
padding: 22px 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header h4 {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-success {
|
|
||||||
width: 100%;
|
|
||||||
padding: 16px 30px;
|
|
||||||
font-size: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control, .form-select {
|
|
||||||
padding: 14px 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Micro-interactions for Enhanced UX */
|
|
||||||
.form-control:valid {
|
|
||||||
border-left: 3px solid #10b981;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form-control:invalid:not(:focus):not(:placeholder-shown) {
|
|
||||||
border-left: 3px solid #ef4444;
|
|
||||||
animation: shake 0.5s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes shake {
|
|
||||||
0%, 100% { transform: translateX(0); }
|
|
||||||
25% { transform: translateX(-5px); }
|
|
||||||
75% { transform: translateX(5px); }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Smooth scrolling for better UX */
|
|
||||||
html {
|
|
||||||
scroll-behavior: smooth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Performance optimized animations */
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
* {
|
|
||||||
animation-duration: 0.01ms !important;
|
|
||||||
animation-iteration-count: 1 !important;
|
|
||||||
transition-duration: 0.01ms !important;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<div class="card shadow-sm">
|
|
||||||
|
|
||||||
|
{{-- Invoice Preview Section --}}
|
||||||
|
<div class="card shadow-sm mb-4">
|
||||||
<div class="card-header bg-light">
|
<div class="card-header bg-light">
|
||||||
|
<h4 class="fw-bold mb-0">
|
||||||
|
<i class="fas fa-file-invoice me-2"></i> Invoice Details
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
@include('admin.popup_invoice', [
|
||||||
|
'invoice' => $invoice,
|
||||||
|
'shipment' => $shipment,
|
||||||
|
'embedded' => true
|
||||||
|
])
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- --------------------------------------------------
|
||||||
|
HTML CONTENT (UNCHANGED)
|
||||||
|
-------------------------------------------------- -->
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header">
|
||||||
<h4>Edit Invoice</h4>
|
<h4>Edit Invoice</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<form action="{{ route('admin.invoices.update', $invoice->id) }}" method="POST">
|
<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">Final Amount (₹)</label>
|
||||||
|
<input type="number" step="0.01" class="form-control" name="final_amount"
|
||||||
|
value="{{ $invoice->final_amount }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ✅ TAX TYPE -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label d-block">Tax Type</label>
|
||||||
|
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio"
|
||||||
|
name="tax_type" value="gst"
|
||||||
|
@checked($invoice->tax_type === 'gst')>
|
||||||
|
<label class="form-check-label">GST (CGST+SGST)</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-check form-check-inline">
|
||||||
|
<input class="form-check-input" type="radio"
|
||||||
|
name="tax_type" value="igst"
|
||||||
|
@checked($invoice->tax_type === 'igst')>
|
||||||
|
<label class="form-check-label">IGST</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- ✅ One unified input -->
|
||||||
|
<div class="col-md-4">
|
||||||
|
<label class="form-label">Tax Percentage (%)</label>
|
||||||
|
<input type="number" step="0.01" min="0" max="28"
|
||||||
|
class="form-control"
|
||||||
|
name="tax_percent"
|
||||||
|
value="{{ old('tax_percent',
|
||||||
|
$invoice->tax_type == 'gst'
|
||||||
|
? $invoice->cgst_percent + $invoice->sgst_percent
|
||||||
|
: $invoice->igst_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>
|
||||||
|
|
||||||
|
@php
|
||||||
|
$totalPaid = $invoice->installments->sum('amount');
|
||||||
|
$remaining = $invoice->final_amount_with_gst - $totalPaid;
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||||
|
<h3 class="fw-bold mb-0">Installment Payments</h3>
|
||||||
|
|
||||||
|
@if($remaining > 0)
|
||||||
|
<button id="toggleInstallmentForm" class="btn btn-success">
|
||||||
|
+ Add Installment
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Installment Form -->
|
||||||
|
<div id="installmentForm" class="card shadow-sm d-none mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4>Add Installment</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
<form id="installmentSubmitForm">
|
||||||
@csrf
|
@csrf
|
||||||
|
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label">Invoice Date</label>
|
<label class="form-label">Installment Date</label>
|
||||||
<input type="date" class="form-control"
|
<input type="date" name="installment_date" class="form-control" required>
|
||||||
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>
|
||||||
|
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<label class="form-label">Payment Method</label>
|
<label class="form-label">Payment Method</label>
|
||||||
<input type="text" class="form-control"
|
<select name="payment_method" class="form-select" required>
|
||||||
name="payment_method"
|
<option value="cash">Cash</option>
|
||||||
value="{{ $invoice->payment_method }}">
|
<option value="bank">Bank Transfer</option>
|
||||||
</div>
|
<option value="upi">UPI</option>
|
||||||
|
<option value="cheque">Cheque</option>
|
||||||
<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>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12">
|
<div class="col-md-4">
|
||||||
<label class="form-label">Notes</label>
|
<label class="form-label">Reference No (optional)</label>
|
||||||
<textarea class="form-control" rows="3" name="notes">
|
<input type="text" name="reference_no" class="form-control">
|
||||||
{{ $invoice->notes }}
|
|
||||||
</textarea>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-12 text-end mt-3">
|
<div class="col-md-12">
|
||||||
<button type="submit" class="btn btn-success">
|
<label class="form-label">Amount</label>
|
||||||
Update Invoice
|
<input type="number" name="amount" id="installmentAmount" class="form-control"
|
||||||
|
step="0.01" min="1" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-12 text-end mt-2">
|
||||||
|
<button type="submit" class="btn btn-success" id="installmentSubmitBtn">
|
||||||
|
Submit Installment
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -435,47 +374,229 @@ html {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="card shadow-sm mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between fs-5 fw-bold">
|
||||||
|
<span>Total Amount (Before Tax):</span>
|
||||||
|
<span>₹{{ number_format($invoice->final_amount, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between text-primary fs-5 fw-bold">
|
||||||
|
<span>Tax Type:</span>
|
||||||
|
<span>
|
||||||
|
@if($invoice->tax_type === 'gst')
|
||||||
|
GST (CGST + SGST)
|
||||||
|
@else
|
||||||
|
IGST
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between text-primary fs-5 fw-bold">
|
||||||
|
<span>Tax Percentage:</span>
|
||||||
|
<span>
|
||||||
|
@if($invoice->tax_type === 'gst')
|
||||||
|
{{ $invoice->cgst_percent + $invoice->sgst_percent }}%
|
||||||
|
@else
|
||||||
|
{{ $invoice->igst_percent }}%
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between text-warning fs-5 fw-bold">
|
||||||
|
<span>GST Amount:</span>
|
||||||
|
<span>₹{{ number_format($invoice->gst_amount, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between fs-4 fw-bold text-dark">
|
||||||
|
<span>Total Invoice Amount (Including GST):</span>
|
||||||
|
<span>₹{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between text-success fs-5 fw-bold">
|
||||||
|
<span>Total Paid:</span>
|
||||||
|
<span id="paidAmount">₹{{ number_format($totalPaid, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between text-danger fs-5 fw-bold">
|
||||||
|
<span>Remaining:</span>
|
||||||
|
<span id="remainingAmount">₹{{ number_format($remaining, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Installment History -->
|
||||||
|
<div class="card shadow-sm">
|
||||||
|
<div class="card-header">
|
||||||
|
<h4>Installment History</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body p-0">
|
||||||
|
<table class="table table-hover mb-0" id="installmentTable">
|
||||||
|
<thead style="background:#f1f5f9;">
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Date</th>
|
||||||
|
<th>Payment Method</th>
|
||||||
|
<th>Reference No</th>
|
||||||
|
<th>Amount</th>
|
||||||
|
<th>Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach($invoice->installments as $i)
|
||||||
|
<tr data-id="{{ $i->id }}">
|
||||||
|
<td>{{ $loop->iteration }}</td>
|
||||||
|
<td>{{ $i->installment_date }}</td>
|
||||||
|
<td>{{ ucfirst($i->payment_method) }}</td>
|
||||||
|
<td>{{ $i->reference_no }}</td>
|
||||||
|
<td class="fw-bold text-success">₹{{ number_format($i->amount, 2) }}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger btn-sm deleteInstallment">
|
||||||
|
❌ Delete
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// World-Class Interactive Enhancements - No Parallax
|
document.addEventListener("DOMContentLoaded", function () {
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
const form = document.querySelector('form');
|
// ✅ Toggle Installment Form
|
||||||
const submitBtn = document.querySelector('.btn-success');
|
const toggleBtn = document.getElementById("toggleInstallmentForm");
|
||||||
const inputs = document.querySelectorAll('input, select, textarea');
|
const formBox = document.getElementById("installmentForm");
|
||||||
|
|
||||||
// Enhanced form submission
|
if (toggleBtn) {
|
||||||
form.addEventListener('submit', function(e) {
|
toggleBtn.addEventListener("click", () => formBox.classList.toggle("d-none"));
|
||||||
submitBtn.classList.add('loading');
|
}
|
||||||
submitBtn.innerHTML = 'Updating Invoice...';
|
|
||||||
|
// ✅ Add Installment
|
||||||
// Add a small delay to show loading state
|
const submitForm = document.getElementById("installmentSubmitForm");
|
||||||
setTimeout(() => {
|
const submitBtn = document.getElementById("installmentSubmitBtn");
|
||||||
if (!form.checkValidity()) {
|
|
||||||
submitBtn.classList.remove('loading');
|
const formatINR = amt =>
|
||||||
submitBtn.innerHTML = 'Update Invoice';
|
"₹" + Number(amt).toLocaleString("en-IN", {
|
||||||
}
|
minimumFractionDigits: 2,
|
||||||
}, 100);
|
maximumFractionDigits: 2
|
||||||
});
|
|
||||||
|
|
||||||
// Real-time validation with visual feedback
|
|
||||||
inputs.forEach(input => {
|
|
||||||
input.addEventListener('input', function() {
|
|
||||||
if (this.checkValidity()) {
|
|
||||||
this.style.borderLeftColor = '#10b981';
|
|
||||||
} else {
|
|
||||||
this.style.borderLeftColor = '#ef4444';
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add focus effects
|
submitForm.addEventListener("submit", function (e) {
|
||||||
input.addEventListener('focus', function() {
|
e.preventDefault();
|
||||||
this.parentElement.style.transform = 'translateY(-2px)';
|
|
||||||
});
|
submitBtn.textContent = "Processing...";
|
||||||
|
submitBtn.disabled = true;
|
||||||
input.addEventListener('blur', function() {
|
|
||||||
this.parentElement.style.transform = 'translateY(0)';
|
fetch("{{ route('admin.invoice.installment.store', $invoice->id) }}", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": submitForm.querySelector("input[name=_token]").value,
|
||||||
|
"Accept": "application/json"
|
||||||
|
},
|
||||||
|
body: new FormData(submitForm)
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
submitBtn.textContent = "Submit Installment";
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
|
||||||
|
if (data.status === "error") {
|
||||||
|
alert(data.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const table = document.querySelector("#installmentTable tbody");
|
||||||
|
const index = table.rows.length + 1;
|
||||||
|
|
||||||
|
table.insertAdjacentHTML("beforeend", `
|
||||||
|
<tr data-id="${data.installment.id}">
|
||||||
|
<td>${index}</td>
|
||||||
|
<td>${data.installment.installment_date}</td>
|
||||||
|
<td>${data.installment.payment_method.toUpperCase()}</td>
|
||||||
|
<td>${data.installment.reference_no ?? ""}</td>
|
||||||
|
<td class="fw-bold text-success">${formatINR(data.installment.amount)}</td>
|
||||||
|
<td>
|
||||||
|
<button class="btn btn-danger btn-sm deleteInstallment">❌ Delete</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// ✅ Update totals (Gst included)
|
||||||
|
document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
|
||||||
|
document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
|
||||||
|
|
||||||
|
submitForm.reset();
|
||||||
|
|
||||||
|
// ✅ If fully paid — hide Add Installment button & form
|
||||||
|
if (data.isCompleted) {
|
||||||
|
toggleBtn?.remove();
|
||||||
|
formBox.classList.add("d-none");
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(data.message);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
submitBtn.textContent = "Submit Installment";
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
alert("Something went wrong");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ✅ Delete Installment (event delegation)
|
||||||
|
document.addEventListener("click", function (e) {
|
||||||
|
if (!e.target.classList.contains("deleteInstallment")) return;
|
||||||
|
|
||||||
|
if (!confirm("Are you sure you want to delete this installment?")) return;
|
||||||
|
|
||||||
|
const row = e.target.closest("tr");
|
||||||
|
const id = row.getAttribute("data-id");
|
||||||
|
|
||||||
|
fetch("{{ url('/admin/installment') }}/" + id, {
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
"X-CSRF-TOKEN": "{{ csrf_token() }}",
|
||||||
|
"Accept": "application/json"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === "success") {
|
||||||
|
row.remove();
|
||||||
|
|
||||||
|
document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
|
||||||
|
document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
|
||||||
|
|
||||||
|
// ✅ If remaining exists but Add Installment button disappeared → reload UI
|
||||||
|
if (data.remaining > 0 && !toggleBtn) {
|
||||||
|
location.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ✅ Remove entire card if no installments left
|
||||||
|
if (data.isZero) {
|
||||||
|
document.getElementById("installmentTable").closest(".card").remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(data.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@endsection
|
|
||||||
|
|
||||||
|
|
||||||
|
@endsection
|
||||||
|
|||||||
@@ -164,7 +164,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.content-wrapper {
|
.content-wrapper {
|
||||||
padding: 18px 16px 0 16px;
|
padding: 18px 16px 16px 16px;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,207 +1,280 @@
|
|||||||
<div class="p-4">
|
{{-- INVOICE CONTENT (NO POPUP WRAPPERS HERE) --}}
|
||||||
<!-- Invoice Header -->
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h2 class="fw-bold text-primary mb-1">
|
|
||||||
<i class="fas fa-file-invoice me-2"></i>INVOICE
|
|
||||||
</h2>
|
|
||||||
<h4 class="fw-bold text-dark mb-0">{{ $invoice->invoice_number }}</h4>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6 text-end">
|
|
||||||
<div class="d-inline-block bg-light rounded-3 p-3">
|
|
||||||
<span class="badge
|
|
||||||
@if($invoice->status=='paid') bg-success
|
|
||||||
@elseif($invoice->status=='overdue') bg-danger
|
|
||||||
@elseif($invoice->status=='pending') bg-warning text-dark
|
|
||||||
@else bg-secondary @endif
|
|
||||||
fs-6 px-3 py-2">
|
|
||||||
<i class="fas
|
|
||||||
@if($invoice->status=='paid') fa-check-circle
|
|
||||||
@elseif($invoice->status=='overdue') fa-exclamation-circle
|
|
||||||
@elseif($invoice->status=='pending') fa-clock
|
|
||||||
@else fa-question-circle @endif me-1"></i>
|
|
||||||
{{ ucfirst($invoice->status) }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Dates - Compact Professional Layout -->
|
<!-- ============================
|
||||||
<div class="row mb-3">
|
INVOICE HEADER
|
||||||
<div class="col-12">
|
============================ -->
|
||||||
<div class="card border-0 shadow-sm">
|
<div class="mb-4">
|
||||||
<div class="card-body py-2">
|
|
||||||
<div class="row align-items-center text-center">
|
|
||||||
<div class="col-md-5">
|
|
||||||
<div class="mb-0">
|
|
||||||
<div class="text-muted fw-semibold small">INVOICE DATE</div>
|
|
||||||
</div>
|
|
||||||
<div class="fw-bold text-dark" style="font-size: 0.95rem;">
|
|
||||||
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-2">
|
|
||||||
<div class="date-connector">
|
|
||||||
<i class="fas fa-arrow-right text-muted small"></i>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-5">
|
|
||||||
<div class="mb-0">
|
|
||||||
<div class="text-muted fw-semibold small">DUE DATE</div>
|
|
||||||
</div>
|
|
||||||
<div class="fw-bold @if($invoice->status == 'overdue') text-danger @else text-dark @endif" style="font-size: 0.95rem;">
|
|
||||||
{{ \Carbon\Carbon::parse($invoice->due_date)->format('M d, Y') }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Customer Details -->
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card border-0 shadow-sm">
|
|
||||||
<div class="card-header bg-light py-2">
|
|
||||||
<h6 class="mb-0 fw-bold text-dark">
|
|
||||||
<i class="fas fa-user me-2"></i>Customer Details
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-6">
|
|
||||||
<h6 class="fw-bold text-primary mb-1">{{ $invoice->customer_name }}</h6>
|
|
||||||
@if($invoice->company_name)
|
|
||||||
<p class="mb-1">
|
|
||||||
<strong>Company:</strong> {{ $invoice->company_name }}
|
|
||||||
</p>
|
|
||||||
@endif
|
|
||||||
<p class="mb-1">
|
|
||||||
<strong>Mobile:</strong> {{ $invoice->customer_mobile }}
|
|
||||||
</p>
|
|
||||||
<p class="mb-1">
|
|
||||||
<strong>Email:</strong> {{ $invoice->customer_email }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
|
||||||
<p class="mb-0">
|
|
||||||
<strong>Address:</strong><br>
|
|
||||||
{{ $invoice->customer_address }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Invoice Items -->
|
|
||||||
<div class="row mb-4">
|
|
||||||
<div class="col-12">
|
|
||||||
<div class="card border-0 shadow-sm">
|
|
||||||
<div class="card-header bg-light py-2">
|
|
||||||
<h6 class="mb-0 fw-bold text-dark">
|
|
||||||
<i class="fas fa-list me-2"></i>Invoice Items
|
|
||||||
</h6>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<table class="table table-bordered table-hover align-middle mb-0">
|
|
||||||
<thead class="table-light">
|
|
||||||
<tr>
|
|
||||||
<th class="text-center">#</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th class="text-center">CTN</th>
|
|
||||||
<th class="text-center">QTY</th>
|
|
||||||
<th class="text-center">TTL/QTY</th>
|
|
||||||
<th class="text-center">Unit</th>
|
|
||||||
<th class="text-center">Price</th>
|
|
||||||
<th class="text-center">TTL Amount</th>
|
|
||||||
<th class="text-center">CBM</th>
|
|
||||||
<th class="text-center">TTL CBM</th>
|
|
||||||
<th class="text-center">KG</th>
|
|
||||||
<th class="text-center">TTL KG</th>
|
|
||||||
<th class="text-center">Shop No</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
@foreach($invoice->items as $i => $item)
|
|
||||||
<tr>
|
|
||||||
<td class="text-center fw-bold text-muted">{{ $i+1 }}</td>
|
|
||||||
<td class="fw-semibold">{{ $item->description }}</td>
|
|
||||||
<td class="text-center">{{ $item->ctn }}</td>
|
|
||||||
<td class="text-center">{{ $item->qty }}</td>
|
|
||||||
<td class="text-center fw-bold">{{ $item->ttl_qty }}</td>
|
|
||||||
<td class="text-center">{{ $item->unit }}</td>
|
|
||||||
<td class="text-center text-success fw-bold">₹{{ number_format($item->price,2) }}</td>
|
|
||||||
<td class="text-center text-primary fw-bold">₹{{ number_format($item->ttl_amount,2) }}</td>
|
|
||||||
<td class="text-center">{{ $item->cbm }}</td>
|
|
||||||
<td class="text-center">{{ $item->ttl_cbm }}</td>
|
|
||||||
<td class="text-center">{{ $item->kg }}</td>
|
|
||||||
<td class="text-center">{{ $item->ttl_kg }}</td>
|
|
||||||
<td class="text-center">
|
|
||||||
<span class="badge bg-light text-dark border">{{ $item->shop_no }}</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
@endforeach
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Final Summary -->
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6 offset-md-6">
|
<div class="col-md-6">
|
||||||
<div class="card border-0 bg-light">
|
|
||||||
<div class="card-header bg-dark text-white py-2">
|
<h2 class="fw-bold text-primary mb-1">
|
||||||
<h6 class="mb-0 fw-bold">
|
<i class="fas fa-file-invoice me-2"></i> INVOICE
|
||||||
<i class="fas fa-calculator me-2"></i>Final Summary
|
</h2>
|
||||||
</h6>
|
|
||||||
</div>
|
<h4 class="fw-bold text-dark mb-0">{{ $invoice->invoice_number }}</h4>
|
||||||
<div class="card-body">
|
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-1 border-bottom">
|
{{-- ORDER + SHIPMENT INFO --}}
|
||||||
<span class="fw-semibold">Amount:</span>
|
<div class="mt-2 small">
|
||||||
<span class="fw-bold text-dark">₹{{ number_format($invoice->final_amount,2) }}</span>
|
|
||||||
|
{{-- ORDER ID --}}
|
||||||
|
@if($invoice->order_id)
|
||||||
|
<div>
|
||||||
|
<strong>Order ID:</strong>
|
||||||
|
{{ $invoice->order->order_id ?? $invoice->order_id }}
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between align-items-center mb-2 pb-1 border-bottom">
|
@endif
|
||||||
<span class="fw-semibold">GST ({{ $invoice->gst_percent }}%):</span>
|
|
||||||
<span class="fw-bold text-danger">₹{{ number_format($invoice->gst_amount,2) }}</span>
|
{{-- SHIPMENT ID --}}
|
||||||
</div>
|
@if(isset($shipment) && $shipment)
|
||||||
<div class="d-flex justify-content-between align-items-center pt-1">
|
<div>
|
||||||
<span class="fw-bold text-dark">Total With GST:</span>
|
<strong>Shipment ID:</strong> {{ $shipment->shipment_id }}
|
||||||
<span class="fw-bold text-success">₹{{ number_format($invoice->final_amount_with_gst,2) }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6 text-end">
|
||||||
|
|
||||||
|
<span class="badge fs-6 px-3 py-2
|
||||||
|
@if($invoice->status=='paid') bg-success
|
||||||
|
@elseif($invoice->status=='overdue') bg-danger
|
||||||
|
@elseif($invoice->status=='pending') bg-warning text-dark
|
||||||
|
@else bg-secondary @endif">
|
||||||
|
|
||||||
|
<i class="fas
|
||||||
|
@if($invoice->status=='paid') fa-check-circle
|
||||||
|
@elseif($invoice->status=='overdue') fa-exclamation-circle
|
||||||
|
@elseif($invoice->status=='pending') fa-clock
|
||||||
|
@else fa-question-circle @endif me-1"></i>
|
||||||
|
|
||||||
|
{{ ucfirst($invoice->status) }}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================
|
||||||
|
DATES SECTION
|
||||||
|
============================ -->
|
||||||
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
|
<div class="card-body py-3">
|
||||||
|
|
||||||
|
<div class="row text-center align-items-center">
|
||||||
|
|
||||||
|
<div class="col-md-5">
|
||||||
|
<small class="text-muted fw-semibold">INVOICE DATE</small>
|
||||||
|
<div class="fw-bold text-dark">
|
||||||
|
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-2">
|
||||||
|
<i class="fas fa-arrow-right text-muted"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-5">
|
||||||
|
<small class="text-muted fw-semibold">DUE DATE</small>
|
||||||
|
<div class="fw-bold {{ $invoice->status=='overdue' ? 'text-danger' : 'text-dark' }}">
|
||||||
|
{{ \Carbon\Carbon::parse($invoice->due_date)->format('M d, Y') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
|
||||||
.date-connector {
|
<!-- ============================
|
||||||
display: flex;
|
CUSTOMER DETAILS
|
||||||
align-items: center;
|
============================ -->
|
||||||
justify-content: center;
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
height: 100%;
|
|
||||||
color: #6c757d;
|
<div class="card-header bg-light py-2">
|
||||||
|
<h6 class="fw-bold mb-0"><i class="fas fa-user me-2"></i> Customer Details</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h6 class="fw-bold text-primary">{{ $invoice->customer_name }}</h6>
|
||||||
|
|
||||||
|
@if($invoice->company_name)
|
||||||
|
<p class="mb-1"><strong>Company:</strong> {{ $invoice->company_name }}</p>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<p class="mb-1"><strong>Mobile:</strong> {{ $invoice->customer_mobile }}</p>
|
||||||
|
<p class="mb-1"><strong>Email:</strong> {{ $invoice->customer_email }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-md-6">
|
||||||
|
<p class="mb-1"><strong>Address:</strong><br>{{ $invoice->customer_address }}</p>
|
||||||
|
<p class="mb-1"><strong>Pincode:</strong> {{ $invoice->pincode }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================
|
||||||
|
INVOICE ITEMS
|
||||||
|
============================ -->
|
||||||
|
<div class="card border-0 shadow-sm mb-4">
|
||||||
|
|
||||||
|
<div class="card-header bg-light py-2">
|
||||||
|
<h6 class="fw-bold mb-0"><i class="fas fa-list me-2"></i> Invoice Items</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="table-responsive">
|
||||||
|
|
||||||
|
<table class="table table-bordered table-hover align-middle mb-0">
|
||||||
|
|
||||||
|
<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 class="text-center fw-bold">{{ $i + 1 }}</td>
|
||||||
|
<td>{{ $item->description }}</td>
|
||||||
|
<td class="text-center">{{ $item->ctn }}</td>
|
||||||
|
<td class="text-center">{{ $item->qty }}</td>
|
||||||
|
<td class="text-center">{{ $item->ttl_qty }}</td>
|
||||||
|
<td class="text-center">{{ $item->unit }}</td>
|
||||||
|
<td class="text-center">₹{{ number_format($item->price,2) }}</td>
|
||||||
|
<td class="text-center fw-bold text-primary">₹{{ number_format($item->ttl_amount,2) }}</td>
|
||||||
|
<td class="text-center">{{ $item->cbm }}</td>
|
||||||
|
<td class="text-center">{{ $item->ttl_cbm }}</td>
|
||||||
|
<td class="text-center">{{ $item->kg }}</td>
|
||||||
|
<td class="text-center">{{ $item->ttl_kg }}</td>
|
||||||
|
<td class="text-center">{{ $item->shop_no }}</td>
|
||||||
|
</tr>
|
||||||
|
@endforeach
|
||||||
|
</tbody>
|
||||||
|
|
||||||
|
</table>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================
|
||||||
|
FINAL SUMMARY
|
||||||
|
============================ -->
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 offset-md-6">
|
||||||
|
|
||||||
|
<div class="card border-0 bg-light">
|
||||||
|
|
||||||
|
<div class="card-header bg-dark text-white py-2">
|
||||||
|
<h6 class="fw-bold mb-0"><i class="fas fa-calculator me-2"></i> Final Summary</h6>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body">
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between mb-2 border-bottom pb-1">
|
||||||
|
<span class="fw-semibold">Amount:</span>
|
||||||
|
<span class="fw-bold">₹{{ number_format($invoice->final_amount, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if($invoice->tax_type === 'gst')
|
||||||
|
{{-- CGST --}}
|
||||||
|
<div class="d-flex justify-content-between mb-2 border-bottom pb-1">
|
||||||
|
<span class="fw-semibold">CGST ({{ $invoice->cgst_percent }}%):</span>
|
||||||
|
<span class="fw-bold text-danger">₹{{ number_format($invoice->gst_amount/2, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{-- SGST --}}
|
||||||
|
<div class="d-flex justify-content-between mb-2 border-bottom pb-1">
|
||||||
|
<span class="fw-semibold">SGST ({{ $invoice->sgst_percent }}%):</span>
|
||||||
|
<span class="fw-bold text-danger">₹{{ number_format($invoice->gst_amount/2, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@elseif($invoice->tax_type === 'igst')
|
||||||
|
{{-- IGST --}}
|
||||||
|
<div class="d-flex justify-content-between mb-2 border-bottom pb-1">
|
||||||
|
<span class="fw-semibold">IGST ({{ $invoice->igst_percent }}%):</span>
|
||||||
|
<span class="fw-bold text-danger">₹{{ number_format($invoice->gst_amount, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-between pt-1">
|
||||||
|
<span class="fw-bold text-dark">Total Payable:</span>
|
||||||
|
<span class="fw-bold text-success">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================
|
||||||
|
FOOTER — DOWNLOAD & SHARE
|
||||||
|
============================ -->
|
||||||
|
<div class="modal-footer">
|
||||||
|
|
||||||
|
@if($invoice->pdf_path)
|
||||||
|
<a href="{{ asset($invoice->pdf_path) }}" class="btn btn-primary" download>
|
||||||
|
<i class="bi bi-download me-1"></i> Download PDF
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<button class="btn btn-success" onclick="shareInvoice()">
|
||||||
|
<i class="bi bi-share me-1"></i> Share
|
||||||
|
</button>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
<!-- <button class="btn btn-secondary" data-bs-dismiss="modal">Close</button> -->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- ============================
|
||||||
|
SHARE SCRIPT
|
||||||
|
============================ -->
|
||||||
|
<script>
|
||||||
|
function shareInvoice() {
|
||||||
|
const shareData = {
|
||||||
|
title: "Invoice {{ $invoice->invoice_number }}",
|
||||||
|
text: "Sharing invoice {{ $invoice->invoice_number }}",
|
||||||
|
url: "{{ asset($invoice->pdf_path) }}"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (navigator.share) {
|
||||||
|
navigator.share(shareData).catch(() => {});
|
||||||
|
} else {
|
||||||
|
navigator.clipboard.writeText(shareData.url);
|
||||||
|
alert("Link copied! Sharing not supported on this browser.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.date-connector i {
|
</script>
|
||||||
background: #f8f9fa;
|
|
||||||
padding: 4px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 1px solid #e9ecef;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
border-radius: 6px;
|
|
||||||
}
|
|
||||||
.table {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
.table > :not(caption) > * > * {
|
|
||||||
padding: 10px 6px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -143,6 +143,18 @@ Route::prefix('admin')
|
|||||||
Route::post('/invoices/{id}/update', [AdminInvoiceController::class, 'update'])
|
Route::post('/invoices/{id}/update', [AdminInvoiceController::class, 'update'])
|
||||||
->name('admin.invoices.update');
|
->name('admin.invoices.update');
|
||||||
|
|
||||||
|
Route::post('/invoices/{invoice}/installments', [AdminInvoiceController::class, 'storeInstallment'])
|
||||||
|
->name('admin.invoice.installment.store');
|
||||||
|
|
||||||
|
Route::post('/invoices/{id}/installment', [AdminInvoiceController::class, 'storeInstallment'])
|
||||||
|
->name('admin.invoice.installment.store');
|
||||||
|
|
||||||
|
Route::delete('/installment/{id}', [AdminInvoiceController::class, 'deleteInstallment'])
|
||||||
|
->name('admin.invoice.installment.delete');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Add New Invoice
|
//Add New Invoice
|
||||||
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
|
Route::get('/admin/invoices/create', [InvoiceController::class, 'create'])->name('admin.invoices.create');
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user