Expenses

A complete guide to the Expenses module — recording clinic operational costs, categorizing expenses by department, linking to vendors and inventory, and tracking financial outflows.

Written By Dev010

Last updated 19 days ago

The Expenses module tracks all money going out of the clinic — from medical supplies and equipment purchases to utility bills and staff costs. Expenses are categorized by type and department, and can be linked to lab vendors and inventory items for complete financial traceability. Together with Invoices and Payments, Expenses completes the financial picture of clinic operations.

Accessing the Module

Sidebar → Expenses

Direct URL:

https://yourdomain.com/dashboard/expenses

Who can access:

Action

Admin

Doctor

Receptionist

Nurse

View expenses

Create expense

Edit expense

Delete expense

Expenses are accessible to Admin and Receptionist roles by default. Adjust in Dashboard → Permissions if needed.

Expenses List

The main expenses page shows a paginated, searchable list of all recorded clinic expenses.

Each row displays:

  • Title

  • Category

  • Amount

  • Payment method

  • Department

  • Vendor

  • Date

  • Status badge

  • Action buttons (edit, delete)

Search

The search bar filters expenses by:

  • Title

  • Vendor name

  • Category

Results update as you type.

Expense Statuses

Status

Description

pending

Expense submitted but not yet approved or paid

approved

Expense approved for payment

paid

Payment has been made

rejected

Expense was rejected

cancelled

Expense was voided

Status defaults to pending when an expense is first recorded. Update to paid once the payment has been made.

Creating an Expense

Click the Add Expense button on the expenses list page.

Direct URL:

https://yourdomain.com/dashboard/expenses/new

Required Fields

Field

Notes

Title

Short description of the expense (e.g. Medical Gloves Restock, Electricity Bill)

Amount

Total cost of the expense

Category

Expense category — see categories below

Payment Method

How the expense was or will be paid

Date

Date the expense was incurred

Status

Defaults to pending

Optional Fields

Description:

Field

Notes

Description

Detailed notes about what was purchased or why

Classification:

Field

Notes

Department

Link to the department this expense belongs to

Submitted By

The staff member who submitted the expense

Vendor:

Field

Notes

Vendor

Free text vendor name

Vendor ID

Link to a Lab Vendor record for formal supplier tracking

Inventory:

Field

Notes

Inventory Item

Link to an inventory item if the expense relates to purchasing stock

Supporting Document:

Field

Notes

Receipt URL

URL to an uploaded receipt or invoice document

Notes:

Field

Notes

Notes

Additional internal notes

Saving the Expense

Click Create Expense to save. The expense appears immediately in the list and is included in the Admin dashboard financial calculations.

Expense Categories

Categories classify what type of expense was incurred. Using consistent categories makes financial reporting meaningful.

CareNova includes the following default expense categories:

Category

Examples

Medical Supplies

Gloves, syringes, dressings, consumables

Equipment

Medical devices, computers, furniture

Medications

Drug stock purchases

Laboratory

Lab reagents, test kits, equipment

Utilities

Electricity, water, internet, phone

Rent

Clinic premises rent or lease

Salaries

Payroll and staff compensation

Insurance

Malpractice insurance, building insurance

Marketing

Advertising, website, printed materials

Maintenance

Building repairs, equipment servicing

Training

CPD courses, staff training programs

Other

Any expense that does not fit above

Categories are defined as constants in lib/constants/expenses.ts. To add custom categories, update this file in the codebase.

Payment Methods

Expense payment methods follow the same free text approach as invoice payment methods. Common values:

Method

Use Case

Cash

Petty cash purchases

Card

Business debit or credit card

Bank Transfer

Supplier bank payment

Cheque

Payment by business cheque

Direct Debit

Recurring utility or subscription

Online

Online supplier payment

Use consistent naming across your team to ensure clean payment method reporting and reconciliation.

Linking Expenses to Departments

Assigning an expense to a department enables department-level financial reporting — essential for understanding the true cost of running each part of your clinic.

When creating an expense:

  1. In the Department field, select the relevant department

  2. Save

To compare department expenses against their budgets:

SELECT d.name as department,
       d.annual_budget,
       SUM(e.amount) as total_expenses,
       d.annual_budget - SUM(e.amount) as budget_remaining
FROM expenses e
JOIN departments d ON e.department_id = d.id
WHERE e.status = 'paid'
  AND DATE_TRUNC('year', e.date) = DATE_TRUNC('year', now())
GROUP BY d.id, d.name, d.annual_budget
ORDER BY total_expenses DESC;

Linking Expenses to Vendors

If the expense was paid to a supplier that exists in the Lab Vendors module, link the expense to the vendor record:

  1. In the Vendor ID field, search for and select the vendor

  2. The free text Vendor field is also available for vendors not in the system

This creates a complete picture of how much the clinic spends with each supplier — useful for contract negotiations and supplier performance reviews.

Total Spend Per Vendor

SELECT lv.name as vendor,
       COUNT(e.id) as expense_count,
       SUM(e.amount) as total_spend
FROM expenses e
JOIN lab_vendors lv ON e.vendor_id = lv.id
WHERE e.status = 'paid'
GROUP BY lv.id, lv.name
ORDER BY total_spend DESC;

Linking Expenses to Inventory

When an expense represents a stock purchase, link it to the inventory item being restocked:

  1. In the Inventory Item field, search for and select the item

  2. After saving the expense, update the item quantity in Sidebar → Inventory

This connects the financial cost of purchasing stock to the physical inventory item — giving visibility into the true per-unit cost of maintaining specific stock items.

Cost Per Inventory Item

SELECT i.name as item,
       i.quantity,
       i.unit,
       COUNT(e.id) as purchase_count,
       SUM(e.amount) as total_spent
FROM inventory i
LEFT JOIN expenses e ON e.inventory_item_id = i.id
  AND e.status = 'paid'
GROUP BY i.id, i.name, i.quantity, i.unit
ORDER BY total_spent DESC;

Receipt Management

The Receipt URL field stores a link to a digital receipt or supplier invoice. Recommended workflow:

  1. Upload the receipt to Supabase Storage (medical-attachments bucket or a dedicated receipts bucket)

  2. Copy the public URL

  3. Paste into the Receipt URL field when creating the expense

CareNova does not currently include a built-in receipt upload button on the expense form. Upload files manually via Supabase Storage and paste the URL into the field.

Expense Approval Workflow

For clinics that require expense approval before payment:

Step 1 — Receptionist submits:

  1. Sidebar → Expenses → Add Expense

  2. Fill in all details

  3. Set Status to pending

  4. Set Submitted By to their own user account

  5. Save

Step 2 — Admin reviews:

  1. Sidebar → Expenses

  2. Filter by pending status

  3. Review expense details

  4. Edit → set Status to approved or rejected

  5. Save

Step 3 — Admin marks as paid:

  1. After payment is made to vendor

  2. Edit expense → set Status to paid

  3. Add transaction reference in Notes if applicable

  4. Save

Revenue vs Expenses Chart

Expense data feeds directly into the Admin dashboard Revenue vs Expenses bar chart. The chart compares monthly revenue (from paid invoices) against monthly expenses (from paid expense records).

For accurate chart data:

  • Set expense status to paid once the payment has been made

  • Ensure the Date field reflects when the expense was actually incurred

  • Keep consistent category usage for meaningful breakdowns

Financial Reporting Queries

Monthly Expense Summary

SELECT DATE_TRUNC('month', date) as month,
       COUNT(*) as expense_count,
       SUM(amount) as total_expenses
FROM expenses
WHERE status = 'paid'
GROUP BY DATE_TRUNC('month', date)
ORDER BY month DESC;

Expenses by Category This Month

SELECT category,
       COUNT(*) as count,
       SUM(amount) as total
FROM expenses
WHERE status = 'paid'
  AND DATE_TRUNC('month', date) = 
      DATE_TRUNC('month', now())
GROUP BY category
ORDER BY total DESC;

Pending Expenses Awaiting Approval

SELECT title, amount, category,
       vendor, date, submitted_by
FROM expenses
WHERE status = 'pending'
ORDER BY date ASC;

Net Income Calculation

SELECT 
  (SELECT SUM(amount) FROM payments 
   WHERE status = 'completed'
   AND DATE_TRUNC('month', created_at) = 
       DATE_TRUNC('month', now())) as revenue,
  (SELECT SUM(amount) FROM expenses 
   WHERE status = 'paid'
   AND DATE_TRUNC('month', date) = 
       DATE_TRUNC('month', now())) as expenses,
  (SELECT SUM(amount) FROM payments 
   WHERE status = 'completed'
   AND DATE_TRUNC('month', created_at) = 
       DATE_TRUNC('month', now())) -
  (SELECT SUM(amount) FROM expenses 
   WHERE status = 'paid'
   AND DATE_TRUNC('month', date) = 
       DATE_TRUNC('month', now())) as net_income;

Database Schema Reference

expenses

Column

Type

Description

id

uuid

Unique expense ID

title

text

Expense title

description

text

Detailed description

amount

numeric

Expense amount

category

text

Expense category

payment_method

text

Payment method used

status

text

pending / approved / paid / rejected / cancelled

date

timestamptz

Date expense was incurred

vendor

text

Free text vendor name

receipt_url

text

URL to receipt document

notes

text

Additional notes

department_id

uuid

FK → departments (set null on delete)

submitted_by

uuid

FK → users (set null on delete)

vendor_id

uuid

FK → lab_vendors (set null on delete)

inventory_item_id

uuid

FK → inventory (set null on delete)

created_at

timestamptz

Record creation timestamp

updated_at

timestamptz

Last updated timestamp

Indexes are set on date, status, category, department_id, submitted_by, vendor_id, and inventory_item_id for fast filtering and reporting.

Relationship to Other Modules

Module

Relationship

Departments

Expenses assigned to departments for budget tracking

Lab Vendors

Expenses linked to supplier vendor records

Inventory

Expenses linked to stock purchase items

Users

Expense submitted_by links to staff user account

Dashboard

Expense totals feed Admin revenue vs expenses chart

Workflow Examples

Recording a medical supplies purchase:

  1. Supplier delivers new stock

  2. Sidebar → Expenses → Add Expense

  3. Title: Surgical Gloves Restock — March

  4. Amount: 245.00

  5. Category: Medical Supplies

  6. Payment Method: Bank Transfer

  7. Date: today

  8. Department: General Medicine

  9. Vendor ID: link to supplier in Lab Vendors

  10. Inventory Item: link to Surgical Gloves in Inventory

  11. Status: paid (if already paid) or pending (if awaiting payment)

  12. Save

  13. Also update inventory quantity in Sidebar → Inventory

Recording a monthly utility bill:

  1. Sidebar → Expenses → Add Expense

  2. Title: Electricity Bill — March 2026

  3. Amount: 380.00

  4. Category: Utilities

  5. Payment Method: Direct Debit

  6. Date: billing date

  7. Department: Administration

  8. Status: paid

  9. Save

Submitting an expense for admin approval:

  1. Receptionist → Add Expense

  2. Fill in all details

  3. Submitted By: select own account

  4. Status: pending

  5. Save — admin will review and approve or reject

Month-end financial review:

  1. Run monthly expense summary query above in SQL Editor

  2. Compare against previous months

  3. Review expenses by category to identify unusual spending

  4. Compare department totals against budgets

  5. Ensure all pending expenses are approved and marked paid

Tracking a recurring expense: Each month when the bill arrives:

  1. Add Expense with current month in the title

  2. Use consistent category and department each month

  3. This builds a clean monthly history for that expense type

Troubleshooting

Expenses not visible in sidebar:

  • Confirm your role has billing.view permission in Dashboard → Permissions

  • Doctor and Nurse do not have billing access by default

Revenue vs Expenses chart not showing expense data:

  • Confirm expense status is paid — pending and approved expenses are excluded from chart calculations

  • Confirm the expense Date falls within the chart's displayed date range

  • Hard refresh the Admin dashboard

Department dropdown empty:

  • No active departments configured

  • Go to Sidebar → Departments and create departments first

Vendor ID dropdown empty:

  • No Lab Vendor records exist

  • Go to Sidebar → Lab Vendors and create vendor records first

  • Or use the free text Vendor field for vendors not in the system

Inventory Item dropdown empty:

  • No active inventory items exist

  • Go to Sidebar → Inventory and add items first

Cannot delete an expense:

  • Expense deletion is restricted to Admin role only

  • Receptionist can create and edit but not delete

  • Consider setting status to cancelled instead of deleting to preserve the audit trail

Expense amount not appearing in department budget comparison:

  • Confirm the expense has a department assigned

  • Confirm expense status is paid

  • Run the department budget comparison query above manually in SQL Editor

Next Step

Continue to the Blog & News module guide to learn how to create and manage clinic blog posts and news articles in CareNova.