How we built a complete procurement platform from scratch using modern Java technologies, handling everything from tender creation to contract management and delivery tracking.
When we set out to build Pultsnab, we were frustrated by the fragmented nature of procurement processes. Tenders scattered across spreadsheets, proposals lost in email threads, and contract management handled through legacy tools created inefficiencies and audit nightmares. We needed a unified platform where customers could publish tenders, suppliers could submit proposals, and managers could oversee the entire lifecycle—from evaluation to contract award, delivery tracking, invoicing, and reporting—all with proper role-based access and audit trails.

Why We Built Pultsnab
The procurement and tender processes in many organizations remain stuck in the past, relying on manual workflows that are error-prone and difficult to scale. We envisioned a system where every step of the tender-to-contract journey would be tracked, auditable, and accessible through a single interface. This meant creating a backend that could handle complex workflows while maintaining data integrity and security.
Tech Stack at a Glance
We deliberately chose a stable, proven stack rather than chasing the latest experimental features:
- Runtime: Java 21 (LTS)
- Framework: Spring Boot 3.5
- Database: PostgreSQL with JPA/Hibernate
- Migrations: Liquibase for versioned schema changes
- Authentication: Spring Security with JWT (access + refresh tokens)
- Validation: Hibernate Validator 8
- Mapping: MapStruct for clean DTO-to-entity conversion
- File Storage: AWS S3-compatible (MinIO for development)
- Documents: Apache POI for Excel, OpenPDF for PDF generation
- Mail: Spring Boot Mail for notifications
Nothing exotic—just a solid, maintainable stack that's easy to deploy and reason about.
What the System Does
Pultsnab isn't just a CRUD API; it's a complete tender-to-contract-to-delivery-to-invoice pipeline:
Tenders: Create, publish, and manage tenders through their lifecycle: Draft → Published → Bidding → Evaluation → Awarded (or Cancelled). Each tender includes items, deadlines, and terms.
Supplier Proposals: Suppliers submit proposals per tender with built-in comparison and price analysis tools.
Contracts: Awarded tenders automatically convert into contracts with items, delivery terms, and payment schedules.
Deliveries & Warehouses: Track deliveries against contract items and manage warehouse inventory.
Invoicing: Generate invoices linked to contracts, create PDF bank payment slips, and track payment status.
Companies & Contacts: Manage customer and supplier companies with contacts, bank details, and legal requisites.
Requests: Internal requests that can be converted into tenders, creating a request → tender → contract flow.
Dashboard & Reports: Role-based dashboards by period, operational and financial reports, and Excel export capabilities.
Alerts & Notifications: Email notifications for key events like tender publication, proposal submission, award decisions, and deadline reminders.
Security & Audit: Role-based access control (Admin, Manager, Supplier, Viewer), JWT authentication, and comprehensive audit logs for important actions.
Architecture Highlights
Our REST API follows a clear structure with dozens of controllers under /api/*, each endpoint explicitly protected with @PreAuthorize for role checks. We maintain a clean layered architecture:
- Controllers → Services → Repositories
- DTOs and MapStruct mappers prevent entity exposure
- Validation on DTOs with
@Validand Hibernate Validator keeps invalid data out
For the database, we use PostgreSQL with Liquibase for versioned migrations. We set ddl-auto: none and rely entirely on Liquibase for schema changes. Hibernate is tuned for batching and fetch depth to avoid N+1 queries and lazy loading issues.
File storage uses an AWS S3-compatible API (MinIO in development). Uploads go to a configurable bucket, and generated documents like PDF bank invoices can be stored or streamed directly.
We also implemented scheduled jobs using Spring's @Scheduled for periodic tasks like tender deadline checks and reminder notifications.
Key Technical Decisions
JWT Setup: We implemented both access and refresh tokens, storing them securely and integrating them into Spring Security's filter chain. This provides stateless authentication while allowing token refresh without re-login.
Role-Based Access: We mapped roles to endpoint capabilities and UI features. An Admin sees everything, Managers handle tenders and contracts, Suppliers can only view and submit proposals, and Viewers have read-only access.
From Request to Tender to Contract: This workflow maintains data consistency through service-layer orchestration. When a request becomes a tender, we clone relevant data; when a tender becomes a contract, we preserve the audit trail while creating new contract-specific entities.
PDF Generation: Using OpenPDF with Cyrillic font support, we generate bank invoices and other documents that meet local regulatory requirements. The templates are configurable and support dynamic data insertion.
Excel Import/Export: Apache POI handles bulk data operations and report exports. We implemented configurable column mapping so the system can adapt to different reporting requirements without code changes.
What We Learned
Spring Boot 3 + Java 21: The experience was smooth—no need for experimental features, just stable LTS support and framework maturity. The performance improvements and language features (like virtual threads, though we didn't need them yet) were nice bonuses.
Liquibase: Versioned migrations made schema evolution predictable across development, staging, and production environments. Team members could work on database changes independently without conflicts.
MapStruct: This eliminated mapping boilerplate and made it easy to add new DTOs without touching service logic. The compile-time validation catches mapping errors early.
S3-Compatible Storage: One integration (AWS SDK v2) works seamlessly with MinIO locally and AWS in production. This made development and testing much easier without requiring AWS credentials.
Clear API Boundaries: REST + DTOs + validation made it straightforward to add a frontend and later mobile clients without touching core business logic.
App Preview
Below is a mockup of the main screens with sample data: dashboard, tenders, contracts, and deliveries. Switch tabs to explore.

Try It or Contribute
The project is open for anyone building similar procurement, contracts, or document-heavy backends. You can run it with Java 21, PostgreSQL, and an S3-compatible store like MinIO. The repository includes configuration and optional Docker/local setup instructions.
Visit the app at pultsnab.ru.
If you're interested in deeper dives, we're happy to explore any aspect further—whether it's JWT and Spring Security implementation, workflow orchestration, PDF/Excel generation, or deployment strategies.
What would you like to read about next? JWT + Spring Security, the tender→contract flow, or PDF/Excel generation?

Scale globally with MongoDB Atlas. Try free.
MongoDB Atlas is the global, multi-cloud database for modern apps trusted by developers and enterprises to build, scale, and run cutting-edge applications, with automated scaling, built-in security, and 125+ cloud regions. Learn More

Comments
Please log in or register to join the discussion