pdf Updated, Invoice Updated, Report Updated

This commit is contained in:
Utkarsh Khedkar
2026-03-17 19:14:47 +05:30
parent 0257b68f16
commit 19d7f423b3
23 changed files with 2320 additions and 1750 deletions

View File

@@ -40,7 +40,6 @@
margin-top: 2px;
}
/* DOWNLOAD BUTTONS - NEW STYLES */
.cm-download-pdf {
background: linear-gradient(100deg, #4c6fff 0%, #8e54e9 100%) !important;
color: #fff !important;
@@ -287,7 +286,7 @@
}
.cm-table-scroll-outer {
margin: 10px 14px 0 14px;
margin: 10px 14px 30px 14px;
border-radius: 14px;
border: 1.5px solid #c9a359;
box-shadow: 0 4px 14px rgba(76,111,255,0.18);
@@ -487,11 +486,6 @@
grid-template-columns: 1fr;
}
}
.cm-table-scroll-outer {
margin: 10px 14px 30px 14px; /* फक्त हे बदल करा */
}
</style>
<div class="container-fluid cm-wrapper">
@@ -522,7 +516,6 @@
</div>
</div>
<!-- बाकीचा सगळा code same आहे - काही बदल नाही -->
<div class="card cm-main-card">
<div class="card-header">
<h5>Container Information</h5>
@@ -682,7 +675,7 @@
<div class="cm-filter-bar">
<span class="cm-row-count">
Total rows: {{ $container->rows->count() }}   Edit cells then click "Save Changes".
Total rows: {{ $container->rows->count() }} Edit cells then click "Save Changes".
</span>
<input type="text" id="cmRowSearch" class="cm-filter-input"
placeholder="Quick search..." onkeyup="cmFilterRows()">
@@ -745,9 +738,9 @@
str_contains($norm, 'TOTALAMOUNT')
);
$isTotalColumn = $isTotalQty || $isTotalCbm || $isTotalKg || $isAmount;
$isTotalColumn = $isTotalQty || $isTotalCbm || $isTotalKg || $isAmount;
$isLockedByInvoice = in_array($row->row_index, $lockedRowIndexes ?? []);
$isReadOnly = $isTotalColumn || $container->status !== 'pending' || $isLockedByInvoice;
$isReadOnly = $isTotalColumn || $container->status !== 'pending' || $isLockedByInvoice;
@endphp
<td>
@@ -792,7 +785,6 @@
</button>
@endif
<!-- Toast notification missing होती, add केली -->
<div id="cmToast" class="cm-toast"></div>
<script>
@@ -800,7 +792,7 @@ function cmFilterRows() {
const input = document.getElementById('cmRowSearch');
if (!input) return;
const filter = input.value.toLowerCase();
const table = document.getElementById('cmExcelTable');
const table = document.getElementById('cmExcelTable');
if (!table) return;
const rows = table.getElementsByTagName('tr');
for (let i = 1; i < rows.length; i++) {
@@ -831,6 +823,7 @@ document.addEventListener('DOMContentLoaded', function () {
setTimeout(() => toast.classList.remove('cm-show'), 2500);
}
// फक्त number काढण्यासाठी helper, पण cell मध्ये format नाही करणार
function parseNumber(str) {
if (!str) return 0;
const cleaned = String(str).replace(/,/g, '').trim();
@@ -838,11 +831,6 @@ document.addEventListener('DOMContentLoaded', function () {
return isNaN(val) ? 0 : val;
}
function formatNumber(val, decimals) {
if (isNaN(val)) val = 0;
return val.toFixed(decimals);
}
function recalcRow(row) {
const inputs = row.querySelectorAll('.cm-cell-input');
@@ -894,27 +882,28 @@ document.addEventListener('DOMContentLoaded', function () {
}
});
// इथे आपण फक्त VALUE बदलतो, कोणतंही toFixed नाही वापरत
if (ttlQtyInput && ctnInput && qtyInput) {
const newTtlQty = ctn * qty;
ttlQtyInput.value = formatNumber(newTtlQty, 0);
ttlQtyInput.value = newTtlQty === 0 ? '' : String(newTtlQty);
ttlQty = newTtlQty;
}
if (ttlCbmInput && cbmInput && ctnInput) {
const newTtlCbm = cbm * ctn;
ttlCbmInput.value = formatNumber(newTtlCbm, 3);
ttlCbmInput.value = newTtlCbm === 0 ? '' : String(newTtlCbm);
ttlCbm = newTtlCbm;
}
if (ttlKgInput && kgInput && ctnInput) {
const newTtlKg = kg * ctn;
ttlKgInput.value = formatNumber(newTtlKg, 2);
ttlKgInput.value = newTtlKg === 0 ? '' : String(newTtlKg);
ttlKg = newTtlKg;
}
if (amountInput && priceInput && ttlQtyInput) {
const newAmount = price * ttlQty;
amountInput.value = formatNumber(newAmount, 2);
amountInput.value = newAmount === 0 ? '' : String(newAmount);
amount = newAmount;
}
}

View File

@@ -661,7 +661,8 @@
<th class="table-header">Customer ID</th>
<th class="table-header">Orders</th>
<th class="table-header">Order Total</th>
<th class="table-header">Total Payable</th>
<th class="table-header">GST Amount</th>
<th class="table-header">Total Paid</th>
<th class="table-header">Remaining</th>
<th class="table-header">Create Date</th>
<th class="table-header">Status</th>
@@ -672,20 +673,27 @@
<tbody id="customersTableBody">
@forelse($customers as $c)
@php
// Orders = invoice count
// 1) Orders = total invoice count
$ordersCount = $c->invoices->count();
// Order Total = items total from all invoices (final_amount)
$orderTotal = $c->invoices->sum('final_amount');
// 2) Order Total = सर्व invoices च्या charge groups चा base total (without GST)
$orderTotal = $c->invoices->sum(function($invoice) {
return $invoice->chargeGroups->sum('total_charge');
});
// Total payable = grand total with GST + groups
$totalPayable = $c->invoices->sum('grand_total_with_charges');
// 3) GST Amount = सर्व invoices च्या gst_amount चा sum
$gstTotal = $c->invoices->sum('gst_amount');
// Total paid via installments
$totalPaid = $c->invoiceInstallments->sum('amount');
// 3) Total Payable = customer ने किती paid केले (installments sum)
$totalPaid = $c->invoices->flatMap->installments->sum('amount');
// Remaining amount
$remainingAmount = max($totalPayable - $totalPaid, 0);
// 4) Remaining = grand_total_with_charges - paid
$grandTotal = $c->invoices->sum(function($invoice) {
$base = $invoice->chargeGroups->sum('total_charge');
$gst = (float)($invoice->gst_amount ?? 0);
return $base + $gst;
});
$remainingAmount = max($grandTotal - $totalPaid, 0);
@endphp
<tr>
<td class="customer-info-column">
@@ -724,7 +732,13 @@
<td class="total-column">
<span class="total-amount">
{{ number_format($totalPayable, 2) }}
{{ number_format($gstTotal, 2) }}
</span>
</td>
<td class="total-column">
<span class="total-amount">
{{ number_format($totalPaid, 2) }}
</span>
</td>
@@ -771,7 +785,7 @@
</tr>
@empty
<tr>
<td colspan="9" class="text-center py-4">
<td colspan="10" class="text-center py-4">
<i class="bi bi-people display-4 text-muted d-block mb-2"></i>
<span class="text-muted">No customers found.</span>
</td>
@@ -836,4 +850,4 @@
});
</script>
@endsection
@endsection

View File

@@ -1236,7 +1236,7 @@
<th class="column-header">Customer</th>
<th class="column-header">Container</th> {{-- NEW --}}
<th class="column-header">Final Amount</th>
<th class="column-header">GST %</th>
<th class="column-header">GST Amount</th>
<th class="column-header">Total w/GST</th>
<th class="column-header">Status</th>
<th class="column-header">Invoice Date</th>
@@ -1286,8 +1286,8 @@
{{ number_format($invoice->final_amount, 2) }}
</td>
<td class="gst-cell">
{{ $invoice->gst_percent }}%
<td class="amount-cell">
{{ number_format($invoice->gst_amount, 2) }}
</td>
<td class="amount-cell">
@@ -1379,8 +1379,8 @@
<span class="mobile-detail-value">{{ number_format($invoice->final_amount, 2) }}</span>
</div>
<div class="mobile-detail-item">
<span class="mobile-detail-label">GST</span>
<span class="mobile-detail-value">{{ $invoice->gst_percent }}%</span>
<span class="mobile-detail-label">GST Amount</span>
<span class="mobile-detail-value">{{ number_format($invoice->gst_amount, 2) }}</span>
</div>
<div class="mobile-detail-item">
<span class="mobile-detail-label">Total</span>
@@ -1694,7 +1694,7 @@ document.addEventListener('DOMContentLoaded', function() {
${invoice.container ? (invoice.container.container_number ?? '—') : '—'}
</td>
<td class="amount-cell">${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
<td class="gst-cell">${invoice.gst_percent}%</td>
<td class="amount-cell">${parseFloat(invoice.gst_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
<td class="amount-cell">${parseFloat(invoice.final_amount_with_gst).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</td>
<td>
<span class="badge badge-${invoice.status}">
@@ -1756,8 +1756,8 @@ document.addEventListener('DOMContentLoaded', function() {
<span class="mobile-detail-value">${parseFloat(invoice.final_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
</div>
<div class="mobile-detail-item">
<span class="mobile-detail-label">GST</span>
<span class="mobile-detail-value">${invoice.gst_percent}%</span>
<span class="mobile-detail-label">GST Amount</span>
<span class="mobile-detail-value">${parseFloat(invoice.gst_amount).toLocaleString('en-IN', {minimumFractionDigits: 2, maximumFractionDigits: 2})}</span>
</div>
<div class="mobile-detail-item">
<span class="mobile-detail-label">Total</span>

View File

@@ -325,95 +325,15 @@
</div>
</div>
{{-- Edit Invoice Header --}}
<div class="glass-card">
<div class="card-header-compact d-flex justify-content-between align-items-center">
<h4>
<i class="fas fa-edit me-2"></i>
Edit Invoice Details
</h4>
<small id="headerUpdateMsg" class="text-light"></small>
</div>
<div class="card-body-compact">
<form id="invoiceHeaderForm" action="{{ route('admin.invoices.update', $invoice->id) }}" method="POST">
@csrf
<div class="form-grid-compact">
{{-- Invoice Date --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-calendar-day"></i> Invoice Date
</label>
<input type="date"
name="invoice_date"
class="form-control-compact"
value="{{ old('invoice_date', $invoice->invoice_date) }}"
required>
</div>
{{-- Due Date --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-clock"></i> Due Date
</label>
<input type="date"
name="due_date"
class="form-control-compact"
value="{{ old('due_date', $invoice->due_date) }}"
required>
</div>
{{-- Final Amount (With GST) Charge Groups Total --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-money-bill-wave"></i> Final Amount (With GST)
</label>
<input type="number"
step="0.01"
class="form-control-compact"
value="{{ $invoice->grand_total_with_charges }}"
readonly>
</div>
{{-- Status --}}
<div class="form-group-compact">
<label class="form-label-compact">
<i class="fas fa-tasks"></i> Status
</label>
<select name="status" id="statusSelect" class="form-select-compact" required>
<option value="pending" {{ old('status', $invoice->status) === 'pending' ? 'selected' : '' }}>Pending</option>
<option value="paying" {{ old('status', $invoice->status) === 'paying' ? 'selected' : '' }}>Paying</option>
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
<option value="overdue" {{ old('status', $invoice->status) === 'overdue' ? 'selected' : '' }}>Overdue</option>
</select>
</div>
{{-- Notes --}}
<div class="form-group-compact" style="grid-column: 1 / -1;">
<label class="form-label-compact">
<i class="fas fa-sticky-note"></i> Notes
</label>
<textarea name="notes"
rows="3"
class="form-control-compact"
placeholder="Add any additional notes...">{{ old('notes', $invoice->notes) }}</textarea>
</div>
</div>
<div class="text-end mt-3">
<button type="submit" id="btnHeaderSave" class="btn-success-compact btn-compact">
<i class="fas fa-save me-2"></i>Update Invoice
</button>
</div>
</form>
</div>
</div>
@if(false)
{{-- Edit Invoice Details card HIDDEN --}}
{{-- तुझा full header edit form इथे hidden ठेवलेला आहे --}}
@endif
@php
$totalPaid = $invoice->totalPaid();
$remaining = $invoice->remainingAmount();
// Mixed tax type label from charge groups
$taxTypes = $invoice->chargeGroups
? $invoice->chargeGroups->pluck('tax_type')->filter()->unique()->values()
: collect([]);
@@ -440,47 +360,10 @@
}
@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">Tax Type</span>
<span class="breakdown-value text-primary">
{{ $taxTypeLabel }}
</span>
</div>
<div class="breakdown-row">
<span class="breakdown-label">GST Amount</span>
<span class="breakdown-value text-warning" id="gstAmount">
{{ 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">Grand Total (Charges + GST)</span>
<span class="breakdown-value fw-bold text-dark" id="totalInvoiceWithGst">
{{ number_format($invoice->grand_total_with_charges, 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(max(0, $remaining), 2) }}
</span>
</div>
</div>
@if(false)
{{-- Amount Breakdown HIDDEN --}}
{{-- जुनं breakdown section इथे hidden आहे --}}
@endif
{{-- Summary cards --}}
<div class="summary-grid-compact">
@@ -491,13 +374,13 @@
<div class="summary-label-compact">Grand Total (Charges + GST)</div>
</div>
<div class="summary-card-compact paid">
<div class="summary-value-compact text-primary">
<div class="summary-value-compact text-primary" id="paidAmount">
{{ 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">
<div class="summary-value-compact text-warning" id="remainingAmount">
{{ number_format(max(0, $remaining), 2) }}
</div>
<div class="summary-label-compact">Remaining</div>
@@ -683,91 +566,128 @@ document.addEventListener("DOMContentLoaded", function () {
headers: {
"X-CSRF-TOKEN": submitForm.querySelector('input[name="_token"]').value,
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
},
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;
.then(async res => {
let data;
try {
data = await res.json();
} catch (e) {
throw new Error("Invalid server response.");
}
if (data.status === "error") {
alert(data.message);
return;
}
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
const table = document.getElementById("installmentTable");
const index = table.rows.length + 1;
if (!res.ok) {
const msg =
(data && data.message) ||
(data && data.errors && Object.values(data.errors)[0][0]) ||
"Something went wrong.";
alert(msg);
return;
}
document.getElementById("noInstallmentsMsg")?.classList.add("d-none");
if (data.status === "error") {
alert(data.message || "Something went wrong.");
return;
}
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
? `<span class="text-muted">${data.installment.reference_no}</span>`
: '<span class="text-muted">-</span>'}
</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>
`);
const table = document.getElementById("installmentTable");
const index = table.querySelectorAll("tr").length + 1;
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.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
document.getElementById("noInstallmentsMsg")?.classList.add("d-none");
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
const pmMethod = data.installment.payment_method
? data.installment.payment_method.toUpperCase()
: "-";
const refNo = data.installment.reference_no
? `<span class="text-muted">${data.installment.reference_no}</span>`
: '<span class="text-muted">-</span>';
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
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">
${pmMethod}
</span>
</td>
<td>${refNo}</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>
`);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
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.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
submitForm.reset();
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
if (data.isCompleted && toggleBtn) {
toggleBtn.remove();
formBox.classList.add("d-none");
}
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
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.");
});
const fsTotal = document.getElementById("finalSummaryTotalPaid");
if (fsTotal) fsTotal.textContent = "" + formatINR(data.totalPaid);
const fsRemaining = document.getElementById("finalSummaryRemaining");
if (fsRemaining) {
fsRemaining.textContent = "" + formatINR(data.remaining);
fsRemaining.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
}
const fsRemainingLabel = document.getElementById("finalSummaryRemainingLabel");
if (fsRemainingLabel) {
fsRemainingLabel.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
fsRemainingLabel.innerHTML = `<i class="fas fa-${data.remaining > 0 ? 'exclamation-circle' : 'check-circle'} me-1" style="font-size:0.8rem;"></i>Remaining`;
}
if (data.newStatus && typeof updateStatusBadge === "function") {
updateStatusBadge(data.newStatus);
}
submitForm.reset();
if (data.isCompleted && toggleBtn) {
toggleBtn.remove();
formBox.classList.add("d-none");
}
alert(data.message || "Installment added successfully.");
})
.catch((err) => {
console.error("Installment submit error:", err);
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
submitBtn.disabled = false;
alert(err.message || "Something went wrong. Please try again.");
});
});
}
@@ -791,74 +711,78 @@ document.addEventListener("DOMContentLoaded", function () {
headers: {
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content"),
"Accept": "application/json",
"X-Requested-With": "XMLHttpRequest",
},
})
.then(res => res.json())
.then(data => {
if (data.status === "success") {
row.style.opacity = 0;
setTimeout(() => row.remove(), 300);
.then(async res => {
let data;
try { data = await res.json(); } catch(e) { throw new Error("Invalid server response."); }
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.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
if (!res.ok || data.status !== "success") {
const msg = data && data.message ? data.message : "Something went wrong. Please try again.";
throw new Error(msg);
}
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
row.style.opacity = 0;
setTimeout(() => row.remove(), 300);
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
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.chargeGroupsTotal);
}
if (document.getElementById("gstAmount")) {
document.getElementById("gstAmount").textContent = "" + formatINR(data.gstAmount);
}
if (document.getElementById("totalInvoiceWithGst")) {
document.getElementById("totalInvoiceWithGst").textContent =
"" + formatINR(data.grandTotal);
}
const totalCard = document.getElementById("totalInvoiceWithGstCard");
if (totalCard) {
totalCard.textContent = "" + formatINR(data.grandTotal);
}
if (data.isZero) {
document.getElementById("noInstallmentsMsg")?.classList.remove("d-none");
}
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
if (paidCard) paidCard.textContent = "" + formatINR(data.totalPaid);
alert(data.message);
} else {
alert(data.message || "Something went wrong. Please try again.");
}
})
.catch(() => {
alert("Something went wrong. Please try again.");
});
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
if (remainingCard) remainingCard.textContent = "" + formatINR(data.remaining);
const fsTotal = document.getElementById("finalSummaryTotalPaid");
if (fsTotal) fsTotal.textContent = "" + formatINR(data.totalPaid);
const fsRemaining = document.getElementById("finalSummaryRemaining");
if (fsRemaining) {
fsRemaining.textContent = "" + formatINR(data.remaining);
fsRemaining.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
}
const fsRemainingLabel = document.getElementById("finalSummaryRemainingLabel");
if (fsRemainingLabel) {
fsRemainingLabel.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
fsRemainingLabel.innerHTML = `<i class="fas fa-${data.remaining > 0 ? 'exclamation-circle' : 'check-circle'} me-1" style="font-size:0.8rem;"></i>Remaining`;
}
if (data.newStatus && typeof updateStatusBadge === "function") {
updateStatusBadge(data.newStatus);
}
if (data.isZero) {
document.getElementById("noInstallmentsMsg")?.classList.remove("d-none");
}
alert(data.message || "Installment deleted.");
})
.catch(err => {
alert(err.message || "Something went wrong. Please try again.");
});
});
// Auto due date = invoice date + 10 days
const invoiceDateInput = document.querySelector('input[name="invoice_date"]');
const dueDateInput = document.querySelector('input[name="due_date"]');
if (invoiceDateInput && dueDateInput) {
invoiceDateInput.addEventListener('change', function() {
const selectedDate = new Date(this.value);
if (!isNaN(selectedDate.getTime())) {
selectedDate.setDate(selectedDate.getDate() + 10);
const year = selectedDate.getFullYear();
const month = String(selectedDate.getMonth() + 1).padStart(2, '0');
const day = String(selectedDate.getDate()).padStart(2, '0');
dueDateInput.value = `${year}-${month}-${day}`;
}
});
}
// ✅ Invoice header AJAX save (no page refresh)
// Header AJAX (सध्या card hidden आहे, तरीही ठेवलेलं)
const headerForm = document.getElementById('invoiceHeaderForm');
const headerBtn = document.getElementById('btnHeaderSave');
const headerMsg = document.getElementById('headerUpdateMsg');
@@ -872,7 +796,6 @@ document.addEventListener("DOMContentLoaded", function () {
headerBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
const formData = new FormData(headerForm);
fetch(headerForm.action, {
method: 'POST',
@@ -884,7 +807,7 @@ document.addEventListener("DOMContentLoaded", function () {
body: formData
})
.then(async res => {
let data = null;
let data;
try { data = await res.json(); } catch(e) {}
if (!res.ok) {
@@ -899,7 +822,6 @@ document.addEventListener("DOMContentLoaded", function () {
headerMsg.classList.remove('text-danger');
headerMsg.classList.add('text-light');
// popup_invoice वरचा status badge update करायचा असल्यास:
const status = document.getElementById('statusSelect')?.value;
const badge = document.querySelector('.status-badge');
if (badge && status) {

View File

@@ -1,272 +1,623 @@
<!DOCTYPE html>
<html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{{ $invoice->invoice_number }}</title>
<title>Invoice #{{ $invoice->invoice_number }}</title>
<style>
body {
font-family: 'Segoe UI', 'Roboto', Arial, sans-serif;
background: #F7FBFC;
color: #1A222B;
font-family: 'DejaVu Sans', sans-serif;
font-size: 10px;
color: #1a202c;
line-height: 1.4;
margin: 0;
padding: 0;
font-size: 15px;
}
.container {
max-width: 850px;
margin: 24px auto 0 auto;
padding: 18px;
background: #fff;
border-radius: 13px;
box-shadow: 0 2px 14px rgba(40,105,160,0.08);
padding: 35px 32px 18px 32px;
}
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
border-bottom: 2px solid #E6EBF0;
padding-bottom: 13px;
}
.logo-company {
display: flex;
align-items: flex-start;
}
.logo {
height: 50px;
margin-right: 13px;
}
.company-details {
margin-top: 0;
font-size: 15px;
}
.company-title {
font-size: 21px;
font-weight: bold;
margin-bottom: 2px;
}
.company-sub {
font-size: 16px;
/* ── TOP HEADER ── */
.top-header {
margin-bottom: 8px;
font-weight: 500;
border-bottom: 2px solid #1a202c;
padding-bottom: 6px;
}
.invoice-details {
text-align: right;
min-width: 220px;
}
.invoice-title {
font-weight: bold;
font-size: 23px;
letter-spacing: 0.5px;
margin-bottom: 2px;
}
.paid-label {
margin-top: 8px;
margin-bottom: 2px;
}
.paid-tag {
background: #23BF47;
color: #fff;
font-weight: bold;
border-radius: 8px;
padding: 4px 16px 4px 22px;
font-size: 17px;
display: inline-block;
position: relative;
}
.paid-tag:before {
content: '';
position: absolute;
left: 7px;
top: 7px;
width: 10px;
height: 10px;
background: #fff;
border-radius: 50%;
}
.paid-date {
font-size: 14px;
color: #23BF47;
margin-top: 2px;
}
.bill-section {
background: #F3F7FB;
border-radius: 11px;
padding: 20px 18px 13px 18px;
margin: 28px 0 16px 0;
box-shadow: 0 0px 0px #0000;
}
.bill-title {
font-size: 17px;
font-weight: bold;
color: #23355D;
margin-bottom: 4px;
letter-spacing: 0.3px;
}
.bill-details {
font-size: 15px;
line-height: 1.6;
}
table {
.header-table {
width: 100%;
border-collapse: collapse;
margin-top: 9px;
margin-bottom: 13px;
background: #fff;
border-radius: 10px;
overflow: hidden;
}
th {
background: #F6F7F9;
padding: 10px 0;
font-size: 15px;
color: #6781A6;
font-weight: bold;
border: none;
.header-table td {
vertical-align: middle;
padding: 0;
}
/* Logo cell — fixed width, logo fits inside */
.logo-cell {
width: 60px;
}
.logo-cell img {
width: 55px;
height: 55px;
display: block;
}
/* Company name cell */
.title-cell {
padding-left: 10px;
text-align: left;
}
td {
padding: 7px 0;
color: #222;
font-size: 15px;
border: none;
text-align: left;
}
tbody tr:not(:last-child) td {
border-bottom: 1px solid #E6EBF0;
}
tbody tr:last-child td {
border-bottom: none;
}
.totals-row td {
.company-name {
font-size: 18px;
font-weight: bold;
color: #23355D;
color: #1a202c;
text-transform: uppercase;
letter-spacing: 2px;
}
.gst-row td {
font-weight: 500;
color: #23BF47;
}
.total-row td {
.company-subtitle {
font-size: 11px;
font-weight: bold;
font-size: 17px;
color: #222;
color: #2d3748;
letter-spacing: 1px;
}
.payment-info {
margin-top: 24px;
margin-bottom: 9px;
font-size: 15px;
.company-tagline {
font-size: 8px;
color: #718096;
margin-top: 3px;
}
.ref-number {
font-size: 14px;
color: #6781A6;
margin-bottom: 8px;
margin-top: 2px;
/* ── INFO TABLE (Customer | Invoice side by side) ── */
.info-outer {
width: 100%;
border-collapse: collapse;
border: 1px solid #cbd5e0;
margin-bottom: 10px;
}
.footer {
border-top: 1.2px solid #E6EBF0;
margin-top: 25px;
padding-top: 12px;
font-size: 16px;
color: #888;
.info-outer td {
vertical-align: top;
padding: 10px 12px;
}
.info-left { width: 50%; }
.info-right { width: 50%; border-left: 1px solid #cbd5e0; }
.section-title {
font-size: 8px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
color: #718096;
border-bottom: 1px solid #e2e8f0;
padding-bottom: 3px;
margin-bottom: 5px;
}
.info-line {
font-size: 9px;
color: #2d3748;
margin-bottom: 2px;
line-height: 1.5;
}
/* ── STATUS BADGE ── */
.badge {
display: inline;
padding: 2px 6px;
font-size: 8px;
font-weight: bold;
text-transform: uppercase;
border-radius: 3px;
}
.badge-paid { background: #c6f6d5; color: #22543d; }
.badge-pending { background: #fef3c7; color: #92400e; }
.badge-paying { background: #dbeafe; color: #1e40af; }
.badge-overdue { background: #fee2e2; color: #991b1b; }
/* ── BILL TO ── */
.bill-to-box {
border: 1px solid #cbd5e0;
padding: 8px 12px;
margin-bottom: 10px;
background: #f7fafc;
}
.bill-to-label {
font-size: 8px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
color: #718096;
margin-bottom: 4px;
}
.bill-name { font-size: 12px; font-weight: bold; color: #1a202c; margin-bottom: 2px; }
.bill-sub { font-size: 10px; font-weight: bold; color: #2d3748; margin-bottom: 2px; }
.bill-detail { font-size: 9px; color: #4a5568; line-height: 1.8; }
/* ── SUMMARY GRID — 4 boxes per row ── */
.summary-wrap { margin-bottom: 12px; }
.summary-label {
font-size: 9px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
color: #4a5568;
margin-bottom: 4px;
}
.summary-outer {
width: 100%;
border-collapse: collapse;
}
.sbox {
width: 25%;
border: 1px solid #cbd5e0;
padding: 7px 8px;
text-align: center;
vertical-align: middle;
}
.footer strong {
color: #222;
font-weight: 500;
.sbox-row1 { background: #ffffff; }
.sbox-row2 { background: #f7fafc; }
.sbox-lbl {
font-size: 7px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
color: #718096;
margin-bottom: 3px;
}
.sbox-val {
font-size: 11px;
font-weight: bold;
color: #1a202c;
}
.sbox-sub {
font-size: 7px;
color: #a0aec0;
margin-top: 1px;
}
/* ── CHARGE GROUPS ── */
.cg-header {
font-size: 10px;
font-weight: bold;
color: #ffffff;
background: #2d3748;
padding: 5px 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.cg-group-wrap {
border: 1px solid #cbd5e0;
margin-bottom: 10px;
}
.cg-sum-table {
width: 100%;
border-collapse: collapse;
font-size: 9px;
}
.cg-sum-table th {
background: #edf2f7;
padding: 5px;
font-size: 8px;
font-weight: bold;
text-transform: uppercase;
color: #4a5568;
border: 1px solid #cbd5e0;
text-align: left;
}
.cg-sum-table td {
padding: 5px;
border: 1px solid #cbd5e0;
color: #2d3748;
font-size: 9px;
}
.cg-item-table {
width: 100%;
border-collapse: collapse;
font-size: 8px;
}
.cg-item-table th {
background: #f0fff4;
padding: 4px 5px;
font-size: 7px;
font-weight: bold;
text-transform: uppercase;
color: #276749;
border: 1px solid #c6f6d5;
text-align: left;
white-space: nowrap;
}
.cg-item-table td {
padding: 4px 5px;
border: 1px solid #e2e8f0;
color: #2d3748;
white-space: nowrap;
}
.row-even { background: #f7fafc; }
/* ── GRAND TOTAL ── */
.grand-outer {
width: 100%;
border-collapse: collapse;
margin-top: 10px;
margin-bottom: 10px;
}
.grand-spacer { width: 60%; }
.grand-inner { width: 40%; vertical-align: top; }
.grand-table { width: 100%; border-collapse: collapse; font-size: 9px; }
.grand-table td { padding: 4px 8px; border: 1px solid #e2e8f0; }
.g-lbl { text-align: right; font-weight: bold; color: #4a5568; background: #f7fafc; }
.g-val { text-align: right; font-weight: bold; color: #1a202c; }
.g-total-lbl { text-align: right; font-size: 11px; font-weight: bold; color: #1a202c; background: #ebf8ff; border-top: 2px solid #2b6cb0; }
.g-total-val { text-align: right; font-size: 11px; font-weight: bold; color: #1a202c; background: #ebf8ff; border-top: 2px solid #2b6cb0; }
/* ── INSTALLMENTS ── */
.install-section { margin-top: 12px; }
.install-header {
font-size: 9px;
font-weight: bold;
color: #fff;
background: #2d3748;
padding: 5px 10px;
text-transform: uppercase;
letter-spacing: 1px;
}
.install-table { width: 100%; border-collapse: collapse; font-size: 9px; }
.install-table th {
background: #edf2f7;
padding: 5px 6px;
font-size: 8px;
font-weight: bold;
text-transform: uppercase;
color: #4a5568;
border: 1px solid #cbd5e0;
}
.install-table td { padding: 4px 6px; border: 1px solid #e2e8f0; color: #2d3748; }
/* ── HELPERS ── */
.tr { text-align: right; }
.tc { text-align: center; }
.bold { font-weight: bold; }
/* ── FOOTER ── */
.footer {
margin-top: 16px;
padding-top: 8px;
border-top: 1px solid #e2e8f0;
font-size: 8px;
color: #718096;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<!-- Header Section -->
<div class="header">
<div class="logo-company">
<img src="{{ public_path('images/kent_logo2.png') }}" class="logo">
<div class="company-details">
<div class="company-title">{{ $invoice->company_name ?? 'Kent International Pvt. Ltd.' }}</div>
<div class="company-sub"></div>
{{ $invoice->company_address ?? '123 Business Park, Sector 5' }}<br>
{{ $invoice->company_city ?? 'Gurugram, Haryana 122001' }}<br>
{{ $invoice->company_country ?? 'India' }}<br>
GST: {{ $invoice->company_gst ?? 'GST123456789' }}<br>
Email: {{ $invoice->company_email ?? 'billing@kent.com' }}<br>
Phone: {{ $invoice->company_phone ?? '+91 124 123 4567' }}
{{-- ══ VARIABLES ══ --}}
@php
$companyName = $invoice->company_name ?: ($invoice->customer->company_name ?? null);
$custName = $invoice->customer_name;
$custAddress = $invoice->customer_address ?: ($invoice->customer->address ?? null);
$custPincode = $invoice->pincode ?: ($invoice->customer->pincode ?? null);
$custEmail = $invoice->customer_email ?: ($invoice->customer->email ?? null);
$custMobile = $invoice->customer_mobile ?: ($invoice->customer->mobile_no ?? null);
@endphp
{{-- ══ 1) HEADER Logo + Company Name ══ --}}
<div class="top-header">
<table class="header-table">
<tr>
{{-- Logo: 55x55px DomPDF साठी public_path() वापरतो --}}
<td class="logo-cell">
<img src="{{ public_path('images/kent_logo2.png') }}" alt="Logo" width="55" height="55">
</td>
{{-- Company Name + Subtitle + Tagline --}}
<td class="title-cell">
<div class="company-name">KENT</div>
<div class="company-subtitle">International Pvt. Ltd.</div>
<div class="company-tagline">
Address Line 1, City, State Pincode &nbsp;|&nbsp;
Email: info@company.com &nbsp;|&nbsp;
Phone: +91 1234567890
</div>
</td>
</tr>
</table>
</div>
{{-- ══ 2) CUSTOMER | INVOICE ══ --}}
<table class="info-outer">
<tr>
<td class="info-left">
<div class="section-title">Customer Details</div>
@if($companyName)
<div class="info-line bold" style="font-size:11px;">{{ $companyName }}</div>
<div class="info-line">{{ $custName }}</div>
@else
<div class="info-line bold" style="font-size:11px;">{{ $custName }}</div>
@endif
@if($custAddress)<div class="info-line">{{ $custAddress }}</div>@endif
@if($custPincode)<div class="info-line">{{ $custPincode }}</div>@endif
@if($custEmail)<div class="info-line">Email: {{ $custEmail }}</div>@endif
@if($custMobile)<div class="info-line">Phone: {{ $custMobile }}</div>@endif
</td>
<td class="info-right">
<div class="section-title">Invoice Details</div>
<div class="info-line"><strong>Invoice #:</strong> {{ $invoice->invoice_number }}</div>
<div class="info-line"><strong>Date:</strong> {{ \Carbon\Carbon::parse($invoice->invoice_date)->format('d M, Y') }}</div>
<div class="info-line"><strong>Due Date:</strong> {{ \Carbon\Carbon::parse($invoice->due_date)->format('d M, Y') }}</div>
<div class="info-line">
<strong>Status:</strong>
<span class="badge badge-{{ strtolower($invoice->status) }}">{{ strtoupper($invoice->status) }}</span>
</div>
<div class="invoice-details">
<div class="invoice-title">INVOICE</div>
Invoice #: {{ $invoice->invoice_number }}<br>
Issue Date: {{ date('d M Y', strtotime($invoice->invoice_date)) }}<br>
Due Date: {{ date('d M Y', strtotime($invoice->due_date)) }}
@if(strtolower($invoice->status) == 'paid')
<div class="paid-label">
<span class="paid-tag">Paid</span>
</div>
<div class="paid-date">
Paid on: {{ date('d M Y', strtotime($invoice->paid_date ?? now())) }}
</div>
@else
<div class="paid-date" style="color:#d00;">Status: {{ ucfirst($invoice->status) }}</div>
@endif
</div>
</div>
<!-- Bill To Section -->
<div class="bill-section">
<div class="bill-title">Bill To</div>
<div class="bill-details">
<strong>{{ $invoice->customer_name }}</strong><br>
{{ $invoice->customer_address }}<br>
GST: {{ $invoice->customer_gst ?? '-' }}<br>
Email: {{ $invoice->customer_email }}<br>
Phone: {{ $invoice->customer_mobile }}
</div>
</div>
<!-- Items Table -->
<table>
<thead>
<tr>
<th>Description</th>
<th>Qty</th>
<th>Rate ()</th>
<th>Amount ()</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $item)
<tr>
<td>{{ $item->description }}</td>
<td>{{ $item->qty }}</td>
<td>{{ number_format($item->price, 0) }}</td>
<td>{{ number_format($item->ttl_amount, 0) }}</td>
</tr>
@endforeach
<tr class="totals-row">
<td colspan="3" style="text-align:right;">Subtotal:</td>
<td>{{ number_format($invoice->subtotal, 0) }}</td>
</tr>
<tr class="gst-row">
<td colspan="3" style="text-align:right;">GST ({{ $invoice->gst_percent }}%):</td>
<td>{{ number_format($invoice->gst_amount, 0) }}</td>
</tr>
<tr class="total-row">
<td colspan="3" style="text-align:right;">Total:</td>
<td>{{ number_format($invoice->final_amount_with_gst, 0) }}</td>
</tr>
</tbody>
</table>
<!-- Payment Info & Reference -->
<div class="payment-info">
<strong>Payment Method:</strong> {{ $invoice->payment_method ?? 'Bank Transfer' }}
</div>
<div class="ref-number">
Reference Number: {{ $invoice->reference_no ?? "REF123456789" }}
</div>
<!-- Footer -->
<div class="footer">
Thank you for your business!<br>
For any queries, please contact us at <strong>{{ $invoice->company_email ?? 'billing@kent.com' }}</strong>
</div>
<div class="info-line"><strong>GST Type:</strong> {{ strtoupper($invoice->tax_type ?? 'GST') }}</div>
<div class="info-line"><strong>GST %:</strong> {{ number_format($invoice->gst_percent ?? 0, 2) }}%</div>
</td>
</tr>
</table>
{{-- ══ 3) BILL TO ══ --}}
<div class="bill-to-box">
<div class="bill-to-label">Bill To</div>
@if($companyName)
<div class="bill-name">{{ $companyName }}</div>
<div class="bill-sub">{{ $custName }}</div>
@else
<div class="bill-name">{{ $custName }}</div>
@endif
<div class="bill-detail">
@if($custAddress){{ $custAddress }}<br>@endif
@if($custPincode){{ $custPincode }}<br>@endif
@if($custEmail)Email: {{ $custEmail }}<br>@endif
@if($custMobile)Phone: {{ $custMobile }}@endif
</div>
</div>
{{-- ══ 4) 8 SUMMARY BOXES ══ --}}
@php
$allGroupItems = $invoice->chargeGroups->flatMap(fn($g) => $g->items);
$invoiceItemIds = $allGroupItems->pluck('invoice_item_id')->unique()->values();
$invoiceItems = $invoice->items->whereIn('id', $invoiceItemIds);
$totalMarkCount = $invoiceItems->pluck('mark_no')->filter()->unique()->count();
$totalCtn = $invoiceItems->sum('ctn');
$totalQty = $invoiceItems->sum('ttl_qty');
$totalCbm = $invoiceItems->sum('ttl_cbm');
$totalKg = $invoiceItems->sum('ttl_kg');
@endphp
<div class="summary-wrap">
<div class="summary-label">Container &amp; Shipment Summary</div>
<table class="summary-outer">
<tr>
<td class="sbox sbox-row1">
<div class="sbox-lbl">Container Name</div>
<div class="sbox-val">{{ $invoice->container->container_name ?? '—' }}</div>
</td>
<td class="sbox sbox-row1">
<div class="sbox-lbl">Container Date</div>
<div class="sbox-val">
@if($invoice->container && $invoice->container->container_date)
{{ \Carbon\Carbon::parse($invoice->container->container_date)->format('d M, Y') }}
@else@endif
</div>
</td>
<td class="sbox sbox-row1">
<div class="sbox-lbl">Container No.</div>
<div class="sbox-val">{{ $invoice->container->container_number ?? '—' }}</div>
</td>
<td class="sbox sbox-row1">
<div class="sbox-lbl">Total Mark No.</div>
<div class="sbox-val">{{ $totalMarkCount }}</div>
<div class="sbox-sub">Unique marks</div>
</td>
</tr>
<tr>
<td class="sbox sbox-row2">
<div class="sbox-lbl">Total CTN</div>
<div class="sbox-val">{{ number_format($totalCtn, 0) }}</div>
<div class="sbox-sub">Cartons</div>
</td>
<td class="sbox sbox-row2">
<div class="sbox-lbl">Total QTY</div>
<div class="sbox-val">{{ number_format($totalQty, 0) }}</div>
<div class="sbox-sub">Pieces</div>
</td>
<td class="sbox sbox-row2">
<div class="sbox-lbl">Total CBM</div>
<div class="sbox-val">{{ number_format($totalCbm, 3) }}</div>
<div class="sbox-sub">Cubic Meter</div>
</td>
<td class="sbox sbox-row2">
<div class="sbox-lbl">Total KG</div>
<div class="sbox-val">{{ number_format($totalKg, 2) }}</div>
<div class="sbox-sub">Kilograms</div>
</td>
</tr>
</table>
</div>
{{-- ══ 5) CHARGE GROUPS ══ --}}
@if($invoice->chargeGroups && $invoice->chargeGroups->count() > 0)
<div style="margin-bottom:14px;">
<div class="cg-header">Charge Groups</div>
@foreach($invoice->chargeGroups as $group)
@php
$groupItemIds = $group->items->pluck('invoice_item_id')->toArray();
$groupInvItems = $invoice->items->whereIn('id', $groupItemIds);
@endphp
<div class="cg-group-wrap">
<table class="cg-sum-table">
<thead>
<tr>
<th style="width:22%;">Group Name</th>
<th style="width:11%;">Basis</th>
<th style="width:11%;">Basis Value</th>
<th style="width:10%;">Rate</th>
<th style="width:13%;" class="tr">Total Charge</th>
<th style="width:8%;">GST %</th>
<th style="width:9%;">Tax Type</th>
<th style="width:16%;" class="tr">Total With GST</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>{{ $group->group_name ?? 'Group '.$group->id }}</strong></td>
<td>{{ strtoupper($group->basis_type) }}</td>
<td>{{ number_format($group->basis_value, 3) }}</td>
<td>{{ number_format($group->rate, 2) }}</td>
<td class="tr">{{ number_format($group->total_charge, 2) }}</td>
<td>{{ number_format($group->gst_percent ?? 0, 2) }}%</td>
<td>{{ strtoupper($group->tax_type ?? 'GST') }}</td>
<td class="tr"><strong>{{ number_format($group->total_with_gst, 2) }}</strong></td>
</tr>
</tbody>
</table>
@if($groupInvItems->count() > 0)
<table class="cg-item-table">
<thead>
<tr>
<th style="width:3%;">#</th>
<th style="width:15%;">Description</th>
<th style="width:7%;">Mark No</th>
<th style="width:5%;" class="tc">QTY</th>
<th style="width:6%;" class="tr">TTL QTY</th>
<th style="width:6%;" class="tr">CBM</th>
<th style="width:6%;" class="tr">TTL CBM</th>
<th style="width:5%;" class="tr">KG</th>
<th style="width:6%;" class="tr">TTL KG</th>
<th style="width:7%;" class="tr">TTL Amt</th>
<th style="width:5%;" class="tr">Rate</th>
<th style="width:7%;" class="tr">Total Charge</th>
<th style="width:5%;" class="tr">GST %</th>
<th style="width:6%;" class="tr">Item GST</th>
<th style="width:6%;" class="tr">Item Total</th>
</tr>
</thead>
<tbody>
@foreach($groupInvItems as $idx => $item)
@php
$groupBasisTotal = $groupInvItems->sum(function($i) use ($group) {
return match($group->basis_type) {
'ttl_qty' => $i->ttl_qty ?? 0,
'ttl_cbm' => $i->ttl_cbm ?? 0,
'ttl_kg' => $i->ttl_kg ?? 0,
'amount' => $i->ttl_amount ?? 0,
default => $i->ttl_amount ?? 0,
};
});
$itemBasisValue = match($group->basis_type) {
'ttl_qty' => $item->ttl_qty ?? 0,
'ttl_cbm' => $item->ttl_cbm ?? 0,
'ttl_kg' => $item->ttl_kg ?? 0,
'amount' => $item->ttl_amount ?? 0,
default => $item->ttl_amount ?? 0,
};
$itemCharge = $groupBasisTotal > 0
? ($itemBasisValue / $groupBasisTotal) * $group->total_charge
: 0;
$gstPct = $group->gst_percent ?? 0;
$itemGst = $itemCharge * $gstPct / 100;
$itemTotal = $itemCharge + $itemGst;
@endphp
<tr class="{{ $idx % 2 === 1 ? 'row-even' : '' }}">
<td class="tc">{{ $idx + 1 }}</td>
<td>{{ $item->description }}</td>
<td>{{ $item->mark_no ?? '—' }}</td>
<td class="tc">{{ $item->qty ?? 0 }}</td>
<td class="tr">{{ number_format($item->ttl_qty ?? 0, 0) }}</td>
<td class="tr">{{ number_format($item->cbm ?? 0, 3) }}</td>
<td class="tr">{{ number_format($item->ttl_cbm ?? 0, 3) }}</td>
<td class="tr">{{ number_format($item->kg ?? 0, 2) }}</td>
<td class="tr">{{ number_format($item->ttl_kg ?? 0, 2) }}</td>
<td class="tr">{{ number_format($item->ttl_amount ?? 0, 2) }}</td>
<td class="tr">{{ number_format($group->rate, 2) }}</td>
<td class="tr">{{ number_format($itemCharge, 2) }}</td>
<td class="tr">{{ number_format($gstPct, 2) }}%</td>
<td class="tr">{{ number_format($itemGst, 2) }}</td>
<td class="tr"><strong>{{ number_format($itemTotal, 2) }}</strong></td>
</tr>
@endforeach
</tbody>
</table>
@endif
</div>
@endforeach
</div>
@endif
{{-- ══ GRAND TOTAL ══ --}}
<table class="grand-outer">
<tr>
<td class="grand-spacer"></td>
<td class="grand-inner">
<table class="grand-table">
<tr>
<td class="g-lbl">Charge Groups Total:</td>
<td class="g-val">{{ number_format($invoice->charge_groups_total ?? 0, 2) }}</td>
</tr>
<tr>
<td class="g-lbl">GST Amount ({{ number_format($invoice->gst_percent ?? 0, 2) }}%):</td>
<td class="g-val">{{ number_format($invoice->gst_amount ?? 0, 2) }}</td>
</tr>
<tr>
<td class="g-total-lbl">Grand Total:</td>
<td class="g-total-val">{{ number_format($invoice->grand_total_with_charges ?? 0, 2) }}</td>
</tr>
</table>
</td>
</tr>
</table>
{{-- ══ INSTALLMENTS ══ --}}
@if($invoice->installments && $invoice->installments->count() > 0)
<div class="install-section">
<div class="install-header">Payment Installments</div>
<table class="install-table">
<thead>
<tr>
<th style="width:6%;" class="tc">#</th>
<th style="width:20%;">Date</th>
<th style="width:25%;">Payment Method</th>
<th style="width:25%;">Reference No</th>
<th style="width:24%;" class="tr">Amount ()</th>
</tr>
</thead>
<tbody>
@foreach($invoice->installments as $idx => $inst)
<tr>
<td class="tc">{{ $idx + 1 }}</td>
<td>{{ \Carbon\Carbon::parse($inst->installment_date)->format('d M, Y') }}</td>
<td>{{ strtoupper($inst->payment_method) }}</td>
<td>{{ $inst->reference_no ?? '—' }}</td>
<td class="tr">{{ number_format($inst->amount, 2) }}</td>
</tr>
@endforeach
</tbody>
</table>
@php
$totalPaid = $invoice->installments->sum('amount');
$grandTotal = $invoice->grand_total_with_charges ?? 0;
$remaining = max(0, $grandTotal - $totalPaid);
@endphp
<table class="grand-outer" style="margin-top:6px;">
<tr>
<td class="grand-spacer"></td>
<td class="grand-inner">
<table class="grand-table">
<tr>
<td class="g-lbl" style="color:#059669;">Total Paid:</td>
<td class="g-val" style="color:#059669;">{{ number_format($totalPaid, 2) }}</td>
</tr>
<tr>
<td class="g-lbl" style="color:#dc2626;">Remaining:</td>
<td class="g-val" style="color:#dc2626;">{{ number_format($remaining, 2) }}</td>
</tr>
</table>
</td>
</tr>
</table>
</div>
@endif
{{-- ══ FOOTER ══ --}}
<div class="footer">
Thank you for your business! &nbsp;|&nbsp; For queries: info@company.com &nbsp;|&nbsp; +91 1234567890
</div>
</body>
</html>
</html>

View File

@@ -95,6 +95,7 @@
.status-paid { background: #dcfce7; color: #166534; }
.status-overdue { background: #fee2e2; color: #991b1b; }
.status-pending { background: #fef3c7; color: #92400e; }
.status-paying { background: #dbeafe; color: #1e40af; }
.status-default { background: #f1f5f9; color: #475569; }
/* ── ID BOXES ── */
@@ -681,19 +682,23 @@
</div>
<div>
@if($invoice->status == 'paid')
<span class="status-badge status-paid">
<span class="status-badge status-paid" id="invoiceStatusBadge">
<i class="fas fa-check-circle"></i> Paid
</span>
@elseif($invoice->status == 'overdue')
<span class="status-badge status-overdue">
<span class="status-badge status-overdue" id="invoiceStatusBadge">
<i class="fas fa-exclamation-circle"></i> Overdue
</span>
@elseif($invoice->status == 'paying')
<span class="status-badge status-paying" id="invoiceStatusBadge">
<i class="fas fa-spinner"></i> Paying
</span>
@elseif($invoice->status == 'pending')
<span class="status-badge status-pending">
<span class="status-badge status-pending" id="invoiceStatusBadge">
<i class="fas fa-clock"></i> Pending
</span>
@else
<span class="status-badge status-default">
<span class="status-badge status-default" id="invoiceStatusBadge">
<i class="fas fa-question-circle"></i> {{ ucfirst($invoice->status) }}
</span>
@endif
@@ -1194,6 +1199,7 @@
<tr>
<th>#</th>
<th>Description</th>
<th class="text-center">Mark No</th>
<th class="text-center">QTY</th>
<th class="text-center">TTL QTY</th>
<th class="text-center">CBM</th>
@@ -1203,7 +1209,6 @@
<th class="text-end">TTL Amount</th>
<th class="text-end">Rate</th>
<th class="text-end">Total Charge</th>
{{-- नवीन GST % कॉलम --}}
<th class="text-end">GST %</th>
<th class="text-end">Item GST</th>
<th class="text-end">Item Total (with GST)</th>
@@ -1243,34 +1248,27 @@
$itemTotalWithGst = $itemTotal + $itemGst;
}
$itemGstPercent = $groupGstPercent ?? 0; // same as group
$itemGstPercent = $groupGstPercent ?? 0;
@endphp
<tr>
<td>{{ $giIndex + 1 }}</td>
<td>{{ $it->description }}</td>
<td class="text-center">{{ $it->mark_no ?? '-' }}</td>
<td class="text-center">{{ $it->qty }}</td>
<td class="text-center">{{ $it->ttl_qty }}</td>
<td class="text-center">{{ $it->cbm }}</td>
<td class="text-center">{{ $it->ttl_cbm }}</td>
<td class="text-center">{{ $it->kg }}</td>
<td class="text-center">{{ $it->ttl_kg }}</td>
<td>{{ $item->mark_no }}</td>
<td class="text-end">
{{ number_format($it->ttl_amount, 2) }}
</td>
<td class="text-end">
{{ number_format($rate, 2) }}
</td>
<td class="text-end">{{ number_format($it->ttl_amount, 2) }}</td>
<td class="text-end">{{ number_format($rate, 2) }}</td>
<td class="text-end" style="color:#06b6d4;font-weight:700;">
{{ number_format($itemTotal, 2) }}
</td>
{{-- GST % (per item = group GST %) --}}
<td class="text-end">
{{ number_format($itemGstPercent, 2) }}%
</td>
<td class="text-end" style="color:#ef4444;">
{{ $itemGst > 0 ? number_format($itemGst, 2) : '0.00' }}
</td>
@@ -1293,8 +1291,23 @@
</div>
</div>
@endif
{{-- ===== FINAL SUMMARY (POPUP) ===== --}}
@php
// Base amount (without GST) invoice level
$baseAmount = $invoice->final_amount;
$gstAmount = $invoice->gst_amount;
$groupsWithGst = $invoice->chargeGroups->sum('total_with_gst');
// Installments वरून live calculate (DB extra column नाही)
$summaryTotalPaid = $invoice->totalPaid();
$summaryRemaining = $invoice->remainingAmount();
// ✅ इथे isPaid define कर
$grandTotal = $invoice->grand_total_with_charges ?? 0;
$totalPaid = $invoice->installments->sum('amount') ?? 0;
$isPaid = ($invoice->status === 'paid') || ($grandTotal > 0 && round($totalPaid, 2) >= round($grandTotal, 2));
@endphp
{{-- ===== FINAL SUMMARY (POPUP) ===== --}}
<div class="summary-card-compact mt-3">
<div class="summary-header-compact">
<i class="fas fa-calculator"></i>
@@ -1302,13 +1315,6 @@
</div>
<div class="summary-body-compact">
@php
// Base amount (without GST) invoice level
$baseAmount = $invoice->final_amount; // items + groups base, जर तू तसे ठेवले असेल
$gstAmount = $invoice->gst_amount; // total GST
$groupsWithGst = $invoice->chargeGroups->sum('total_with_gst'); // सर्व charge groups with GST
@endphp
{{-- Total Charge (Before GST) --}}
<div class="summary-row-compact">
<span class="label">Total Charge (Before GST)</span>
@@ -1333,23 +1339,53 @@
</span>
</div>
{{--
<div class="summary-row-compact total">
<span class="label">Total Charge With GST</span>
<span class="value">
{{ number_format($invoice->final_amount_with_gst, 2) }}
{{-- Total Paid (installments sum) --}}
<div class="summary-row-compact">
<span class="label" style="color:#10b981;font-weight:600;">
<i class="fas fa-check-circle me-1" style="font-size:0.8rem;"></i>Total Paid
</span>
<span class="value" id="finalSummaryTotalPaid" style="color:#10b981;">
{{ number_format($summaryTotalPaid, 2) }}
</span>
</div>
{{-- Remaining Balance --}}
<div class="summary-row-compact" style="border-bottom:none;">
<span class="label" id="finalSummaryRemainingLabel"
style="color:{{ $summaryRemaining > 0 ? '#ef4444' : '#10b981' }};font-weight:600;">
<i class="fas fa-{{ $summaryRemaining > 0 ? 'exclamation-circle' : 'check-circle' }} me-1"
style="font-size:0.8rem;"></i>Remaining
</span>
<span class="value" id="finalSummaryRemaining"
style="color:{{ $summaryRemaining > 0 ? '#ef4444' : '#10b981' }};">
{{ number_format($summaryRemaining, 2) }}
</span>
</div>
--}}
</div>
</div>
@if($isPaid)
<div class="popup-download-actions" style="margin-top: 20px; padding-top: 16px; border-top: 2px solid #e5e7eb; text-align: right;">
<a href="{{ route('admin.invoice.download.pdf', $invoice->id) }}"
class="btn btn-primary"
style="display:inline-flex; align-items:center; gap:6px; padding:10px 20px; text-decoration:none; background:#4a5568; color:#fff; border-radius:6px; font-weight:600; margin-right:8px;">
<i class="fas fa-file-pdf"></i>
Download PDF
</a>
<a href="{{ route('admin.invoice.share', $invoice->id) }}"
class="btn btn-success"
style="display:inline-flex; align-items:center; gap:6px; padding:10px 20px; text-decoration:none; background:#059669; color:#fff; border-radius:6px; font-weight:600;">
<i class="fas fa-share-alt"></i>
Share
</a>
</div>
@endif
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
<div class="invoice-footer">
<!-- <div class="invoice-footer">
@if($invoice->pdf_path && $showActions)
<a href="{{ asset($invoice->pdf_path) }}" class="btn-download" download>
<i class="fas fa-download"></i> Download PDF
@@ -1363,7 +1399,7 @@
<p style="margin:0;">For any inquiries, contact us at support@Kent Logistic</p>
</div>
</div>
</div>
</div> -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
function shareInvoice() {

File diff suppressed because it is too large Load Diff