Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 97 additions & 3 deletions app/blocks/income-statement/export-options.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,107 @@
import { IconDownload, IconFileText } from "@tabler/icons-react";
import styles from "./export-options.module.css";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";

interface Props { className?: string; }
interface Props {
className?: string;
sales?: any[];
expenses?: any[];
}

export function ExportOptions({ className, sales = [], expenses = [] }: Props) {
const handleExportPDF = () => {
const doc = new jsPDF();

doc.setFontSize(20);
doc.text("FlipTrack Income Statement", 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Generated on ${new Date().toLocaleDateString()}`, 14, 30);

const revenue = sales.reduce((sum, s) => sum + s.salePrice, 0);
const cogs = sales.reduce((sum, s) => sum + (s.inventoryItem?.purchasePrice || 0), 0);
const platformFees = sales.reduce((sum, s) => sum + s.platformFee, 0);
const shippingCosts = sales.reduce((sum, s) => sum + s.shippingCost, 0);
const totalExpenses = expenses.reduce((sum, e) => sum + e.amount, 0);

const grossProfit = revenue - cogs;
const netProfit = grossProfit - platformFees - shippingCosts - totalExpenses;

autoTable(doc, {
startY: 40,
head: [["Metric", "Amount"]],
body: [
["Total Revenue", `$${revenue.toFixed(2)}`],
["Cost of Goods Sold (COGS)", `$${cogs.toFixed(2)}`],
["Gross Profit", `$${grossProfit.toFixed(2)}`],
["Platform Fees", `$${platformFees.toFixed(2)}`],
["Shipping Costs", `$${shippingCosts.toFixed(2)}`],
["Other Expenses", `$${totalExpenses.toFixed(2)}`],
["Net Profit", `$${netProfit.toFixed(2)}`],
],
theme: 'grid',
headStyles: { fillColor: [66, 66, 66] },
});

const monthlyData: Record<string, { revenue: number, costs: number, expenses: number }> = {};

sales.forEach(s => {
const date = new Date(s.saleDate);
const month = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
if (!monthlyData[month]) monthlyData[month] = { revenue: 0, costs: 0, expenses: 0 };
monthlyData[month].revenue += s.salePrice;
monthlyData[month].costs += (s.inventoryItem?.purchasePrice || 0) + s.platformFee + s.shippingCost;
});

expenses.forEach(e => {
const date = new Date(e.date);
const month = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
if (!monthlyData[month]) monthlyData[month] = { revenue: 0, costs: 0, expenses: 0 };
monthlyData[month].expenses += e.amount;
});

const monthlyRows = Object.entries(monthlyData)
.sort((a, b) => a[0].localeCompare(b[0]))
.map(([month, data]) => [
month,
`$${data.revenue.toFixed(2)}`,
`$${data.costs.toFixed(2)}`,
`$${data.expenses.toFixed(2)}`,
`$${(data.revenue - data.costs - data.expenses).toFixed(2)}`
]);

if (monthlyRows.length > 0) {
autoTable(doc, {
startY: (doc as any).lastAutoTable.finalY + 10,
head: [["Month", "Revenue", "COGS & Fees", "Expenses", "Net Profit"]],
body: monthlyRows,
theme: 'striped',
headStyles: { fillColor: [66, 66, 66] },
});
}

if (expenses.length > 0) {
autoTable(doc, {
startY: (doc as any).lastAutoTable.finalY + 10,
head: [["Date", "Expense Category", "Amount"]],
body: expenses.map(e => [
new Date(e.date).toLocaleDateString(),
e.category,
`$${e.amount.toFixed(2)}`
]),
theme: 'striped',
headStyles: { fillColor: [66, 66, 66] },
});
}

doc.save("fliptrack_income_statement.pdf");
};

export function ExportOptions({ className }: Props) {
return (
<div className={[styles.section, className].filter(Boolean).join(" ")}>
<a href="/api/export/tax" download className={[styles.btn, styles.csvBtn].join(" ")} style={{ textDecoration: 'none' }}><IconDownload size={16} /> Export CSV</a>
<button className={[styles.btn, styles.pdfBtn].join(" ")}><IconFileText size={16} /> Export PDF</button>
<button className={[styles.btn, styles.pdfBtn].join(" ")} onClick={handleExportPDF}><IconFileText size={16} /> Export PDF</button>
</div>
);
}
61 changes: 57 additions & 4 deletions app/blocks/tax-report-export/export-options.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,65 @@
import styles from "./export-options.module.css";
import jsPDF from "jspdf";
import autoTable from "jspdf-autotable";

interface Props {
taxYear: number;
className?: string;
sales?: any[];
expenses?: any[];
}

export function ExportOptions({ taxYear, className }: Props) {
export function ExportOptions({ taxYear, className, sales = [], expenses = [] }: Props) {
const handleExportPDF = () => {
const doc = new jsPDF();

doc.setFontSize(20);
doc.text(`FlipTrack Tax Report - ${taxYear}`, 14, 22);
doc.setFontSize(11);
doc.setTextColor(100);
doc.text(`Generated on ${new Date().toLocaleDateString()}`, 14, 30);

const revenue = sales.reduce((sum, s) => sum + s.salePrice, 0);
const cogs = sales.reduce((sum, s) => sum + (s.inventoryItem?.purchasePrice || 0), 0);
const platformFees = sales.reduce((sum, s) => sum + s.platformFee, 0);
const shippingCosts = sales.reduce((sum, s) => sum + s.shippingCost, 0);
const totalExpenses = expenses.reduce((sum, e) => sum + e.amount, 0);

const grossProfit = revenue - cogs;
const netProfit = grossProfit - platformFees - shippingCosts - totalExpenses;

autoTable(doc, {
startY: 40,
head: [["Tax Summary Metric", "Amount"]],
body: [
["Total Revenue", `$${revenue.toFixed(2)}`],
["Cost of Goods Sold (COGS)", `$${cogs.toFixed(2)}`],
["Gross Profit", `$${grossProfit.toFixed(2)}`],
["Deductible Fees (Platform)", `$${platformFees.toFixed(2)}`],
["Deductible Costs (Shipping)", `$${shippingCosts.toFixed(2)}`],
["Other Business Expenses", `$${totalExpenses.toFixed(2)}`],
["Net Taxable Income", `$${netProfit.toFixed(2)}`],
],
theme: 'grid',
headStyles: { fillColor: [66, 66, 66] },
});

if (expenses.length > 0) {
autoTable(doc, {
startY: (doc as any).lastAutoTable.finalY + 10,
head: [["Date", "Expense Category", "Amount"]],
body: expenses.map(e => [
new Date(e.date).toLocaleDateString(),
e.category,
`$${e.amount.toFixed(2)}`
]),
theme: 'striped',
headStyles: { fillColor: [66, 66, 66] },
});
}

doc.save(`fliptrack_tax_report_${taxYear}.pdf`);
};
return (
<div className={[styles.section, className].filter(Boolean).join(" ")}>
<a
Expand All @@ -29,8 +83,7 @@ export function ExportOptions({ taxYear, className }: Props) {
</a>
<button
className={styles.btn}
disabled
style={{ opacity: 0.5, cursor: "not-allowed" }}
onClick={handleExportPDF}
>
<span className={styles.btnIcon}>📊</span>
PDF Report
Expand All @@ -42,7 +95,7 @@ export function ExportOptions({ taxYear, className }: Props) {
marginTop: "2px",
}}
>
Coming soon
Download PDF
</span>
</button>
<button
Expand Down
2 changes: 1 addition & 1 deletion app/routes/income-statement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export default function IncomeStatementPage() {
<ExpenseCategoryBreakdown expenses={expenses} />
</div>
<DetailedStatementTable sales={sales} expenses={expenses} />
<ExportOptions />
<ExportOptions sales={sales} expenses={expenses} />
</div>
);
}
2 changes: 1 addition & 1 deletion app/routes/tax-report-export.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export default function TaxReportExportPage() {
<TaxReportHeader />
<ReportGenerator taxYear={taxYear} />
<ReportPreview sales={sales} expenses={expenses} taxYear={taxYear} />
<ExportOptions taxYear={taxYear} />
<ExportOptions taxYear={taxYear} sales={sales} expenses={expenses} />
<ReportHistory />
</div>
);
Expand Down
Loading
Loading