import React, { useMemo, useState } from "react"; import { Download, FileSpreadsheet, Printer, RefreshCw } from "lucide-react"; import { Card, CardContent } from "../components/ui/Card.tsx"; import { Button } from "../components/ui/Button.tsx"; import { api } from "../services/api.ts"; import { useDepartments } from "../hooks/useDepartments.ts"; import * as XLSX from "xlsx"; interface ContractorPaymentData { contractor_id: number; contractor_name: string; as_per_contractor: number; dana: number; tukdi: number; groundnut: number; commission_salary: number; total: number; tds_base_amount: number; payable_before_deduction: number; security_deduction: number; advance: number; final_payable: number; excess_short: number; } interface SubDepartmentTotals { sub_department_id: number; sub_department_name: string; contractors: ContractorPaymentData[]; subTotal: ContractorPaymentData; } export const ContractorPaymentPage: React.FC = () => { const { departments } = useDepartments(); const [selectedDepartment, setSelectedDepartment] = useState(""); const [startDate, setStartDate] = useState(() => { const date = new Date(); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-01`; }); const [endDate, setEndDate] = useState(() => { const date = new Date(); return date.toISOString().split("T")[0]; }); const [loading, setLoading] = useState(false); const [reportData, setReportData] = useState([]); const [companyName, setCompanyName] = useState("DUSAD AGROFOOD PVT LTD"); // Fetch report data const fetchReport = async () => { if (!selectedDepartment) { alert("Please select a department"); return; } setLoading(true); try { // Fetch completed allocations for the date range const params: Record = { startDate, endDate, departmentId: parseInt(selectedDepartment), }; const data = await api.getCompletedAllocationsReport(params); // Process data to group by sub-department and contractor const processedData = processReportData(data.allocations); setReportData(processedData); } catch (err: any) { console.error("Failed to fetch report:", err); alert(err.message || "Failed to fetch report"); } finally { setLoading(false); } }; // Process raw allocation data into the payment report format const processReportData = (allocations: any[]): SubDepartmentTotals[] => { const subDeptMap = new Map>(); const subDeptNames = new Map(); allocations.forEach((alloc) => { const subDeptId = alloc.sub_department_id || 0; const contractorId = alloc.contractor_id || 0; const subDeptName = alloc.sub_department_name || "Other"; subDeptNames.set(subDeptId, subDeptName); if (!subDeptMap.has(subDeptId)) { subDeptMap.set(subDeptId, new Map()); } const contractorMap = subDeptMap.get(subDeptId)!; if (!contractorMap.has(contractorId)) { contractorMap.set(contractorId, { contractor_id: contractorId, contractor_name: alloc.contractor_name || "Unknown", as_per_contractor: 0, dana: 0, tukdi: 0, groundnut: 0, commission_salary: 0, total: 0, tds_base_amount: 0, payable_before_deduction: 0, security_deduction: 0, advance: 0, final_payable: 0, excess_short: 0, }); } const contractor = contractorMap.get(contractorId)!; const amount = parseFloat(alloc.total_amount) || parseFloat(alloc.rate) || 0; const activity = (alloc.activity || "").toLowerCase(); // Categorize by activity type if (activity.includes("dana")) { contractor.dana += amount; } else if (activity.includes("tukdi")) { contractor.tukdi += amount; } else if (activity.includes("groundnut")) { contractor.groundnut += amount; } else if (activity.includes("commission") || activity.includes("salary")) { contractor.commission_salary += amount; } else { contractor.as_per_contractor += amount; } }); // Calculate totals for each contractor const result: SubDepartmentTotals[] = []; subDeptMap.forEach((contractorMap, subDeptId) => { const contractors: ContractorPaymentData[] = []; const subTotal: ContractorPaymentData = { contractor_id: 0, contractor_name: "Sub Total", as_per_contractor: 0, dana: 0, tukdi: 0, groundnut: 0, commission_salary: 0, total: 0, tds_base_amount: 0, payable_before_deduction: 0, security_deduction: 0, advance: 0, final_payable: 0, excess_short: 0, }; contractorMap.forEach((contractor) => { // Calculate derived values contractor.total = contractor.as_per_contractor + contractor.dana + contractor.tukdi + contractor.groundnut + contractor.commission_salary; contractor.tds_base_amount = Math.round(contractor.total * 0.01); // 1% TDS contractor.payable_before_deduction = contractor.total - contractor.tds_base_amount; contractor.security_deduction = Math.round(contractor.total * 0.0035); // 0.35% security contractor.final_payable = contractor.payable_before_deduction - contractor.security_deduction - contractor.advance; contractor.excess_short = contractor.as_per_contractor - contractor.final_payable; // Add to subtotal subTotal.as_per_contractor += contractor.as_per_contractor; subTotal.dana += contractor.dana; subTotal.tukdi += contractor.tukdi; subTotal.groundnut += contractor.groundnut; subTotal.commission_salary += contractor.commission_salary; subTotal.total += contractor.total; subTotal.tds_base_amount += contractor.tds_base_amount; subTotal.payable_before_deduction += contractor.payable_before_deduction; subTotal.security_deduction += contractor.security_deduction; subTotal.advance += contractor.advance; subTotal.final_payable += contractor.final_payable; subTotal.excess_short += contractor.excess_short; contractors.push(contractor); }); result.push({ sub_department_id: subDeptId, sub_department_name: subDeptNames.get(subDeptId) || "Other", contractors, subTotal, }); }); return result; }; // Format date for display const formatDateRange = () => { const start = new Date(startDate); const end = new Date(endDate); return `${start.toLocaleDateString("en-GB", { day: "2-digit", month: "2-digit", year: "numeric" }).replace(/\//g, ".")} TO ${end.toLocaleDateString("en-GB", { day: "2-digit", month: "2-digit", year: "numeric" }).replace(/\//g, ".")}`; }; // Export to Excel const exportToExcel = () => { if (reportData.length === 0) { alert("No data to export"); return; } const wb = XLSX.utils.book_new(); const wsData: any[][] = []; // Header rows wsData.push([companyName]); wsData.push([formatDateRange()]); wsData.push(["ALL CONTRACTOR PAYMENT SHEET"]); wsData.push([]); // Column headers wsData.push([ "S.NO", "NAME CONTRACTOR", "As per Contractor", "", "", "", "", "", "TDS @1%", "Payable", "SECURITY", "ADVANCE", "Final Payable", "Excess/Short" ]); wsData.push([ "", "", "ALL", "DANA", "TUKDI", "GROUNDNUT", "Commission & Salary", "Total", "(BASE AMOUNT)", "(Before deduction)", "DEDUCTION 0.35%", "", "", "" ]); let serialNo = 1; reportData.forEach((subDept) => { // Sub-department header wsData.push([subDept.sub_department_name.toUpperCase()]); subDept.contractors.forEach((contractor) => { wsData.push([ serialNo++, contractor.contractor_name, contractor.as_per_contractor || "", contractor.dana || "", contractor.tukdi || "", contractor.groundnut || "", contractor.commission_salary || "", contractor.total || "", contractor.tds_base_amount || "", contractor.payable_before_deduction || "", contractor.security_deduction || "", contractor.advance || "", contractor.final_payable || "", contractor.excess_short || "", ]); }); // Sub Total row wsData.push([ "", "Sub Total", subDept.subTotal.as_per_contractor, subDept.subTotal.dana, subDept.subTotal.tukdi, subDept.subTotal.groundnut, subDept.subTotal.commission_salary, subDept.subTotal.total, subDept.subTotal.tds_base_amount, subDept.subTotal.payable_before_deduction, subDept.subTotal.security_deduction, subDept.subTotal.advance, subDept.subTotal.final_payable, subDept.subTotal.excess_short, ]); }); // Footer wsData.push([]); wsData.push(["CONTRACTOR", "", "", "", "", "", "CHECKER", "", "", "", "", "", "AUTHORISED AUTHORITY"]); const ws = XLSX.utils.aoa_to_sheet(wsData); // Set column widths ws["!cols"] = [ { wch: 5 }, // S.NO { wch: 30 }, // NAME CONTRACTOR { wch: 12 }, // ALL { wch: 10 }, // DANA { wch: 10 }, // TUKDI { wch: 12 }, // GROUNDNUT { wch: 14 }, // Commission { wch: 12 }, // Total { wch: 12 }, // TDS { wch: 12 }, // Payable { wch: 12 }, // Security { wch: 10 }, // Advance { wch: 14 }, // Final Payable { wch: 12 }, // Excess/Short ]; XLSX.utils.book_append_sheet(wb, ws, "Payment Report"); const filename = `contractor_payment_${startDate}_to_${endDate}.xlsx`; XLSX.writeFile(wb, filename); }; // Print report const printReport = () => { window.print(); }; // Calculate grand totals const grandTotal = useMemo(() => { const total: ContractorPaymentData = { contractor_id: 0, contractor_name: "Grand Total", as_per_contractor: 0, dana: 0, tukdi: 0, groundnut: 0, commission_salary: 0, total: 0, tds_base_amount: 0, payable_before_deduction: 0, security_deduction: 0, advance: 0, final_payable: 0, excess_short: 0, }; reportData.forEach((subDept) => { total.as_per_contractor += subDept.subTotal.as_per_contractor; total.dana += subDept.subTotal.dana; total.tukdi += subDept.subTotal.tukdi; total.groundnut += subDept.subTotal.groundnut; total.commission_salary += subDept.subTotal.commission_salary; total.total += subDept.subTotal.total; total.tds_base_amount += subDept.subTotal.tds_base_amount; total.payable_before_deduction += subDept.subTotal.payable_before_deduction; total.security_deduction += subDept.subTotal.security_deduction; total.advance += subDept.subTotal.advance; total.final_payable += subDept.subTotal.final_payable; total.excess_short += subDept.subTotal.excess_short; }); return total; }, [reportData]); return (

Contractor Payment Report

{/* Filters */}
setCompanyName(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500" />
setStartDate(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500" />
setEndDate(e.target.value)} className="w-full px-3 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500" />
{/* Report Table */} {reportData.length > 0 && (
{/* Report Header */}

{companyName}

{formatDateRange()}

ALL CONTRACTOR PAYMENT SHEET

{/* Table */} {reportData.map((subDept, subDeptIdx) => ( {/* Sub-department header */} {/* Contractors */} {subDept.contractors.map((contractor, idx) => { const rowNum = reportData .slice(0, subDeptIdx) .reduce((acc, sd) => acc + sd.contractors.length, 0) + idx + 1; return ( ); })} {/* Sub Total */} ))} {/* Grand Total */}
S.NO NAME CONTRACTOR As per DAFPL TDS @1%
(BASE AMOUNT)
Payable
(Before deduction)
SECURITY
DEDUCTION 0.35%
ADVANCE Final Payable Excess/Short
ALL DANA TUKDI GROUNDNUT Commission
& Salary
Total
{subDept.sub_department_name.toUpperCase()}
{rowNum} {contractor.contractor_name} {contractor.as_per_contractor || ""} {contractor.dana || ""} {contractor.tukdi || ""} {contractor.groundnut || ""} {contractor.commission_salary || ""} {contractor.total || ""} {contractor.tds_base_amount || ""} {contractor.payable_before_deduction || ""} {contractor.security_deduction || ""} {contractor.advance || ""} {contractor.final_payable || ""} {contractor.excess_short || ""}
Sub Total {subDept.subTotal.as_per_contractor} {subDept.subTotal.dana} {subDept.subTotal.tukdi} {subDept.subTotal.groundnut} {subDept.subTotal.commission_salary} {subDept.subTotal.total} {subDept.subTotal.tds_base_amount} {subDept.subTotal.payable_before_deduction} {subDept.subTotal.security_deduction} {subDept.subTotal.advance} {subDept.subTotal.final_payable} {subDept.subTotal.excess_short}
GRAND TOTAL {grandTotal.as_per_contractor} {grandTotal.dana} {grandTotal.tukdi} {grandTotal.groundnut} {grandTotal.commission_salary} {grandTotal.total} {grandTotal.tds_base_amount} {grandTotal.payable_before_deduction} {grandTotal.security_deduction} {grandTotal.advance} {grandTotal.final_payable} {grandTotal.excess_short}
{/* Footer */}
CONTRACTOR
CHECKER
AUTHORISED AUTHORITY
)} {reportData.length === 0 && !loading && (

Select a department and date range, then click "Generate Report"

)}
{/* Print Styles */}
); };