IA & Automation

Building an expense management app in Quebec with Claude Code: from PRD to deployment

Complete guide to creating a Next.js income and expense tracking application with automatic GST/QST calculation, AI invoice scanning and monthly reports - all generated with Claude Code.

21 mars 202615 min de lecturePASCAL POTVIN
Écouter l'article

Why build your own financial management app with Claude Code

Self-employed workers and small businesses in Quebec juggle a unique tax reality: the federal GST at five percent, the provincial QST at 9.975 percent, quarterly instalments, Revenu Québec-eligible expense categories and mandatory tax registration thresholds. Accounting software such as QuickBooks or Wave cover these needs, but impose monthly subscriptions, overloaded interfaces and little control over business logic. For a developer or designer who has mastered modern tools, building your own application with Claude Code has become not only realistic, but surprisingly fast.

Claude Code is an agentic programming tool that lives in your terminal. Unlike a conventional chat assistant, it understands your complete codebase, executes commands, creates and modifies files, runs tests and manages Git commits - all in natural language. You describe what you want, and Claude Code builds it. The fundamental difference with copying and pasting code from a chatbot is that Claude Code has the context of your entire project: it knows which components exist, which database you're using, which dependencies are installed. Every instruction is executed in the actual context of your application.

This step-by-step guide will take you through the process of building a complete income and expense management application adapted to the Quebec tax system. You'll write a structured PRD that Claude Code can interpret, define a Next.js architecture with Supabase, implement invoice scanning using AI vision, automate GST/QST calculations and generate monthly reports that can be exported to PDF. Each step includes the exact prompt to be given to Claude Code and the expected result.

Step 1 - The PRD: give Claude Code a clear vision of the product

Before writing a single line of code, Claude Code needs a structured reference document: the Product Requirements Document. A well-written PRD for an AI agent is not a traditional prose specification. It's a modular document with clearly identified sections, atomic user stories and acceptance criteria formulated as verifiable checkpoints. Claude Code interprets this format as a list of executable tasks. Here's the prompt to start the project and the PRD you'll create in a PRD.md file in the project root.

The initial prompt to give Claude Code is: "Read the PRD.md file and initialize the Next.js project with App Router, Supabase for database and authentication, and Tailwind CSS for styling. Create the folder structure, install the dependencies and configure the CLAUDE.md with the project conventions." The PRD.md itself must contain these sections: Introduction with the Quebec context and target audience (self-employed workers, micro-businesses), Problem describing the complexity of manual GST/QST tracking, Numbered User Stories (US1: as a self-employed worker, I want to photograph an invoice and see the amounts extracted automatically; US2: I want to see my income and expenses by category with GST/QST calculated; US3: I want to generate a monthly PDF report ready for my accountant), Technical Specifications with database schema, and Acceptance Criteria in the form of verifiable bullets.

The complementary CLAUDE.md file - which Claude Code will automatically read at each conversation - defines the technical conventions: "Use Server Components by default, reserve use client for interactive components. Supabase for auth and DB. Amounts are stored in cents (integer) to avoid rounding errors. Tax rates are constants in lib/taxes.js: GST 5 per cent, QST 9.975 per cent. Each component has its own file in components/ with a PascalCase name. The API routes are in app/api/ and validate the inputs with Zod." This file is the highest productivity lever with Claude Code: it avoids repeating the same instructions at each prompt.

md
# CLAUDE.md
- Server Components by default; use client reserved for interactive use
- Supabase for auth and database
- Amounts stored in cents (integer) - zero rounding errors
- Tax rates in lib/taxes.js: 5% GST, 9.975% QST
- API routes in app/api/, inputs validated with Zod
- PascalCase components, one file per component
```

## Step 2 - Supabase database architecture and schema

The architecture is based on three layers. The presentation layer is a Next.js App Router frontend with Server Components for initial loading and Client Components for interactive forms and dashboards. The business logic layer lives in Next.js Route APIs, which manage tax calculation, OCR extraction and report generation. The data layer is Supabase PostgreSQL with Row Level Security to isolate each user's data. The prompt to give Claude Code: "Create Supabase migrations for the following tables according to the PRD schema. Activate RLS on each table with a policy that restricts access to the authenticated user's user_id."

The database schema comprises four main tables. The transactions table contains the fields id (UUID), user_id (reference auth.users), type (enum: income or expense), description (text), amount_cents (integer, amount before taxes), tps_cents (integer), tvq_cents (integer), total_cents (integer), category (text among the Revenu Québec categories: supplies, vehicle, office, telecommunications, meals_representation, subcontracting, advertising, training, insurance, other), date (date), receipt_url (nullable text, URL of file in Supabase Storage), ocr_data (nullable jsonb, extracted raw data), notes (nullable text), created_at and updated_at (timestamptz). The categories table stores customizable categories with a tax_deductible_percentage field for partial deduction categories such as 50 percent meals. The monthly_reports table stores reports generated with period (text in YYYY-MM format), total_revenue_cents, total_expenses_cents, tps_collected_cents, tvq_collected_cents, tps_paid_cents, tvq_paid_cents, net_tax_owing_cents and pdf_url.

Supabase Storage manages invoice files. A private bucket named receipts with an RLS policy that limits access to the owner. The prompt for Claude Code: "Configure Supabase Storage with a receipts bucket. Create a ReceiptUpload component that accepts a photo (camera or file), compresses it client-side to 1200 pixels wide maximum, uploads it to the bucket with a unique name based on user_id and date, and returns the signed public URL." Claude Code will create the component, upload logic and image compression in a single pass.

## Step 3 - Intelligent invoice scanning with AI vision

The feature that transforms this application from a simple spreadsheet into a truly useful tool is the automatic scanning of invoices by photo. Users take a photo of their invoice with their phone, and the application extracts the supplier, date, amount before tax, GST, QST, total and suggests a category. Two technical approaches are viable. The first uses Claude's Vision API to analyze the image directly - Claude understands the context of Quebec invoices and distinguishes GST from QST naturally. The second combines Tesseract.js for client-side OCR with a call to Claude to structure the extracted data.

The recommended approach for extraction quality is Claude's Vision API. The prompt for Claude Code: "Create an API route app/api/scan-receipt/route.js that receives a base64 image, sends it to the Claude API with the claude-sonnet-4-6 template in vision mode, and returns a structured JSON object with the fields vendor, date, subtotal, tps, tvq, total and suggested_category. The system prompt should specify that we're in Quebec, that GST is 5 percent and QST is 9.975 percent, and that the template should extract these amounts from the invoice. If a tax amount is not visible, calculate it from the subtotal." The system prompt sent to Claude in the API route looks like this: "You're an assistant accountant specializing in Quebec taxation. Extract the following information from this invoice: supplier name, date, amount before tax, GST (5 percent), QST (9.975 percent), total. Returns a strict JSON with no comments. If taxes are not displayed separately, calculate them from the subtotal. Suggest an expense category from among: supplies, vehicle, office, telecommunications, meals_representation, subcontracting, advertising, training, insurance, other."

For users who want a solution without external API costs, Tesseract.js works entirely client-side. The prompt for Claude Code: "Add a local OCR alternative with Tesseract.js. Create a useLocalOCR hook that loads the Tesseract worker in lazy, performs recognition on the compressed image, and returns the raw text. Next, create a parseQuebecReceipt function in lib/receiptParser.js that uses regexes to extract the GST and QST amounts typical of Quebec invoices - look for the patterns GST, GST, TVQ, QST followed by dollar amounts." This approach is free, but less accurate on handwritten or poorly photographed invoices. Ideally, you should offer both options and let the user choose in the settings.

## Step 4 - The dashboard: income, expenses and tax balance in real time

The heart of the application is a dashboard that displays the financial situation at a glance. The prompt for Claude Code: "Creates an app/dashboard/page.jsx page that displays four KPI cards at the top (revenue for the month, expenses for the month, net GST/QST to be remitted, net profit), a bar chart of revenue vs. expenses per month over the last 12 months with Recharts, and a table of recent transactions with sorting by date, filtering by category and searching by description. Data comes from Supabase via Server Components for initial loading. Graphics and filters are Client Components. Claude Code will create all the components, Supabase queries and responsive layout.

The calculation of net GST/QST is the core business logic. The formula is simple, but there are many borderline cases. For a self-employed person registered in the tax files, the net GST/QST to be remitted equals the GST/QST collected on revenues minus the GST/QST paid on eligible expenses - that's the input tax credit (ITC for GST) and the input tax refund (ITR for QST). But beware: some categories have restrictions. Meals and entertainment expenses only qualify for 50% of the ITC/ITR. Vehicle expenses are limited to the percentage of commercial use. The prompt for Claude Code: "In lib/taxes.js, create the functions calculateTPS, calculateTVQ, calculateNetTaxOwing and calculateDeductibleAmount. The latter takes a category and an amount, and applies the deductible percentage according to Revenu Québec rules: 100 percent for most categories, 50 percent for repas_représentation. Add unit tests in __tests__/taxes.test.js for each case."

```js
// lib/taxes.js
export const GST = 0.05; // 5%.
export const TVQ = 0.09975; // 9,975 %

export const calculateTPS = (cents) => Math.round(cents * GST);
export const calculateTVQ = (cents) => Math.round(cents * QST);

// Revenu Québec deductibility: meal/representation = 50
export const calculateDeductibleAmount = (category, cents) =>
  category === "repas_representation" ? Math.round(cents * 0.5) : cents;
```

The add transaction form is the screen the user sees most often. It must be fast and frictionless. The prompt: "Creates a TransactionForm component with fields type (toggle income/expense), description, amount before taxes, category (dropdown), date (default today), optional notes and optional receipt upload. When the user enters the pre-tax amount, GST, QST and the real-time total are automatically calculated and displayed below the field. If a receipt photo is uploaded, pre-fills fields with extracted OCR data and lets user confirm or correct before saving. Use React Hook Form for form management and Zod for validation."

## Step 5 - Automated monthly reports and PDF export

Each month, the self-employed worker needs to prepare his figures for his accountant or for his own declarations. The application generates a complete monthly report at the click of a button. The prompt for Claude Code: "Create an API route app/api/reports/monthly/route.js that accepts a period parameter in YYYY-MM format. The route aggregates all transactions for the month: total revenues, total expenses by category, GST collected, QST collected, GST paid on expenses (with adjustment for categories with partial deductibility), QST paid, net GST balance to remit, net QST balance to remit. Stores the report in the monthly_reports table and generates a PDF with react-pdf. The PDF should have a header with the user's name and period, a summary of KPIs, a table of expenses by category and a detailed table of all transactions."

The format of the PDF report is designed with the accountant in mind. The first page displays the summary: gross revenues, total expenses, net profit, net GST and net QST, with the detailed calculation showing amounts collected minus ITC/ITR. The second page shows expenses broken down by category, with the total amount, the amount of GST recoverable and the amount of QST recoverable for each category. The following pages list each transaction with date, description, supplier, amount and an indication of whether a receipt is attached. The prompt: "The PDF uses the user's brand colors if defined, otherwise a sober professional theme. Adds a page number at the bottom and the words Generated automatically by the application on [date]. The file is saved in Supabase Storage in a reports bucket and the URL is stored in monthly_reports."

For automation, a cron job generates the previous month's report on the first day of each month. The prompt: "Create an API route app/api/cron/monthly-report/route.js protected by a CRON_SECRET header. This route identifies all active users, generates the previous month's report for each and sends a notification (email via Resend or in-app notification) with the PDF download link. Set up a Vercel cron job in vercel.json that calls this route on the first of each month at 6 a.m. EST." The user wakes up on the first of the month with his full report ready to send to his accountant.

## Step 6 - Deployment, testing and iteration with Claude Code

Deployment on Vercel is the final step. The prompt for Claude Code: "Configure the project for Vercel deployment. Add the necessary environment variables in .env.example: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, ANTHROPIC_API_KEY for OCR vision, CRON_SECRET for automated tasks. Checks that the build passes without error, that the TypeScript types are valid and that the tests pass. Create a README.md with installation instructions. Claude Code will execute the build, correct any errors and prepare the project for production.

Claude Code's major advantage in this workflow is the rapid iteration loop. After initial deployment, each enhancement follows the same pattern: you describe the change in natural language, Claude Code modifies the files concerned by understanding the existing context, runs the tests and proposes a commit. For example: "Add a pie chart to the dashboard showing the breakdown of expenses by category for the current month. Use the same colors as the category badges." Claude Code already knows where the category colors are, which chart component is used in the project and how to query Supabase - it only modifies what's necessary.

In summary, the complete workflow for building this application with Claude Code follows six steps: write a structured PRD and CLAUDE.md with your conventions, let Claude Code initialize the project and create the database schema, implement invoice scanning with Claude's AI vision, build the dashboard with real-time GST/QST calculations, automate monthly PDF reports and cron tasks, then deploy to Vercel and iterate. Each step is a natural language prompt. The most impressive thing about this approach is that most of the code is generated by understanding your intent - you guide the architecture and product decisions, Claude Code takes care of the implementation. This is exactly the type of project I support at PASCALPOTVIN.COM: transforming an idea into a functional application by making the most of modern AI tools.
§ COMMENTAIRES

Laisser un commentaire