Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/DragonesMagicos/ferromax_v0.8/llms.txt

Use this file to discover all available pages before exploring further.

Ferromax ERP is a two-tier web application. The Java backend — a Spring Boot 3.2.5 application packaged as a single executable JAR — exposes a RESTful HTTP API and a WebSocket endpoint. The React frontend — a Vite-bundled single-page application — communicates exclusively through those two interfaces: HTTP via Axios for standard CRUD operations, and WebSocket STOMP for real-time push notifications. All persistent state lives in a PostgreSQL 16 database accessed through JPA/Hibernate. The two processes run independently, so they can be deployed together on a single machine during development or separated behind a reverse proxy in production.

Project Layout

ferromax_v0.8/
├── src/main/java/com/ferromax/erp/   # Spring Boot backend
│   ├── controller/                    # REST controllers
│   ├── service/                       # Business logic
│   ├── repository/                    # JPA repositories
│   ├── model/                         # JPA entities
│   ├── dto/                           # Request/Response DTOs
│   ├── exception/                     # Exception handlers
│   ├── security/                      # JWT filter, Spring Security config
│   └── config/                        # WebSocket, CORS, data init
├── ferromax-web/                      # React + Vite frontend
│   └── src/
│       ├── pages/                     # Route-level page components
│       ├── components/                # Reusable UI components
│       ├── api/                       # Shared Axios client instance
│       ├── services/                  # Per-resource API service modules
│       └── context/                   # AuthContext (JWT state)
├── scripts/                           # Python data import utilities
└── data/                              # Product catalog XLSX

Backend Layer

The backend follows the standard Spring Boot MVC layering pattern, with each layer having a single responsibility.
LayerPackageResponsibility
Controllercontroller/Receives HTTP requests, validates input with @Valid, delegates to the service layer, and maps the result to an HTTP response code.
Serviceservice/Contains all business logic — stock calculations, sale totalling, invoice parsing, alert triggering. Also owns @Transactional boundaries.
Repositoryrepository/Spring Data JPA interfaces. Each entity has a corresponding repository; complex queries use JPQL @Query annotations.
Modelmodel/JPA @Entity classes mapped to PostgreSQL tables. Lombok @Data / @Builder reduce boilerplate.
DTOdto/Separate request and response objects built with MapStruct mappers, keeping the entity model out of the API surface.
The server listens on port 8081 with a context path of /api, so every endpoint is reachable at http://localhost:8081/api/<resource>. SpringDoc is bundled and exposes an interactive Swagger UI at http://localhost:8081/api/swagger-ui.html.

Frontend Layer

The React SPA is a Vite project under ferromax-web/. Routing is handled by react-router-dom v7, with routes declared in App.jsx. There are three route categories:
  • Public routes/tienda, /tienda/login, /tienda/confirmacion, /tienda/mis-pedidos, /catalogo, and /catalogo/:categoria are accessible without a token.
  • Employee routes — wrapped in <ProtectedRoute requiereEmpleado>, accessible to both ADMIN and EMPLEADO roles. Includes /pos, /productos, /ventas, and /recepcion.
  • Admin-only routes — wrapped in <ProtectedRoute requiereAdmin>, restricted to ADMIN. Includes / (dashboard), /remitos, /ajuste-stock, and /ingreso-factura.
All HTTP calls go through a shared Axios client instance at src/api/axiosClient.js. A request interceptor reads the JWT from localStorage under the key ferromax_token and attaches it as an Authorization: Bearer <token> header on every outbound request. Higher-level service modules in src/services/ wrap this client with per-resource API calls. UI styling uses TailwindCSS 4, transitions and page-level animations use Framer Motion, and dashboard charts are rendered with Recharts.

Authentication Flow

Ferromax uses stateless JWT authentication. The sequence below covers a complete login-to-protected-request cycle.
1

User submits credentials

The frontend POSTs { email, password } to /api/auth/login. Spring Security’s DaoAuthenticationProvider verifies the credentials against the BCrypt-hashed password in the database.
2

Server issues a JWT

On success, AuthController calls JwtTokenProvider to sign a token containing the user’s email, role, and user ID. The response body includes token, rol, nombre, and mensaje.
3

Client stores the token

The frontend stores the token in localStorage under the key ferromax_token. All subsequent Axios requests include the header Authorization: Bearer <token>, attached automatically by the interceptor in src/api/axiosClient.js.
4

Filter validates each request

JwtAuthenticationFilter intercepts every incoming request, extracts the token from the Authorization header, validates the signature and expiry via JwtTokenProvider, and loads the UserDetails from the database. If valid, it populates the Spring Security SecurityContextHolder for the duration of the request.
5

Role check enforces access

SecurityConfig defines URL-level rules (e.g., GET /dashboard/** requires ROLE_ADMIN). Individual controller methods may also carry @PreAuthorize annotations for fine-grained method-level enforcement, enabled by @EnableMethodSecurity.
JWTs expire after 8 hours (jwt.expiration=28800000 ms). A separate refresh expiration is configured at 7 days (app.jwt.refresh-expiration-ms=604800000 ms). Clients should handle 401 Unauthorized responses by redirecting the user back to the login page.

Real-time Updates

Stock changes need to reach every open admin session instantly — polling is too slow for a busy POS environment. Ferromax solves this with a Spring WebSocket STOMP message broker.
PropertyValue
STOMP endpoint/ws (externally /api/ws due to servlet context path)
SockJS fallbackEnabled (transparent HTTP long-polling for environments that block WebSockets)
Topic prefix/topic
App destination prefix/app
Stock update topic/topic/stock-update
When a sale is completed or a manual stock adjustment is saved, the relevant service bean injects SimpMessagingTemplate and calls convertAndSend("/topic/stock-update", payload). Every frontend client subscribed to that topic receives the message within milliseconds and updates its local state — no page refresh needed. The frontend uses @stomp/stompjs and sockjs-client to establish the connection. The connection is initialised in a React useEffect hook and torn down on component unmount to prevent memory leaks.

Database

SettingValue
EnginePostgreSQL 16
Schema managementJPA/Hibernate ddl-auto=update
Connection poolHikariCP — max 10 connections, min idle 2
Pool nameFerromaxHikariPool
TimezoneAmerica/Argentina/Buenos_Aires (applied at JVM and JDBC levels)
CacheCaffeine — max 500 entries, 10 min write-expiry (expireAfterWrite=600s)
ddl-auto=update means Hibernate adds missing columns and creates new tables on startup but never drops existing ones. This is safe for development and low-traffic production deployments. For production migrations at scale, replace it with a dedicated tool such as Flyway or Liquibase.

Security Model

Ferromax defines three roles. Spring Security stores them with the ROLE_ prefix internally, but the application code and API refer to them as ADMIN, EMPLEADO, and CLIENTE.
RoleIntended userKey permissions
ADMINBusiness owner / managerFull access: dashboard, all products (including precioCompra), all sales history, stock adjustments, remitos, invoice OCR, alert management, supplier CRUD
EMPLEADOIn-store staffPOS terminal, product lookup (price and stock only — no precioCompra), stock reception, own daily sales
CLIENTEOnline shopperPublic catalogue browsing, place orders via /pedidos, view own order history via /ventas/mis-compras
Publicly accessible endpoints (no token required):
GET  /api/productos/publico
GET  /api/categorias/**
POST /api/auth/login
POST /api/auth/register
GET  /api/img/**
All other endpoints require a valid JWT. Sessions are fully statelessSessionCreationPolicy.STATELESS is set in SecurityConfig, so the server holds no session state between requests.
CORS is currently configured to allow localhost:5173 through localhost:5175 (and their 127.0.0.1 equivalents) only. Before deploying to a staging or production environment, update the allowedOrigins list in SecurityConfig.corsConfigurationSource() to match your actual domain.

Build docs developers (and LLMs) love