2025-11-26 23:07:12 +05:30
|
|
|
|
<!DOCTYPE html>
|
|
|
|
|
|
<html lang="en">
|
|
|
|
|
|
<head>
|
|
|
|
|
|
<meta charset="UTF-8">
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
|
|
|
<title>Professional Invoice</title>
|
|
|
|
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
|
|
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
<style>
|
|
|
|
|
|
:root {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
--primary: #0f172a;
|
|
|
|
|
|
--secondary: #3b82f6;
|
|
|
|
|
|
--accent: #f59e0b;
|
|
|
|
|
|
--success: #10b981;
|
|
|
|
|
|
--danger: #ef4444;
|
|
|
|
|
|
--warning: #f59e0b;
|
|
|
|
|
|
--light: #f8fafc;
|
|
|
|
|
|
--surface: #ffffff;
|
|
|
|
|
|
--surface2: #f1f5f9;
|
|
|
|
|
|
--border: #e2e8f0;
|
|
|
|
|
|
--text-muted: #64748b;
|
|
|
|
|
|
--text-secondary: #475569;
|
|
|
|
|
|
--radius: 12px;
|
|
|
|
|
|
--radius-sm: 8px;
|
|
|
|
|
|
--shadow: 0 1px 3px rgba(0,0,0,0.08), 0 4px 16px rgba(0,0,0,0.06);
|
|
|
|
|
|
--shadow-lg: 0 8px 32px rgba(0,0,0,0.1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
* { box-sizing: border-box; }
|
|
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
body {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
|
|
|
|
background: linear-gradient(135deg, #e0f2fe 0%, #f0fdf4 50%, #fef9c3 100%);
|
|
|
|
|
|
min-height: 100vh;
|
|
|
|
|
|
color: var(--primary);
|
|
|
|
|
|
padding: 2rem 1rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── CONTAINER ── */
|
2025-11-26 23:07:12 +05:30
|
|
|
|
.invoice-container {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
max-width: 1400px;
|
|
|
|
|
|
margin: 0 auto;
|
|
|
|
|
|
background: var(--surface);
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
box-shadow: var(--shadow-lg);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
overflow: hidden;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
border: 1px solid var(--border);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── TOP ACCENT BAR ── */
|
|
|
|
|
|
.invoice-accent-bar {
|
|
|
|
|
|
height: 5px;
|
|
|
|
|
|
background: linear-gradient(90deg, #3b82f6 0%, #8b5cf6 40%, #10b981 70%, #f59e0b 100%);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── HEADER ── */
|
2025-11-26 23:07:12 +05:30
|
|
|
|
.invoice-header {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
padding: 2rem 2.5rem 1.5rem;
|
|
|
|
|
|
background: var(--surface);
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
position: relative;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
.invoice-title {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
font-size: 2rem;
|
|
|
|
|
|
font-weight: 800;
|
|
|
|
|
|
letter-spacing: -0.03em;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
color: var(--primary);
|
2026-02-27 10:51:26 +05:30
|
|
|
|
line-height: 1;
|
|
|
|
|
|
margin-bottom: 0.25rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.invoice-number {
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
letter-spacing: 0.04em;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
.status-badge {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
padding: 0.5rem 1.1rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
border-radius: 50px;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.status-paid { background: #dcfce7; color: #166534; }
|
|
|
|
|
|
.status-overdue { background: #fee2e2; color: #991b1b; }
|
|
|
|
|
|
.status-pending { background: #fef3c7; color: #92400e; }
|
|
|
|
|
|
.status-default { background: #f1f5f9; color: #475569; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── ID BOXES ── */
|
|
|
|
|
|
.id-grid {
|
|
|
|
|
|
display: grid;
|
|
|
|
|
|
grid-template-columns: 1fr 1fr;
|
|
|
|
|
|
gap: 1rem;
|
|
|
|
|
|
padding: 1.5rem 2.5rem;
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
.id-box {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
background: var(--surface);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 1.1rem 1.25rem;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
2025-12-22 17:30:47 +05:30
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 1rem;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
transition: box-shadow 0.2s;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.id-box:hover { box-shadow: var(--shadow); }
|
|
|
|
|
|
|
|
|
|
|
|
.id-icon-wrap {
|
|
|
|
|
|
width: 42px;
|
|
|
|
|
|
height: 42px;
|
|
|
|
|
|
border-radius: 10px;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
font-size: 1rem;
|
2025-12-22 17:30:47 +05:30
|
|
|
|
flex-shrink: 0;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.id-icon-blue { background: #eff6ff; color: #3b82f6; }
|
|
|
|
|
|
.id-icon-green { background: #f0fdf4; color: #10b981; }
|
|
|
|
|
|
|
|
|
|
|
|
.id-label {
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.08em;
|
|
|
|
|
|
margin-bottom: 0.2rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.id-value {
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace;
|
|
|
|
|
|
font-size: 0.92rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── DATES ── */
|
|
|
|
|
|
.date-strip {
|
|
|
|
|
|
padding: 1.5rem 2.5rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.date-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 1rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.date-card {
|
2025-12-22 17:30:47 +05:30
|
|
|
|
flex: 1;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
background: var(--surface);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
padding: 1rem 1.25rem;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.85rem;
|
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.date-icon-wrap {
|
|
|
|
|
|
width: 38px;
|
|
|
|
|
|
height: 38px;
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
background: #eff6ff;
|
|
|
|
|
|
color: #3b82f6;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
font-size: 0.9rem;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.date-label {
|
|
|
|
|
|
font-size: 0.68rem;
|
|
|
|
|
|
font-weight: 700;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
text-transform: uppercase;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
letter-spacing: 0.07em;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-bottom: 0.2rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.date-value {
|
|
|
|
|
|
font-weight: 700;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-size: 0.95rem;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
color: var(--primary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.date-value.overdue { color: #ef4444; }
|
|
|
|
|
|
|
|
|
|
|
|
.date-arrow {
|
|
|
|
|
|
width: 36px;
|
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
border-radius: 50%;
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
font-size: 0.8rem;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── SECTION PANEL ── */
|
|
|
|
|
|
.panel {
|
|
|
|
|
|
margin: 1.5rem 2.5rem;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.panel-header {
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
padding: 0.85rem 1.25rem;
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-weight: 700;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
font-size: 0.88rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
color: var(--primary);
|
2026-02-27 10:51:26 +05:30
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.5rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.panel-header i {
|
|
|
|
|
|
color: var(--secondary);
|
2025-12-22 17:30:47 +05:30
|
|
|
|
font-size: 0.85rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.panel-body {
|
|
|
|
|
|
padding: 1.25rem;
|
|
|
|
|
|
background: var(--surface);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── CUSTOMER ── */
|
|
|
|
|
|
.customer-name {
|
|
|
|
|
|
font-size: 1.05rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--secondary);
|
|
|
|
|
|
margin-bottom: 0.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.customer-detail {
|
|
|
|
|
|
font-size: 0.85rem;
|
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
|
margin-bottom: 0.3rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
display: flex;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.customer-detail strong { color: var(--primary); font-weight: 600; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── TABLE ── */
|
|
|
|
|
|
.invoice-table {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
min-width: 1100px;
|
|
|
|
|
|
border-collapse: collapse;
|
|
|
|
|
|
font-size: 0.83rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* Items table specifically even wider */
|
|
|
|
|
|
.items-table {
|
|
|
|
|
|
min-width: 1300px;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.invoice-table thead tr {
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.invoice-table thead th {
|
|
|
|
|
|
padding: 0.85rem 1rem;
|
|
|
|
|
|
font-size: 0.71rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-weight: 700;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.06em;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
border-bottom: 2px solid var(--border);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.invoice-table tbody tr {
|
|
|
|
|
|
border-bottom: 1px solid var(--border);
|
|
|
|
|
|
transition: background 0.15s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.invoice-table tbody tr:hover { background: #f0f7ff; }
|
|
|
|
|
|
.invoice-table tbody tr:last-child { border-bottom: none; }
|
|
|
|
|
|
|
|
|
|
|
|
.invoice-table tbody td {
|
|
|
|
|
|
padding: 0.85rem 1rem;
|
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Description column can wrap */
|
|
|
|
|
|
.invoice-table tbody td.desc-col {
|
|
|
|
|
|
white-space: normal;
|
|
|
|
|
|
min-width: 180px;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.invoice-table tbody tr.grouped-item-row {
|
|
|
|
|
|
background: #f8f9ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.badge-shop {
|
|
|
|
|
|
display: inline-block;
|
|
|
|
|
|
padding: 0.2rem 0.55rem;
|
|
|
|
|
|
border-radius: 5px;
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace;
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: var(--text-secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.price-green { color: #10b981; font-weight: 700; }
|
|
|
|
|
|
.price-blue { color: #3b82f6; font-weight: 700; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── ACTION BAR ── */
|
|
|
|
|
|
.action-bar {
|
2025-11-26 23:07:12 +05:30
|
|
|
|
display: flex;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
justify-content: space-between;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 0.75rem 1.25rem;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.action-bar small { color: var(--text-muted); font-size: 0.8rem; }
|
|
|
|
|
|
.action-bar span { font-weight: 700; color: var(--secondary); }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── BUTTONS ── */
|
|
|
|
|
|
.btn-create-group {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.4rem;
|
|
|
|
|
|
background: var(--secondary);
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 0.45rem 1rem;
|
|
|
|
|
|
font-size: 0.8rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-weight: 600;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background 0.2s, opacity 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-create-group:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
|
|
|
|
.btn-create-group:not(:disabled):hover { background: #2563eb; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── CHARGE GROUP FORM PANEL ── */
|
|
|
|
|
|
.cg-panel {
|
|
|
|
|
|
margin: 0 2.5rem 1.5rem;
|
|
|
|
|
|
border: 2px solid #3b82f6;
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-shadow: 0 0 0 4px rgba(59,130,246,0.08);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.cg-panel-header {
|
|
|
|
|
|
background: linear-gradient(135deg, #1d4ed8, #3b82f6);
|
|
|
|
|
|
padding: 0.9rem 1.25rem;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
font-weight: 700;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-size: 0.9rem;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.5rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.cg-body { padding: 1.25rem; background: var(--surface); }
|
|
|
|
|
|
|
|
|
|
|
|
.form-label-custom {
|
|
|
|
|
|
font-size: 0.72rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.07em;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-bottom: 0.35rem;
|
|
|
|
|
|
display: block;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.form-control-custom,
|
|
|
|
|
|
.form-select-custom {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
padding: 0.55rem 0.85rem;
|
|
|
|
|
|
border: 1.5px solid var(--border);
|
|
|
|
|
|
border-radius: 8px;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
font-size: 0.85rem;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
font-family: 'Plus Jakarta Sans', sans-serif;
|
|
|
|
|
|
color: var(--primary);
|
|
|
|
|
|
background: var(--surface);
|
|
|
|
|
|
transition: border-color 0.15s, box-shadow 0.15s;
|
|
|
|
|
|
outline: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-control-custom:focus,
|
|
|
|
|
|
.form-select-custom:focus {
|
|
|
|
|
|
border-color: var(--secondary);
|
|
|
|
|
|
box-shadow: 0 0 0 3px rgba(59,130,246,0.12);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.basis-box {
|
|
|
|
|
|
background: var(--surface2);
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
padding: 1rem;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.basis-box-label {
|
|
|
|
|
|
font-size: 0.7rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
text-transform: uppercase;
|
|
|
|
|
|
letter-spacing: 0.07em;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-bottom: 0.5rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.basis-value-display {
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace;
|
|
|
|
|
|
font-size: 1.2rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: var(--secondary);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.basis-label-display {
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
margin-left: 0.5rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.basis-hint { font-size: 0.75rem; color: var(--text-muted); margin-top: 0.35rem; }
|
|
|
|
|
|
|
|
|
|
|
|
.suggested-total {
|
|
|
|
|
|
font-family: 'JetBrains Mono', monospace;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
color: #10b981;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── CHARGE GROUPS TABLE ── */
|
|
|
|
|
|
.cg-groups-panel {
|
|
|
|
|
|
margin: 0 2.5rem 1.5rem;
|
|
|
|
|
|
border: 1px solid var(--border);
|
|
|
|
|
|
border-radius: var(--radius);
|
|
|
|
|
|
overflow: hidden;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.cg-groups-header {
|
|
|
|
|
|
background: var(--primary);
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
padding: 0.85rem 1.25rem;
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-size: 0.88rem;
|
2025-12-22 17:30:47 +05:30
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
gap: 0.5rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
.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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── FOOTER ── */
|
|
|
|
|
|
.invoice-footer {
|
|
|
|
|
|
padding: 1.5rem 2.5rem;
|
|
|
|
|
|
border-top: 1px solid var(--border);
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
background: var(--surface2);
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.btn-download {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.45rem;
|
|
|
|
|
|
background: var(--secondary);
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
padding: 0.65rem 1.4rem;
|
|
|
|
|
|
font-size: 0.88rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
text-decoration: none;
|
|
|
|
|
|
transition: background 0.2s;
|
|
|
|
|
|
margin-right: 0.5rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.btn-download:hover { background: #2563eb; color: white; }
|
|
|
|
|
|
|
|
|
|
|
|
.btn-share {
|
|
|
|
|
|
display: inline-flex;
|
2025-12-22 17:30:47 +05:30
|
|
|
|
align-items: center;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
gap: 0.45rem;
|
|
|
|
|
|
background: #10b981;
|
|
|
|
|
|
color: white;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
border-radius: 9px;
|
|
|
|
|
|
padding: 0.65rem 1.4rem;
|
|
|
|
|
|
font-size: 0.88rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background 0.2s;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.btn-share:hover { background: #059669; }
|
|
|
|
|
|
|
|
|
|
|
|
.footer-note {
|
|
|
|
|
|
margin-top: 1.25rem;
|
|
|
|
|
|
color: var(--text-muted);
|
|
|
|
|
|
font-size: 0.82rem;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── CHECKBOX STYLE ── */
|
|
|
|
|
|
input[type="checkbox"] {
|
|
|
|
|
|
width: 15px;
|
|
|
|
|
|
height: 15px;
|
|
|
|
|
|
accent-color: var(--secondary);
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* ── RESPONSIVE ── */
|
2025-11-26 23:07:12 +05:30
|
|
|
|
@media (max-width: 768px) {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
body { padding: 0.75rem 0.5rem; }
|
|
|
|
|
|
.invoice-header,
|
|
|
|
|
|
.date-strip,
|
|
|
|
|
|
.summary-wrap,
|
|
|
|
|
|
.invoice-footer { padding-left: 1.25rem; padding-right: 1.25rem; }
|
|
|
|
|
|
.id-grid { grid-template-columns: 1fr; padding: 1rem 1.25rem; }
|
|
|
|
|
|
.date-row { flex-direction: column; }
|
|
|
|
|
|
.panel, .cg-panel, .cg-groups-panel { margin-left: 1.25rem; margin-right: 1.25rem; }
|
|
|
|
|
|
.invoice-table { font-size: 0.75rem; }
|
2025-12-22 17:30:47 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
/* ── GROUP ITEMS HIDDEN ROW ── */
|
|
|
|
|
|
.cg-items-row { background: #f8faff; }
|
|
|
|
|
|
.cg-toggle-btn {
|
|
|
|
|
|
display: inline-flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
gap: 0.3rem;
|
|
|
|
|
|
padding: 0.3rem 0.75rem;
|
|
|
|
|
|
border: 1.5px solid #3b82f6;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-size: 0.75rem;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #3b82f6;
|
|
|
|
|
|
background: transparent;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: all 0.15s;
|
2025-11-26 23:07:12 +05:30
|
|
|
|
}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
.cg-toggle-btn:hover { background: #eff6ff; }
|
|
|
|
|
|
|
|
|
|
|
|
/* ── DIVIDER ── */
|
|
|
|
|
|
.section-divider { height: 1px; background: var(--border); margin: 0 2.5rem; }
|
2025-11-26 23:07:12 +05:30
|
|
|
|
</style>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<div class="invoice-container">
|
2025-11-25 13:14:53 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<!-- ACCENT BAR -->
|
|
|
|
|
|
<div class="invoice-accent-bar"></div>
|
|
|
|
|
|
|
|
|
|
|
|
@php
|
|
|
|
|
|
$showActions = $showActions ?? true;
|
|
|
|
|
|
@endphp
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ═══════════════════════════ HEADER ═══════════════════════════ -->
|
|
|
|
|
|
<div class="invoice-header">
|
|
|
|
|
|
<div class="d-flex justify-content-between align-items-start flex-wrap gap-3">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="invoice-title">
|
|
|
|
|
|
<i class="fas fa-file-invoice" style="color:#3b82f6;font-size:1.5rem;vertical-align:middle;margin-right:0.4rem;"></i>INVOICE
|
2025-11-26 23:07:12 +05:30
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<div class="invoice-number mt-1">{{ $invoice->invoice_number }}</div>
|
2025-11-25 13:14:53 +05:30
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<div>
|
|
|
|
|
|
@if($invoice->status == 'paid')
|
|
|
|
|
|
<span class="status-badge status-paid">
|
|
|
|
|
|
<i class="fas fa-check-circle"></i> Paid
|
|
|
|
|
|
</span>
|
|
|
|
|
|
@elseif($invoice->status == 'overdue')
|
|
|
|
|
|
<span class="status-badge status-overdue">
|
|
|
|
|
|
<i class="fas fa-exclamation-circle"></i> Overdue
|
|
|
|
|
|
</span>
|
|
|
|
|
|
@elseif($invoice->status == 'pending')
|
|
|
|
|
|
<span class="status-badge status-pending">
|
|
|
|
|
|
<i class="fas fa-clock"></i> Pending
|
|
|
|
|
|
</span>
|
|
|
|
|
|
@else
|
|
|
|
|
|
<span class="status-badge status-default">
|
|
|
|
|
|
<i class="fas fa-question-circle"></i> {{ ucfirst($invoice->status) }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-25 13:14:53 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
<!-- ═══════════════════════════ 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>
|
|
|
|
|
|
|
2025-11-25 13:14:53 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<!-- ═══════════════════════════ CUSTOMER ═══════════════════════════ -->
|
|
|
|
|
|
<div class="panel">
|
|
|
|
|
|
<div class="panel-header">
|
|
|
|
|
|
<i class="fas fa-user-circle"></i> Customer Details
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="panel-body">
|
|
|
|
|
|
<div class="row">
|
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
|
<div class="customer-name">{{ $invoice->customer_name }}</div>
|
|
|
|
|
|
@if($invoice->company_name)
|
|
|
|
|
|
<div class="customer-detail"><strong>Company:</strong> {{ $invoice->company_name }}</div>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
<div class="customer-detail"><strong>Mobile:</strong> {{ $invoice->customer_mobile }}</div>
|
|
|
|
|
|
<div class="customer-detail"><strong>Email:</strong> {{ $invoice->customer_email }}</div>
|
2025-11-26 23:07:12 +05:30
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
|
<div class="customer-detail"><strong>Address:</strong></div>
|
|
|
|
|
|
<div class="customer-detail">{{ $invoice->customer_address }}</div>
|
|
|
|
|
|
<div class="customer-detail"><strong>Pincode:</strong> {{ $invoice->pincode }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ═══════════════════════════ ITEMS TABLE ═══════════════════════════ -->
|
|
|
|
|
|
@php
|
|
|
|
|
|
$isEmbedded = isset($embedded) && $embedded;
|
|
|
|
|
|
@endphp
|
|
|
|
|
|
|
|
|
|
|
|
<div class="panel">
|
|
|
|
|
|
<div class="panel-header">
|
|
|
|
|
|
<i class="fas fa-list"></i> Invoice Items
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="table-responsive">
|
2026-03-09 10:24:44 +05:30
|
|
|
|
<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>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
@endif
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
|
|
|
|
|
<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>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
<!-- ACTION BAR -->
|
|
|
|
|
|
<div class="action-bar">
|
|
|
|
|
|
<small>Selected items for charge group: <span id="selectedItemsCount">0</span></small>
|
|
|
|
|
|
<button type="button" id="btnCreateChargeGroup" class="btn-create-group" disabled>
|
|
|
|
|
|
<i class="fas fa-layer-group"></i> Create Charge Group
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
@if($isEmbedded)
|
|
|
|
|
|
<div class="text-end p-3" style="border-top:1px solid var(--border);">
|
|
|
|
|
|
<button type="submit" class="btn btn-primary btn-sm">
|
|
|
|
|
|
<i class="fas fa-save me-1"></i> Update Items & Recalculate
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- ═══════════════════════════ CHARGE GROUP FORM ═══════════════════════════ -->
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div id="chargeGroupBox" class="cg-panel d-none">
|
|
|
|
|
|
<div class="cg-panel-header">
|
|
|
|
|
|
<i class="fas fa-layer-group"></i> Create Charge Group for Selected Items
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="cg-body">
|
|
|
|
|
|
<form method="POST" id="chargeGroupForm"
|
|
|
|
|
|
action="{{ route('admin.invoices.charge-group.store', $invoice->id) }}">
|
|
|
|
|
|
@csrf
|
|
|
|
|
|
|
|
|
|
|
|
<div class="row g-3 mb-3">
|
|
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">Group Name</label>
|
|
|
|
|
|
<input type="text" class="form-control-custom" id="cgGroupName"
|
|
|
|
|
|
name="groupname" placeholder="e.g. Group #1">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">Price based on</label>
|
|
|
|
|
|
<select class="form-select-custom" id="cgBasis">
|
|
|
|
|
|
<option value="" selected disabled>Select basis</option>
|
|
|
|
|
|
<option value="ttl_qty">TTL/QTY</option>
|
|
|
|
|
|
<option value="amount">AMOUNT</option>
|
|
|
|
|
|
<option value="ttl_cbm">TTL CBM</option>
|
|
|
|
|
|
<option value="ttl_kg">TTL KG</option>
|
|
|
|
|
|
</select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">Rate per selected basis</label>
|
|
|
|
|
|
<input type="number" step="0.0001" class="form-control-custom"
|
|
|
|
|
|
id="cgRate" placeholder="Enter rate">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div class="row g-3 mb-3">
|
|
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">Tax Type</label>
|
|
|
|
|
|
<select class="form-select-custom" id="cgTaxType" name="tax_type">
|
|
|
|
|
|
<option value="" selected disabled>Select tax type</option>
|
|
|
|
|
|
<!-- <option value="none">No Tax</option> -->
|
|
|
|
|
|
<option value="gst">GST (CGST+SGST)</option>
|
|
|
|
|
|
<option value="igst">IGST</option>
|
|
|
|
|
|
</select>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">GST %</label>
|
|
|
|
|
|
<input type="number" step="0.01"
|
|
|
|
|
|
class="form-control-custom"
|
|
|
|
|
|
id="cgGstPercent"
|
|
|
|
|
|
name="gst_percent"
|
|
|
|
|
|
placeholder="e.g. 18">
|
|
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div class="col-md-4">
|
|
|
|
|
|
<label class="form-label-custom">Total charges with GST</label>
|
|
|
|
|
|
<input type="number" step="0.01"
|
|
|
|
|
|
class="form-control-custom"
|
|
|
|
|
|
id="cgTotalWithGst"
|
|
|
|
|
|
name="total_with_gst"
|
|
|
|
|
|
placeholder="Auto calculated"
|
|
|
|
|
|
readonly>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-11-25 13:14:53 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- Hidden fields (controller validate साठी names match केलेत) --}}
|
|
|
|
|
|
<input type="hidden" name="basistype" id="cgBasisTypeInput">
|
|
|
|
|
|
<input type="hidden" name="basisvalue" id="cgBasisValueInput">
|
|
|
|
|
|
<input type="hidden" name="rate" id="cgRateHidden">
|
|
|
|
|
|
<input type="hidden" name="autototal" id="cgAutoTotal">
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- itemids[] हे JS मधून append होतात (checkbox selected items) --}}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div class="row g-3 mb-3">
|
|
|
|
|
|
<!-- LEFT: Total basis value -->
|
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
|
<div class="basis-box">
|
|
|
|
|
|
<div class="basis-box-label">Total basis value</div>
|
|
|
|
|
|
<div style="display:flex;align-items:baseline;gap:0.4rem;">
|
|
|
|
|
|
<span class="basis-value-display" id="cgBasisValue">0</span>
|
|
|
|
|
|
<span class="basis-label-display" id="cgBasisLabel"></span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="basis-hint" id="cgBasisHint">
|
|
|
|
|
|
Select basis to see total TTLQTY, CBM, KG or Amount.
|
|
|
|
|
|
</div>
|
2025-11-26 23:07:12 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-23 00:30:18 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<!-- RIGHT: Total charges (admin input) -->
|
|
|
|
|
|
<div class="col-md-6">
|
|
|
|
|
|
<div class="basis-box">
|
|
|
|
|
|
<div class="basis-box-label">Total charges (admin input)</div>
|
|
|
|
|
|
<input type="number"
|
|
|
|
|
|
step="0.01"
|
|
|
|
|
|
class="form-control-custom"
|
|
|
|
|
|
id="cgTotalChargeInput"
|
|
|
|
|
|
placeholder="Enter total charges"
|
|
|
|
|
|
readonly>
|
|
|
|
|
|
<div class="basis-hint" style="margin-top:0.5rem">
|
|
|
|
|
|
Suggested Rate × basis:
|
|
|
|
|
|
<span class="suggested-total" id="cgSuggestedTotal">0</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</div>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="mb-3">
|
|
|
|
|
|
<label class="form-label-custom" style="margin-bottom:0.5rem;">Selected items in this group</label>
|
|
|
|
|
|
<div class="table-responsive" style="border:1px solid var(--border);border-radius:9px;overflow:hidden;">
|
|
|
|
|
|
<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-center">TTL Amount</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody id="cgItemsTableBody"></tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-23 00:30:18 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div class="d-flex justify-content-end gap-2">
|
|
|
|
|
|
<button type="button" id="cgCancelBtn"
|
|
|
|
|
|
style="padding:0.5rem 1.1rem;border:1.5px solid var(--border);border-radius:8px;background:transparent;font-size:0.85rem;font-weight:600;cursor:pointer;color:var(--text-secondary);">
|
|
|
|
|
|
Cancel
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button type="submit" id="cgSaveBtn"
|
|
|
|
|
|
style="display:inline-flex;align-items:center;gap:0.4rem;padding:0.5rem 1.25rem;border:none;border-radius:8px;background:#10b981;color:white;font-size:0.85rem;font-weight:700;cursor:pointer;">
|
|
|
|
|
|
<i class="fas fa-save"></i> Save Charge Group
|
|
|
|
|
|
</button>
|
2025-12-23 00:30:18 +05:30
|
|
|
|
</div>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
</form>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
{{-- ═══════════════════════════ CHARGE GROUPS LIST ═══════════════════════════ --}}
|
|
|
|
|
|
@if($invoice->chargeGroups->isNotEmpty())
|
|
|
|
|
|
<div class="cg-groups-panel">
|
|
|
|
|
|
<div class="cg-groups-header">
|
|
|
|
|
|
<i class="fas fa-layer-group"></i> Charge Groups
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="table-responsive">
|
|
|
|
|
|
<table class="invoice-table">
|
|
|
|
|
|
<thead>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th>#</th>
|
|
|
|
|
|
<th>Group Name</th>
|
|
|
|
|
|
<th>Basis</th>
|
|
|
|
|
|
<th class="text-end">Basis Value</th>
|
|
|
|
|
|
<th class="text-end">Rate</th>
|
|
|
|
|
|
<th class="text-end">Total Charge</th>
|
2026-03-12 11:48:42 +05:30
|
|
|
|
<th class="text-end">GST %</th>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<th class="text-center">Tax Type</th>
|
2026-03-12 11:48:42 +05:30
|
|
|
|
<th class="text-end">Total With GST</th>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<th class="text-center">Action</th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
@foreach($invoice->chargeGroups as $index => $group)
|
|
|
|
|
|
@php
|
|
|
|
|
|
// Base total आणि GST calculation
|
|
|
|
|
|
$groupBaseTotal = $group->total_charge ?? 0;
|
|
|
|
|
|
$groupTotalWithGst = $group->total_with_gst ?? $groupBaseTotal;
|
|
|
|
|
|
$groupGstAmount = max(0, $groupTotalWithGst - $groupBaseTotal);
|
|
|
|
|
|
$groupGstPercent = $group->gst_percent ?? 0;
|
|
|
|
|
|
@endphp
|
|
|
|
|
|
|
|
|
|
|
|
{{-- Main group row --}}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<tr>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<td>{{ $index + 1 }}</td>
|
|
|
|
|
|
|
|
|
|
|
|
<td style="font-weight:600;">
|
|
|
|
|
|
{{ $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_format($group->basis_value, 3) }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
|
|
|
|
|
<td class="text-end"
|
|
|
|
|
|
style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
|
|
|
|
|
{{ number_format($group->rate, 2) }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
2026-03-12 11:48:42 +05:30
|
|
|
|
{{-- Base total without GST --}}
|
|
|
|
|
|
<td class="text-end price-blue">
|
|
|
|
|
|
₹{{ number_format($groupBaseTotal, 2) }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- GST % --}}
|
|
|
|
|
|
<td class="text-end"
|
|
|
|
|
|
style="font-family:'JetBrains Mono',monospace;font-size:0.82rem;">
|
|
|
|
|
|
{{ number_format($groupGstPercent, 2) }}%
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
2026-03-12 11:48:42 +05:30
|
|
|
|
{{-- Group tax type --}}
|
|
|
|
|
|
<td class="text-center">
|
|
|
|
|
|
{{ $group->tax_type ?? '-' }}
|
2026-03-11 20:02:43 +05:30
|
|
|
|
</td>
|
|
|
|
|
|
|
2026-03-12 11:48:42 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- Group total with GST --}}
|
|
|
|
|
|
<td class="text-end" style="font-weight:700;color:#16a34a;">
|
|
|
|
|
|
₹{{ number_format($groupTotalWithGst, 2) }}
|
|
|
|
|
|
</td>
|
|
|
|
|
|
|
2026-03-12 11:48:42 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<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>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</tr>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{{-- Hidden items row --}}
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<tr class="cg-items-row d-none" data-group-id="{{ $group->id }}">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<td colspan="10">
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<div style="padding:1rem;background:#f8faff;border-radius:8px;margin:0.25rem 0;">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div style="font-weight:700;font-size:0.8rem;margin-bottom:0.6rem;color:var(--primary);">
|
|
|
|
|
|
Items in this group
|
|
|
|
|
|
<span style="font-weight:500;color:#64748b;margin-left:8px;">
|
|
|
|
|
|
(GST: {{ number_format($groupGstPercent ?? 0, 2) }}%)
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
@if($group->items->isEmpty())
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<div style="color:var(--text-muted);font-size:0.82rem;">
|
|
|
|
|
|
No items linked.
|
|
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
@else
|
|
|
|
|
|
<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>
|
|
|
|
|
|
<th class="text-end">Rate</th>
|
|
|
|
|
|
<th class="text-end">Total Charge</th>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- नवीन GST % कॉलम --}}
|
|
|
|
|
|
<th class="text-end">GST %</th>
|
|
|
|
|
|
<th class="text-end">Item GST</th>
|
|
|
|
|
|
<th class="text-end">Item Total (with GST)</th>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
@foreach($group->items as $giIndex => $gi)
|
|
|
|
|
|
@php
|
|
|
|
|
|
$it = $gi->item;
|
|
|
|
|
|
$rate = $group->rate;
|
|
|
|
|
|
$itemBasis = 0;
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
switch ($group->basis_type) {
|
2026-03-11 20:02:43 +05:30
|
|
|
|
case 'ttl_qty':
|
|
|
|
|
|
$itemBasis = $it->ttl_qty ?? 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'amount':
|
|
|
|
|
|
$itemBasis = $it->ttl_amount ?? 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'ttl_cbm':
|
|
|
|
|
|
$itemBasis = $it->ttl_cbm ?? 0;
|
|
|
|
|
|
break;
|
|
|
|
|
|
case 'ttl_kg':
|
|
|
|
|
|
$itemBasis = $it->ttl_kg ?? 0;
|
|
|
|
|
|
break;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
}
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
$itemTotal = $itemBasis * $rate;
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
// Per‑item GST share (proportional)
|
|
|
|
|
|
$itemGst = 0;
|
|
|
|
|
|
$itemTotalWithGst = $itemTotal;
|
|
|
|
|
|
|
|
|
|
|
|
if ($groupBaseTotal > 0 && $groupGstAmount > 0) {
|
|
|
|
|
|
$share = $itemTotal / $groupBaseTotal;
|
|
|
|
|
|
$itemGst = $groupGstAmount * $share;
|
|
|
|
|
|
$itemTotalWithGst = $itemTotal + $itemGst;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
$itemGstPercent = $groupGstPercent ?? 0; // same as group
|
2026-02-27 10:51:26 +05:30
|
|
|
|
@endphp
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<tr>
|
|
|
|
|
|
<td>{{ $giIndex + 1 }}</td>
|
|
|
|
|
|
<td>{{ $it->description }}</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>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<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>
|
|
|
|
|
|
<td class="text-end" style="font-weight:700;color:#16a34a;">
|
|
|
|
|
|
{{ number_format($itemTotalWithGst, 2) }}
|
|
|
|
|
|
</td>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</tr>
|
|
|
|
|
|
@endforeach
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
@endforeach
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</div>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
{{-- ===== 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">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
@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>
|
|
|
|
|
|
<span class="value">
|
|
|
|
|
|
₹{{ number_format($baseAmount, 2) }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- GST Amount (total) --}}
|
|
|
|
|
|
<div class="summary-row-compact">
|
|
|
|
|
|
<span class="label">GST Amount</span>
|
|
|
|
|
|
<span class="value red">
|
|
|
|
|
|
₹{{ number_format($gstAmount, 2) }}
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
{{-- Charge Groups Total (with GST) --}}
|
2026-03-09 10:24:44 +05:30
|
|
|
|
<div class="summary-row-compact">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
<span class="label">Charge Groups Total (with GST)</span>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
<span class="value">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
₹{{ number_format($groupsWithGst, 2) }}
|
2026-03-09 10:24:44 +05:30
|
|
|
|
</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
{{--
|
|
|
|
|
|
<div class="summary-row-compact total">
|
|
|
|
|
|
<span class="label">Total Charge With GST</span>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
<span class="value">
|
2026-03-11 20:02:43 +05:30
|
|
|
|
₹{{ number_format($invoice->final_amount_with_gst, 2) }}
|
2026-03-09 10:24:44 +05:30
|
|
|
|
</span>
|
2026-03-11 20:02:43 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
--}}
|
2025-12-23 00:30:18 +05:30
|
|
|
|
</div>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-23 00:30:18 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
<!-- ═══════════════════════════ 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
|
|
|
|
|
|
</a>
|
|
|
|
|
|
<button class="btn-share" onclick="shareInvoice()">
|
|
|
|
|
|
<i class="fas fa-share"></i> Share
|
|
|
|
|
|
</button>
|
|
|
|
|
|
@endif
|
|
|
|
|
|
<div class="footer-note">
|
|
|
|
|
|
<p style="margin-bottom:0.25rem;">Thank you for your business!</p>
|
|
|
|
|
|
<p style="margin:0;">For any inquiries, contact us at support@Kent Logistic</p>
|
2025-11-25 13:14:53 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</div>
|
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
|
|
<script>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
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.");
|
2025-11-25 13:14:53 +05:30
|
|
|
|
}
|
2026-03-09 10:24:44 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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++;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-12-23 00:30:18 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
2026-03-09 10:24:44 +05:30
|
|
|
|
const selectAll = document.getElementById('selectAllItems');
|
|
|
|
|
|
const itemCheckboxes = document.querySelectorAll('.item-select-checkbox');
|
|
|
|
|
|
const countSpan = document.getElementById('selectedItemsCount');
|
|
|
|
|
|
const btnCreate = document.getElementById('btnCreateChargeGroup');
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
const chargeGroupBox = document.getElementById('chargeGroupBox');
|
|
|
|
|
|
const cgCancelBtn = document.getElementById('cgCancelBtn');
|
2026-02-27 10:51:26 +05:30
|
|
|
|
|
|
|
|
|
|
const cgItemsTableBody = document.getElementById('cgItemsTableBody');
|
|
|
|
|
|
const cgBasisSelect = document.getElementById('cgBasis');
|
|
|
|
|
|
const cgRateInput = document.getElementById('cgRate');
|
|
|
|
|
|
const cgBasisValueSpan = document.getElementById('cgBasisValue');
|
|
|
|
|
|
const cgBasisLabelSpan = document.getElementById('cgBasisLabel');
|
|
|
|
|
|
const cgSuggestedTotalSpan = document.getElementById('cgSuggestedTotal');
|
|
|
|
|
|
const cgTotalChargeInput = document.getElementById('cgTotalChargeInput');
|
|
|
|
|
|
|
|
|
|
|
|
const cgAutoTotalInput = document.getElementById('cgAutoTotal');
|
|
|
|
|
|
|
|
|
|
|
|
const cgBasisTypeInput = document.getElementById('cgBasisTypeInput');
|
|
|
|
|
|
const cgBasisValueInput = document.getElementById('cgBasisValueInput');
|
|
|
|
|
|
const cgRateHidden = document.getElementById('cgRateHidden');
|
|
|
|
|
|
const cgForm = document.getElementById('chargeGroupForm');
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
const cgGroupName = document.getElementById('cgGroupName');
|
|
|
|
|
|
|
|
|
|
|
|
// Tax related fields
|
|
|
|
|
|
const cgTaxType = document.getElementById('cgTaxType');
|
|
|
|
|
|
const cgGstPercent = document.getElementById('cgGstPercent');
|
|
|
|
|
|
const cgTotalWithGst = document.getElementById('cgTotalWithGst');
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
function updateSelectionState() {
|
|
|
|
|
|
let selectedCount = 0;
|
|
|
|
|
|
itemCheckboxes.forEach(cb => {
|
|
|
|
|
|
if (cb.checked && !cb.disabled) selectedCount++;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (countSpan) countSpan.textContent = selectedCount;
|
|
|
|
|
|
if (btnCreate) btnCreate.disabled = (selectedCount === 0);
|
|
|
|
|
|
if (selectedCount === 0 && chargeGroupBox) {
|
|
|
|
|
|
chargeGroupBox.classList.add('d-none');
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function getSelectedItemsDataBasic() {
|
|
|
|
|
|
const items = [];
|
|
|
|
|
|
itemCheckboxes.forEach(cb => {
|
|
|
|
|
|
if (cb.checked && !cb.disabled) {
|
|
|
|
|
|
const row = cb.closest('tr');
|
|
|
|
|
|
if (!row) return;
|
|
|
|
|
|
const cellText = (n) =>
|
|
|
|
|
|
row.querySelector(`td:nth-child(${n})`)?.textContent.trim() ?? '';
|
|
|
|
|
|
items.push({
|
|
|
|
|
|
description: cellText(3),
|
2026-03-09 10:24:44 +05:30
|
|
|
|
qty: cellText(5),
|
|
|
|
|
|
ttlqty: cellText(6),
|
|
|
|
|
|
cbm: cellText(10),
|
|
|
|
|
|
ttlcbm: cellText(11),
|
|
|
|
|
|
kg: cellText(12),
|
|
|
|
|
|
ttlkg: cellText(13),
|
|
|
|
|
|
amount: cellText(9),
|
2026-02-27 10:51:26 +05:30
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
return items;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function parseNumber(str) {
|
|
|
|
|
|
if (!str) return 0;
|
|
|
|
|
|
const cleaned = str.replace(/[₹,\s]/g, '');
|
|
|
|
|
|
const val = parseFloat(cleaned);
|
|
|
|
|
|
return isNaN(val) ? 0 : val;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
// GST calculation based on total charge and selected tax type
|
|
|
|
|
|
function refreshChargeGroupGst() {
|
|
|
|
|
|
if (!cgTotalChargeInput || !cgTotalWithGst) return;
|
|
|
|
|
|
|
|
|
|
|
|
const base = parseNumber(cgTotalChargeInput.value);
|
|
|
|
|
|
let gstPercent = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (cgTaxType && cgGstPercent && cgTaxType.value && cgTaxType.value !== 'none') {
|
|
|
|
|
|
gstPercent = parseNumber(cgGstPercent.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const gstAmount = base * gstPercent / 100;
|
|
|
|
|
|
const totalWithGst = base + gstAmount;
|
|
|
|
|
|
|
|
|
|
|
|
cgTotalWithGst.value = totalWithGst ? totalWithGst.toFixed(2) : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
function fillChargeGroupItemsTable() {
|
|
|
|
|
|
if (!cgItemsTableBody) return;
|
|
|
|
|
|
const items = getSelectedItemsDataBasic();
|
|
|
|
|
|
cgItemsTableBody.innerHTML = '';
|
|
|
|
|
|
items.forEach((it, index) => {
|
|
|
|
|
|
cgItemsTableBody.insertAdjacentHTML('beforeend', `
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td>${index + 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-center">${it.amount}</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
`);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function refreshBasisSummaryAndSuggestion() {
|
|
|
|
|
|
if (!chargeGroupBox || chargeGroupBox.classList.contains('d-none')) return;
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
const items = getSelectedItemsDataBasic();
|
|
|
|
|
|
const basis = cgBasisSelect ? cgBasisSelect.value : '';
|
|
|
|
|
|
let totalBasis = 0;
|
|
|
|
|
|
let label = '';
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (basis === 'ttl_qty') {
|
|
|
|
|
|
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlqty), 0);
|
|
|
|
|
|
label = 'Total TTL/QTY';
|
|
|
|
|
|
} else if (basis === 'amount') {
|
|
|
|
|
|
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.amount), 0);
|
|
|
|
|
|
label = 'Total Amount';
|
|
|
|
|
|
} else if (basis === 'ttl_cbm') {
|
|
|
|
|
|
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlcbm), 0);
|
|
|
|
|
|
label = 'Total TTL CBM';
|
|
|
|
|
|
} else if (basis === 'ttl_kg') {
|
|
|
|
|
|
totalBasis = items.reduce((sum, it) => sum + parseNumber(it.ttlkg), 0);
|
|
|
|
|
|
label = 'Total TTL KG';
|
|
|
|
|
|
}
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (cgBasisValueSpan) cgBasisValueSpan.textContent = totalBasis ? totalBasis.toFixed(3) : '0';
|
|
|
|
|
|
if (cgBasisLabelSpan) cgBasisLabelSpan.textContent = label || '–';
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
const rate = parseNumber(cgRateInput ? cgRateInput.value : '0');
|
|
|
|
|
|
const suggested = rate * totalBasis;
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (cgSuggestedTotalSpan) cgSuggestedTotalSpan.textContent = suggested ? suggested.toFixed(2) : '0';
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (cgBasisTypeInput) cgBasisTypeInput.value = basis || '';
|
|
|
|
|
|
if (cgBasisValueInput) cgBasisValueInput.value = totalBasis || 0;
|
|
|
|
|
|
if (cgRateHidden && cgRateInput) cgRateHidden.value = cgRateInput.value || 0;
|
|
|
|
|
|
if (cgAutoTotalInput) cgAutoTotalInput.value = suggested || 0;
|
|
|
|
|
|
if (cgTotalChargeInput) {
|
2026-03-09 10:24:44 +05:30
|
|
|
|
cgTotalChargeInput.value = suggested ? suggested.toFixed(2) : '0';
|
|
|
|
|
|
}
|
2026-03-11 20:02:43 +05:30
|
|
|
|
|
|
|
|
|
|
// base बदलला की लगेच GST सह total पण refresh
|
|
|
|
|
|
refreshChargeGroupGst();
|
2026-02-27 10:51:26 +05:30
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
itemCheckboxes.forEach(cb => {
|
|
|
|
|
|
cb.addEventListener('change', function () {
|
|
|
|
|
|
updateSelectionState();
|
|
|
|
|
|
const total = itemCheckboxes.length;
|
|
|
|
|
|
const checked = Array.from(itemCheckboxes).filter(c => c.checked && !c.disabled).length;
|
|
|
|
|
|
if (selectAll) {
|
|
|
|
|
|
selectAll.checked = (checked > 0 && checked === total);
|
|
|
|
|
|
selectAll.indeterminate = (checked > 0 && checked < total);
|
|
|
|
|
|
}
|
2026-03-09 10:24:44 +05:30
|
|
|
|
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
fillChargeGroupItemsTable();
|
|
|
|
|
|
refreshBasisSummaryAndSuggestion();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (selectAll) {
|
|
|
|
|
|
selectAll.addEventListener('change', function () {
|
|
|
|
|
|
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = selectAll.checked; });
|
|
|
|
|
|
updateSelectionState();
|
2026-03-09 10:24:44 +05:30
|
|
|
|
if (chargeGroupBox && !chargeGroupBox.classList.contains('d-none')) {
|
2026-02-27 10:51:26 +05:30
|
|
|
|
fillChargeGroupItemsTable();
|
|
|
|
|
|
refreshBasisSummaryAndSuggestion();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cgBasisSelect) cgBasisSelect.addEventListener('change', refreshBasisSummaryAndSuggestion);
|
|
|
|
|
|
if (cgRateInput) cgRateInput.addEventListener('input', refreshBasisSummaryAndSuggestion);
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
// GST fields event listeners
|
|
|
|
|
|
if (cgGstPercent) {
|
|
|
|
|
|
cgGstPercent.addEventListener('input', refreshChargeGroupGst);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cgTaxType) {
|
|
|
|
|
|
cgTaxType.addEventListener('change', refreshChargeGroupGst);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (cgTotalChargeInput) {
|
|
|
|
|
|
cgTotalChargeInput.addEventListener('input', refreshChargeGroupGst);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (btnCreate && chargeGroupBox) {
|
|
|
|
|
|
btnCreate.addEventListener('click', function () {
|
|
|
|
|
|
const hasSelection = Array.from(itemCheckboxes).some(cb => cb.checked && !cb.disabled);
|
|
|
|
|
|
if (!hasSelection) return;
|
|
|
|
|
|
chargeGroupBox.classList.remove('d-none');
|
|
|
|
|
|
fillChargeGroupItemsTable();
|
|
|
|
|
|
refreshBasisSummaryAndSuggestion();
|
|
|
|
|
|
chargeGroupBox.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cgCancelBtn && chargeGroupBox) {
|
|
|
|
|
|
cgCancelBtn.addEventListener('click', function () {
|
|
|
|
|
|
chargeGroupBox.classList.add('d-none');
|
|
|
|
|
|
itemCheckboxes.forEach(cb => { if (!cb.disabled) cb.checked = false; });
|
|
|
|
|
|
if (selectAll) { selectAll.checked = false; selectAll.indeterminate = false; }
|
|
|
|
|
|
updateSelectionState();
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
// normal form submit, फक्त hidden item_ids तयार करतो
|
2026-02-27 10:51:26 +05:30
|
|
|
|
if (cgForm) {
|
2026-03-12 11:48:42 +05:30
|
|
|
|
cgForm.addEventListener('submit', function (e) {
|
|
|
|
|
|
|
|
|
|
|
|
e.preventDefault(); // 🚀 STOP PAGE REFRESH
|
|
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
const selectedIds = [];
|
2026-03-12 11:48:42 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
itemCheckboxes.forEach(cb => {
|
|
|
|
|
|
if (cb.checked && !cb.disabled) {
|
|
|
|
|
|
selectedIds.push(cb.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
if (selectedIds.length === 0) {
|
|
|
|
|
|
alert('Please select at least one item for this charge group.');
|
2026-03-12 11:48:42 +05:30
|
|
|
|
return;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
}
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
if (!cgBasisSelect || !cgBasisSelect.value) {
|
|
|
|
|
|
alert('Please select a basis for this charge group.');
|
2026-03-12 11:48:42 +05:30
|
|
|
|
return;
|
2026-02-27 10:51:26 +05:30
|
|
|
|
}
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
if (!cgTotalChargeInput ||
|
|
|
|
|
|
!cgTotalChargeInput.value ||
|
2026-03-12 11:48:42 +05:30
|
|
|
|
parseFloat(cgTotalChargeInput.value) <= 0) {
|
|
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
alert('Please enter total charges for this group.');
|
2026-03-12 11:48:42 +05:30
|
|
|
|
return;
|
2026-03-09 10:24:44 +05:30
|
|
|
|
}
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
refreshChargeGroupGst();
|
|
|
|
|
|
|
2026-03-12 11:48:42 +05:30
|
|
|
|
const formData = new FormData(cgForm);
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
selectedIds.forEach(id => {
|
2026-03-12 11:48:42 +05:30
|
|
|
|
formData.append('itemids[]', id);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
fetch(cgForm.action, {
|
|
|
|
|
|
method: "POST",
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
"X-CSRF-TOKEN": document
|
|
|
|
|
|
.querySelector('meta[name="csrf-token"]')
|
|
|
|
|
|
.getAttribute("content")
|
|
|
|
|
|
},
|
|
|
|
|
|
body: formData
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(async res => {
|
|
|
|
|
|
|
|
|
|
|
|
const data = await res.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (!res.ok) {
|
|
|
|
|
|
|
|
|
|
|
|
// Laravel validation errors
|
|
|
|
|
|
if (data.errors) {
|
|
|
|
|
|
|
|
|
|
|
|
let msg = "";
|
|
|
|
|
|
|
|
|
|
|
|
Object.values(data.errors).forEach(errArr => {
|
|
|
|
|
|
msg += errArr.join("\n") + "\n";
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
throw new Error(msg);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
throw new Error(data.message || "Something went wrong");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return data;
|
|
|
|
|
|
})
|
|
|
|
|
|
.then(data => {
|
|
|
|
|
|
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
|
|
|
|
|
|
alert("Charge group created successfully!");
|
|
|
|
|
|
|
|
|
|
|
|
location.reload();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
.catch(err => {
|
|
|
|
|
|
|
|
|
|
|
|
alert("Check Group details again");
|
|
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
});
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
|
|
|
|
|
});
|
2026-03-09 10:24:44 +05:30
|
|
|
|
}
|
2026-02-28 11:00:48 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
updateSelectionState();
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-03-09 10:24:44 +05:30
|
|
|
|
// View/Hide group items
|
2026-02-27 10:51:26 +05:30
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
|
|
|
|
document.addEventListener('click', function (e) {
|
|
|
|
|
|
if (!e.target.classList.contains('cg-toggle-items') &&
|
|
|
|
|
|
!e.target.closest('.cg-toggle-items')) return;
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
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;
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-02-27 10:51:26 +05:30
|
|
|
|
row.classList.toggle('d-none');
|
|
|
|
|
|
const isHidden = row.classList.contains('d-none');
|
|
|
|
|
|
btn.innerHTML = isHidden
|
|
|
|
|
|
? '<i class="fas fa-eye"></i> View'
|
|
|
|
|
|
: '<i class="fas fa-eye-slash"></i> Hide';
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
2026-03-11 20:02:43 +05:30
|
|
|
|
// simple select all (duplicate राहिला तरी harmless)
|
2026-03-09 10:24:44 +05:30
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
2026-03-11 20:02:43 +05:30
|
|
|
|
const selectAll = document.getElementById('selectAllItems');
|
2026-03-09 10:24:44 +05:30
|
|
|
|
const checkboxes = document.querySelectorAll('.item-select-checkbox');
|
|
|
|
|
|
|
|
|
|
|
|
if (selectAll) {
|
|
|
|
|
|
selectAll.addEventListener('change', function () {
|
|
|
|
|
|
checkboxes.forEach(cb => {
|
|
|
|
|
|
if (!cb.disabled) {
|
|
|
|
|
|
cb.checked = selectAll.checked;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-02-27 10:51:26 +05:30
|
|
|
|
</script>
|
2026-03-09 10:24:44 +05:30
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-11-26 23:07:12 +05:30
|
|
|
|
</body>
|
|
|
|
|
|
</html>
|