Files
Kent-logistics-Laravel/resources/views/admin/invoice_edit.blade.php
Abhishek Mali bebe0711f4 gst
2025-11-25 13:14:53 +05:30

603 lines
18 KiB
PHP

@extends('admin.layouts.app')
@section('page-title', 'Edit Invoice')
@section('content')
<style>
/* --------------------------------------------------
GLOBAL VARIABLES
-------------------------------------------------- */
:root {
--primary-gradient: linear-gradient(135deg,#667eea,#764ba2);
--success-gradient: linear-gradient(135deg,#10b981,#059669);
--glass-bg: #fff;
--shadow-soft: 0 8px 32px rgba(0,0,0,0.1);
--shadow-medium: 0 12px 48px rgba(0,0,0,0.15);
--shadow-strong: 0 20px 60px rgba(0,0,0,0.2);
}
/* --------------------------------------------------
CARD DESIGN
-------------------------------------------------- */
.card {
background: var(--glass-bg);
border-radius: 12px;
border: 1px solid #e4e6ef;
box-shadow: var(--shadow-strong);
position: relative;
overflow: hidden;
animation: fadeUp 0.8s ease;
}
.card::before {
content:"";
position:absolute; inset:0;
background:linear-gradient(45deg,
rgba(102,126,234,.03),
rgba(118,75,162,.03) 50%,
rgba(16,185,129,.03));
pointer-events:none;
}
@keyframes fadeUp {
0% {opacity:0; transform:translateY(30px) scale(.95);}
100% {opacity:1; transform:translateY(0) scale(1);}
}
/* --------------------------------------------------
CARD HEADER
-------------------------------------------------- */
.card-header {
background:var(--primary-gradient);
color:#fff;
padding:11px 19px;
border:none;
position:relative;
overflow:hidden;
}
.card-header::before {
content:"";
position:absolute;
top:-50%; left:-50%;
width:200%; height:200%;
background:linear-gradient(45deg,transparent,
rgba(255,255,255,.1) 50%,transparent);
animation: shimmer 6s infinite linear;
}
@keyframes shimmer {
from {transform:translateX(-100%) rotate(45deg);}
to {transform:translateX(100%) rotate(45deg);}
}
.card-header h4 {
margin:0; font-weight:800; font-size:1.6rem;
display:flex; align-items:center; gap:12px;
text-shadow:0 2px 4px rgba(0,0,0,.1);
}
.card-header h4::before {
content:""; font-size:1.4rem;
}
/* --------------------------------------------------
BODY + FORM ELEMENTS
-------------------------------------------------- */
.card-body { padding:15px; background:#f8fafc; }
.form-label {
font-weight:700; color:#1e293b;
margin-bottom:10px; font-size:.95rem;
letter-spacing:.5px; text-transform:uppercase;
display:flex; align-items:center; gap:8px;
}
.form-label::before {
content:""; width:4px; height:16px;
background:var(--primary-gradient);
border-radius:2px;
}
.form-control, .form-select {
border: 2px solid #e2e8f0;
padding: 8px 15px;
border-radius: 10px;
background: #fff;
font-size: 1rem;
transition: .4s;
box-shadow: 0 2px 12px rgba(0, 0, 0, .05);
}
.form-control:focus, .form-select:focus {
border-color:#667eea;
box-shadow:
0 0 0 4px rgba(102,126,234,.15),
0 8px 24px rgba(102,126,234,.2);
transform:translateY(-2px) scale(1.02);
}
/* hover */
.form-control:hover, .form-select:hover {
border-color:#cbd5e1;
transform:translateY(-2px);
box-shadow:0 6px 20px rgba(0,0,0,.08);
}
/* textarea */
textarea.form-control {
height: 65px;
resize: vertical;
}
/* --------------------------------------------------
BUTTON STYLING
-------------------------------------------------- */
.btn-success {
background: var(--success-gradient);
border: none;
padding: 10px 25px;
border-radius: 7px;
font-weight: 700;
font-size: 1.1rem;
color: #fff;
transition: .4s;
box-shadow: var(--shadow-medium);
position: relative;
overflow: hidden;
}
.btn-success::before {
content:"";
position:absolute; left:-100%; top:0;
width:100%; height:100%;
background:linear-gradient(90deg,transparent,
rgba(255,255,255,.4),transparent);
transition:.6s;
}
.btn-success:hover {
transform:translateY(-4px) scale(1.05);
box-shadow:0 16px 40px rgba(16,185,129,.4);
}
.btn-success:hover::before { left:100%; }
.btn-success.loading {
pointer-events:none; padding-right:60px;
}
.btn-success.loading::after {
content:"";
position:absolute; right:20px; top:50%;
width:20px; height:20px; margin-top:-10px;
border-radius:50%;
border:2px solid transparent;
border-top-color:#fff;
animation:spin 1s linear infinite;
}
@keyframes spin {to{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;}
}
/* Only animation reduction */
@media(prefers-reduced-motion:reduce){
*{animation:none!important; transition:none!important;}
}
</style>
{{-- Invoice Preview Section --}}
<div class="card shadow-sm mb-4">
<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>
</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">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
<div class="row g-3">
<div class="col-md-4">
<label class="form-label">Installment Date</label>
<input type="date" name="installment_date" class="form-control" required>
</div>
<div class="col-md-4">
<label class="form-label">Payment Method</label>
<select name="payment_method" class="form-select" required>
<option value="cash">Cash</option>
<option value="bank">Bank Transfer</option>
<option value="upi">UPI</option>
<option value="cheque">Cheque</option>
</select>
</div>
<div class="col-md-4">
<label class="form-label">Reference No (optional)</label>
<input type="text" name="reference_no" class="form-control">
</div>
<div class="col-md-12">
<label class="form-label">Amount</label>
<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>
</div>
</div>
</form>
</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>
document.addEventListener("DOMContentLoaded", function () {
// ✅ Toggle Installment Form
const toggleBtn = document.getElementById("toggleInstallmentForm");
const formBox = document.getElementById("installmentForm");
if (toggleBtn) {
toggleBtn.addEventListener("click", () => formBox.classList.toggle("d-none"));
}
// ✅ Add Installment
const submitForm = document.getElementById("installmentSubmitForm");
const submitBtn = document.getElementById("installmentSubmitBtn");
const formatINR = amt =>
"" + Number(amt).toLocaleString("en-IN", {
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
submitForm.addEventListener("submit", function (e) {
e.preventDefault();
submitBtn.textContent = "Processing...";
submitBtn.disabled = true;
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>
@endsection