ABI

Abhijith P A

01Blog
Back to Blog
ReactDjangoPostgreSQL

How I Built Finwage — Payroll & HRMS with React and Django

AlgoBiz · 2026

June 25, 2026 · 15 min read

How I Built Finwage — Payroll & HRMS with React and Django

A deep dive into building Finwage, a payroll management system and HRMS using React, Django, PostgreSQL & AWS. Architecture decisions, salary calculations, attendance tracking, and lessons learned at AlgoBiz.

ReactDjangoPostgreSQLERPHRMSPayrollAlgoBiz

Finwage started as a simple payroll calculator and grew into a full HRMS handling attendance, leave management, salary processing, tax deductions, and employee self-service for multiple organizations. Here's how I built it at AlgoBiz using React, Django, PostgreSQL, and AWS.

The Problem

Most businesses in Kerala still manage payroll on spreadsheets. They track attendance manually, calculate salaries by hand, and forget about tax compliance until the deadline. We needed a system that handles the entire lifecycle: attendance in → salary calculation → tax deduction → payslip generation → bank disbursement file.

Architecture Overview

Finwage is a multi-tenant SaaS — each company gets isolated data with shared infrastructure. The frontend is React with TypeScript and Tailwind CSS for the dashboard. The backend is Django REST Framework with PostgreSQL for transactional data. AWS handles hosting (EC2), file storage (S3 for payslips), and email notifications (SES).

Frontend: React + TypeScript + Tailwind CSS\nBackend: Django REST Framework + Python 3.12\nDatabase: PostgreSQL 16 (multi-tenant with schema isolation)\nQueue: Celery + Redis (for payroll batch processing)\nStorage: AWS S3 (payslips, documents)\nHosting: AWS EC2 + RDS + CloudFront\nCI/CD: GitHub Actions → Docker → ECR → ECS

Attendance Management

Attendance is the foundation of payroll. Finwage supports multiple clock-in methods: biometric device integration (via API), mobile GPS check-in, and manual entry with approval workflows. The tricky part is handling shift patterns — rotational shifts, split shifts, overtime calculation, and half-day logic. I modeled shifts as PostgreSQL JSONB columns with validation at the Django serializer level.

Salary Calculation Engine

Salary calculation looks simple until you account for Indian payroll complexity: PF (Provident Fund), ESI (Employee State Insurance), Professional Tax (state-specific), TDS (Tax Deducted at Source), LOP (Loss of Pay), overtime, bonuses, and reimbursements. I built a rule engine where each component is a Python class with a calculate() method. Components can depend on other components (PF depends on basic salary), so I use topological sorting to determine calculation order.

class SalaryComponent(ABC):\n    @abstractmethod\n    def calculate(self, context: PayrollContext) -> Decimal:\n        pass\n\nclass BasicSalary(SalaryComponent):\n    def calculate(self, ctx: PayrollContext) -> Decimal:\n        working_days = ctx.attendance.working_days\n        total_days = ctx.period.total_days\n        return (ctx.employee.ctc_basic * working_days) / total_days\n\nclass PFContribution(SalaryComponent):\n    depends_on = ['basic_salary']\n    \n    def calculate(self, ctx: PayrollContext) -> Decimal:\n        basic = ctx.computed['basic_salary']\n        return min(basic * Decimal('0.12'), Decimal('1800'))

Payslip Generation

Payslips are generated as PDFs using WeasyPrint (HTML/CSS to PDF) with Jinja2 templates. Each company can customize their payslip template — logo, layout, which components to show. The generation runs as a Celery batch job because processing 500+ employees' payslips synchronously would timeout. We generate them, upload to S3, and email links to employees.

Multi-Tenant Architecture

Multi-tenancy in Django is tricky. I chose PostgreSQL schema-based isolation using django-tenants. Each company gets its own schema with identical table structures. Shared data (plans, billing) lives in the public schema. This gives us true data isolation (a bug in one tenant's query can never leak another tenant's data) while keeping infrastructure costs manageable.

Frontend Dashboard

The React dashboard is where HR managers spend their time. It shows attendance summaries, pending approvals, salary status, and compliance alerts. I used Recharts for salary trend visualizations and TanStack Table for the employee data grid (sorting, filtering, pagination on 1000+ rows). The calendar view for attendance uses a custom component built with date-fns.

Lessons Learned

  • Indian payroll has edge cases that no documentation covers — you learn by getting it wrong
  • Multi-tenant schema isolation is worth the complexity for financial data
  • Batch processing with Celery prevents timeout issues on payroll runs
  • Always store monetary values as Decimal, never float
  • Generate audit logs for every salary calculation — compliance requires it
  • PDF generation is slow — always do it async and cache the result
  • Test with real payroll data (anonymized) — synthetic data misses edge cases

What's Next

We're adding AI-powered anomaly detection for attendance (flagging unusual patterns), automated tax filing integration, and a mobile app for employee self-service. Finwage started as a side project and now handles real payroll for multiple companies — the most rewarding kind of software to build.