changes in order,popup invoice ,invoice pdf,invoice edit,customer add,customer a

This commit is contained in:
divya abdar
2025-12-25 10:19:20 +05:30
parent 9423c79c80
commit cb24cf575b
42 changed files with 2589 additions and 2508 deletions

View File

@@ -274,6 +274,30 @@ body {
color: white;
}
.btn-info-compact {
background: linear-gradient(135deg, #06b6d4 0%, #0ea5e9 100%);
color: white;
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.3);
}
.btn-info-compact:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(6, 182, 212, 0.4);
color: white;
}
.btn-warning-compact {
background: var(--warning-gradient);
color: white;
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
}
.btn-warning-compact:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(245, 158, 11, 0.4);
color: white;
}
/* --------------------------------------------------
COMPACT SUMMARY CARDS
-------------------------------------------------- */
@@ -316,7 +340,7 @@ body {
-------------------------------------------------- */
.amount-breakdown-compact {
background: white;
padding: 1rem;
padding: 1.5rem;
border-radius: 8px;
box-shadow: var(--shadow-soft);
margin-bottom: 1.5rem;
@@ -391,6 +415,15 @@ body {
font-weight: 600;
}
/* --------------------------------------------------
HEADER ACTION BUTTONS
-------------------------------------------------- */
.header-actions {
display: flex;
gap: 0.75rem;
align-items: center;
}
/* --------------------------------------------------
RESPONSIVE DESIGN
-------------------------------------------------- */
@@ -422,6 +455,13 @@ body {
.table-compact {
font-size: 0.8rem;
}
.header-actions {
flex-direction: column;
width: 100%;
gap: 0.5rem;
margin-top: 0.5rem;
}
}
@media print {
@@ -454,10 +494,21 @@ body {
<!-- Invoice Preview Section -->
<div class="glass-card">
<div class="card-header-compact">
<div class="card-header-compact d-flex justify-content-between align-items-center">
<h4>
<i class="fas fa-file-invoice me-2"></i>Invoice Overview
</h4>
<div class="header-actions">
<!-- Share Invoice -->
<button class="btn-info-compact btn-compact" id="shareInvoiceBtn">
<i class="fas fa-share-alt me-2"></i>Share Invoice
</button>
<!-- Download Invoice -->
<a href="{{ route('admin.invoices.download', $invoice->id) }}"
class="btn-warning-compact btn-compact" target="_blank">
<i class="fas fa-download me-2"></i>Download Invoice
</a>
</div>
</div>
<div class="card-body-compact invoice-preview-wrapper">
@include('admin.popup_invoice', [
@@ -468,6 +519,89 @@ body {
</div>
</div>
<!-- Amount Breakdown -->
<div class="glass-card">
<div class="card-header-compact">
<h4>
<i class="fas fa-calculator me-2"></i>Amount Breakdown
</h4>
</div>
<div class="card-body-compact">
<div class="amount-breakdown-compact">
@php
$totalPaid = $invoice->installments->sum('amount');
$remaining = $invoice->final_amount_with_gst - $totalPaid;
// Calculate GST/IGST percentage dynamically
$taxPercentage = $invoice->tax_type === 'gst'
? ($invoice->cgst_percent + $invoice->sgst_percent)
: $invoice->igst_percent;
@endphp
<div class="breakdown-row">
<span class="breakdown-label">Total Amount (Before Tax):</span>
<span class="breakdown-value">{{ number_format($invoice->final_amount, 2) }}</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">
@if($invoice->tax_type === 'gst')
GST ({{ number_format($taxPercentage, 2) }}%)
@else
IGST ({{ number_format($taxPercentage, 2) }}%)
@endif:
</span>
<span class="breakdown-value text-warning">{{ number_format($invoice->gst_amount, 2) }}</span>
</div>
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
<span class="breakdown-label fw-bold">Total Invoice Amount (Including GST):</span>
<span class="breakdown-value fw-bold text-dark">{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label text-success">Total Paid:</span>
<span class="breakdown-value fw-bold text-success" id="paidAmount">{{ number_format($totalPaid, 2) }}</span>
</div>
<div class="breakdown-row" style="border-bottom: none;">
<span class="breakdown-label text-danger">Remaining:</span>
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">{{ number_format($remaining, 2) }}</span>
</div>
<!-- NEW: Customer's Total Amount Due Across All Invoices -->
<div class="breakdown-row" style="border-top: 2px solid #3b82f6; background: rgba(59, 130, 246, 0.05);">
<span class="breakdown-label fw-bold text-primary">
<i class="fas fa-users me-1"></i>Total Amount Due (Including Tax):
<small class="d-block text-muted" style="font-size: 0.75rem; font-weight: normal;">
</small>
</span>
<span class="breakdown-value fw-bold text-primary" style="font-size: 1.1rem;">
{{ number_format($customerTotalDue, 2) }}
</span>
</div>
</div>
<!-- Installment Summary -->
<div class="summary-grid-compact mt-3">
<div class="summary-card-compact total">
<div class="summary-value-compact text-success">{{ number_format($invoice->final_amount_with_gst, 2) }}</div>
<div class="summary-label-compact">Total Amount</div>
</div>
<div class="summary-card-compact paid">
<div class="summary-value-compact text-primary">{{ number_format($totalPaid, 2) }}</div>
<div class="summary-label-compact">Total Paid</div>
</div>
<div class="summary-card-compact remaining">
<div class="summary-value-compact text-warning">{{ number_format($remaining, 2) }}</div>
<div class="summary-label-compact">Remaining</div>
</div>
</div>
</div>
</div>
<!-- Edit Invoice Form -->
<div class="glass-card">
<div class="card-header-compact">
@@ -577,81 +711,6 @@ body {
</div>
</div>
@php
$totalPaid = $invoice->installments->sum('amount');
$remaining = $invoice->final_amount_with_gst - $totalPaid;
@endphp
<!-- Amount Breakdown -->
<div class="amount-breakdown-compact">
<h6 class="fw-bold mb-3 text-dark">
<i class="fas fa-calculator me-2"></i>Amount Breakdown
</h6>
<div class="breakdown-row">
<span class="breakdown-label">Total Amount (Before Tax):</span>
<span class="breakdown-value">{{ number_format($invoice->final_amount, 2) }}</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">Tax Type:</span>
<span class="breakdown-value text-primary">
@if($invoice->tax_type === 'gst')
GST (CGST + SGST)
@else
IGST
@endif
</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">Tax Percentage:</span>
<span class="breakdown-value text-primary">
@if($invoice->tax_type === 'gst')
{{ $invoice->cgst_percent + $invoice->sgst_percent }}%
@else
{{ $invoice->igst_percent }}%
@endif
</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">GST Amount:</span>
<span class="breakdown-value text-warning">{{ number_format($invoice->gst_amount, 2) }}</span>
</div>
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
<span class="breakdown-label fw-bold">Total Invoice Amount (Including GST):</span>
<span class="breakdown-value fw-bold text-dark">{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label text-success">Total Paid:</span>
<span class="breakdown-value fw-bold text-success" id="paidAmount">{{ number_format($totalPaid, 2) }}</span>
</div>
<div class="breakdown-row" style="border-bottom: none;">
<span class="breakdown-label text-danger">Remaining:</span>
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">{{ number_format($remaining, 2) }}</span>
</div>
</div>
<!-- Installment Summary -->
<div class="summary-grid-compact">
<div class="summary-card-compact total">
<div class="summary-value-compact text-success">{{ number_format($invoice->final_amount_with_gst, 2) }}</div>
<div class="summary-label-compact">Total Amount</div>
</div>
<div class="summary-card-compact paid">
<div class="summary-value-compact text-primary">{{ number_format($totalPaid, 2) }}</div>
<div class="summary-label-compact">Total Paid</div>
</div>
<div class="summary-card-compact remaining">
<div class="summary-value-compact text-warning">{{ number_format($remaining, 2) }}</div>
<div class="summary-label-compact">Remaining</div>
</div>
</div>
<!-- Installment Management -->
<div class="glass-card">
<div class="card-header-compact d-flex justify-content-between align-items-center">
@@ -736,6 +795,7 @@ body {
<h6 class="fw-bold mb-2 text-dark">
<i class="fas fa-history me-2"></i>Installment History
</h6>
@if($invoice->installments->count() > 0)
<div class="table-responsive">
<table class="table-compact">
<thead>
@@ -776,24 +836,87 @@ body {
</tbody>
</table>
</div>
@else
<div id="noInstallmentsMsg" class="text-center text-muted fw-bold py-4">
No installments found. Click "Add Installment" to create one.
</div>
@endif
</div>
</div>
</div>
<!-- Add this just above the table -->
<div id="noInstallmentsMsg" class="d-none text-center text-muted fw-bold py-4">
No installments found. Click "Add Installment" to create one.
</div>
<table ...>
<tbody id="installmentTable">
@foreach($invoice->installments as $i)
...
@endforeach
</tbody>
</table>
<script>
document.addEventListener("DOMContentLoaded", function () {
// Share Invoice Functionality
const shareInvoiceBtn = document.getElementById('shareInvoiceBtn');
if (shareInvoiceBtn) {
shareInvoiceBtn.addEventListener('click', function() {
const shareUrl = window.location.href;
const invoiceNo = "{{ $invoice->invoice_no }}";
const message = `Invoice #${invoiceNo} - Total: ₹{{ number_format($invoice->final_amount_with_gst, 2) }}\nView: ${shareUrl}`;
if (navigator.share) {
navigator.share({
title: `Invoice #${invoiceNo}`,
text: `Invoice #${invoiceNo} - Total: ₹{{ number_format($invoice->final_amount_with_gst, 2) }}`,
url: shareUrl
}).catch(console.error);
} else if (navigator.clipboard) {
navigator.clipboard.writeText(message)
.then(() => {
alert('Invoice link copied to clipboard!');
})
.catch(err => {
console.error('Failed to copy: ', err);
// Fallback to prompt
prompt('Copy this link to share:', shareUrl);
});
} else {
prompt('Copy this link to share:', shareUrl);
}
});
}
// Print Invoice Function
window.printInvoice = function() {
const printWindow = window.open('', '_blank');
const invoiceContent = document.querySelector('.invoice-preview-wrapper').innerHTML;
const printContent = `
<!DOCTYPE html>
<html>
<head>
<title>Invoice {{ $invoice->invoice_no }}</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.invoice-container { max-width: 800px; margin: 0 auto; }
.text-end { text-align: right; }
.fw-bold { font-weight: bold; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; border: 1px solid #ddd; }
th { background-color: #f8f9fa; }
.total-row { background-color: #f8f9fa; font-weight: bold; }
</style>
</head>
<body>
<div class="invoice-container">
${invoiceContent}
</div>
<script>
window.onload = function() {
window.print();
setTimeout(function() {
window.close();
}, 500);
};
<\/script>
</body>
</html>
`;
printWindow.document.write(printContent);
printWindow.document.close();
};
// Toggle Installment Form
const toggleBtn = document.getElementById("toggleInstallmentForm");
const formBox = document.getElementById("installmentForm");
@@ -814,82 +937,111 @@ document.addEventListener("DOMContentLoaded", function () {
maximumFractionDigits: 2
});
submitForm.addEventListener("submit", function (e) {
e.preventDefault();
if (submitForm) {
submitForm.addEventListener("submit", function (e) {
e.preventDefault();
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
submitBtn.disabled = true;
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>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.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
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.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
if (data.status === "error") {
alert(data.message);
return;
}
const table = document.querySelector("#installmentTable");
const noInstallmentsMsg = document.getElementById("noInstallmentsMsg");
if (noInstallmentsMsg) {
noInstallmentsMsg.classList.add("d-none");
}
if (!table) {
// Create table if it doesn't exist
const tableHTML = `
<div class="table-responsive">
<table class="table-compact">
<thead>
<tr>
<th>#</th>
<th>Date</th>
<th>Payment Method</th>
<th>Reference No</th>
<th>Amount</th>
<th>Action</th>
</tr>
</thead>
<tbody id="installmentTable">
</tbody>
</table>
</div>
`;
const parent = submitForm.closest('.card-body-compact');
parent.insertAdjacentHTML('beforeend', tableHTML);
}
const newTable = document.querySelector("#installmentTable");
const index = newTable.rows.length + 1;
newTable.insertAdjacentHTML("beforeend", `
<tr data-id="${data.installment.id}">
<td class="fw-bold text-muted">${index}</td>
<td>${data.installment.installment_date}</td>
<td>
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
${data.installment.payment_method.toUpperCase()}
</span>
</td>
<td>${data.installment.reference_no || '-'}</td>
<td class="fw-bold text-success">${formatINR(data.installment.amount)}</td>
<td>
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
<i class="fas fa-trash me-1"></i>Delete
</button>
</td>
</tr>
`);
// Update all displayed values
if (document.getElementById("paidAmount")) document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
if (document.getElementById("remainingAmount")) document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
// Update summary cards
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = formatINR(data.totalPaid);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = formatINR(data.remaining);
submitForm.reset();
// If fully paid, disable/add display logic
if (data.isCompleted) {
toggleBtn?.remove();
formBox.classList.add("d-none");
}
if (data.status === "error") {
alert(data.message);
return;
}
const table = document.querySelector("#installmentTable");
const index = table.rows.length + 1;
table.insertAdjacentHTML("beforeend", `
<tr data-id="${data.installment.id}">
<td class="fw-bold text-muted">${index}</td>
<td>${data.installment.installment_date}</td>
<td>
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
${data.installment.payment_method.toUpperCase()}
</span>
</td>
<td>${data.installment.reference_no || '-'}</td>
<td class="fw-bold text-success">${formatINR(data.installment.amount)}</td>
<td>
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
<i class="fas fa-trash me-1"></i>Delete
</button>
</td>
</tr>
`);
// Update all displayed values using GST fields!
if (document.getElementById("paidAmount")) document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
if (document.getElementById("remainingAmount")) document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
if (document.getElementById("baseAmount")) document.getElementById("baseAmount").textContent = formatINR(data.baseAmount);
if (document.getElementById("gstAmount")) document.getElementById("gstAmount").textContent = formatINR(data.gstAmount);
if (document.getElementById("totalInvoiceWithGst")) document.getElementById("totalInvoiceWithGst").textContent = formatINR(data.finalAmountWithGst);
if (document.getElementById("invoiceStatus")) document.getElementById("invoiceStatus").textContent = data.isCompleted ? "Paid" : "Pending";
// Update summary cards if used
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = formatINR(data.totalPaid);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = formatINR(data.remaining);
submitForm.reset();
// If fully paid, disable/add display logic
if (data.isCompleted) {
toggleBtn?.remove();
formBox.classList.add("d-none");
}
alert(data.message);
})
.catch(() => {
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
alert("Something went wrong. Please try again.");
})
.catch(() => {
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
alert("Something went wrong. Please try again.");
});
});
});
}
// Delete Installment
document.addEventListener("click", function (e) {
@@ -911,23 +1063,43 @@ document.addEventListener("DOMContentLoaded", function () {
.then(data => {
if (data.status === "success") {
row.style.opacity = "0";
setTimeout(() => row.remove(), 300);
setTimeout(() => {
row.remove();
// Update row numbers
const rows = document.querySelectorAll("#installmentTable tr");
rows.forEach((row, index) => {
row.querySelector("td:first-child").textContent = index + 1;
});
// Show no installments message if empty
if (rows.length === 0) {
const noInstallmentsMsg = document.getElementById("noInstallmentsMsg");
if (noInstallmentsMsg) {
noInstallmentsMsg.classList.remove("d-none");
}
}
}, 300);
// Update all displayed values using GST fields!
// Update all displayed values
if (document.getElementById("paidAmount")) document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
if (document.getElementById("remainingAmount")) document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
if (document.getElementById("baseAmount")) document.getElementById("baseAmount").textContent = formatINR(data.baseAmount);
if (document.getElementById("gstAmount")) document.getElementById("gstAmount").textContent = formatINR(data.gstAmount);
if (document.getElementById("totalInvoiceWithGst")) document.getElementById("totalInvoiceWithGst").textContent = formatINR(data.finalAmountWithGst);
if (document.getElementById("invoiceStatus")) document.getElementById("invoiceStatus").textContent = data.remaining === 0 ? "Paid" : "Pending";
// Update summary cards
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = formatINR(data.totalPaid);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = formatINR(data.remaining);
// Show add installment button if there's remaining amount
if (data.remaining > 0 && !document.getElementById("toggleInstallmentForm")) {
const header = document.querySelector(".glass-card .card-header-compact");
header.innerHTML += `
<button id="toggleInstallmentForm" class="btn-primary-compact btn-compact">
<i class="fas fa-plus-circle me-2"></i>Add Installment
</button>
`;
}
alert(data.message);
} else {
@@ -940,6 +1112,4 @@ document.addEventListener("DOMContentLoaded", function () {
});
});
</script>
@endsection