Odoo Hackathon X GandhiNagar

Project overview

AssetFlow ERP

AssetFlow is a role-aware asset and shared-resource management system for organizations. It replaces spreadsheets, chat-based approvals, and disconnected records with one platform for inventory, custody, bookings, maintenance, audits, notifications, and operational history.

This document explains the problem the project solves and the code that currently implements it.

Problem statement

Organizations own laptops, furniture, rooms, vehicles, tools, and other resources that move between people, departments, locations, and lifecycle states. Informal tracking creates recurring problems:

  • no reliable view of an asset's custodian, location, condition, or availability;
  • employees cannot easily discover or request equipment;
  • handovers and returns lack an approval trail;
  • shared resources are double-booked;
  • maintenance reports are separated from the affected asset;
  • physical audits require manual reconciliation;
  • sensitive actions have unclear role boundaries;
  • managers lack trustworthy operational data.

AssetFlow treats these as connected workflows. An asset can be requested, allocated, transferred, returned, booked, repaired, and audited while important changes generate notifications and activity records.

Goals

  1. Maintain a canonical inventory with unique tags, serial numbers, categories, locations, conditions, and lifecycle states.
  2. Make employee and department custody visible.
  3. Govern requests, allocations, transfers, and returns.
  4. Prevent conflicting shared-resource reservations.
  5. Route maintenance issues from reporting through resolution.
  6. Support scoped physical-audit cycles.
  7. Enforce permissions on the server.
  8. Provide dashboards, notifications, reports, and traceability.

Functional scope

| Module | Responsibility | | --- | --- | | Dashboard | Live inventory totals and operational snapshot | | Organization | Departments, employees, roles, hierarchy, and categories | | Assets | Registry, catalog, condition, location, ownership, and status | | Requests | Employee requests and manager approval/rejection | | Allocations | Custody, return requests, and completed returns | | Transfers | Move active custody to another employee or department | | Bookings | Conflict-checked reservations for rooms or equipment | | Maintenance | Issue priority, review, technician assignment, and resolution | | Audits | Scoped cycles, auditors, verification, and discrepancies | | Notifications | Employee-specific domain events and read state | | Activity/reports | Cross-module operational history and summaries |

Roles and permissions

Roles live on the employee record and are enforced by API handlers through requireApiEmployee.

| Capability | Admin | Asset manager | Department head | Employee | | --- | :---: | :---: | :---: | :---: | | Use authenticated modules | Yes | Yes | Yes | Yes | | Register assets | Yes | Yes | No | No | | Allocate assets | Yes | Yes | Yes | No | | Complete returns | Yes | Yes | No | No | | Request assets, transfers, or returns | Yes | Yes | Yes | Yes | | Create bookings/maintenance reports | Yes | Yes | Yes | Yes | | Process maintenance | Yes | Yes | No | No | | Resolve transfers | Yes | Yes | Yes | No | | Manage departments, categories, and roles | Yes | No | No | No | | Create audit cycles | Yes | No | No | No | | View organization-wide activity | Yes | Yes | Yes | No |

List services can further scope results by employee identity or department. UI controls mirror permissions, but the API is the security boundary.

Bootstrap rule: the first signup in an empty database becomes admin; later signups become employee. Protect the first registration in public deployments.

Core workflows

Request, allocation, and return

An employee requests an available catalog asset. An admin, asset manager, or department head resolves it; approval creates custody and changes the asset to allocated. A partial unique database index prevents two active allocations for one asset. The holder can request a return, which an admin or asset manager completes with condition notes, returning the asset to availability.

Transfer

A user requests that an active allocation move to another employee or department. An admin, asset manager, or department head approves or rejects it, preserving the original and new custody trail.

Booking

A resource may be standalone (for example, a room) or reference an asset. The service rejects overlapping active time windows for the same resource. Booking states are upcoming, ongoing, completed, and cancelled.

Maintenance

Any employee can report an asset issue with a priority and optional photo URL. Admins and asset managers move it through pending, approval/rejection, technician assignment, work in progress, and resolution. Services coordinate asset state, notifications, and activity.

Audit

An admin creates a dated cycle scoped optionally to a department or location and assigns auditors. Each asset has an audit item whose result becomes verified, missing, or damaged; discrepancies can create notifications and history.

Architecture

AssetFlow uses the Next.js 16 App Router. Pages/layouts are Server Components unless marked "use client"; interactive workspaces call JSON route handlers.

Browser
  -> App Router pages and client workspaces
  -> /api handlers (session, employee, role, Zod validation)
  -> domain services (rules, transactions, logs, notifications)
  -> Drizzle ORM
  -> Neon/PostgreSQL
  • src/app: routes, layouts, pages, and HTTP handlers.
  • src/components: feature workspaces and shared UI primitives.
  • src/server/auth.ts: API authentication and authorization.
  • src/server/services: business rules and persistence; handlers stay thin.
  • src/lib/validations: Zod schemas for untrusted input.
  • src/db/schema: tables, enums, indexes, and relationships.
  • src/index.ts: Neon HTTP client and Drizzle instance.
  • src/proxy.ts: early cookie-presence redirects; not the authorization boundary.

Authentication

Better Auth persists users, sessions, accounts, and verification data through Drizzle. It supports email/password, email OTP through SMTP, and optional GitHub/Google OAuth. Sessions last seven days and refresh daily. Creating an auth user also creates a domain employee. Pages use requireAuth; APIs validate the session, active employee, and allowed role.

Data model

| Area | Tables | | --- | --- | | Identity | user, session, account, verification | | Organization | employees, departments, asset_categories | | Inventory/custody | assets, asset_requests, allocations, transfers | | Operations | resources, bookings, maintenance_requests | | Audit | audit_cycles, audit_cycle_auditors, audit_items | | Oversight | notifications, activity_logs |

Assets have unique asset tags and QR values. Departments support parent-child hierarchy. Category-specific properties live in JSON metadata. Lifecycle values are PostgreSQL enums, foreign keys preserve relationships, and indexes optimize asset status, notifications, booking times, and active allocations.

Routes and API

Authenticated pages include /dashboard, /organization, /departments, /employees, /assets, /requests, /allocations, /bookings, /maintenance, /audits, /reports, /activity, and /notifications. The landing page and /login//signup are public.

| Endpoint | Methods | Purpose | | --- | --- | --- | | /api/auth/[...all] | Better Auth | Authentication transport | | /api/dashboard | GET | Dashboard snapshot | | /api/assets | GET, POST | List/catalog or register assets | | /api/asset-requests | GET, POST | List and submit requests | | /api/asset-requests/:id | PUT | Resolve request | | /api/allocations | GET, POST | List/create allocations | | /api/allocations/:id/request-return | POST | Request return | | /api/allocations/:id/return | POST | Complete return | | /api/transfers | GET, POST | List/request transfers | | /api/transfers/:id/resolve | POST | Resolve transfer | | /api/resources | GET | List bookable resources | | /api/bookings | GET, POST | List/create bookings | | /api/maintenance | GET, POST | List/report maintenance | | /api/maintenance/:id | PUT | Advance maintenance state | | /api/audits | GET, POST | List/create audit cycles/items | | /api/organization | GET | Organization snapshot | | /api/organization/departments | POST | Create department | | /api/organization/categories | POST | Create category | | /api/organization/roles | POST | Change employee role | | /api/notifications | GET | Current user's notifications | | /api/notifications/:id | PUT | Mark notification read | | /api/activity | GET | Organization activity |

Success responses generally use { "data": ... }. Domain errors use { "error", "code", "details"? } with 401, 403, or 409; unexpected errors return 500.

Project structure

drizzle/                 generated migrations and snapshots
public/                  static assets and reference screenshots
src/
  app/                   App Router pages and API handlers
  components/            feature workspaces and UI primitives
  db/schema/             Drizzle schema source of truth
  db/seed.ts             idempotent demo seed
  lib/                   auth, email, guards, validation, helpers
  server/services/       domain rules and persistence
  server/auth.ts         API RBAC boundary
  server/http.ts         domain-error response mapping
  proxy.ts               protected-page redirect
  index.ts               database connection

Local setup

Requirements: Node.js 20+, npm, and a PostgreSQL database accessible using a Neon-compatible URL.

npm install

Create .env:

DATABASE_URL=postgresql://USER:PASSWORD@HOST/DATABASE?sslmode=require
NEXT_PUBLIC_APP_URL=http://localhost:3000
BETTER_AUTH_SECRET=replace-with-a-long-random-secret
BETTER_AUTH_URL=http://localhost:3000

# Email OTP and login/welcome emails
SMTP_HOST=smtp.example.com
SMTP_PORT=587
SMTP_SECURE=false
SMTP_USER=your-user
SMTP_PASS=your-password
EMAIL_FROM="AssetFlow <no-reply@example.com>"

# Optional; a provider activates only when both values exist
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=

DATABASE_URL must start with postgres:// or postgresql://. The project can compile with its inert fallback URL, but runtime queries require a real database.

npm run db:migrate
npm run db:seed    # optional demo records
npm run dev

Open http://localhost:3000. The seed creates two user/employee rows, departments, categories, assets, and a room, but does not create password accounts. Use normal signup for an interactive account.

Scripts

| Command | Description | | --- | --- | | npm run dev | Development server | | npm run build | Production build | | npm run start | Run production build | | npm run lint | ESLint | | npm run db:generate | Generate migration from schema changes | | npm run db:migrate | Apply migrations | | npm run db:seed | Insert demo data |

For schema changes, edit src/db/schema, generate and review the SQL under drizzle/, migrate, then update validation and seeds. The TypeScript schema—not generated SQL—is the source of truth.

Current limitations

  • PostgreSQL/Drizzle is the active datastore. src/lib/mongodb.ts, Mongoose models, and src/action.ts are legacy artifacts outside the main request path.
  • The hierarchy page currently displays static summary metrics.
  • Photo fields store URLs; no object-storage upload pipeline is implemented.
  • No automated test suite/script exists yet; verification currently relies on lint, production build, and manual flows.
  • Activity records are append-oriented by convention, not database-enforced immutability.
  • Proxy cookie checks only improve redirects; APIs and server pages perform real session/role validation.

Stack

Next.js 16.2, React 19, TypeScript 5, PostgreSQL/Neon, Drizzle ORM, Better Auth, Zod 4, Tailwind CSS 4, shadcn/ui/Radix, Recharts, Motion, and React Three Fiber.

Production checklist

  • Set a strong auth secret and canonical HTTPS URLs.
  • Secure or pre-provision the first admin registration.
  • Apply migrations before application startup.
  • Configure SMTP and OAuth callback URLs.
  • Use TLS and least-privilege database credentials.
  • Add managed file storage and validation if uploads are needed.
  • Add service, authorization, and end-to-end tests.
  • Review role/department visibility against organizational policy.

Built for the Odoo Hackathon as an end-to-end demonstration of governed organizational asset operations.

Build with love by Urvil Patel