ES
bikefy — banner
← Back to projects

bikefy

· 6 min read

Platform for bicycle workshop management that tracks inventory, repairs, and finances in one place, replacing spreadsheets and cutting manual errors.

Next.js JavaScript TailwindCSS Express PostgreSQL

About the project

Bicycle workshops handle multiple fronts at once: parts, repairs, finances, and suppliers. bikefy centralizes that entire operation on a single platform, eliminating spreadsheets and paperwork.

The system records every movement—from buying a part from a supplier to selling it to a customer—keeping full traceability and updating stock automatically.

Objectives:

  • Inventory control with stock traceability
  • Purchase and sale recording with automatic stock updates
  • Repair and maintenance service management
  • Financial management with balances, income, and expenses
  • Kanban board for team tasks
  • User management with roles (admin and employee)

Key features

ModuleFeatures
InventoryItem CRUD, automatic stock, categories, purchase/sale prices, Excel/PDF export
Purchases & SalesTransaction recording linked to suppliers, automatic stock adjustment, complete traceability
FinancesGeneral balance, income/expense charts, transaction history
ServicesRepair recording, pricing, payment methods, service status
SuppliersCRUD with contact details, associated purchase history
TasksKanban board with draggable columns and cards
UsersAdmin/employee roles, JWT authentication, email password recovery

Tech stack

LayerTechnologyPurpose
FrontendNext.js 15 (Pages Router)React framework with SSR
Tailwind CSS + CSS VariablesUtility-first styles with light/dark theme
Shadcn/ui (New York) + Radix UI + MUIAccessible UI components
Formik + YupForm handling and validation
Chart.js + react-chartjs-2Finance charts
AxiosHTTP client for API consumption
BackendExpress.js 4REST API
express-validator + JoiInput data validation
jsonwebtokenJWT Bearer authentication
bcryptPassword hashing
CloudinaryProfile image storage
NodemailerEmail sending (password recovery)
DatabasePostgreSQL 16Relational database
pg (Client)Connection driver
DeployVercel (backend) + PM2 (frontend)Production

Purchase flow

User ──▶ Form (Frontend)


         POST /api/inventory/purchase


         inventory.controller → inventory.service

              ├──▶ 1. Creates transaction (INSERT transaction)
              ├──▶ 2. For each item:
              │       ├──▶ Does the item exist?
              │       │     ├── Yes → updateStock(id, quantity, 'add')
              │       │     └── No  → createItem(new item)
              │       ├──▶ Creates detail (INSERT transaction_item)
              │       └──▶ Links supplier (INSERT/UPDATE item_supplier)


         Stock updated + transaction recorded

Sale flow

User ──▶ Selects items from inventory


         validateStock() — Is there enough stock?

              ├── No  → Error: "Insufficient stock"

              └── Yes → Creates 'sale' transaction

                         └──▶ For each item:
                                 ├── updateStock(id, quantity, 'subtract')
                                 └── Creates detail (INSERT transaction_item)

Deletion with rollback

When deleting a purchase or sale, the system automatically reverts the stock:

  • Delete sale → returns stock to inventory (add)
  • Delete purchase → deducts the purchased stock (subtract), and if the supplier has no remaining active transactions with that item, it is unlinked

My role and contribution

Within the 4-person team, I was responsible for the complete design and implementation of the following modules, covering both backend and frontend:

Inventory

Full CRUD of items with name, description, category, stock, and sale price. Each item can be linked to one or more suppliers with their respective purchase price and date.

Frontend components:

  • InventoryTable — table with search, filters by category and supplier, sorting
  • AddItemDialog / EditItemDialog — forms with Yup validation
  • ItemDetailsDialog — detailed view with associated suppliers
  • ExportButtons — export to Excel (XLSX) and PDF (jsPDF)

Purchases & Sales

The biggest module in the system. It records purchases from suppliers and sales to customers, adjusting inventory stock automatically.

Purchase:

  • Selection of existing items or creation on the fly
  • Assignment of supplier, quantity, unit price, and payment method
  • Automatic stock increment
  • Recording of the item-supplier relationship with purchase price

Sale:

  • Selection of items from inventory with available stock
  • Sufficient stock validation before confirming
  • Automatic stock decrement
  • Transaction recording with payment method

Editing and deletion:

  • Quantity modification with differential stock adjustment
  • Deletion with full inventory rollback

Modules in detail

Backend

  • Item model (backend/src/models/Item.js): Item CRUD with parameterized SQL queries, name search (ILIKE), associated supplier aggregation with ARRAY_AGG, and updateStock operation with add/subtract mode for atomic inventory adjustments.

  • Inventory model (backend/src/models/Inventory.js): Data access layer for purchase/sale transactions. Includes:

    • createTransaction / createTransactionDetails — header and detail insertion
    • updateSupplier — upsert of the item-supplier relationship with purchase price
    • validateStock — stock verification before selling
    • getPurchases / getSales — queries with JOINs to item, supplier, and transaction_item
    • softDeleteTransaction — logical deletion (is_deleted = TRUE)
    • getActiveTransactionsByItemAndSupplier — to decide whether to unlink supplier on deletion
  • Transaction model (backend/src/models/Transaction.js): CRUD of financial transactions and getSummary for income/expense balance.

  • inventory.service.js service: All business logic for the purchase/sale flow:

    • createPurchase — creates or updates items, assigns suppliers, adjusts stock
    • createSale — validates stock, creates transaction, deducts inventory
    • updatePurchase / updateSale — differential stock adjustment (difference between new and previous quantity)
    • deletePurchase / deleteSale — stock rollback and supplier unlinking
  • Controllers (inventory.controller.js, item.controller.js, transaction.controller.js): HTTP request handling, validation with Joi, and responses with appropriate status codes.

  • Validations (item.validation.js, transaction.validation.js): Joi schemas for creating and updating items and transactions.

  • Routes (inventory.routes.js, item.routes.js, transaction.routes.js): Endpoint definitions with authentication and authorization middleware.

Implemented endpoints:

MethodRouteDescription
GET/api/itemsList items
POST/api/itemsCreate item
GET/api/items/:idGet item by ID
PUT/api/items/:idUpdate item
DELETE/api/items/:idDelete item (soft delete)
GET/api/inventory/purchasesList purchases
POST/api/inventory/purchaseRecord purchase
PUT/api/inventory/purchase/:id_transactionUpdate purchase
DELETE/api/inventory/purchase/:idDelete purchase
GET/api/inventory/salesList sales
POST/api/inventory/saleRecord sale
PUT/api/inventory/sale/:id_transactionUpdate sale
DELETE/api/inventory/sale/:idDelete sale

Frontend

  • inventory.jsx page: 3-tab layout (Products / Purchases / Sales) with tab navigation.

  • InventoryTable.jsx: Interactive table with search, filters by category and supplier, column sorting, context menu, and modal dialogs for creating, editing, detailing, and deleting.

  • PurchasesTable.jsx / SalesTable.jsx: Transaction history tables with currency formatting, localized dates, and export.

  • Dialogs: AddItemDialog, EditItemDialog, ItemDetailsDialog, SellItemDialog, AddPurchaseDialog, EditPurchaseDialog, EditSaleDialog, PurchaseDetailsDialog, SaleDetailsDialog, NewPurchaseForm, ExistingPurchaseForm.

  • ExportButtons.jsx: Export of inventory, purchases, and sales to Excel and PDF using xlsx and jspdf-autotable.

  • API layer (src/api/inventory.js): Axios functions to consume each backend endpoint.

  • Frontend validations (src/validations/): Yup schemas for newItem, modifyItem, newPurchase, modifyPurchase, newSale, modifySale.


Learnings & challenges

Real-time stock synchronization

The main challenge was ensuring inventory consistency across purchases, sales, edits, and deletions. I solved it with atomic stock adjustments (stock = stock +/- quantity) in SQL, which avoids race conditions. When editing a transaction, the service calculates the difference between the new and previous quantity and adjusts only the delta. Deleting reverts the inventory effect completely.

Item-supplier association with purchase prices

The same item can be purchased from different suppliers at different prices. I implemented an item_supplier intermediate table that records each relationship with its purchase_price and purchase_date. When deleting a purchase, the system checks whether the supplier has other active transactions with that item before unlinking it, avoiding loss of history.

Soft delete with referential integrity

All deletions are logical (is_deleted = TRUE) to preserve history. Queries always include the WHERE is_deleted = FALSE filter, and cascading logical deletions require manual verification in the service before proceeding.

Chilean RUT validation and formatting

I implemented the Chilean RUT validation algorithm (modulo 11 check digit) in both backend and frontend; it uniquely identifies users and suppliers.


References

  • Next.js — React framework with Pages Router and SSR.
  • Express.js — Minimalist framework for REST APIs in Node.js.
  • PostgreSQL — Relational database management system.
  • Tailwind CSS — Utility-first CSS framework with variables and dark theme.
  • Shadcn/ui — Reusable React components with Radix UI and Tailwind.
  • JWT — Bearer authentication token standard.
  • PM2 — Process manager for Node.js in production.
  • Cloudinary — Cloud-based image storage and management.

Project developed together with Rocío Rivas, Álvaro Loyola and Anaís Saldías.