Pdf Changes Done

This commit is contained in:
Utkarsh Khedkar
2026-03-09 10:24:44 +05:30
parent c11467068c
commit 9cc6959396
32 changed files with 3416 additions and 2188 deletions

View File

@@ -477,7 +477,7 @@
}
/* ── SUMMARY ── */
.summary-wrap {
/* .summary-wrap {
padding: 0 2.5rem 2rem;
}
@@ -516,7 +516,79 @@
.summary-row .value { font-weight: 700; color: var(--primary); }
.summary-row.total .label { font-size: 1rem; font-weight: 700; color: var(--primary); }
.summary-row.total .value { font-size: 1.15rem; color: #10b981; }
.summary-row .value.red { color: #ef4444; }
.summary-row .value.red { color: #ef4444; } */
.summary-card-compact {
margin: 1.5rem 2.5rem 1.5rem;
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow-lg);
background: var(--surface);
}
/* Header full width, curved top corners */
.summary-header-compact {
background: var(--primary);
color: #fff;
padding: 0.8rem 1.25rem;
font-weight: 700;
font-size: 0.9rem;
display: flex;
align-items: center;
gap: 0.5rem;
width: 100%;
border-top-left-radius: var(--radius);
border-top-right-radius: var(--radius);
}
/* Icon थोडा highlight */
.summary-header-compact i {
font-size: 0.9rem;
opacity: 0.9;
}
.summary-body-compact {
padding: 1rem 1.25rem 0.9rem;
}
.summary-row-compact {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.45rem 0;
border-bottom: 1px solid var(--border);
font-size: 0.88rem;
}
.summary-row-compact:last-child {
border-bottom: none;
}
.summary-row-compact .label {
color: var(--text-secondary);
font-weight: 500;
}
.summary-row-compact .value {
font-weight: 700;
color: var(--primary);
}
.summary-row-compact.total .label {
font-size: 0.95rem;
font-weight: 700;
}
.summary-row-compact.total .value {
font-size: 1.05rem;
color: #10b981;
}
.summary-row-compact.muted .value {
color: var(--text-muted);
}
/* ── FOOTER ── */
.invoice-footer {
@@ -653,8 +725,8 @@
</div>
<!-- ═══════════════════════════ ID BOXES ═══════════════════════════ -->
<div class="id-grid">
<!-- Invoice ID -->
<!-- <div class="id-grid">
<div class="id-box">
<div class="id-icon-wrap id-icon-blue">
<i class="fas fa-receipt"></i>
@@ -663,10 +735,10 @@
<div class="id-label">Invoice ID</div>
<div class="id-value">{{ $invoice->invoice_number }}</div>
</div>
</div>
</div> -->
<!-- Container ID -->
<div class="id-box">
<!-- <div class="id-box">
<div class="id-icon-wrap id-icon-green">
<i class="fas fa-box"></i>
</div>
@@ -684,25 +756,56 @@
</div>
</div>
</div>
</div>
</div> -->
<!-- ═══════════════════════════ DATES ═══════════════════════════ -->
<div class="date-strip">
<!-- ═══════════════════════════ ID + DATES (ONE ROW) ═══════════════════════════ -->
<!-- <div class="date-strip">
<div class="date-row">
{{-- Container ID --}}
<div class="date-card" style="flex: 1.2;">
<div class="date-icon-wrap" style="background:#ecfeff;color:#0e7490;">
<i class="fas fa-box"></i>
</div>
<div>
<div class="date-label">Container ID</div>
<div class="date-value">
@if($invoice->container && $invoice->container->container_number)
{{ $invoice->container->container_number }}
@elseif($invoice->container_id)
{{ $invoice->container_id }}
@else
N/A
@endif
</div>
</div>
</div>
{{-- छोटा arrow --}}
<div class="date-arrow">
<i class="fas fa-arrow-right"></i>
</div>
{{-- Invoice Date --}}
<div class="date-card">
<div class="date-icon-wrap">
<i class="fas fa-calendar-alt"></i>
</div>
<div>
<div class="date-label">Invoice Date</div>
<div class="date-value">{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}</div>
<div class="date-value">
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
</div>
</div>
</div>
{{-- दुसरा arrow --}}
<div class="date-arrow">
<i class="fas fa-arrow-right"></i>
</div>
{{-- Due Date --}}
<div class="date-card">
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
<i class="fas fa-clock"></i>
@@ -714,8 +817,74 @@
</div>
</div>
</div>
</div>
</div>
-->
<!-- ═══════════════════════════ CONTAINER + INVOICE DATE + DUE DATE (ONE ROW) ═══════════════════════════ -->
<div class="date-strip">
<div class="date-row">
{{-- Container ID --}}
<div class="date-card" style="flex: 1.2;">
<div class="date-icon-wrap" style="background:#ecfeff;color:#0e7490;">
<i class="fas fa-box"></i>
</div>
<div>
<div class="date-label">Container ID</div>
<div class="date-value">
@if($invoice->container && $invoice->container->container_number)
{{ $invoice->container->container_number }}
@elseif($invoice->container_id)
{{ $invoice->container_id }}
@else
N/A
@endif
</div>
</div>
</div>
{{-- छोटा arrow --}}
<div class="date-arrow">
<i class="fas fa-arrow-right"></i>
</div>
{{-- Invoice Date --}}
<div class="date-card">
<div class="date-icon-wrap">
<i class="fas fa-calendar-alt"></i>
</div>
<div>
<div class="date-label">Invoice Date</div>
<div class="date-value">
{{ \Carbon\Carbon::parse($invoice->invoice_date)->format('M d, Y') }}
</div>
</div>
</div>
{{-- दुसरा arrow --}}
<div class="date-arrow">
<i class="fas fa-arrow-right"></i>
</div>
{{-- Due Date --}}
<div class="date-card">
<div class="date-icon-wrap" style="background:#fff7ed;color:#f59e0b;">
<i class="fas fa-clock"></i>
</div>
<div>
<div class="date-label">Due Date</div>
<div class="date-value {{ $invoice->status == 'overdue' ? 'overdue' : '' }}">
{{ \Carbon\Carbon::parse($invoice->due_date)->format('M d, Y') }}
</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════ CUSTOMER ═══════════════════════════ -->
<div class="panel">
@@ -758,82 +927,83 @@
@endif -->
<div class="table-responsive">
<table class="invoice-table items-table">
<thead>
<tr>
<th class="text-center" style="width:44px;">
<input type="checkbox" id="selectAllItems">
</th>
<th class="text-center" style="min-width:50px;">#</th>
<th style="min-width:200px;">Description</th>
<th class="text-center" style="min-width:75px;">CTN</th>
<th class="text-center" style="min-width:75px;">QTY</th>
<th class="text-center" style="min-width:95px;">TTL/QTY</th>
<th class="text-center" style="min-width:75px;">Unit</th>
<th class="text-center" style="min-width:110px;">Price</th>
<th class="text-center" style="min-width:125px;">TTL Amount</th>
<th class="text-center" style="min-width:85px;">CBM</th>
<th class="text-center" style="min-width:95px;">TTL CBM</th>
<th class="text-center" style="min-width:80px;">KG</th>
<th class="text-center" style="min-width:95px;">TTL KG</th>
<th class="text-center" style="min-width:95px;">Shop No</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $i => $item)
@php
$alreadyGrouped = in_array($item->id, $groupedItemIds ?? []);
@endphp
<tr class="{{ $alreadyGrouped ? 'grouped-item-row' : '' }}">
<td class="text-center">
<input type="checkbox"
class="item-select-checkbox"
value="{{ $item->id }}"
{{ $alreadyGrouped ? 'disabled' : '' }}>
</td>
<td class="text-center" style="font-weight:600;color:var(--text-muted);">{{ $i + 1 }}</td>
<td class="desc-col" style="font-weight:600;color:var(--primary);">{{ $item->description }}</td>
<td class="text-center">{{ $item->ctn }}</td>
<td class="text-center">{{ $item->qty }}</td>
<td class="text-center" style="font-weight:700;">{{ $item->ttl_qty }}</td>
<td class="text-center">{{ $item->unit }}</td>
<table class="invoice-table items-table">
<thead>
<tr>
<th class="text-center" style="width:44px;">
<input type="checkbox" id="selectAllItems">
</th>
<th class="text-center" style="min-width:50px;">#</th>
<th style="min-width:200px;">Description</th>
<th class="text-center" style="min-width:75px;">CTN</th>
<th class="text-center" style="min-width:75px;">QTY</th>
<th class="text-center" style="min-width:95px;">TTL/QTY</th>
<th class="text-center" style="min-width:75px;">Unit</th>
<th class="text-center" style="min-width:110px;">Price</th>
<th class="text-center" style="min-width:125px;">TTL Amount</th>
<th class="text-center" style="min-width:85px;">CBM</th>
<th class="text-center" style="min-width:95px;">TTL CBM</th>
<th class="text-center" style="min-width:80px;">KG</th>
<th class="text-center" style="min-width:95px;">TTL KG</th>
<th class="text-center" style="min-width:95px;">Shop No</th>
</tr>
</thead>
<tbody>
@foreach($invoice->items as $i => $item)
@php
$alreadyGrouped = in_array($item->id, $groupedItemIds ?? []);
@endphp
<tr class="{{ $alreadyGrouped ? 'grouped-item-row' : '' }}">
<td class="text-center">
<input type="checkbox"
class="item-select-checkbox"
name="item_ids[]"
value="{{ $item->id }}"
{{ $alreadyGrouped ? 'disabled' : '' }}>
</td>
<td class="text-center" style="font-weight:600;color:var(--text-muted);">{{ $i + 1 }}</td>
<td class="desc-col" style="font-weight:600;color:var(--primary);">{{ $item->description }}</td>
<td class="text-center">{{ $item->ctn }}</td>
<td class="text-center">{{ $item->qty }}</td>
<td class="text-center" style="font-weight:700;">{{ $item->ttl_qty }}</td>
<td class="text-center">{{ $item->unit }}</td>
@if($isEmbedded)
<td class="text-center" style="min-width:120px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][price]"
value="{{ old('items.' . $item->id . '.price', $item->price) }}"
class="form-control form-control-sm text-end">
</td>
<td class="text-center" style="min-width:140px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][ttl_amount]"
value="{{ old('items.' . $item->id . '.ttl_amount', $item->ttl_amount) }}"
class="form-control form-control-sm text-end">
</td>
@else
<td class="text-center price-green">{{ number_format($item->price, 2) }}</td>
<td class="text-center price-blue">{{ number_format($item->ttl_amount, 2) }}</td>
@endif
<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-shop">{{ $item->shop_no }}</span></td>
</tr>
@endforeach
@if($invoice->items->isEmpty())
<tr>
<td colspan="15" class="text-center py-4" style="color:var(--text-muted);font-weight:600;">
<i class="fas fa-inbox me-2" style="font-size:1.3rem;opacity:.4;"></i><br>No invoice items found.
</td>
</tr>
@if($isEmbedded)
<td class="text-center" style="min-width:120px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][price]"
value="{{ old('items.' . $item->id . '.price', $item->price) }}"
class="form-control form-control-sm text-end">
</td>
<td class="text-center" style="min-width:140px;">
<input type="number" step="0.01" min="0"
name="items[{{ $item->id }}][ttl_amount]"
value="{{ old('items.' . $item->id . '.ttl_amount', $item->ttl_amount) }}"
class="form-control form-control-sm text-end">
</td>
@else
<td class="text-center price-green">{{ number_format($item->price, 2) }}</td>
<td class="text-center price-blue">{{ number_format($item->ttl_amount, 2) }}</td>
@endif
</tbody>
</table>
</div>
<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-shop">{{ $item->shop_no }}</span></td>
</tr>
@endforeach
@if($invoice->items->isEmpty())
<tr>
<td colspan="15" class="text-center py-4" style="color:var(--text-muted);font-weight:600;">
<i class="fas fa-inbox me-2" style="font-size:1.3rem;opacity:.4;"></i><br>No invoice items found.
</td>
</tr>
@endif
</tbody>
</table>
</div>
<!-- ACTION BAR -->
<div class="action-bar">
@@ -1065,7 +1235,7 @@
@endif
<!-- ═══════════════════════════ SUMMARY ═══════════════════════════ -->
<div class="summary-wrap">
<!-- <div class="summary-wrap">
<div class="row justify-content-end">
<div class="col-md-5">
<div class="summary-card">
@@ -1107,8 +1277,70 @@
</div>
</div>
</div>
</div> -->
{{-- ===== FINAL SUMMARY (POPUP) ===== --}}
<div class="summary-card-compact mt-3">
<div class="summary-header-compact">
<i class="fas fa-calculator"></i>
<span>Final Summary</span>
</div>
<div class="summary-body-compact">
@if($invoice->tax_type === 'gst')
<div class="summary-row-compact">
<span class="label">
CGST {{ $invoice->cgst_percent ?? $invoice->gst_percent / 2 }}%
</span>
<span class="value red">
{{ number_format($invoice->gst_amount / 2, 2) }}
</span>
</div>
<div class="summary-row-compact">
<span class="label">
SGST {{ $invoice->sgst_percent ?? $invoice->gst_percent / 2 }}%
</span>
<span class="value red">
{{ number_format($invoice->gst_amount / 2, 2) }}
</span>
</div>
@elseif($invoice->tax_type === 'igst')
<div class="summary-row-compact">
<span class="label">
IGST {{ $invoice->igst_percent ?? $invoice->gst_percent }}%
</span>
<span class="value red">
{{ number_format($invoice->gst_amount, 2) }}
</span>
</div>
@else
<div class="summary-row-compact">
<span class="label">
GST {{ $invoice->gst_percent }}%
</span>
<span class="value red">
{{ number_format($invoice->gst_amount, 2) }}
</span>
</div>
@endif
<div class="summary-row-compact">
<span class="label">Charge Groups Total</span>
<span class="value">
{{ number_format($invoice->charge_groups_total, 2) }}
</span>
</div>
<!-- <div class="summary-row-compact total">
<span class="label">Grand Total (Items + GST + Groups)</span>
<span class="value">
{{ number_format($invoice->grand_total_with_charges, 2) }}
</span>
</div> -->
</div>
</div>
<!-- ═══════════════════════════ FOOTER ═══════════════════════════ -->
<div class="invoice-footer">
@if($invoice->pdf_path && $showActions)
@@ -1125,31 +1357,52 @@
</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() {
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.");
}
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.");
}
}
function renumberChargeGroups() {
const groupsTbody = document.querySelector(
'.cg-groups-panel table.invoice-table tbody'
);
if (!groupsTbody) return;
let index = 1;
groupsTbody.querySelectorAll('tr').forEach(row => {
if (row.classList.contains('cg-items-row')) return;
const next = row.nextElementSibling;
const isMainGroupRow = next && next.classList.contains('cg-items-row');
if (!isMainGroupRow) return;
const firstCell = row.querySelector('td:first-child');
if (firstCell) {
firstCell.textContent = index++;
}
});
}
document.addEventListener('DOMContentLoaded', function () {
const selectAll = document.getElementById('selectAllItems');
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
const countSpan = document.getElementById('selectedItemsCount');
const btnCreate = document.getElementById('btnCreateChargeGroup');
const selectAll = document.getElementById('selectAllItems');
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
const countSpan = document.getElementById('selectedItemsCount');
const btnCreate = document.getElementById('btnCreateChargeGroup');
const chargeGroupBox = document.getElementById('chargeGroupBox');
const cgCancelBtn = document.getElementById('cgCancelBtn');
const chargeGroupBox = document.getElementById('chargeGroupBox');
const cgCancelBtn = document.getElementById('cgCancelBtn');
const cgItemsTableBody = document.getElementById('cgItemsTableBody');
const cgBasisSelect = document.getElementById('cgBasis');
@@ -1166,6 +1419,8 @@ document.addEventListener('DOMContentLoaded', function () {
const cgRateHidden = document.getElementById('cgRateHidden');
const cgForm = document.getElementById('chargeGroupForm');
const cgGroupName = document.getElementById('cgGroupName');
function updateSelectionState() {
let selectedCount = 0;
itemCheckboxes.forEach(cb => {
@@ -1188,13 +1443,13 @@ document.addEventListener('DOMContentLoaded', function () {
row.querySelector(`td:nth-child(${n})`)?.textContent.trim() ?? '';
items.push({
description: cellText(3),
qty: cellText(5),
ttlqty: cellText(6),
cbm: cellText(10),
ttlcbm: cellText(11),
kg: cellText(12),
ttlkg: cellText(13),
amount: cellText(9),
qty: cellText(5),
ttlqty: cellText(6),
cbm: cellText(10),
ttlcbm: cellText(11),
kg: cellText(12),
ttlkg: cellText(13),
amount: cellText(9),
});
}
});
@@ -1258,8 +1513,8 @@ document.addEventListener('DOMContentLoaded', function () {
if (cgRateHidden && cgRateInput) cgRateHidden.value = cgRateInput.value || 0;
if (cgAutoTotalInput) cgAutoTotalInput.value = suggested || 0;
if (cgTotalChargeInput) {
cgTotalChargeInput.value = suggested ? suggested.toFixed(2) : '0';
}
cgTotalChargeInput.value = suggested ? suggested.toFixed(2) : '0';
}
}
itemCheckboxes.forEach(cb => {
@@ -1271,7 +1526,7 @@ document.addEventListener('DOMContentLoaded', function () {
selectAll.checked = (checked > 0 && checked === total);
selectAll.indeterminate = (checked > 0 && checked < total);
}
if (!chargeGroupBox.classList.contains('d-none')) {
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
fillChargeGroupItemsTable();
refreshBasisSummaryAndSuggestion();
}
@@ -1282,7 +1537,7 @@ document.addEventListener('DOMContentLoaded', function () {
selectAll.addEventListener('change', function () {
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = selectAll.checked; });
updateSelectionState();
if (!chargeGroupBox.classList.contains('d-none')) {
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
fillChargeGroupItemsTable();
refreshBasisSummaryAndSuggestion();
}
@@ -1312,244 +1567,65 @@ document.addEventListener('DOMContentLoaded', function () {
});
}
// MAIN CHANGE: normal form submit, फक्त hidden item_ids तयार करतो
if (cgForm) {
cgForm.addEventListener('submit', function (e) {
// Stop normal form submit (no page reload)
e.preventDefault();
// 1) Collect selected item IDs
const selectedIds = [];
itemCheckboxes.forEach(cb => {
if (cb.checked && !cb.disabled) {
selectedIds.push(cb.value);
}
});
// 2) Frontend validations (same as before)
if (selectedIds.length === 0) {
alert('Please select at least one item for this charge group.');
return;
}
if (!cgBasisSelect || !cgBasisSelect.value) {
alert('Please select a basis for this charge group.');
return;
}
if (
!cgTotalChargeInput ||
!cgTotalChargeInput.value ||
parseFloat(cgTotalChargeInput.value) <= 0
) {
alert('Please enter total charges for this group.');
return;
}
// 3) Remove previously added hidden item_ids[]
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
oldHidden.forEach(el => el.remove());
// 4) Add fresh hidden item_ids[] for current selection
selectedIds.forEach(id => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'item_ids[]';
input.value = id;
cgForm.appendChild(input);
});
// 5) Build AJAX request
const url = cgForm.action;
const formData = new FormData(cgForm);
// Optional: disable save button while processing
if (cgSaveBtn) {
cgSaveBtn.disabled = true;
cgSaveBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Saving...';
}
fetch(url, {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(res => {
if (!res.ok) {
throw new Error('Request failed with status ' + res.status);
}
return res.json();
})
.then(data => {
if (!data.success) {
throw new Error(data.message || 'Failed to create charge group.');
}
const group = data.group;
// 6) Append new group main row + hidden items row
const groupsTbody = document.querySelector(
'.cg-groups-panel table.invoice-table tbody'
);
if (groupsTbody && group) {
const currentMainRows = groupsTbody.querySelectorAll(
'tr:not(.cg-items-row)'
).length;
const index = currentMainRows + 1;
// Main summary row (same structure as Blade)
const mainRow = document.createElement('tr');
mainRow.innerHTML = `
<td>${index}</td>
<td style="font-weight:600;">
${group.group_name ? group.group_name : 'Group #' + group.id}
</td>
<td>
<span style="text-transform:uppercase;font-size:0.75rem;font-weight:700;color:var(--text-muted);">
${group.basis_type}
</span>
</td>
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
${Number(group.basis_value).toFixed(3)}
</td>
<td class="text-end" style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
${Number(group.rate).toFixed(2)}
</td>
<td class="text-end price-blue">
${Number(group.total_charge).toFixed(2)}
</td>
<td class="text-center">
<button type="button"
class="cg-toggle-btn cg-toggle-items"
data-group-id="${group.id}">
<i class="fas fa-eye"></i> View
</button>
</td>
`;
// Hidden items row (same idea as Blade .cg-items-row)
const itemsRow = document.createElement('tr');
itemsRow.className = 'cg-items-row d-none';
itemsRow.setAttribute('data-group-id', group.id);
let itemsTableHtml = `
<div style="padding:1rem;background:#f8faff;border-radius:8px;margin:0.25rem 0;">
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">
Items in this group
</div>
`;
if (!group.items || group.items.length === 0) {
itemsTableHtml += `
<div style="color:var(--text-muted);font-size:0.82rem;">
No items linked.
</div>
`;
} else {
itemsTableHtml += `
<div class="table-responsive">
<table class="invoice-table">
<thead>
<tr>
<th>#</th>
<th>Description</th>
<th class="text-center">QTY</th>
<th class="text-center">TTL QTY</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-end">TTL Amount</th>
</tr>
</thead>
<tbody>
`;
group.items.forEach((it, idx) => {
itemsTableHtml += `
<tr>
<td>${idx + 1}</td>
<td>${it.description ?? ''}</td>
<td class="text-center">${it.qty ?? ''}</td>
<td class="text-center">${it.ttlqty ?? ''}</td>
<td class="text-center">${it.cbm ?? ''}</td>
<td class="text-center">${it.ttlcbm ?? ''}</td>
<td class="text-center">${it.kg ?? ''}</td>
<td class="text-center">${it.ttlkg ?? ''}</td>
<td class="text-end">${Number(it.amount ?? 0).toFixed(2)}</td>
</tr>
`;
cgForm.addEventListener('submit', function () {
const selectedIds = [];
itemCheckboxes.forEach(cb => {
if (cb.checked && !cb.disabled) {
selectedIds.push(cb.value);
}
});
itemsTableHtml += `
</tbody>
</table>
</div>
`;
}
itemsTableHtml += `</div>`;
itemsRow.innerHTML = `
<td colspan="7">
${itemsTableHtml}
</td>
`;
// Append main row + items row in order
groupsTbody.appendChild(mainRow);
groupsTbody.appendChild(itemsRow);
}
// 7) Reset Charge Group UI (hide panel, uncheck items)
if (chargeGroupBox) {
chargeGroupBox.classList.add('d-none');
}
itemCheckboxes.forEach(cb => {
if (!cb.disabled) cb.checked = false;
});
if (selectAll) {
selectAll.checked = false;
selectAll.indeterminate = false;
}
updateSelectionState();
// Optional: clear form
if (cgGroupName) cgGroupName.value = '';
if (cgBasisSelect) cgBasisSelect.value = '';
if (cgRateInput) cgRateInput.value = '';
if (cgBasisValueSpan) cgBasisValueSpan.textContent = '0';
if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = '';
if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = '0';
if (cgTotalChargeInput) cgTotalChargeInput.value = '';
})
.catch(err => {
console.error(err);
alert('Error creating charge group. Please try again.');
})
.finally(() => {
if (cgSaveBtn) {
cgSaveBtn.disabled = false;
cgSaveBtn.innerHTML = '<i class="fas fa-save"></i> Save Charge Group';
if (selectedIds.length === 0) {
alert('Please select at least one item for this charge group.');
// default submit रोखण्यासाठी return false
return false;
}
if (!cgBasisSelect || !cgBasisSelect.value) {
alert('Please select a basis for this charge group.');
return false;
}
if (!cgTotalChargeInput ||
!cgTotalChargeInput.value ||
parseFloat(cgTotalChargeInput.value) <= 0
) {
alert('Please enter total charges for this group.');
return false;
}
const oldHidden = cgForm.querySelectorAll('input[name="item_ids[]"]');
oldHidden.forEach(el => el.remove());
selectedIds.forEach(id => {
const input = document.createElement('input');
input.type = 'hidden';
input.name = 'item_ids[]';
input.value = id;
cgForm.appendChild(input);
});
// इथे e.preventDefault नाही; normal submit होऊ दे
return true;
});
});
}
}
updateSelectionState();
});
// View/Hide group items
document.addEventListener('DOMContentLoaded', function () {
document.addEventListener('click', function (e) {
if (!e.target.classList.contains('cg-toggle-items') &&
!e.target.closest('.cg-toggle-items')) return;
const btn = e.target.closest('.cg-toggle-items') || e.target;
const groupId = btn.getAttribute('data-group-id');
const row = document.querySelector('.cg-items-row[data-group-id="' + groupId + '"]');
if (!row) return;
row.classList.toggle('d-none');
const isHidden = row.classList.contains('d-none');
btn.innerHTML = isHidden
@@ -1557,6 +1633,25 @@ document.addEventListener('DOMContentLoaded', function () {
: '<i class="fas fa-eye-slash"></i> Hide';
});
});
// simple select all (duplicate राहिला तरी harmless; गरज असल्यास काढू शकतो)
document.addEventListener('DOMContentLoaded', function () {
const selectAll = document.getElementById('selectAllItems');
const checkboxes = document.querySelectorAll('.item-select-checkbox');
if (selectAll) {
selectAll.addEventListener('change', function () {
checkboxes.forEach(cb => {
if (!cb.disabled) {
cb.checked = selectAll.checked;
}
});
});
}
});
</script>
</body>
</html>