149 lines
8.6 KiB
PHP
149 lines
8.6 KiB
PHP
@extends('layouts.app')
|
|
|
|
@section('content')
|
|
<div class="max-w-4xl mx-auto space-y-6">
|
|
<div class="flex items-center gap-4">
|
|
<a href="{{ route('payroll.index') }}" class="p-2 text-gray-500 hover:text-gray-900 hover:bg-gray-100 rounded-lg transition-colors">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/></svg>
|
|
</a>
|
|
<h1 class="text-2xl font-bold text-gray-900">{{ __('payroll.final_settlement') }}</h1>
|
|
</div>
|
|
|
|
<div class="p-4 bg-amber-50 border border-amber-200 rounded-lg text-sm text-amber-800">
|
|
<strong>{{ __('payroll.settlement_warning_title') }}</strong><br>
|
|
{{ __('payroll.settlement_warning') }}
|
|
</div>
|
|
|
|
<!-- Settlement Form -->
|
|
<div class="bg-white rounded-xl border border-gray-200 p-6 space-y-5">
|
|
<form method="POST" action="{{ route('payroll.process-settlement') }}" id="settlementForm">
|
|
@csrf
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label for="employee_id" class="block text-sm font-medium text-gray-700 mb-1">{{ __('payroll.employee') }} <span class="text-red-500">*</span></label>
|
|
<select id="employee_id" name="employee_id" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
|
|
<option value="">{{ __('common.select') }}</option>
|
|
@foreach($employees as $emp)
|
|
<option value="{{ $emp->id }}">{{ $emp->full_name ?? ($emp->first_name . ' ' . $emp->last_name) }} {{ $emp->job_title ? '(' . $emp->job_title . ')' : '' }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="settlement_type" class="block text-sm font-medium text-gray-700 mb-1">{{ __('payroll.settlement_type') }} <span class="text-red-500">*</span></label>
|
|
<select id="settlement_type" name="settlement_type" class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
|
|
<option value="resignation">{{ __('payroll.settlement_resignation') }}</option>
|
|
<option value="termination">{{ __('payroll.settlement_termination') }}</option>
|
|
<option value="end_of_contract">{{ __('payroll.settlement_end_of_contract') }}</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label for="settlement_date" class="block text-sm font-medium text-gray-700 mb-1">{{ __('payroll.settlement_date') }} <span class="text-red-500">*</span></label>
|
|
<x-date-input name="settlement_date" :value="now()->format('Y-m-d')" />
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500" required>
|
|
</div>
|
|
<div>
|
|
<label for="last_working_date" class="block text-sm font-medium text-gray-700 mb-1">{{ __('payroll.last_working_date') }}</label>
|
|
<x-date-input name="last_working_date" :value="old('last_working_date')" />
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500">
|
|
</div>
|
|
<div class="md:col-span-2">
|
|
<label for="notes" class="block text-sm font-medium text-gray-700 mb-1">{{ __('payroll.notes') }}</label>
|
|
<textarea id="notes" name="notes" rows="3"
|
|
class="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-teal-500"></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preview Section (filled by AJAX) -->
|
|
<div id="settlementPreview" class="hidden mt-6 border border-gray-200 rounded-lg overflow-hidden">
|
|
<div class="px-5 py-3 bg-gray-50 border-b border-gray-200">
|
|
<h3 class="text-sm font-semibold text-gray-800">{{ __('payroll.settlement_preview') }}</h3>
|
|
</div>
|
|
<div id="previewContent" class="p-5 space-y-4">
|
|
<!-- Filled dynamically -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex items-center justify-end gap-3 pt-4 border-t border-gray-200">
|
|
<a href="{{ route('payroll.index') }}" class="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-lg hover:bg-gray-200 transition-colors">{{ __('common.cancel') }}</a>
|
|
<button type="button" onclick="previewSettlement()" class="px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-lg hover:bg-gray-50 transition-colors">{{ __('payroll.preview') }}</button>
|
|
<button type="submit" class="px-6 py-2 text-sm font-medium text-white bg-red-600 rounded-lg hover:bg-red-700 transition-colors" onclick="return confirm('{{ __('payroll.confirm_settlement') }}')">{{ __('payroll.process_settlement') }}</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.getElementById('employee_id')?.addEventListener('change', function() {
|
|
document.getElementById('settlementPreview').classList.add('hidden');
|
|
});
|
|
|
|
function previewSettlement() {
|
|
const employeeId = document.getElementById('employee_id').value;
|
|
if (!employeeId) {
|
|
alert('{{ __("common.select") }}');
|
|
return;
|
|
}
|
|
|
|
const formData = new FormData(document.getElementById('settlementForm'));
|
|
const data = Object.fromEntries(formData);
|
|
|
|
fetch('{{ route("payroll.preview-settlement") }}', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
|
'Accept': 'application/json'
|
|
},
|
|
body: JSON.stringify(data)
|
|
})
|
|
.then(res => res.json())
|
|
.then(result => {
|
|
const preview = document.getElementById('settlementPreview');
|
|
const content = document.getElementById('previewContent');
|
|
|
|
let html = `
|
|
<div class="grid grid-cols-2 md:grid-cols-4 gap-4 mb-4">
|
|
<div class="bg-gray-50 rounded-lg p-3">
|
|
<div class="text-xs text-gray-500">{{ __('payroll.total_payable') }}</div>
|
|
<div class="text-lg font-bold text-gray-900">${number_format(result.total_payable)}</div>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-3">
|
|
<div class="text-xs text-gray-500">{{ __('payroll.total_deductions') }}</div>
|
|
<div class="text-lg font-bold text-red-600">${number_format(result.total_deductions + result.total_advances)}</div>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-3">
|
|
<div class="text-xs text-gray-500">{{ __('payroll.net_payable') }}</div>
|
|
<div class="text-lg font-bold text-teal-600">${number_format(result.net_payable)}</div>
|
|
</div>
|
|
<div class="bg-gray-50 rounded-lg p-3">
|
|
<div class="text-xs text-gray-500">{{ __('payroll.months_worked') }}</div>
|
|
<div class="text-lg font-bold text-gray-900">${result.months_worked}</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
if (result.unpaid_financials && result.unpaid_financials.length > 0) {
|
|
html += '<div class="overflow-x-auto"><table class="w-full text-sm"><thead class="bg-gray-50"><tr><th class="px-3 py-2 text-start text-xs font-medium text-gray-500">{{ __("payroll.type") }}</th><th class="px-3 py-2 text-start text-xs font-medium text-gray-500">{{ __("payroll.amount") }}</th><th class="px-3 py-2 text-start text-xs font-medium text-gray-500">{{ __("common.description") }}</th></tr></thead><tbody>';
|
|
result.unpaid_financials.forEach(f => {
|
|
html += `<tr class="border-t border-gray-100"><td class="px-3 py-2">${f.type}</td><td class="px-3 py-2 font-medium">${number_format(f.amount)}</td><td class="px-3 py-2 text-gray-500">${f.description || '-'}</td></tr>`;
|
|
});
|
|
html += '</tbody></table></div>';
|
|
} else {
|
|
html += '<p class="text-sm text-gray-500 text-center py-4">{{ __("payroll.no_unpaid_records") }}</p>';
|
|
}
|
|
|
|
content.innerHTML = html;
|
|
preview.classList.remove('hidden');
|
|
})
|
|
.catch(err => {
|
|
console.error(err);
|
|
alert('{{ __("common.error") }}');
|
|
});
|
|
}
|
|
|
|
function number_format(num) {
|
|
return Number(num).toLocaleString('en-US');
|
|
}
|
|
</script>
|
|
@endsection
|