Add cash management schema and immediate variance alerts
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { useSupabaseEmployeeData } from "@/hooks/useSupabaseEmployeeData";
|
||||
import { Currency, convert, formatCurrency, getUsdToLbpRate } from "@/lib/currency";
|
||||
|
||||
interface DetailedTransaction {
|
||||
location: string;
|
||||
@@ -17,18 +17,12 @@ interface DetailedTransaction {
|
||||
}
|
||||
|
||||
export const DetailedEmployeePaymentReport = () => {
|
||||
const { employees, transactions } = useSupabaseEmployeeData();
|
||||
const [displayCurrency, setDisplayCurrency] = useState<Currency>("USD");
|
||||
const { employees, transactions } = useSupabaseEmployeeData(displayCurrency);
|
||||
const [selectedEmployeeId, setSelectedEmployeeId] = useState<string>('all');
|
||||
|
||||
const formatCurrency = (amount: number) => {
|
||||
return new Intl.NumberFormat('en-IN', {
|
||||
style: 'currency',
|
||||
currency: 'INR'
|
||||
}).format(amount);
|
||||
};
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
return new Date(date).toLocaleDateString('en-IN');
|
||||
return new Date(date).toLocaleDateString('en-US');
|
||||
};
|
||||
|
||||
// Process transactions with the specific business logic
|
||||
@@ -38,6 +32,11 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
|
||||
const employeeTransactions = transactions
|
||||
.filter(t => t.employee_id === employeeId)
|
||||
.map(t => ({
|
||||
...t,
|
||||
collection_amount: convert(t.collection_amount, t.currency, displayCurrency),
|
||||
deposit_amount: convert(t.deposit_amount, t.currency, displayCurrency),
|
||||
}))
|
||||
.sort((a, b) => new Date(a.transaction_date).getTime() - new Date(b.transaction_date).getTime());
|
||||
|
||||
const detailedTransactions: DetailedTransaction[] = [];
|
||||
@@ -62,7 +61,7 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
if (collections > 0) {
|
||||
pendingCollections.push({ amount: collections, date });
|
||||
detailedTransactions.push({
|
||||
location: 'BGRoad, Karnataka',
|
||||
location: employee.location || 'BGRoad, Karnataka',
|
||||
empId: employee.emp_id.replace('EMP', ''),
|
||||
empName: employee.name,
|
||||
collectionAmount: collections,
|
||||
@@ -106,7 +105,7 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
// If there's remaining deposit after clearing collections, add separate deposit entries
|
||||
while (remainingDeposit > 0) {
|
||||
detailedTransactions.push({
|
||||
location: 'BGRoad, Karnataka',
|
||||
location: employee.location || 'BGRoad, Karnataka',
|
||||
empId: employee.emp_id.replace('EMP', ''),
|
||||
empName: employee.name,
|
||||
collectionAmount: 0,
|
||||
@@ -133,30 +132,54 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
|
||||
const detailedTransactions = getTransactionsToShow();
|
||||
|
||||
// Calculate totals
|
||||
// Calculate totals (combined, in display currency)
|
||||
const totalCollection = detailedTransactions.reduce((sum, t) => sum + t.collectionAmount, 0);
|
||||
const totalDeposit = detailedTransactions.reduce((sum, t) => sum + t.depositAmount, 0);
|
||||
const totalDifference = totalDeposit - totalCollection;
|
||||
|
||||
// Per-currency totals from raw transactions, filtered to current selection
|
||||
const filteredRawTx = selectedEmployeeId === 'all'
|
||||
? transactions
|
||||
: transactions.filter(t => t.employee_id === selectedEmployeeId);
|
||||
const totalCollectionUSD = filteredRawTx.filter(t => t.currency === 'USD').reduce((s, t) => s + t.collection_amount, 0);
|
||||
const totalCollectionLBP = filteredRawTx.filter(t => t.currency === 'LBP').reduce((s, t) => s + t.collection_amount, 0);
|
||||
const totalDepositUSD = filteredRawTx.filter(t => t.currency === 'USD').reduce((s, t) => s + t.deposit_amount, 0);
|
||||
const totalDepositLBP = filteredRawTx.filter(t => t.currency === 'LBP').reduce((s, t) => s + t.deposit_amount, 0);
|
||||
const totalDifferenceUSD = totalDepositUSD - totalCollectionUSD;
|
||||
const totalDifferenceLBP = totalDepositLBP - totalCollectionLBP;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<h2 className="text-xl font-semibold text-purple-600">Employee Payment Report (Detailed)</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-xs text-gray-500">Rate: 1 USD = {getUsdToLbpRate().toLocaleString()} LBP</span>
|
||||
<Select value={displayCurrency} onValueChange={(v) => setDisplayCurrency(v as Currency)}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="USD">View in USD</SelectItem>
|
||||
<SelectItem value="LBP">View in LBP</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Summary Cards */}
|
||||
<div className="grid grid-cols-3 gap-6 mb-6">
|
||||
<Card className="bg-white shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-gray-100 rounded-full flex items-center justify-center">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 bg-gray-100 rounded-full flex items-center justify-center shrink-0">
|
||||
<div className="w-6 h-6 bg-gray-600 rounded-full"></div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 mb-1">Total Collection</p>
|
||||
<p className="text-sm text-gray-500">(MM) Amount</p>
|
||||
<p className="text-2xl font-bold text-gray-800">{formatCurrency(totalCollection)}</p>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm text-gray-500 mb-1">Total Collection (MM)</p>
|
||||
<p className="text-xs text-gray-500">USD: <span className="font-medium text-gray-700">{formatCurrency(totalCollectionUSD, "USD")}</span></p>
|
||||
<p className="text-xs text-gray-500">LBP: <span className="font-medium text-gray-700">{formatCurrency(totalCollectionLBP, "LBP")}</span></p>
|
||||
<p className="text-lg font-bold text-gray-800 mt-1">≈ {formatCurrency(totalCollection, displayCurrency)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -164,16 +187,17 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
|
||||
<Card className="bg-white shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 bg-green-100 rounded-full flex items-center justify-center shrink-0">
|
||||
<div className="w-6 h-6 bg-green-500 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-xs">✓</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm text-gray-500 mb-1">Total Deposit</p>
|
||||
<p className="text-sm text-gray-500">Amount</p>
|
||||
<p className="text-2xl font-bold text-gray-800">{formatCurrency(totalDeposit)}</p>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm text-gray-500 mb-1">Total Deposit Amount</p>
|
||||
<p className="text-xs text-gray-500">USD: <span className="font-medium text-gray-700">{formatCurrency(totalDepositUSD, "USD")}</span></p>
|
||||
<p className="text-xs text-gray-500">LBP: <span className="font-medium text-gray-700">{formatCurrency(totalDepositLBP, "LBP")}</span></p>
|
||||
<p className="text-lg font-bold text-gray-800 mt-1">≈ {formatCurrency(totalDeposit, displayCurrency)}</p>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -181,17 +205,18 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
|
||||
<Card className="bg-white shadow-sm">
|
||||
<CardContent className="p-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center">
|
||||
<div className="flex items-start gap-4">
|
||||
<div className="w-12 h-12 bg-red-100 rounded-full flex items-center justify-center shrink-0">
|
||||
<div className="w-6 h-6 bg-red-500 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-xs">=</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm text-gray-500 mb-1">Net Difference</p>
|
||||
<p className="text-sm text-gray-500">Amount</p>
|
||||
<p className={`text-2xl font-bold ${totalDifference >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||||
{formatCurrency(totalDifference)}
|
||||
<p className="text-xs text-gray-500">USD: <span className={`font-medium ${totalDifferenceUSD >= 0 ? 'text-green-600' : 'text-red-600'}`}>{formatCurrency(totalDifferenceUSD, "USD")}</span></p>
|
||||
<p className="text-xs text-gray-500">LBP: <span className={`font-medium ${totalDifferenceLBP >= 0 ? 'text-green-600' : 'text-red-600'}`}>{formatCurrency(totalDifferenceLBP, "LBP")}</span></p>
|
||||
<p className={`text-lg font-bold mt-1 ${totalDifference >= 0 ? 'text-green-600' : 'text-red-600'}`}>
|
||||
≈ {formatCurrency(totalDifference, displayCurrency)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -239,13 +264,13 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
<TableCell className="font-medium py-4">{transaction.empId}</TableCell>
|
||||
<TableCell className="py-4 font-medium text-gray-800">{transaction.empName}</TableCell>
|
||||
<TableCell className="py-4 font-medium">
|
||||
{transaction.collectionAmount > 0 ? transaction.collectionAmount.toLocaleString() : '-'}
|
||||
{transaction.collectionAmount > 0 ? formatCurrency(transaction.collectionAmount, displayCurrency) : '-'}
|
||||
</TableCell>
|
||||
<TableCell className="py-4 text-gray-600">
|
||||
{transaction.collectionDate ? formatDate(transaction.collectionDate) : '-'}
|
||||
</TableCell>
|
||||
<TableCell className="py-4 font-medium">
|
||||
{transaction.depositAmount > 0 ? transaction.depositAmount.toLocaleString() : '-'}
|
||||
{transaction.depositAmount > 0 ? formatCurrency(transaction.depositAmount, displayCurrency) : '-'}
|
||||
</TableCell>
|
||||
<TableCell className="py-4 text-gray-600">
|
||||
{transaction.depositDate ? formatDate(transaction.depositDate) : '-'}
|
||||
@@ -255,7 +280,7 @@ export const DetailedEmployeePaymentReport = () => {
|
||||
transaction.difference === 0 ? 'text-gray-600' :
|
||||
transaction.difference > 0 ? 'text-green-600' : 'text-red-600'
|
||||
}`}>
|
||||
{transaction.difference === 0 ? '-' : transaction.difference.toLocaleString()}
|
||||
{transaction.difference === 0 ? '-' : formatCurrency(transaction.difference, displayCurrency)}
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
Reference in New Issue
Block a user