chore: initial commit of existing codebase
This commit is contained in:
commit
11e94889b2
292 files changed
+38586
No files matched your search
@@ -0,0 +1,76 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Add Customer')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Add Customer</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.customers.index') }}" class="text-blue-600 hover:underline">Customers</a> / New</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.customers.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Customer Name <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="name" value="{{ old('name') }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Contact Person</label>
|
||||
<input type="text" name="contact_person" value="{{ old('contact_person') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" name="email" value="{{ old('email') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="text" name="phone" value="{{ old('phone') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Tax Number</label>
|
||||
<input type="text" name="tax_number" value="{{ old('tax_number') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Credit Limit</label>
|
||||
<input type="number" name="credit_limit" value="{{ old('credit_limit', 0) }}" min="0" step="0.01" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Address</label>
|
||||
<textarea name="address" rows="3" class="form-textarea">{{ old('address') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2 flex items-center gap-2">
|
||||
<input type="checkbox" name="is_active" id="is_active" value="1"
|
||||
{{ old('is_active', '1') ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
|
||||
<label for="is_active" class="form-label mb-0">Active</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Save Customer</button>
|
||||
<a href="{{ route('sales.customers.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,77 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Customer')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Edit Customer</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.customers.index') }}" class="text-blue-600 hover:underline">Customers</a> / Edit</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.customers.update', $customer) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Customer Name <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="name" value="{{ old('name', $customer->name) }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Contact Person</label>
|
||||
<input type="text" name="contact_person" value="{{ old('contact_person', $customer->contact_person) }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Email</label>
|
||||
<input type="email" name="email" value="{{ old('email', $customer->email) }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Phone</label>
|
||||
<input type="text" name="phone" value="{{ old('phone', $customer->phone) }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Tax Number</label>
|
||||
<input type="text" name="tax_number" value="{{ old('tax_number', $customer->tax_number) }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Credit Limit</label>
|
||||
<input type="number" name="credit_limit" value="{{ old('credit_limit', $customer->credit_limit) }}" min="0" step="0.01" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Address</label>
|
||||
<textarea name="address" rows="3" class="form-textarea">{{ old('address', $customer->address) }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2 flex items-center gap-2">
|
||||
<input type="checkbox" name="is_active" id="is_active" value="1"
|
||||
{{ old('is_active', $customer->is_active) ? 'checked' : '' }}
|
||||
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
|
||||
<label for="is_active" class="form-label mb-0">Active</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Update Customer</button>
|
||||
<a href="{{ route('sales.customers.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,70 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Customers')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Customers</h1>
|
||||
<p class="page-subtitle">Manage your customer directory</p>
|
||||
</div>
|
||||
<a href="{{ route('sales.customers.create') }}" class="btn-primary">
|
||||
+ Add Customer
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Contact</th>
|
||||
<th>Email</th>
|
||||
<th class="text-right">Credit Limit</th>
|
||||
<th class="text-right">Outstanding Balance</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($customers as $customer)
|
||||
<tr>
|
||||
<td class="font-medium text-gray-800">{{ $customer->name }}</td>
|
||||
<td>{{ $customer->contact_person }}</td>
|
||||
<td>{{ $customer->email }}</td>
|
||||
<td class="text-right text-gray-700">{{ number_format($customer->credit_limit, 2) }}</td>
|
||||
<td class="text-right {{ ($customer->outstanding_balance ?? 0) > 0 ? 'text-red-600 font-semibold' : 'text-gray-500' }}">
|
||||
{{ number_format($customer->outstanding_balance ?? 0, 2) }}
|
||||
</td>
|
||||
<td>
|
||||
@if($customer->is_active)
|
||||
<span class="badge-green">Active</span>
|
||||
@else
|
||||
<span class="badge-gray">Inactive</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2">
|
||||
<a href="{{ route('sales.customers.edit', $customer) }}" class="btn-secondary btn-sm">Edit</a>
|
||||
<form action="{{ route('sales.customers.destroy', $customer) }}" method="POST"
|
||||
onsubmit="confirmDelete(this,'Delete this customer?','This customer record will be permanently removed.'); return false;">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-4 py-8 text-center text-gray-400">No customers found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($customers->hasPages())
|
||||
<div class="mt-4">{{ $customers->links() }}</div>
|
||||
@endif
|
||||
@endsection
|
||||
@@ -0,0 +1,129 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'New Delivery Note')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">New Delivery Note</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.delivery-notes.index') }}" class="text-blue-600 hover:underline">Delivery Notes</a> / New</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('sales.delivery-notes.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<div class="card card-body mb-4">
|
||||
<h2 class="text-base font-semibold text-gray-700 mb-4">Delivery Details</h2>
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Sales Order <span class="text-red-500">*</span></label>
|
||||
<select name="sales_order_id" id="so-select" required onchange="loadSOItems(this)" class="form-select">
|
||||
<option value="">-- Select Confirmed Order --</option>
|
||||
@foreach($salesOrders as $so)
|
||||
<option value="{{ $so->id }}"
|
||||
data-items="{{ json_encode($so->items) }}"
|
||||
{{ (old('sales_order_id', request('sales_order_id'))) == $so->id ? 'selected' : '' }}>
|
||||
{{ $so->order_number ?? 'SO-' . str_pad($so->id, 5, '0', STR_PAD_LEFT) }} - {{ $so->customer->name ?? '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
|
||||
<select name="warehouse_id" required class="form-select">
|
||||
<option value="">-- Select Warehouse --</option>
|
||||
@foreach($warehouses as $warehouse)
|
||||
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
|
||||
{{ $warehouse->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Delivery Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="delivery_date" value="{{ old('delivery_date', date('Y-m-d')) }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items from SO -->
|
||||
<div class="card card-body mb-4">
|
||||
<h2 class="text-base font-semibold text-gray-700 mb-4">Items to Deliver</h2>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200">
|
||||
<th class="pb-2 text-left font-semibold text-gray-600">Product</th>
|
||||
<th class="pb-2 text-left font-semibold text-gray-600 w-28">SO Qty</th>
|
||||
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Deliver Qty</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dn-items-body">
|
||||
<tr>
|
||||
<td colspan="3" class="py-4 text-center text-gray-400">Select a Sales Order to load items.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Create Delivery Note</button>
|
||||
<a href="{{ route('sales.delivery-notes.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
function loadSOItems(select) {
|
||||
var tbody = document.getElementById('dn-items-body');
|
||||
var option = select.options[select.selectedIndex];
|
||||
tbody.innerHTML = '';
|
||||
|
||||
if (!option.value) {
|
||||
tbody.innerHTML = '<tr><td colspan="3" class="py-4 text-center text-gray-400">Select a Sales Order to load items.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
var items = JSON.parse(option.dataset.items || '[]');
|
||||
if (!items.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="3" class="py-4 text-center text-gray-400">No items on this order.</td></tr>';
|
||||
return;
|
||||
}
|
||||
|
||||
items.forEach(function(item, idx) {
|
||||
var row = document.createElement('tr');
|
||||
row.className = 'border-b border-gray-100';
|
||||
row.innerHTML = `
|
||||
<td class="py-2 pr-2 text-gray-800">${item.item ? item.item.item_name : 'Item #' + item.item_id}
|
||||
<input type="hidden" name="items[${idx}][item_id]" value="${item.item_id}">
|
||||
</td>
|
||||
<td class="py-2 pr-2 text-gray-500">${item.quantity}</td>
|
||||
<td class="py-2 pr-2">
|
||||
<input type="number" name="items[${idx}][quantity]"
|
||||
value="${item.quantity}" min="0" step="0.01"
|
||||
max="${item.quantity}"
|
||||
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400">
|
||||
</td>`;
|
||||
tbody.appendChild(row);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,57 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Delivery Note')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Edit Delivery Note</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.delivery-notes.index') }}" class="text-blue-600 hover:underline">Delivery Notes</a> / Edit</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.delivery-notes.update', $deliveryNote) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
|
||||
<select name="warehouse_id" required class="form-select">
|
||||
<option value="">-- Select Warehouse --</option>
|
||||
@foreach($warehouses as $warehouse)
|
||||
<option value="{{ $warehouse->id }}" {{ old('warehouse_id', $deliveryNote->warehouse_id) == $warehouse->id ? 'selected' : '' }}>
|
||||
{{ $warehouse->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Delivery Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="delivery_date" value="{{ old('delivery_date', $deliveryNote->delivery_date ? \Carbon\Carbon::parse($deliveryNote->delivery_date)->format('Y-m-d') : '') }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea name="notes" rows="3" class="form-textarea">{{ old('notes', $deliveryNote->notes) }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Update Delivery Note</button>
|
||||
<a href="{{ route('sales.delivery-notes.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,77 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Delivery Notes')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Delivery Notes</h1>
|
||||
<p class="page-subtitle">Manage goods dispatch to customers</p>
|
||||
</div>
|
||||
<a href="{{ route('sales.delivery-notes.create') }}" class="btn-primary">
|
||||
+ New Delivery Note
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>DN #</th>
|
||||
<th>Sales Order</th>
|
||||
<th>Customer</th>
|
||||
<th>Warehouse</th>
|
||||
<th>Date</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($deliveryNotes as $dn)
|
||||
<tr>
|
||||
<td class="font-mono text-gray-700">{{ $dn->dn_number ?? 'DN-' . str_pad($dn->id, 5, '0', STR_PAD_LEFT) }}</td>
|
||||
<td class="font-mono text-gray-600">{{ $dn->salesOrder->order_number ?? 'SO-' . str_pad($dn->sales_order_id, 5, '0', STR_PAD_LEFT) }}</td>
|
||||
<td class="text-gray-800">{{ $dn->salesOrder->customer->name ?? '' }}</td>
|
||||
<td class="text-gray-700">{{ $dn->warehouse->name ?? '' }}</td>
|
||||
<td class="text-gray-600">{{ $dn->delivery_date ? \Carbon\Carbon::parse($dn->delivery_date)->format('d M Y') : '' }}</td>
|
||||
<td>
|
||||
@php
|
||||
$badgeClass = match($dn->status ?? 'pending') {
|
||||
'pending' => 'badge-yellow',
|
||||
'dispatched' => 'badge-green',
|
||||
'cancelled' => 'badge-red',
|
||||
default => 'badge-gray',
|
||||
};
|
||||
@endphp
|
||||
<span class="{{ $badgeClass }}">{{ ucfirst($dn->status ?? 'pending') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
@if($dn->status === 'pending')
|
||||
<form action="{{ route('sales.delivery-notes.dispatch', $dn) }}" method="POST">
|
||||
@csrf @method('PATCH')
|
||||
<button type="submit" class="btn-success btn-sm">Dispatch</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('sales.delivery-notes.edit', $dn) }}" class="btn-secondary btn-sm">Edit</a>
|
||||
<form action="{{ route('sales.delivery-notes.destroy', $dn) }}" method="POST"
|
||||
onsubmit="confirmDelete(this,'Delete this delivery note?','This delivery note will be permanently removed.'); return false;">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="px-4 py-8 text-center text-gray-400">No delivery notes found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($deliveryNotes->hasPages())
|
||||
<div class="mt-4">{{ $deliveryNotes->links() }}</div>
|
||||
@endif
|
||||
@endsection
|
||||
@@ -0,0 +1,128 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'New Sales Invoice')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">New Sales Invoice</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.invoices.index') }}" class="text-blue-600 hover:underline">Sales Invoices</a> / New</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.invoices.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Sales Order <span class="text-red-500">*</span></label>
|
||||
<select name="sales_order_id" id="inv-so-select" required onchange="fillCustomer(this)" class="form-select">
|
||||
<option value="">-- Select Dispatched Order --</option>
|
||||
@foreach($salesOrders as $so)
|
||||
<option value="{{ $so->id }}"
|
||||
data-customer="{{ $so->customer_id }}"
|
||||
data-total="{{ $so->total_amount }}"
|
||||
{{ old('sales_order_id') == $so->id ? 'selected' : '' }}>
|
||||
{{ $so->order_number ?? 'SO-' . str_pad($so->id, 5, '0', STR_PAD_LEFT) }} - {{ $so->customer->name ?? '' }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Customer</label>
|
||||
<select name="customer_id" id="inv-customer" class="form-select">
|
||||
<option value="">-- Auto-filled from SO --</option>
|
||||
@foreach($customers as $customer)
|
||||
<option value="{{ $customer->id }}" {{ old('customer_id') == $customer->id ? 'selected' : '' }}>
|
||||
{{ $customer->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Invoice Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="invoice_date" value="{{ old('invoice_date', date('Y-m-d')) }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Due Date</label>
|
||||
<input type="date" name="due_date" value="{{ old('due_date') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Subtotal <span class="text-red-500">*</span></label>
|
||||
<input type="number" name="subtotal" id="inv-subtotal" value="{{ old('subtotal') }}"
|
||||
min="0" step="0.01" required onchange="calcSalesInvoiceTotal()" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">VAT Rate (%)</label>
|
||||
<input type="number" name="vat_rate" id="inv-vat-rate" value="{{ old('vat_rate', 0) }}"
|
||||
min="0" max="100" step="0.01" onchange="calcSalesInvoiceTotal()" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">VAT Amount</label>
|
||||
<input type="number" name="vat_amount" id="inv-vat-amount" value="{{ old('vat_amount', 0) }}"
|
||||
min="0" step="0.01" readonly
|
||||
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Total Amount</label>
|
||||
<input type="number" name="total_amount" id="inv-total" value="{{ old('total_amount') }}"
|
||||
readonly
|
||||
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Save Invoice</button>
|
||||
<a href="{{ route('sales.invoices.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function fillCustomer(select) {
|
||||
var option = select.options[select.selectedIndex];
|
||||
var customerId = option.dataset.customer;
|
||||
var total = option.dataset.total;
|
||||
var customerSelect = document.getElementById('inv-customer');
|
||||
|
||||
// Auto-select customer
|
||||
for (var i = 0; i < customerSelect.options.length; i++) {
|
||||
if (customerSelect.options[i].value == customerId) {
|
||||
customerSelect.selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-fill subtotal from SO
|
||||
if (total) {
|
||||
document.getElementById('inv-subtotal').value = parseFloat(total).toFixed(2);
|
||||
calcSalesInvoiceTotal();
|
||||
}
|
||||
}
|
||||
|
||||
function calcSalesInvoiceTotal() {
|
||||
var subtotal = parseFloat(document.getElementById('inv-subtotal').value) || 0;
|
||||
var vatRate = parseFloat(document.getElementById('inv-vat-rate').value) || 0;
|
||||
var vatAmount = subtotal * vatRate / 100;
|
||||
document.getElementById('inv-vat-amount').value = vatAmount.toFixed(2);
|
||||
document.getElementById('inv-total').value = (subtotal + vatAmount).toFixed(2);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,81 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Sales Invoice')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Edit Sales Invoice</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.invoices.index') }}" class="text-blue-600 hover:underline">Sales Invoices</a> / Edit</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.invoices.update', $invoice) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Invoice Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="invoice_date" value="{{ old('invoice_date', $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('Y-m-d') : '') }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Due Date</label>
|
||||
<input type="date" name="due_date" value="{{ old('due_date', $invoice->due_date ? \Carbon\Carbon::parse($invoice->due_date)->format('Y-m-d') : '') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Subtotal</label>
|
||||
<input type="number" name="subtotal" id="edit-subtotal" value="{{ old('subtotal', $invoice->subtotal) }}"
|
||||
min="0" step="0.01" onchange="calcEditTotal()" class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">VAT Amount</label>
|
||||
<input type="number" name="vat_amount" id="edit-vat" value="{{ old('vat_amount', $invoice->vat_amount) }}"
|
||||
min="0" step="0.01" onchange="calcEditTotal()" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Total Amount</label>
|
||||
<input type="number" name="total_amount" id="edit-total" value="{{ old('total_amount', $invoice->total_amount) }}"
|
||||
readonly
|
||||
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Status</label>
|
||||
<select name="status" class="form-select">
|
||||
<option value="unpaid" {{ old('status', $invoice->status) === 'unpaid' ? 'selected' : '' }}>Unpaid</option>
|
||||
<option value="partial" {{ old('status', $invoice->status) === 'partial' ? 'selected' : '' }}>Partial</option>
|
||||
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Update Invoice</button>
|
||||
<a href="{{ route('sales.invoices.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function calcEditTotal() {
|
||||
var sub = parseFloat(document.getElementById('edit-subtotal').value) || 0;
|
||||
var vat = parseFloat(document.getElementById('edit-vat').value) || 0;
|
||||
document.getElementById('edit-total').value = (sub + vat).toFixed(2);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,77 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Sales Invoices')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Sales Invoices</h1>
|
||||
<p class="page-subtitle">Track customer invoices and payments</p>
|
||||
</div>
|
||||
<a href="{{ route('sales.invoices.create') }}" class="btn-primary">
|
||||
+ New Invoice
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice #</th>
|
||||
<th>Customer</th>
|
||||
<th>SO #</th>
|
||||
<th>Date</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th class="text-right">Paid</th>
|
||||
<th class="text-right">Outstanding</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($invoices as $invoice)
|
||||
@php $outstanding = $invoice->total_amount - $invoice->paid_amount; @endphp
|
||||
<tr>
|
||||
<td class="font-mono text-gray-700">{{ $invoice->invoice_number }}</td>
|
||||
<td class="text-gray-800">{{ $invoice->customer->name ?? '' }}</td>
|
||||
<td class="font-mono text-gray-600">{{ $invoice->salesOrder->order_number ?? '-' }}</td>
|
||||
<td class="text-gray-600">{{ $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d M Y') : '' }}</td>
|
||||
<td class="text-right text-gray-800">{{ number_format($invoice->total_amount, 2) }}</td>
|
||||
<td class="text-right text-green-700">{{ number_format($invoice->paid_amount, 2) }}</td>
|
||||
<td class="text-right {{ $outstanding > 0 ? 'text-red-600 font-semibold' : 'text-gray-500' }}">{{ number_format($outstanding, 2) }}</td>
|
||||
<td>
|
||||
@php
|
||||
$badgeClass = match($invoice->status ?? 'unpaid') {
|
||||
'unpaid' => 'badge-red',
|
||||
'partial' => 'badge-yellow',
|
||||
'paid' => 'badge-green',
|
||||
default => 'badge-gray',
|
||||
};
|
||||
@endphp
|
||||
<span class="{{ $badgeClass }}">{{ ucfirst($invoice->status ?? 'unpaid') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<a href="{{ route('sales.payments.create', ['invoice_id' => $invoice->id]) }}" class="btn-success btn-sm">Receive</a>
|
||||
<a href="{{ route('sales.invoices.edit', $invoice) }}" class="btn-secondary btn-sm">Edit</a>
|
||||
<form action="{{ route('sales.invoices.destroy', $invoice) }}" method="POST"
|
||||
onsubmit="confirmDelete(this,'Delete this invoice?','This invoice will be permanently removed.'); return false;">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="9" class="px-4 py-8 text-center text-gray-400">No invoices found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($invoices->hasPages())
|
||||
<div class="mt-4">{{ $invoices->links() }}</div>
|
||||
@endif
|
||||
@endsection
|
||||
@@ -0,0 +1,180 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'New Sales Order')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">New Sales Order</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a> / New</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form action="{{ route('sales.orders.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
<!-- Header -->
|
||||
<div class="card card-body mb-4">
|
||||
<h2 class="text-base font-semibold text-gray-700 mb-4">Order Details</h2>
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Customer <span class="text-red-500">*</span></label>
|
||||
<select name="customer_id" required class="form-select">
|
||||
<option value="">-- Select Customer --</option>
|
||||
@foreach($customers as $customer)
|
||||
<option value="{{ $customer->id }}" {{ old('customer_id') == $customer->id ? 'selected' : '' }}>
|
||||
{{ $customer->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Order Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="order_date" value="{{ old('order_date', date('Y-m-d')) }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Delivery Date</label>
|
||||
<input type="date" name="delivery_date" value="{{ old('delivery_date') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2 lg:col-span-3">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items -->
|
||||
<div class="card card-body mb-4">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h2 class="text-base font-semibold text-gray-700">Order Items</h2>
|
||||
<button type="button" onclick="addSORow()" class="btn-primary btn-sm">+ Add Row</button>
|
||||
</div>
|
||||
|
||||
<div class="overflow-x-auto">
|
||||
<table class="min-w-full text-sm">
|
||||
<thead>
|
||||
<tr class="border-b border-gray-200">
|
||||
<th class="pb-2 text-left font-semibold text-gray-600">Product</th>
|
||||
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Quantity</th>
|
||||
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Unit Price</th>
|
||||
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Total</th>
|
||||
<th class="pb-2 w-10"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="so-items-body">
|
||||
<tr class="so-item-row">
|
||||
<td class="py-1 pr-2">
|
||||
<select name="items[0][item_id]"
|
||||
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400">
|
||||
<option value="">-- Select Product --</option>
|
||||
@foreach($products as $product)
|
||||
<option value="{{ $product->id }}">{{ $product->item_code }} - {{ $product->item_name }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[0][quantity]" placeholder="0" min="0" step="0.01"
|
||||
onchange="calcSORowTotal(this)"
|
||||
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-qty">
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[0][price]" placeholder="0.00" min="0" step="0.01"
|
||||
onchange="calcSORowTotal(this)"
|
||||
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-price">
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[0][total]" placeholder="0.00" readonly
|
||||
class="w-full border border-gray-200 bg-gray-50 rounded px-2 py-1.5 text-sm so-total">
|
||||
</td>
|
||||
<td class="py-1">
|
||||
<button type="button" onclick="removeSORow(this)" class="text-red-400 hover:text-red-600 text-lg leading-none">×</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 flex justify-end">
|
||||
<div class="text-sm font-semibold text-gray-700">
|
||||
Grand Total: <span id="so-grand-total" class="text-blue-700">0.00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Create Sales Order</button>
|
||||
<a href="{{ route('sales.orders.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
var soRowIndex = 1;
|
||||
var soItemsHtml = `@foreach($products as $product)<option value="{{ $product->id }}">{{ $product->item_code }} - {{ $product->item_name }}</option>@endforeach`;
|
||||
|
||||
function addSORow() {
|
||||
var tbody = document.getElementById('so-items-body');
|
||||
var row = document.createElement('tr');
|
||||
row.className = 'so-item-row';
|
||||
row.innerHTML = `
|
||||
<td class="py-1 pr-2">
|
||||
<select name="items[${soRowIndex}][item_id]" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm">
|
||||
<option value="">-- Select Product --</option>
|
||||
${soItemsHtml}
|
||||
</select>
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[${soRowIndex}][quantity]" placeholder="0" min="0" step="0.01"
|
||||
onchange="calcSORowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-qty">
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[${soRowIndex}][price]" placeholder="0.00" min="0" step="0.01"
|
||||
onchange="calcSORowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-price">
|
||||
</td>
|
||||
<td class="py-1 pr-2">
|
||||
<input type="number" name="items[${soRowIndex}][total]" placeholder="0.00" readonly
|
||||
class="w-full border border-gray-200 bg-gray-50 rounded px-2 py-1.5 text-sm so-total">
|
||||
</td>
|
||||
<td class="py-1">
|
||||
<button type="button" onclick="removeSORow(this)" class="text-red-400 hover:text-red-600 text-lg leading-none">×</button>
|
||||
</td>`;
|
||||
tbody.appendChild(row);
|
||||
soRowIndex++;
|
||||
}
|
||||
|
||||
function removeSORow(btn) {
|
||||
var rows = document.querySelectorAll('.so-item-row');
|
||||
if (rows.length > 1) {
|
||||
btn.closest('tr').remove();
|
||||
updateSOGrandTotal();
|
||||
}
|
||||
}
|
||||
|
||||
function calcSORowTotal(input) {
|
||||
var row = input.closest('tr');
|
||||
var qty = parseFloat(row.querySelector('.so-qty').value) || 0;
|
||||
var price = parseFloat(row.querySelector('.so-price').value) || 0;
|
||||
row.querySelector('.so-total').value = (qty * price).toFixed(2);
|
||||
updateSOGrandTotal();
|
||||
}
|
||||
|
||||
function updateSOGrandTotal() {
|
||||
var totals = document.querySelectorAll('.so-total');
|
||||
var grand = 0;
|
||||
totals.forEach(function(t) { grand += parseFloat(t.value) || 0; });
|
||||
document.getElementById('so-grand-total').textContent = grand.toFixed(2);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
@@ -0,0 +1,62 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Edit Sales Order')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Edit Sales Order</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a> / Edit</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.orders.update', $order) }}" method="POST">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div>
|
||||
<label class="form-label">Customer <span class="text-red-500">*</span></label>
|
||||
<select name="customer_id" required class="form-select">
|
||||
<option value="">-- Select Customer --</option>
|
||||
@foreach($customers as $customer)
|
||||
<option value="{{ $customer->id }}" {{ old('customer_id', $order->customer_id) == $customer->id ? 'selected' : '' }}>
|
||||
{{ $customer->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Order Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="order_date" value="{{ old('order_date', $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('Y-m-d') : '') }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Delivery Date</label>
|
||||
<input type="date" name="delivery_date" value="{{ old('delivery_date', $order->delivery_date ? \Carbon\Carbon::parse($order->delivery_date)->format('Y-m-d') : '') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea name="notes" rows="3" class="form-textarea">{{ old('notes', $order->notes) }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Update Order</button>
|
||||
<a href="{{ route('sales.orders.show', $order) }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,78 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Sales Orders')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Sales Orders</h1>
|
||||
<p class="page-subtitle">Manage customer orders</p>
|
||||
</div>
|
||||
<a href="{{ route('sales.orders.create') }}" class="btn-primary">
|
||||
+ New Order
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Order #</th>
|
||||
<th>Customer</th>
|
||||
<th>Date</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($orders as $order)
|
||||
<tr>
|
||||
<td class="font-mono text-gray-700">{{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</td>
|
||||
<td class="font-medium text-gray-800">{{ $order->customer->name ?? '' }}</td>
|
||||
<td>{{ $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('d M Y') : '' }}</td>
|
||||
<td class="text-right font-medium text-gray-800">{{ number_format($order->total_amount, 2) }}</td>
|
||||
<td>
|
||||
@php
|
||||
$badgeClass = match($order->status ?? 'draft') {
|
||||
'draft' => 'badge-gray',
|
||||
'confirmed' => 'badge-blue',
|
||||
'dispatched' => 'badge-violet',
|
||||
'invoiced' => 'badge-green',
|
||||
'cancelled' => 'badge-red',
|
||||
default => 'badge-gray',
|
||||
};
|
||||
@endphp
|
||||
<span class="{{ $badgeClass }}">{{ ucfirst($order->status ?? 'draft') }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<a href="{{ route('sales.orders.show', $order) }}" class="btn-primary btn-sm">View</a>
|
||||
@if($order->status === 'draft')
|
||||
<form action="{{ route('sales.orders.confirm', $order) }}" method="POST">
|
||||
@csrf @method('PATCH')
|
||||
<button type="submit" class="btn-primary btn-sm">Confirm</button>
|
||||
</form>
|
||||
@endif
|
||||
<a href="{{ route('sales.orders.edit', $order) }}" class="btn-secondary btn-sm">Edit</a>
|
||||
<form action="{{ route('sales.orders.destroy', $order) }}" method="POST"
|
||||
onsubmit="confirmDelete(this,'Delete this sales order?','This order and its items will be permanently removed.'); return false;">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn-danger btn-sm">Delete</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No sales orders found.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($orders->hasPages())
|
||||
<div class="mt-4">{{ $orders->links() }}</div>
|
||||
@endif
|
||||
@endsection
|
||||
@@ -0,0 +1,175 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Sales Order')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Sales Order</h1>
|
||||
<p class="page-subtitle">
|
||||
<a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a>
|
||||
/ {{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
@if($order->status === 'draft')
|
||||
<form action="{{ route('sales.orders.confirm', $order) }}" method="POST">
|
||||
@csrf @method('PATCH')
|
||||
<button type="submit" class="btn-primary">Confirm Order</button>
|
||||
</form>
|
||||
@endif
|
||||
@if($order->status === 'confirmed')
|
||||
<a href="{{ route('sales.delivery-notes.create', ['sales_order_id' => $order->id]) }}" class="btn-primary">Create Delivery Note</a>
|
||||
@endif
|
||||
<a href="{{ route('sales.orders.edit', $order) }}" class="btn-secondary">Edit</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
|
||||
<!-- Order Info -->
|
||||
<div class="lg:col-span-2 card card-body">
|
||||
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Order Details</h2>
|
||||
<dl class="grid grid-cols-2 gap-4 text-sm">
|
||||
<div>
|
||||
<dt class="text-gray-500">Order Number</dt>
|
||||
<dd class="font-mono font-semibold text-gray-800">{{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-gray-500">Status</dt>
|
||||
<dd>
|
||||
@php
|
||||
$badgeClass = match($order->status ?? 'draft') {
|
||||
'draft' => 'badge-gray',
|
||||
'confirmed' => 'badge-blue',
|
||||
'dispatched'=> 'badge-violet',
|
||||
'invoiced' => 'badge-green',
|
||||
default => 'badge-gray',
|
||||
};
|
||||
@endphp
|
||||
<span class="{{ $badgeClass }}">{{ ucfirst($order->status ?? 'draft') }}</span>
|
||||
</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-gray-500">Order Date</dt>
|
||||
<dd class="font-medium text-gray-800">{{ $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('d M Y') : '-' }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt class="text-gray-500">Delivery Date</dt>
|
||||
<dd class="font-medium text-gray-800">{{ $order->delivery_date ? \Carbon\Carbon::parse($order->delivery_date)->format('d M Y') : '-' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<!-- Customer Info -->
|
||||
<div class="card card-body">
|
||||
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Customer</h2>
|
||||
@if($order->customer)
|
||||
<dl class="space-y-2 text-sm">
|
||||
<div><dt class="text-gray-500">Name</dt><dd class="font-semibold text-gray-800">{{ $order->customer->name }}</dd></div>
|
||||
<div><dt class="text-gray-500">Contact</dt><dd class="text-gray-700">{{ $order->customer->contact_person }}</dd></div>
|
||||
<div><dt class="text-gray-500">Email</dt><dd class="text-gray-700">{{ $order->customer->email }}</dd></div>
|
||||
<div><dt class="text-gray-500">Phone</dt><dd class="text-gray-700">{{ $order->customer->phone }}</dd></div>
|
||||
</dl>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Items -->
|
||||
<div class="card mb-6 overflow-hidden">
|
||||
<div class="card-header">
|
||||
<h2 class="text-base font-semibold text-gray-700">Order Items</h2>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th class="text-right">Quantity</th>
|
||||
<th class="text-right">Unit Price</th>
|
||||
<th class="text-right">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($order->items as $item)
|
||||
<tr>
|
||||
<td class="text-gray-800">{{ $item->item->item_name ?? '' }}</td>
|
||||
<td class="text-right text-gray-600">{{ number_format($item->quantity, 2) }}</td>
|
||||
<td class="text-right text-gray-600">{{ number_format($item->price, 2) }}</td>
|
||||
<td class="text-right font-medium text-gray-800">{{ number_format($item->total, 2) }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="4" class="px-4 py-6 text-center text-gray-400">No items.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
<tfoot class="bg-gray-50">
|
||||
<tr>
|
||||
<td colspan="3" class="px-4 py-3 text-right font-semibold text-gray-700">Total</td>
|
||||
<td class="px-4 py-3 text-right font-bold text-gray-900">{{ number_format($order->total_amount, 2) }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delivery Notes -->
|
||||
@if(isset($deliveryNotes) && $deliveryNotes->count())
|
||||
<div class="card mb-6 overflow-hidden">
|
||||
<div class="card-header">
|
||||
<h2 class="text-base font-semibold text-gray-700">Delivery Notes</h2>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>DN #</th>
|
||||
<th>Warehouse</th>
|
||||
<th>Delivery Date</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($deliveryNotes as $dn)
|
||||
<tr>
|
||||
<td class="font-mono text-gray-700">{{ $dn->dn_number ?? 'DN-' . str_pad($dn->id, 5, '0', STR_PAD_LEFT) }}</td>
|
||||
<td class="text-gray-700">{{ $dn->warehouse->name ?? '' }}</td>
|
||||
<td class="text-gray-600">{{ $dn->delivery_date ? \Carbon\Carbon::parse($dn->delivery_date)->format('d M Y') : '' }}</td>
|
||||
<td><span class="badge-blue">{{ ucfirst($dn->status ?? 'pending') }}</span></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- Invoices -->
|
||||
@if(isset($invoices) && $invoices->count())
|
||||
<div class="card overflow-hidden">
|
||||
<div class="card-header">
|
||||
<h2 class="text-base font-semibold text-gray-700">Invoices</h2>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Invoice #</th>
|
||||
<th>Date</th>
|
||||
<th class="text-right">Total</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($invoices as $invoice)
|
||||
<tr>
|
||||
<td class="font-mono text-gray-700">{{ $invoice->invoice_number }}</td>
|
||||
<td class="text-gray-600">{{ $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d M Y') : '' }}</td>
|
||||
<td class="text-right font-medium text-gray-800">{{ number_format($invoice->total_amount, 2) }}</td>
|
||||
<td><span class="badge-green">{{ ucfirst($invoice->status ?? 'unpaid') }}</span></td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
@endsection
|
||||
@@ -0,0 +1,78 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Record Customer Receipt')
|
||||
|
||||
@section('content')
|
||||
<div class="mb-6">
|
||||
<h1 class="page-title">Record Customer Receipt</h1>
|
||||
<p class="page-subtitle"><a href="{{ route('sales.payments.index') }}" class="text-blue-600 hover:underline">Receipts</a> / New</p>
|
||||
</div>
|
||||
|
||||
@if($errors->any())
|
||||
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
|
||||
<ul class="list-disc list-inside space-y-1">
|
||||
@foreach($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="card card-body max-w-2xl">
|
||||
<form action="{{ route('sales.payments.store') }}" method="POST">
|
||||
@csrf
|
||||
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Invoice <span class="text-red-500">*</span></label>
|
||||
<select name="sales_invoice_id" required class="form-select">
|
||||
<option value="">-- Select Unpaid Invoice --</option>
|
||||
@foreach($unpaidInvoices as $invoice)
|
||||
<option value="{{ $invoice->id }}" {{ (old('sales_invoice_id', request('invoice_id'))) == $invoice->id ? 'selected' : '' }}>
|
||||
{{ $invoice->invoice_number }} - {{ $invoice->customer->name ?? '' }}
|
||||
(Outstanding: {{ number_format($invoice->total_amount - $invoice->paid_amount, 2) }})
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Receipt Date <span class="text-red-500">*</span></label>
|
||||
<input type="date" name="receipt_date" value="{{ old('receipt_date', date('Y-m-d')) }}" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Amount <span class="text-red-500">*</span></label>
|
||||
<input type="number" name="amount" value="{{ old('amount') }}" min="0.01" step="0.01" required class="form-input">
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Payment Method <span class="text-red-500">*</span></label>
|
||||
<select name="payment_method" required class="form-select">
|
||||
<option value="">-- Select Method --</option>
|
||||
<option value="cash" {{ old('payment_method') === 'cash' ? 'selected' : '' }}>Cash</option>
|
||||
<option value="bank_transfer" {{ old('payment_method') === 'bank_transfer' ? 'selected' : '' }}>Bank Transfer</option>
|
||||
<option value="cheque" {{ old('payment_method') === 'cheque' ? 'selected' : '' }}>Cheque</option>
|
||||
<option value="other" {{ old('payment_method') === 'other' ? 'selected' : '' }}>Other</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="form-label">Reference Number</label>
|
||||
<input type="text" name="reference_number" value="{{ old('reference_number') }}" class="form-input">
|
||||
</div>
|
||||
|
||||
<div class="sm:col-span-2">
|
||||
<label class="form-label">Notes</label>
|
||||
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-6 flex items-center gap-3">
|
||||
<button type="submit" class="btn-primary">Record Receipt</button>
|
||||
<a href="{{ route('sales.payments.index') }}" class="btn-secondary">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,50 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'Customer Receipts')
|
||||
|
||||
@section('content')
|
||||
<div class="page-header">
|
||||
<div>
|
||||
<h1 class="page-title">Customer Receipts</h1>
|
||||
<p class="page-subtitle">Payment receipts from customers</p>
|
||||
</div>
|
||||
<a href="{{ route('sales.payments.create') }}" class="btn-primary">
|
||||
+ Record Receipt
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="table-wrapper overflow-x-auto">
|
||||
<table class="table-base">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<th>Invoice #</th>
|
||||
<th>Date</th>
|
||||
<th class="text-right">Amount</th>
|
||||
<th>Method</th>
|
||||
<th>Reference</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($payments as $payment)
|
||||
<tr>
|
||||
<td class="text-gray-800">{{ $payment->salesInvoice->customer->name ?? '' }}</td>
|
||||
<td class="font-mono text-gray-700">{{ $payment->salesInvoice->invoice_number ?? '-' }}</td>
|
||||
<td class="text-gray-600">{{ $payment->receipt_date ? \Carbon\Carbon::parse($payment->receipt_date)->format('d M Y') : '' }}</td>
|
||||
<td class="text-right font-medium text-gray-800">{{ number_format($payment->amount, 2) }}</td>
|
||||
<td class="text-gray-600 capitalize">{{ str_replace('_', ' ', $payment->payment_method) }}</td>
|
||||
<td class="text-gray-600">{{ $payment->reference_number ?? '-' }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No receipts recorded.</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@if($payments->hasPages())
|
||||
<div class="mt-4">{{ $payments->links() }}</div>
|
||||
@endif
|
||||
@endsection
|
||||
Reference in new issue
Block a user