851 lines
31 KiB
PHP
851 lines
31 KiB
PHP
@extends('admin.layouts.app')
|
||
|
||
@section('page-title', 'Edit Invoice')
|
||
|
||
@section('content')
|
||
<style>
|
||
:root {
|
||
--primary-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
--success-gradient: linear-gradient(135deg, #10b981 0%, #059669 100%);
|
||
--warning-gradient: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||
--danger-gradient: linear-gradient(135deg, #ef4444 0%, #dc2626 100%);
|
||
--glass-bg: rgba(255, 255, 255, 0.95);
|
||
--glass-border: rgba(255, 255, 255, 0.2);
|
||
--shadow-soft: 0 8px 32px rgba(0, 0, 0, 0.08);
|
||
--shadow-medium: 0 12px 48px rgba(0, 0, 0, 0.12);
|
||
--shadow-strong: 0 20px 60px rgba(0, 0, 0, 0.15);
|
||
--border-radius: 12px;
|
||
}
|
||
|
||
body {
|
||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||
font-family: "Inter", "Segoe UI", system-ui, sans-serif;
|
||
}
|
||
|
||
@keyframes fadeUp {
|
||
0% {
|
||
opacity: 0;
|
||
transform: translateY(20px) scale(0.98);
|
||
}
|
||
100% {
|
||
opacity: 1;
|
||
transform: translateY(0) scale(1);
|
||
}
|
||
}
|
||
|
||
.glass-card {
|
||
background: var(--glass-bg);
|
||
backdrop-filter: blur(20px);
|
||
border-radius: var(--border-radius);
|
||
border: 1px solid var(--glass-border);
|
||
box-shadow: var(--shadow-strong);
|
||
position: relative;
|
||
overflow: hidden;
|
||
animation: fadeUp 0.6s ease;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.glass-card::before {
|
||
content: "";
|
||
position: absolute;
|
||
inset: 0;
|
||
background: linear-gradient(
|
||
45deg,
|
||
rgba(102, 126, 234, 0.03),
|
||
rgba(118, 75, 162, 0.03) 50%,
|
||
rgba(16, 185, 129, 0.03)
|
||
);
|
||
pointer-events: none;
|
||
}
|
||
|
||
.card-header-compact {
|
||
background: var(--primary-gradient);
|
||
color: #fff;
|
||
padding: 1rem 1.5rem;
|
||
border: none;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.card-header-compact h4 {
|
||
margin: 0;
|
||
font-weight: 700;
|
||
font-size: 1.3rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||
}
|
||
|
||
.card-body-compact {
|
||
padding: 1.5rem;
|
||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||
position: relative;
|
||
z-index: 1;
|
||
}
|
||
|
||
.form-grid-compact {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.form-group-compact {
|
||
position: relative;
|
||
}
|
||
|
||
.form-label-compact {
|
||
font-weight: 600;
|
||
color: #374151;
|
||
margin-bottom: 8px;
|
||
font-size: 0.9rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
}
|
||
|
||
.form-control-compact,
|
||
.form-select-compact {
|
||
border: 2px solid #e2e8f0;
|
||
border-radius: 8px;
|
||
padding: 10px 12px;
|
||
font-size: 0.9rem;
|
||
transition: all 0.3s ease;
|
||
background: #ffffff;
|
||
width: 100%;
|
||
}
|
||
|
||
.form-control-compact:focus,
|
||
.form-select-compact:focus {
|
||
border-color: #667eea;
|
||
box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
|
||
outline: none;
|
||
}
|
||
|
||
.btn-compact {
|
||
padding: 10px 20px;
|
||
border-radius: 8px;
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
transition: all 0.3s ease;
|
||
border: none;
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
text-decoration: none;
|
||
}
|
||
|
||
.btn-success-compact {
|
||
background: var(--success-gradient);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
|
||
}
|
||
|
||
.btn-success-compact:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(16, 185, 129, 0.4);
|
||
color: white;
|
||
}
|
||
|
||
.btn-primary-compact {
|
||
background: var(--primary-gradient);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.btn-primary-compact:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);
|
||
color: white;
|
||
}
|
||
|
||
.btn-danger-compact {
|
||
background: var(--danger-gradient);
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3);
|
||
}
|
||
|
||
.btn-danger-compact:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 6px 20px rgba(239, 68, 68, 0.4);
|
||
color: white;
|
||
}
|
||
|
||
.summary-grid-compact {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||
gap: 1rem;
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.summary-card-compact {
|
||
background: #ffffff;
|
||
padding: 1rem;
|
||
border-radius: 8px;
|
||
box-shadow: var(--shadow-soft);
|
||
border-left: 4px solid;
|
||
text-align: center;
|
||
}
|
||
|
||
.summary-card-compact.total {
|
||
border-left-color: #10b981;
|
||
}
|
||
|
||
.summary-card-compact.paid {
|
||
border-left-color: #3b82f6;
|
||
}
|
||
|
||
.summary-card-compact.remaining {
|
||
border-left-color: #f59e0b;
|
||
}
|
||
|
||
.summary-value-compact {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
margin-bottom: 0.25rem;
|
||
}
|
||
|
||
.summary-label-compact {
|
||
font-size: 0.8rem;
|
||
color: #64748b;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.amount-breakdown-compact {
|
||
background: #ffffff;
|
||
padding: 1rem;
|
||
border-radius: 8px;
|
||
box-shadow: var(--shadow-soft);
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.breakdown-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 0.5rem 0;
|
||
border-bottom: 1px solid #f1f5f9;
|
||
}
|
||
|
||
.breakdown-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.breakdown-label {
|
||
font-weight: 600;
|
||
color: #374151;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.breakdown-value {
|
||
font-weight: 600;
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
.table-compact {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin-bottom: 0;
|
||
font-size: 0.85rem;
|
||
}
|
||
|
||
.table-compact thead th {
|
||
background: var(--primary-gradient);
|
||
color: #ffffff;
|
||
padding: 0.75rem 1rem;
|
||
font-weight: 600;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
font-size: 0.8rem;
|
||
border: none;
|
||
}
|
||
|
||
.table-compact tbody tr {
|
||
background: #ffffff;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.table-compact tbody tr:hover {
|
||
background: #f8fafc;
|
||
}
|
||
|
||
.table-compact tbody td {
|
||
padding: 0.75rem 1rem;
|
||
border-bottom: 1px solid #e2e8f0;
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.badge-compact {
|
||
padding: 0.25rem 0.75rem;
|
||
border-radius: 20px;
|
||
font-size: 0.75rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.glass-card {
|
||
margin: 0.5rem;
|
||
border-radius: 10px;
|
||
}
|
||
.card-body-compact {
|
||
padding: 1rem;
|
||
}
|
||
.form-grid-compact {
|
||
grid-template-columns: 1fr;
|
||
gap: 0.75rem;
|
||
}
|
||
.summary-grid-compact {
|
||
grid-template-columns: 1fr;
|
||
gap: 0.75rem;
|
||
}
|
||
.btn-compact {
|
||
width: 100%;
|
||
justify-content: center;
|
||
}
|
||
.table-compact {
|
||
font-size: 0.8rem;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<div class="container-fluid py-3">
|
||
{{-- Invoice Overview --}}
|
||
<div class="glass-card">
|
||
<div class="card-header-compact">
|
||
<h4>
|
||
<i class="fas fa-file-invoice me-2"></i>
|
||
Invoice Overview
|
||
</h4>
|
||
</div>
|
||
<div class="card-body-compact">
|
||
@include('admin.popup_invoice', ['invoice' => $invoice, 'shipment' => $shipment])
|
||
</div>
|
||
</div>
|
||
|
||
@if(false)
|
||
{{-- Edit Invoice Details card – HIDDEN --}}
|
||
{{-- तुझा full header edit form इथे hidden ठेवलेला आहे --}}
|
||
@endif
|
||
|
||
@php
|
||
$totalPaid = $invoice->totalPaid();
|
||
$remaining = $invoice->remainingAmount();
|
||
|
||
$taxTypes = $invoice->chargeGroups
|
||
? $invoice->chargeGroups->pluck('tax_type')->filter()->unique()->values()
|
||
: collect([]);
|
||
|
||
$taxTypeLabel = 'None';
|
||
|
||
if ($taxTypes->count() === 1) {
|
||
if ($taxTypes[0] === 'gst') {
|
||
$taxTypeLabel = 'GST (CGST + SGST)';
|
||
} elseif ($taxTypes[0] === 'igst') {
|
||
$taxTypeLabel = 'IGST';
|
||
} else {
|
||
$taxTypeLabel = strtoupper($taxTypes[0]);
|
||
}
|
||
} elseif ($taxTypes->count() > 1) {
|
||
$parts = [];
|
||
if ($taxTypes->contains('gst')) {
|
||
$parts[] = 'GST (CGST + SGST)';
|
||
}
|
||
if ($taxTypes->contains('igst')) {
|
||
$parts[] = 'IGST';
|
||
}
|
||
$taxTypeLabel = implode(' + ', $parts);
|
||
}
|
||
@endphp
|
||
|
||
@if(false)
|
||
{{-- Amount Breakdown HIDDEN --}}
|
||
{{-- जुनं breakdown section इथे hidden आहे --}}
|
||
@endif
|
||
|
||
{{-- Summary cards --}}
|
||
<div class="summary-grid-compact">
|
||
<div class="summary-card-compact total">
|
||
<div class="summary-value-compact text-success" id="totalInvoiceWithGstCard">
|
||
₹{{ number_format($invoice->grand_total_with_charges, 2) }}
|
||
</div>
|
||
<div class="summary-label-compact">Grand Total (Charges + GST)</div>
|
||
</div>
|
||
<div class="summary-card-compact paid">
|
||
<div class="summary-value-compact text-primary" id="paidAmount">
|
||
₹{{ number_format($totalPaid, 2) }}
|
||
</div>
|
||
<div class="summary-label-compact">Total Paid</div>
|
||
</div>
|
||
<div class="summary-card-compact remaining">
|
||
<div class="summary-value-compact text-warning" id="remainingAmount">
|
||
₹{{ number_format(max(0, $remaining), 2) }}
|
||
</div>
|
||
<div class="summary-label-compact">Remaining</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Installment Management --}}
|
||
<div class="glass-card">
|
||
<div class="card-header-compact d-flex justify-content-between align-items-center">
|
||
<h4 class="mb-0">
|
||
<i class="fas fa-credit-card me-2"></i>Installment Payments
|
||
</h4>
|
||
@if($remaining > 0)
|
||
<button id="toggleInstallmentForm" class="btn-primary-compact btn-compact">
|
||
<i class="fas fa-plus-circle me-2"></i>Add Installment
|
||
</button>
|
||
@endif
|
||
</div>
|
||
|
||
<div class="card-body-compact">
|
||
{{-- Installment Form --}}
|
||
<div id="installmentForm" class="d-none mb-3">
|
||
<div class="glass-card" style="background: rgba(248, 250, 252, 0.8);">
|
||
<div class="card-header-compact" style="background: var(--success-gradient);">
|
||
<h4 class="mb-0">
|
||
<i class="fas fa-plus-circle me-2"></i>Add New Installment
|
||
</h4>
|
||
</div>
|
||
<div class="card-body-compact">
|
||
<form id="installmentSubmitForm">
|
||
@csrf
|
||
|
||
<div class="form-grid-compact">
|
||
<div class="form-group-compact">
|
||
<label class="form-label-compact">
|
||
<i class="fas fa-calendar-day"></i> Installment Date
|
||
</label>
|
||
<input type="date"
|
||
name="installment_date"
|
||
class="form-control-compact"
|
||
required>
|
||
</div>
|
||
|
||
<div class="form-group-compact">
|
||
<label class="form-label-compact">
|
||
<i class="fas fa-credit-card"></i> Payment Method
|
||
</label>
|
||
<select name="payment_method"
|
||
class="form-select-compact"
|
||
required>
|
||
<option value="cash">Cash</option>
|
||
<option value="bank">Bank Transfer</option>
|
||
<option value="upi">UPI</option>
|
||
<option value="cheque">Cheque</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="form-group-compact">
|
||
<label class="form-label-compact">
|
||
<i class="fas fa-hashtag"></i> Reference No
|
||
</label>
|
||
<input type="text"
|
||
name="reference_no"
|
||
class="form-control-compact"
|
||
placeholder="Enter reference number">
|
||
</div>
|
||
|
||
<div class="form-group-compact">
|
||
<label class="form-label-compact">
|
||
<i class="fas fa-money-bill-wave"></i> Amount
|
||
</label>
|
||
<input type="number"
|
||
name="amount"
|
||
id="installmentAmount"
|
||
class="form-control-compact"
|
||
step="0.01"
|
||
min="1"
|
||
max="{{ max(0, $remaining) }}"
|
||
required
|
||
placeholder="Enter installment amount">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="text-end mt-2">
|
||
<button type="submit"
|
||
class="btn-success-compact btn-compact"
|
||
id="installmentSubmitBtn">
|
||
<i class="fas fa-paper-plane me-2"></i>Submit Installment
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{{-- Installment History --}}
|
||
<h6 class="fw-bold mb-2 text-dark">
|
||
<i class="fas fa-history me-2"></i>Installment History
|
||
</h6>
|
||
|
||
<div id="noInstallmentsMsg"
|
||
class="{{ $invoice->installments->count() ? 'd-none' : '' }} text-center text-muted fw-bold py-4">
|
||
No installments found. Click Add Installment to create one.
|
||
</div>
|
||
|
||
<div class="table-responsive">
|
||
<table class="table-compact">
|
||
<thead>
|
||
<tr>
|
||
<th>#</th>
|
||
<th>Date</th>
|
||
<th>Payment Method</th>
|
||
<th>Reference No</th>
|
||
<th>Amount</th>
|
||
<th>Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody id="installmentTable">
|
||
@foreach($invoice->installments as $i)
|
||
<tr data-id="{{ $i->id }}">
|
||
<td class="fw-bold text-muted">{{ $loop->iteration }}</td>
|
||
<td>{{ $i->installment_date }}</td>
|
||
<td>
|
||
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
|
||
{{ strtoupper($i->payment_method) }}
|
||
</span>
|
||
</td>
|
||
<td>
|
||
@if($i->reference_no)
|
||
<span class="text-muted">{{ $i->reference_no }}</span>
|
||
@else
|
||
<span class="text-muted">-</span>
|
||
@endif
|
||
</td>
|
||
<td class="fw-bold text-success">
|
||
₹{{ number_format($i->amount, 2) }}
|
||
</td>
|
||
<td>
|
||
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
|
||
<i class="fas fa-trash me-1"></i>Delete
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
@endforeach
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener("DOMContentLoaded", function () {
|
||
const toggleBtn = document.getElementById("toggleInstallmentForm");
|
||
const formBox = document.getElementById("installmentForm");
|
||
|
||
if (toggleBtn && formBox) {
|
||
toggleBtn.addEventListener("click", function (e) {
|
||
e.preventDefault();
|
||
formBox.classList.toggle("d-none");
|
||
});
|
||
}
|
||
|
||
const submitForm = document.getElementById("installmentSubmitForm");
|
||
const submitBtn = document.getElementById("installmentSubmitBtn");
|
||
|
||
const formatINR = (amt) => {
|
||
return Number(amt).toLocaleString("en-IN", {
|
||
minimumFractionDigits: 2,
|
||
maximumFractionDigits: 2
|
||
});
|
||
};
|
||
|
||
if (submitForm && submitBtn) {
|
||
submitForm.addEventListener("submit", function (e) {
|
||
e.preventDefault();
|
||
|
||
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
|
||
submitBtn.disabled = true;
|
||
|
||
fetch("{{ route('admin.invoice.installment.store', $invoice->id) }}", {
|
||
method: "POST",
|
||
headers: {
|
||
"X-CSRF-TOKEN": submitForm.querySelector('input[name="_token"]').value,
|
||
"Accept": "application/json",
|
||
"X-Requested-With": "XMLHttpRequest",
|
||
},
|
||
body: new FormData(submitForm)
|
||
})
|
||
.then(async res => {
|
||
let data;
|
||
try {
|
||
data = await res.json();
|
||
} catch (e) {
|
||
throw new Error("Invalid server response.");
|
||
}
|
||
|
||
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
|
||
submitBtn.disabled = false;
|
||
|
||
if (!res.ok) {
|
||
const msg =
|
||
(data && data.message) ||
|
||
(data && data.errors && Object.values(data.errors)[0][0]) ||
|
||
"Something went wrong.";
|
||
alert(msg);
|
||
return;
|
||
}
|
||
|
||
if (data.status === "error") {
|
||
alert(data.message || "Something went wrong.");
|
||
return;
|
||
}
|
||
|
||
const table = document.getElementById("installmentTable");
|
||
const index = table.querySelectorAll("tr").length + 1;
|
||
|
||
document.getElementById("noInstallmentsMsg")?.classList.add("d-none");
|
||
|
||
const pmMethod = data.installment.payment_method
|
||
? data.installment.payment_method.toUpperCase()
|
||
: "-";
|
||
const refNo = data.installment.reference_no
|
||
? `<span class="text-muted">${data.installment.reference_no}</span>`
|
||
: '<span class="text-muted">-</span>';
|
||
|
||
table.insertAdjacentHTML("beforeend", `
|
||
<tr data-id="${data.installment.id}">
|
||
<td class="fw-bold text-muted">${index}</td>
|
||
<td>${data.installment.installment_date}</td>
|
||
<td>
|
||
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
|
||
${pmMethod}
|
||
</span>
|
||
</td>
|
||
<td>${refNo}</td>
|
||
<td class="fw-bold text-success">
|
||
₹${formatINR(data.installment.amount)}
|
||
</td>
|
||
<td>
|
||
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
|
||
<i class="fas fa-trash me-1"></i>Delete
|
||
</button>
|
||
</td>
|
||
</tr>
|
||
`);
|
||
|
||
if (document.getElementById("paidAmount")) {
|
||
document.getElementById("paidAmount").textContent = "₹" + formatINR(data.totalPaid);
|
||
}
|
||
if (document.getElementById("remainingAmount")) {
|
||
document.getElementById("remainingAmount").textContent = "₹" + formatINR(data.remaining);
|
||
}
|
||
if (document.getElementById("baseAmount")) {
|
||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.chargeGroupsTotal);
|
||
}
|
||
if (document.getElementById("gstAmount")) {
|
||
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
||
}
|
||
if (document.getElementById("totalInvoiceWithGst")) {
|
||
document.getElementById("totalInvoiceWithGst").textContent =
|
||
"₹" + formatINR(data.grandTotal);
|
||
}
|
||
const totalCard = document.getElementById("totalInvoiceWithGstCard");
|
||
if (totalCard) {
|
||
totalCard.textContent = "₹" + formatINR(data.grandTotal);
|
||
}
|
||
|
||
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
||
if (paidCard) paidCard.textContent = "₹" + formatINR(data.totalPaid);
|
||
|
||
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
|
||
if (remainingCard) remainingCard.textContent = "₹" + formatINR(data.remaining);
|
||
|
||
const fsTotal = document.getElementById("finalSummaryTotalPaid");
|
||
if (fsTotal) fsTotal.textContent = "₹" + formatINR(data.totalPaid);
|
||
|
||
const fsRemaining = document.getElementById("finalSummaryRemaining");
|
||
if (fsRemaining) {
|
||
fsRemaining.textContent = "₹" + formatINR(data.remaining);
|
||
fsRemaining.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
|
||
}
|
||
const fsRemainingLabel = document.getElementById("finalSummaryRemainingLabel");
|
||
if (fsRemainingLabel) {
|
||
fsRemainingLabel.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
|
||
fsRemainingLabel.innerHTML = `<i class="fas fa-${data.remaining > 0 ? 'exclamation-circle' : 'check-circle'} me-1" style="font-size:0.8rem;"></i>Remaining`;
|
||
}
|
||
|
||
if (data.newStatus && typeof updateStatusBadge === "function") {
|
||
updateStatusBadge(data.newStatus);
|
||
}
|
||
|
||
submitForm.reset();
|
||
|
||
if (data.isCompleted && toggleBtn) {
|
||
toggleBtn.remove();
|
||
formBox.classList.add("d-none");
|
||
}
|
||
|
||
alert(data.message || "Installment added successfully.");
|
||
})
|
||
.catch((err) => {
|
||
console.error("Installment submit error:", err);
|
||
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
|
||
submitBtn.disabled = false;
|
||
alert(err.message || "Something went wrong. Please try again.");
|
||
});
|
||
});
|
||
}
|
||
|
||
document.addEventListener("click", function (e) {
|
||
if (!e.target.classList.contains("deleteInstallment") &&
|
||
!e.target.closest(".deleteInstallment")) {
|
||
return;
|
||
}
|
||
e.preventDefault();
|
||
|
||
if (!confirm("Are you sure you want to delete this installment?")) {
|
||
return;
|
||
}
|
||
|
||
const btn = e.target.closest(".deleteInstallment");
|
||
const row = btn.closest("tr");
|
||
const id = row.getAttribute("data-id");
|
||
|
||
fetch("{{ url('admin/installment') }}/" + id, {
|
||
method: "DELETE",
|
||
headers: {
|
||
"X-CSRF-TOKEN": document.querySelector('meta[name="csrf-token"]').getAttribute("content"),
|
||
"Accept": "application/json",
|
||
"X-Requested-With": "XMLHttpRequest",
|
||
},
|
||
})
|
||
.then(async res => {
|
||
let data;
|
||
try { data = await res.json(); } catch(e) { throw new Error("Invalid server response."); }
|
||
|
||
if (!res.ok || data.status !== "success") {
|
||
const msg = data && data.message ? data.message : "Something went wrong. Please try again.";
|
||
throw new Error(msg);
|
||
}
|
||
|
||
row.style.opacity = 0;
|
||
setTimeout(() => row.remove(), 300);
|
||
|
||
if (document.getElementById("paidAmount")) {
|
||
document.getElementById("paidAmount").textContent = "₹" + formatINR(data.totalPaid);
|
||
}
|
||
if (document.getElementById("remainingAmount")) {
|
||
document.getElementById("remainingAmount").textContent = "₹" + formatINR(data.remaining);
|
||
}
|
||
if (document.getElementById("baseAmount")) {
|
||
document.getElementById("baseAmount").textContent = "₹" + formatINR(data.chargeGroupsTotal);
|
||
}
|
||
if (document.getElementById("gstAmount")) {
|
||
document.getElementById("gstAmount").textContent = "₹" + formatINR(data.gstAmount);
|
||
}
|
||
if (document.getElementById("totalInvoiceWithGst")) {
|
||
document.getElementById("totalInvoiceWithGst").textContent =
|
||
"₹" + formatINR(data.grandTotal);
|
||
}
|
||
const totalCard = document.getElementById("totalInvoiceWithGstCard");
|
||
if (totalCard) {
|
||
totalCard.textContent = "₹" + formatINR(data.grandTotal);
|
||
}
|
||
|
||
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
||
if (paidCard) paidCard.textContent = "₹" + formatINR(data.totalPaid);
|
||
|
||
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
|
||
if (remainingCard) remainingCard.textContent = "₹" + formatINR(data.remaining);
|
||
|
||
const fsTotal = document.getElementById("finalSummaryTotalPaid");
|
||
if (fsTotal) fsTotal.textContent = "₹" + formatINR(data.totalPaid);
|
||
|
||
const fsRemaining = document.getElementById("finalSummaryRemaining");
|
||
if (fsRemaining) {
|
||
fsRemaining.textContent = "₹" + formatINR(data.remaining);
|
||
fsRemaining.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
|
||
}
|
||
const fsRemainingLabel = document.getElementById("finalSummaryRemainingLabel");
|
||
if (fsRemainingLabel) {
|
||
fsRemainingLabel.style.color = data.remaining > 0 ? "#ef4444" : "#10b981";
|
||
fsRemainingLabel.innerHTML = `<i class="fas fa-${data.remaining > 0 ? 'exclamation-circle' : 'check-circle'} me-1" style="font-size:0.8rem;"></i>Remaining`;
|
||
}
|
||
|
||
if (data.newStatus && typeof updateStatusBadge === "function") {
|
||
updateStatusBadge(data.newStatus);
|
||
}
|
||
|
||
if (data.isZero) {
|
||
document.getElementById("noInstallmentsMsg")?.classList.remove("d-none");
|
||
}
|
||
|
||
alert(data.message || "Installment deleted.");
|
||
})
|
||
.catch(err => {
|
||
alert(err.message || "Something went wrong. Please try again.");
|
||
});
|
||
});
|
||
|
||
// Header AJAX (सध्या card hidden आहे, तरीही ठेवलेलं)
|
||
const headerForm = document.getElementById('invoiceHeaderForm');
|
||
const headerBtn = document.getElementById('btnHeaderSave');
|
||
const headerMsg = document.getElementById('headerUpdateMsg');
|
||
|
||
if (headerForm && headerBtn) {
|
||
headerForm.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
headerMsg.textContent = '';
|
||
headerBtn.disabled = true;
|
||
headerBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Saving...';
|
||
|
||
const formData = new FormData(headerForm);
|
||
|
||
fetch(headerForm.action, {
|
||
method: 'POST',
|
||
headers: {
|
||
'X-CSRF-TOKEN': '{{ csrf_token() }}',
|
||
'Accept': 'application/json',
|
||
'X-Requested-With': 'XMLHttpRequest',
|
||
},
|
||
body: formData
|
||
})
|
||
.then(async res => {
|
||
let data;
|
||
try { data = await res.json(); } catch(e) {}
|
||
|
||
if (!res.ok) {
|
||
if (data && data.errors) {
|
||
const firstError = Object.values(data.errors)[0][0] ?? 'Validation error.';
|
||
throw new Error(firstError);
|
||
}
|
||
throw new Error(data && data.message ? data.message : 'Failed to update invoice.');
|
||
}
|
||
|
||
headerMsg.textContent = 'Invoice header updated.';
|
||
headerMsg.classList.remove('text-danger');
|
||
headerMsg.classList.add('text-light');
|
||
|
||
const status = document.getElementById('statusSelect')?.value;
|
||
const badge = document.querySelector('.status-badge');
|
||
if (badge && status) {
|
||
badge.classList.remove('status-paid','status-pending','status-overdue','status-default');
|
||
if (status === 'paid') badge.classList.add('status-paid');
|
||
else if (status === 'pending') badge.classList.add('status-pending');
|
||
else if (status === 'overdue') badge.classList.add('status-overdue');
|
||
else badge.classList.add('status-default');
|
||
|
||
badge.innerHTML = status.charAt(0).toUpperCase() + status.slice(1);
|
||
}
|
||
})
|
||
.catch(err => {
|
||
headerMsg.textContent = err.message || 'Error updating invoice.';
|
||
headerMsg.classList.remove('text-light');
|
||
headerMsg.classList.add('text-warning');
|
||
})
|
||
.finally(() => {
|
||
headerBtn.disabled = false;
|
||
headerBtn.innerHTML = '<i class="fas fa-save me-2"></i>Update Invoice';
|
||
});
|
||
});
|
||
}
|
||
});
|
||
</script>
|
||
@endsection
|