1115 lines
40 KiB
PHP
1115 lines
40 KiB
PHP
@extends('admin.layouts.app')
|
|
|
|
@section('page-title', 'Edit Invoice')
|
|
|
|
@section('content')
|
|
<style>
|
|
/* --------------------------------------------------
|
|
GLOBAL VARIABLES & THEME
|
|
-------------------------------------------------- */
|
|
: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;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
GLOBAL STYLES & ANIMATIONS
|
|
-------------------------------------------------- */
|
|
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); }
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
INVOICE PREVIEW RESPONSIVE FIXES
|
|
-------------------------------------------------- */
|
|
.invoice-preview-wrapper {
|
|
width: 100%;
|
|
overflow: auto;
|
|
max-width: 100%;
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
.invoice-preview-wrapper * {
|
|
box-sizing: border-box;
|
|
}
|
|
|
|
/* Override any fixed width styles that might be in popup_invoice */
|
|
#invoicePreview,
|
|
.invoice-container,
|
|
.invoice-wrapper {
|
|
max-width: 100% !important;
|
|
width: 100% !important;
|
|
}
|
|
|
|
/* Responsive table fixes for invoice */
|
|
.invoice-preview-wrapper table {
|
|
width: 100% !important;
|
|
max-width: 100% !important;
|
|
table-layout: auto !important;
|
|
}
|
|
|
|
.invoice-preview-wrapper .table-responsive {
|
|
overflow-x: auto !important;
|
|
-webkit-overflow-scrolling: touch;
|
|
}
|
|
|
|
/* Ensure all elements scale properly */
|
|
.invoice-preview-wrapper .row,
|
|
.invoice-preview-wrapper .col,
|
|
.invoice-preview-wrapper [class*="col-"] {
|
|
flex: 1 1 auto !important;
|
|
max-width: 100% !important;
|
|
}
|
|
|
|
/* Force responsive behavior for print-style elements */
|
|
@media (max-width: 1200px) {
|
|
.invoice-preview-wrapper {
|
|
font-size: 95%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 992px) {
|
|
.invoice-preview-wrapper {
|
|
font-size: 90%;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.invoice-preview-wrapper {
|
|
font-size: 85%;
|
|
}
|
|
|
|
.invoice-preview-wrapper table th,
|
|
.invoice-preview-wrapper table td {
|
|
padding: 0.5rem !important;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 576px) {
|
|
.invoice-preview-wrapper {
|
|
font-size: 80%;
|
|
}
|
|
|
|
.invoice-preview-wrapper .d-flex {
|
|
flex-direction: column !important;
|
|
}
|
|
|
|
.invoice-preview-wrapper .text-end,
|
|
.invoice-preview-wrapper .text-start {
|
|
text-align: center !important;
|
|
}
|
|
}
|
|
|
|
/* Prevent any fixed pixel widths */
|
|
.invoice-preview-wrapper [style*="width:"]:not([style*="width:100%"]):not([style*="width:auto"]) {
|
|
width: auto !important;
|
|
max-width: 100% !important;
|
|
}
|
|
|
|
.invoice-preview-wrapper [style*="min-width"] {
|
|
min-width: 0 !important;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT CARD DESIGN
|
|
-------------------------------------------------- */
|
|
.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;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT CARD HEADER
|
|
-------------------------------------------------- */
|
|
.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);
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT CARD BODY
|
|
-------------------------------------------------- */
|
|
.card-body-compact {
|
|
padding: 1.5rem;
|
|
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
|
position: relative;
|
|
z-index: 1;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT FORM ELEMENTS
|
|
-------------------------------------------------- */
|
|
.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: white;
|
|
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;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT BUTTONS
|
|
-------------------------------------------------- */
|
|
.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;
|
|
}
|
|
|
|
.btn-info-compact {
|
|
background: linear-gradient(135deg, #06b6d4 0%, #0ea5e9 100%);
|
|
color: white;
|
|
box-shadow: 0 4px 12px rgba(6, 182, 212, 0.3);
|
|
}
|
|
|
|
.btn-info-compact:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(6, 182, 212, 0.4);
|
|
color: white;
|
|
}
|
|
|
|
.btn-warning-compact {
|
|
background: var(--warning-gradient);
|
|
color: white;
|
|
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
|
|
}
|
|
|
|
.btn-warning-compact:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 6px 20px rgba(245, 158, 11, 0.4);
|
|
color: white;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT SUMMARY CARDS
|
|
-------------------------------------------------- */
|
|
.summary-grid-compact {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 1rem;
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.summary-card-compact {
|
|
background: white;
|
|
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;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT AMOUNT BREAKDOWN
|
|
-------------------------------------------------- */
|
|
.amount-breakdown-compact {
|
|
background: white;
|
|
padding: 1.5rem;
|
|
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;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
COMPACT TABLE
|
|
-------------------------------------------------- */
|
|
.table-compact {
|
|
width: 100%;
|
|
border-collapse: collapse;
|
|
margin-bottom: 0;
|
|
font-size: 0.85rem;
|
|
}
|
|
|
|
.table-compact thead th {
|
|
background: var(--primary-gradient);
|
|
color: white;
|
|
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: white;
|
|
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 STYLES
|
|
-------------------------------------------------- */
|
|
.badge-compact {
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 20px;
|
|
font-size: 0.75rem;
|
|
font-weight: 600;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
HEADER ACTION BUTTONS
|
|
-------------------------------------------------- */
|
|
.header-actions {
|
|
display: flex;
|
|
gap: 0.75rem;
|
|
align-items: center;
|
|
}
|
|
|
|
/* --------------------------------------------------
|
|
RESPONSIVE DESIGN
|
|
-------------------------------------------------- */
|
|
@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;
|
|
}
|
|
|
|
.header-actions {
|
|
flex-direction: column;
|
|
width: 100%;
|
|
gap: 0.5rem;
|
|
margin-top: 0.5rem;
|
|
}
|
|
}
|
|
|
|
@media print {
|
|
.invoice-preview-wrapper {
|
|
max-width: 100% !important;
|
|
width: 100% !important;
|
|
overflow: visible !important;
|
|
}
|
|
|
|
.invoice-preview-wrapper * {
|
|
visibility: visible !important;
|
|
}
|
|
|
|
.glass-card {
|
|
box-shadow: none !important;
|
|
border: 1px solid #000 !important;
|
|
}
|
|
|
|
.card-header-compact {
|
|
background: #000 !important;
|
|
color: #fff !important;
|
|
-webkit-print-color-adjust: exact;
|
|
print-color-adjust: exact;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<!-- Main Container -->
|
|
<div class="container-fluid py-3">
|
|
|
|
<!-- Invoice Preview Section -->
|
|
<div class="glass-card">
|
|
<div class="card-header-compact d-flex justify-content-between align-items-center">
|
|
<h4>
|
|
<i class="fas fa-file-invoice me-2"></i>Invoice Overview
|
|
</h4>
|
|
<div class="header-actions">
|
|
<!-- Share Invoice -->
|
|
<button class="btn-info-compact btn-compact" id="shareInvoiceBtn">
|
|
<i class="fas fa-share-alt me-2"></i>Share Invoice
|
|
</button>
|
|
<!-- Download Invoice -->
|
|
<a href="{{ route('admin.invoices.download', $invoice->id) }}"
|
|
class="btn-warning-compact btn-compact" target="_blank">
|
|
<i class="fas fa-download me-2"></i>Download Invoice
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body-compact invoice-preview-wrapper">
|
|
@include('admin.popup_invoice', [
|
|
'invoice' => $invoice,
|
|
'shipment' => $shipment,
|
|
'embedded' => true
|
|
])
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Amount Breakdown -->
|
|
<div class="glass-card">
|
|
<div class="card-header-compact">
|
|
<h4>
|
|
<i class="fas fa-calculator me-2"></i>Amount Breakdown
|
|
</h4>
|
|
</div>
|
|
<div class="card-body-compact">
|
|
<div class="amount-breakdown-compact">
|
|
@php
|
|
$totalPaid = $invoice->installments->sum('amount');
|
|
$remaining = $invoice->final_amount_with_gst - $totalPaid;
|
|
|
|
// Calculate GST/IGST percentage dynamically
|
|
$taxPercentage = $invoice->tax_type === 'gst'
|
|
? ($invoice->cgst_percent + $invoice->sgst_percent)
|
|
: $invoice->igst_percent;
|
|
@endphp
|
|
|
|
<div class="breakdown-row">
|
|
<span class="breakdown-label">Total Amount (Before Tax):</span>
|
|
<span class="breakdown-value">₹{{ number_format($invoice->final_amount, 2) }}</span>
|
|
</div>
|
|
|
|
<div class="breakdown-row">
|
|
<span class="breakdown-label">
|
|
@if($invoice->tax_type === 'gst')
|
|
GST ({{ number_format($taxPercentage, 2) }}%)
|
|
@else
|
|
IGST ({{ number_format($taxPercentage, 2) }}%)
|
|
@endif:
|
|
</span>
|
|
<span class="breakdown-value text-warning">₹{{ number_format($invoice->gst_amount, 2) }}</span>
|
|
</div>
|
|
|
|
<div class="breakdown-row" style="border-top: 2px solid #e2e8f0; padding-top: 0.75rem;">
|
|
<span class="breakdown-label fw-bold">Total Invoice Amount (Including GST):</span>
|
|
<span class="breakdown-value fw-bold text-dark">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</span>
|
|
</div>
|
|
|
|
<div class="breakdown-row">
|
|
<span class="breakdown-label text-success">Total Paid:</span>
|
|
<span class="breakdown-value fw-bold text-success" id="paidAmount">₹{{ number_format($totalPaid, 2) }}</span>
|
|
</div>
|
|
|
|
<div class="breakdown-row" style="border-bottom: none;">
|
|
<span class="breakdown-label text-danger">Remaining:</span>
|
|
<span class="breakdown-value fw-bold text-danger" id="remainingAmount">₹{{ number_format($remaining, 2) }}</span>
|
|
</div>
|
|
|
|
<!-- NEW: Customer's Total Amount Due Across All Invoices -->
|
|
<div class="breakdown-row" style="border-top: 2px solid #3b82f6; background: rgba(59, 130, 246, 0.05);">
|
|
<span class="breakdown-label fw-bold text-primary">
|
|
<i class="fas fa-users me-1"></i>Total Amount Due (Including Tax):
|
|
<small class="d-block text-muted" style="font-size: 0.75rem; font-weight: normal;">
|
|
</small>
|
|
</span>
|
|
<span class="breakdown-value fw-bold text-primary" style="font-size: 1.1rem;">
|
|
₹{{ number_format($customerTotalDue, 2) }}
|
|
</span>
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
<!-- Installment Summary -->
|
|
<div class="summary-grid-compact mt-3">
|
|
<div class="summary-card-compact total">
|
|
<div class="summary-value-compact text-success">₹{{ number_format($invoice->final_amount_with_gst, 2) }}</div>
|
|
<div class="summary-label-compact">Total Amount</div>
|
|
</div>
|
|
<div class="summary-card-compact paid">
|
|
<div class="summary-value-compact text-primary">₹{{ number_format($totalPaid, 2) }}</div>
|
|
<div class="summary-label-compact">Total Paid</div>
|
|
</div>
|
|
<div class="summary-card-compact remaining">
|
|
<div class="summary-value-compact text-warning">₹{{ number_format($remaining, 2) }}</div>
|
|
<div class="summary-label-compact">Remaining</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Edit Invoice Form -->
|
|
<div class="glass-card">
|
|
<div class="card-header-compact">
|
|
<h4>
|
|
<i class="fas fa-edit me-2"></i>Edit Invoice Details
|
|
</h4>
|
|
</div>
|
|
<div class="card-body-compact">
|
|
<form action="{{ route('admin.invoices.update', $invoice->id) }}" method="POST">
|
|
@csrf
|
|
|
|
<div class="form-grid-compact">
|
|
<!-- Invoice Date -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-calendar-day"></i>
|
|
Invoice Date
|
|
</label>
|
|
<input type="date" class="form-control-compact" name="invoice_date"
|
|
value="{{ $invoice->invoice_date }}" required>
|
|
</div>
|
|
|
|
<!-- Due Date -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-clock"></i>
|
|
Due Date
|
|
</label>
|
|
<input type="date" class="form-control-compact" name="due_date"
|
|
value="{{ $invoice->due_date }}" required>
|
|
</div>
|
|
|
|
<!-- Final Amount -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-money-bill-wave"></i>
|
|
Final Amount (₹)
|
|
</label>
|
|
<input type="number" step="0.01" class="form-control-compact" name="final_amount"
|
|
value="{{ $invoice->final_amount }}" required>
|
|
</div>
|
|
|
|
<!-- Tax Type -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-receipt"></i>
|
|
Tax Type
|
|
</label>
|
|
<div class="d-flex gap-3 mt-1">
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="tax_type"
|
|
value="gst" @checked($invoice->tax_type === 'gst')>
|
|
<label class="form-check-label fw-semibold">GST</label>
|
|
</div>
|
|
<div class="form-check">
|
|
<input class="form-check-input" type="radio" name="tax_type"
|
|
value="igst" @checked($invoice->tax_type === 'igst')>
|
|
<label class="form-check-label fw-semibold">IGST</label>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Tax Percentage -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-percentage"></i>
|
|
Tax Percentage (%)
|
|
</label>
|
|
<input type="number" step="0.01" min="0" max="28" class="form-control-compact"
|
|
name="tax_percent"
|
|
value="{{ old('tax_percent', $invoice->tax_type == 'gst' ? $invoice->cgst_percent + $invoice->sgst_percent : $invoice->igst_percent) }}"
|
|
required>
|
|
</div>
|
|
|
|
<!-- Status -->
|
|
<div class="form-group-compact">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-tasks"></i>
|
|
Status
|
|
</label>
|
|
<select class="form-select-compact" name="status" required>
|
|
<option value="pending" @selected($invoice->status=='pending')>⏳ Pending</option>
|
|
<option value="paid" @selected($invoice->status=='paid')>✅ Paid</option>
|
|
<option value="overdue" @selected($invoice->status=='overdue')>⚠️ Overdue</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Notes -->
|
|
<div class="form-group-compact" style="grid-column: 1 / -1;">
|
|
<label class="form-label-compact">
|
|
<i class="fas fa-sticky-note"></i>
|
|
Notes
|
|
</label>
|
|
<textarea class="form-control-compact" rows="3" name="notes"
|
|
placeholder="Add any additional notes...">{{ $invoice->notes }}</textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-end mt-3">
|
|
@can('invoice.edit')
|
|
<button type="submit" class="btn-success-compact btn-compact">
|
|
<i class="fas fa-save me-2"></i>Update Invoice
|
|
</button>
|
|
@endcan
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Installment Management -->
|
|
<div class="glass-card">
|
|
<div class="card-header-compact d-flex justify-content-between align-items-center">
|
|
<h4>
|
|
<i class="fas fa-credit-card me-2"></i>Installment Payments
|
|
</h4>
|
|
@can('invoice.add_installment')
|
|
@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
|
|
@endcan
|
|
</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>
|
|
<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="{{ $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>
|
|
@if($invoice->installments->count() > 0)
|
|
<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">
|
|
{{ ucfirst($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>
|
|
@else
|
|
<div id="noInstallmentsMsg" class="text-center text-muted fw-bold py-4">
|
|
No installments found. Click "Add Installment" to create one.
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener("DOMContentLoaded", function () {
|
|
// Share Invoice Functionality
|
|
const shareInvoiceBtn = document.getElementById('shareInvoiceBtn');
|
|
if (shareInvoiceBtn) {
|
|
shareInvoiceBtn.addEventListener('click', function() {
|
|
const shareUrl = window.location.href;
|
|
const invoiceNo = "{{ $invoice->invoice_no }}";
|
|
const message = `Invoice #${invoiceNo} - Total: ₹{{ number_format($invoice->final_amount_with_gst, 2) }}\nView: ${shareUrl}`;
|
|
|
|
if (navigator.share) {
|
|
navigator.share({
|
|
title: `Invoice #${invoiceNo}`,
|
|
text: `Invoice #${invoiceNo} - Total: ₹{{ number_format($invoice->final_amount_with_gst, 2) }}`,
|
|
url: shareUrl
|
|
}).catch(console.error);
|
|
} else if (navigator.clipboard) {
|
|
navigator.clipboard.writeText(message)
|
|
.then(() => {
|
|
alert('Invoice link copied to clipboard!');
|
|
})
|
|
.catch(err => {
|
|
console.error('Failed to copy: ', err);
|
|
// Fallback to prompt
|
|
prompt('Copy this link to share:', shareUrl);
|
|
});
|
|
} else {
|
|
prompt('Copy this link to share:', shareUrl);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Print Invoice Function
|
|
window.printInvoice = function() {
|
|
const printWindow = window.open('', '_blank');
|
|
const invoiceContent = document.querySelector('.invoice-preview-wrapper').innerHTML;
|
|
const printContent = `
|
|
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<title>Invoice {{ $invoice->invoice_no }}</title>
|
|
<style>
|
|
body { font-family: Arial, sans-serif; margin: 20px; }
|
|
.invoice-container { max-width: 800px; margin: 0 auto; }
|
|
.text-end { text-align: right; }
|
|
.fw-bold { font-weight: bold; }
|
|
table { width: 100%; border-collapse: collapse; }
|
|
th, td { padding: 8px; border: 1px solid #ddd; }
|
|
th { background-color: #f8f9fa; }
|
|
.total-row { background-color: #f8f9fa; font-weight: bold; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="invoice-container">
|
|
${invoiceContent}
|
|
</div>
|
|
<script>
|
|
window.onload = function() {
|
|
window.print();
|
|
setTimeout(function() {
|
|
window.close();
|
|
}, 500);
|
|
};
|
|
<\/script>
|
|
</body>
|
|
</html>
|
|
`;
|
|
|
|
printWindow.document.write(printContent);
|
|
printWindow.document.close();
|
|
};
|
|
|
|
// Toggle Installment Form
|
|
const toggleBtn = document.getElementById("toggleInstallmentForm");
|
|
const formBox = document.getElementById("installmentForm");
|
|
|
|
if (toggleBtn) {
|
|
toggleBtn.addEventListener("click", () => {
|
|
formBox.classList.toggle("d-none");
|
|
});
|
|
}
|
|
|
|
// Add Installment
|
|
const submitForm = document.getElementById("installmentSubmitForm");
|
|
const submitBtn = document.getElementById("installmentSubmitBtn");
|
|
|
|
const formatINR = amt =>
|
|
"₹" + Number(amt).toLocaleString("en-IN", {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2
|
|
});
|
|
|
|
if (submitForm) {
|
|
submitForm.addEventListener("submit", function (e) {
|
|
e.preventDefault();
|
|
|
|
submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Processing...';
|
|
submitBtn.disabled = true;
|
|
|
|
fetch("{{ route('admin.invoice.installment.store', $invoice->id) }}", {
|
|
method: "POST",
|
|
headers: {
|
|
"X-CSRF-TOKEN": submitForm.querySelector("input[name=_token]").value,
|
|
"Accept": "application/json"
|
|
},
|
|
body: new FormData(submitForm)
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
|
|
submitBtn.disabled = false;
|
|
|
|
if (data.status === "error") {
|
|
alert(data.message);
|
|
return;
|
|
}
|
|
|
|
const table = document.querySelector("#installmentTable");
|
|
const noInstallmentsMsg = document.getElementById("noInstallmentsMsg");
|
|
|
|
if (noInstallmentsMsg) {
|
|
noInstallmentsMsg.classList.add("d-none");
|
|
}
|
|
|
|
if (!table) {
|
|
// Create table if it doesn't exist
|
|
const tableHTML = `
|
|
<div class="table-responsive">
|
|
<table class="table-compact">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Date</th>
|
|
<th>Payment Method</th>
|
|
<th>Reference No</th>
|
|
<th>Amount</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="installmentTable">
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
`;
|
|
const parent = submitForm.closest('.card-body-compact');
|
|
parent.insertAdjacentHTML('beforeend', tableHTML);
|
|
}
|
|
|
|
const newTable = document.querySelector("#installmentTable");
|
|
const index = newTable.rows.length + 1;
|
|
|
|
newTable.insertAdjacentHTML("beforeend", `
|
|
<tr data-id="${data.installment.id}">
|
|
<td class="fw-bold text-muted">${index}</td>
|
|
<td>${data.installment.installment_date}</td>
|
|
<td>
|
|
<span class="badge-compact bg-primary bg-opacity-10 text-primary">
|
|
${data.installment.payment_method.toUpperCase()}
|
|
</span>
|
|
</td>
|
|
<td>${data.installment.reference_no || '-'}</td>
|
|
<td class="fw-bold text-success">${formatINR(data.installment.amount)}</td>
|
|
<td>
|
|
<button class="btn-danger-compact btn-compact btn-sm deleteInstallment">
|
|
<i class="fas fa-trash me-1"></i>Delete
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
`);
|
|
|
|
// Update all displayed values
|
|
if (document.getElementById("paidAmount")) document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
|
|
if (document.getElementById("remainingAmount")) document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
|
|
|
|
// Update summary cards
|
|
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
|
if (paidCard) paidCard.textContent = formatINR(data.totalPaid);
|
|
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
|
|
if (remainingCard) remainingCard.textContent = formatINR(data.remaining);
|
|
|
|
submitForm.reset();
|
|
|
|
// If fully paid, disable/add display logic
|
|
if (data.isCompleted) {
|
|
toggleBtn?.remove();
|
|
formBox.classList.add("d-none");
|
|
}
|
|
|
|
alert(data.message);
|
|
})
|
|
.catch(() => {
|
|
submitBtn.innerHTML = '<i class="fas fa-paper-plane me-2"></i>Submit Installment';
|
|
submitBtn.disabled = false;
|
|
alert("Something went wrong. Please try again.");
|
|
});
|
|
});
|
|
}
|
|
|
|
// Delete Installment
|
|
document.addEventListener("click", function (e) {
|
|
if (!e.target.classList.contains("deleteInstallment")) return;
|
|
|
|
if (!confirm("Are you sure you want to delete this installment?")) return;
|
|
|
|
const row = e.target.closest("tr");
|
|
const id = row.getAttribute("data-id");
|
|
|
|
fetch("{{ url('/admin/installment') }}/" + id, {
|
|
method: "DELETE",
|
|
headers: {
|
|
"X-CSRF-TOKEN": "{{ csrf_token() }}",
|
|
"Accept": "application/json"
|
|
}
|
|
})
|
|
.then(res => res.json())
|
|
.then(data => {
|
|
if (data.status === "success") {
|
|
row.style.opacity = "0";
|
|
setTimeout(() => {
|
|
row.remove();
|
|
|
|
// Update row numbers
|
|
const rows = document.querySelectorAll("#installmentTable tr");
|
|
rows.forEach((row, index) => {
|
|
row.querySelector("td:first-child").textContent = index + 1;
|
|
});
|
|
|
|
// Show no installments message if empty
|
|
if (rows.length === 0) {
|
|
const noInstallmentsMsg = document.getElementById("noInstallmentsMsg");
|
|
if (noInstallmentsMsg) {
|
|
noInstallmentsMsg.classList.remove("d-none");
|
|
}
|
|
}
|
|
}, 300);
|
|
|
|
// Update all displayed values
|
|
if (document.getElementById("paidAmount")) document.getElementById("paidAmount").textContent = formatINR(data.totalPaid);
|
|
if (document.getElementById("remainingAmount")) document.getElementById("remainingAmount").textContent = formatINR(data.remaining);
|
|
|
|
// Update summary cards
|
|
const paidCard = document.querySelector(".summary-card-compact.paid .summary-value-compact");
|
|
if (paidCard) paidCard.textContent = formatINR(data.totalPaid);
|
|
const remainingCard = document.querySelector(".summary-card-compact.remaining .summary-value-compact");
|
|
if (remainingCard) remainingCard.textContent = formatINR(data.remaining);
|
|
|
|
// Show add installment button if there's remaining amount
|
|
if (data.remaining > 0 && !document.getElementById("toggleInstallmentForm")) {
|
|
const header = document.querySelector(".glass-card .card-header-compact");
|
|
header.innerHTML += `
|
|
<button id="toggleInstallmentForm" class="btn-primary-compact btn-compact">
|
|
<i class="fas fa-plus-circle me-2"></i>Add Installment
|
|
</button>
|
|
`;
|
|
}
|
|
|
|
alert(data.message);
|
|
} else {
|
|
alert(data.message || "Something went wrong. Please try again.");
|
|
}
|
|
})
|
|
.catch(() => {
|
|
alert("Something went wrong. Please try again.");
|
|
});
|
|
});
|
|
});
|
|
</script>
|
|
@endsection |