Skip to main content

Overview

sgivu-purchase-sale manages purchase and sale contracts for vehicles. It provides creation, advanced search, reporting (PDF/XLSX/CSV), and lifecycle management of contracts. The service integrates with sgivu-user, sgivu-client, and sgivu-vehicle for data enrichment, and with sgivu-auth for authorization.

Technologies

  • Java: 21
  • Spring Boot: 4.0.1
  • Spring Security: Resource Server (JWT validation)
  • Spring Data JPA: PostgreSQL persistence
  • Flyway: Database migrations
  • Spring Cloud Config Client: Centralized configuration
  • Spring Cloud Eureka Client: Service registration
  • SpringDoc OpenAPI: Swagger UI documentation
  • MapStruct: DTO mapping
  • Lombok: Code generation
  • OpenPDF: PDF report generation
  • Apache POI: Excel (XLSX) report generation

Prerequisites

  • JDK 21
  • Maven 3.9+
  • PostgreSQL database
  • sgivu-config and sgivu-discovery services running
  • Related services: sgivu-user, sgivu-client, sgivu-vehicle (for data enrichment)

Running the Service

Development with Docker Compose

cd infra/compose/sgivu-docker-compose
docker compose -f docker-compose.dev.yml up -d

Local Execution

./mvnw clean install -DskipTests
./mvnw spring-boot:run

Docker Build

./build-image.bash
docker build -t sgivu-purchase-sale:local .

Contract Types

Purchase Contract

When: Company purchases a vehicle from a supplier/individual Key Fields:
  • contractType: PURCHASE
  • clientId: Seller/supplier ID
  • vehicleId: Vehicle being purchased
  • userId: Employee handling the purchase
  • purchasePrice: Amount paid to acquire vehicle
  • contractDate: Date of purchase

Sale Contract

When: Company sells a vehicle to a customer Key Fields:
  • contractType: SALE
  • clientId: Buyer/customer ID
  • vehicleId: Vehicle being sold
  • userId: Sales representative
  • salePrice: Amount customer pays
  • contractDate: Date of sale

Contract Status Workflow

1

DRAFT

Initial state when contract is created but not finalizedAllowed Transitions: PENDING, CANCELLED
2

PENDING

Contract submitted and awaiting approvalAllowed Transitions: APPROVED, REJECTED, CANCELLED
3

APPROVED

Contract approved and ready for executionAllowed Transitions: COMPLETED, CANCELLED
4

REJECTED

Contract rejected during approval processTerminal State: No further transitions
5

COMPLETED

Contract executed and transaction completedTerminal State: No further transitions
6

CANCELLED

Contract cancelled before completionTerminal State: No further transitions

Security

Authentication

JWT Token Validation:
  • Tokens issued by sgivu-auth
  • JwtAuthenticationConverter maps rolesAndPermissions claim to authorities
  • Resource Server validates signature via JWKS endpoint

Authorization

Required Permissions:
  • purchase-sale:create - Create contracts
  • purchase-sale:read - View contracts
  • purchase-sale:update - Update contracts
  • purchase-sale:delete - Delete contracts
  • purchase-sale:approve - Approve contracts
  • purchase-sale:report - Generate reports

Internal Service Communication

X-Internal-Service-Key Header:
  • Allows service-to-service calls without JWT
  • Used for internal endpoints
  • Must be kept secret
Internal service key bypasses authentication. Ensure:
  • Key is stored in secret manager
  • Only authorized services have access
  • Key is rotated regularly
  • Gateway blocks /internal/* endpoints

Database Schema

Flyway Migrations

Location: src/main/resources/db/migration V1__initial_schema.sql creates: purchase_sales table:
  • id: Primary key
  • contract_type: ENUM (PURCHASE, SALE)
  • contract_status: ENUM (DRAFT, PENDING, APPROVED, REJECTED, COMPLETED, CANCELLED)
  • contract_date: Transaction date
  • client_id: Reference to client (buyer or seller)
  • user_id: Reference to employee handling transaction
  • vehicle_id: Reference to vehicle
  • purchase_price: Price paid (for purchases)
  • sale_price: Price received (for sales)
  • notes: Additional contract notes
  • created_at, updated_at: Audit timestamps
Indexes:
  • idx_purchase_sale_client_id on client_id
  • idx_purchase_sale_user_id on user_id
  • idx_purchase_sale_vehicle_id on vehicle_id
  • idx_purchase_sale_status on contract_status
  • idx_purchase_sale_type on contract_type
  • idx_purchase_sale_date on created_at

API Endpoints

Contract Management

GET /v1/purchase-sales
protected
List contracts with advanced filteringRequired Permission: purchase-sale:readQuery Parameters:
  • contractType: Filter by PURCHASE or SALE
  • contractStatus: Filter by status
  • clientId: Filter by client
  • vehicleId: Filter by vehicle
  • userId: Filter by user
  • startDate, endDate: Date range
  • page, size, sort: Pagination
GET /v1/purchase-sales/{id}
protected
Get contract by ID with enriched dataRequired Permission: purchase-sale:readReturns: Contract with client, user, and vehicle details
POST /v1/purchase-sales
protected
Create new contractRequired Permission: purchase-sale:createValidations:
  • Valid contract type
  • Client, user, and vehicle exist
  • Appropriate price field populated
  • Future contract date not allowed
PUT /v1/purchase-sales/{id}
protected
Update existing contractRequired Permission: purchase-sale:updateRestrictions: Cannot update completed or cancelled contracts
PATCH /v1/purchase-sales/{id}/status
protected
Update contract statusRequired Permission: purchase-sale:update or purchase-sale:approveBody: {"status": "APPROVED"}Validations: Status transition must be valid per workflow
DELETE /v1/purchase-sales/{id}
protected
Delete contract (soft delete)Required Permission: purchase-sale:deleteRestrictions: Only DRAFT contracts can be deleted

Reporting

GET /v1/purchase-sales/reports/pdf
protected
Generate PDF reportRequired Permission: purchase-sale:reportQuery Parameters: Same as list endpoint for filteringResponse: PDF file download
GET /v1/purchase-sales/reports/xlsx
protected
Generate Excel reportRequired Permission: purchase-sale:reportQuery Parameters: Same as list endpoint for filteringResponse: XLSX file download
GET /v1/purchase-sales/reports/csv
protected
Generate CSV reportRequired Permission: purchase-sale:reportQuery Parameters: Same as list endpoint for filteringResponse: CSV file download

Request/Response Examples

Create Purchase Contract

POST /v1/purchase-sales
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "contractType": "PURCHASE",
  "contractStatus": "DRAFT",
  "contractDate": "2024-03-06",
  "clientId": 123,
  "userId": 456,
  "vehicleId": 789,
  "purchasePrice": 25000.00,
  "notes": "Vehicle purchased from private seller"
}

Response

{
  "id": 1001,
  "contractType": "PURCHASE",
  "contractStatus": "DRAFT",
  "contractDate": "2024-03-06",
  "client": {
    "id": 123,
    "name": "John Doe",
    "type": "PERSON"
  },
  "user": {
    "id": 456,
    "username": "jsmith",
    "fullName": "Jane Smith"
  },
  "vehicle": {
    "id": 789,
    "vin": "1HGBH41JXMN109186",
    "brand": "Toyota",
    "model": "Camry",
    "year": 2023
  },
  "purchasePrice": 25000.00,
  "salePrice": null,
  "notes": "Vehicle purchased from private seller",
  "createdAt": "2024-03-06T10:30:00Z",
  "updatedAt": "2024-03-06T10:30:00Z"
}

Update Contract Status

PATCH /v1/purchase-sales/1001/status
Authorization: Bearer <jwt-token>
Content-Type: application/json

{
  "status": "PENDING"
}

Service Integration

Data Enrichment

Contract responses include enriched data from related services: Client Information (from sgivu-client):
  • Person: Full name, document number
  • Company: Legal name, tax ID
User Information (from sgivu-user):
  • Username, full name
  • Role information
Vehicle Information (from sgivu-vehicle):
  • VIN, brand, model, year
  • Current status
  • Primary image

Communication Pattern

// Example: Enriching contract with client data
@Service
public class ContractEnrichmentService {
    
    @LoadBalanced
    private final WebClient webClient;
    
    public PurchaseSaleDTO enrichContract(PurchaseSale contract) {
        // Fetch client data
        ClientDTO client = webClient.get()
            .uri("http://sgivu-client/v1/clients/{id}", contract.getClientId())
            .header("X-Internal-Service-Key", internalKey)
            .retrieve()
            .bodyToMono(ClientDTO.class)
            .block();
            
        // Similar for user and vehicle...
        
        return mapToDTO(contract, client, user, vehicle);
    }
}

Report Generation

PDF Reports

Library: OpenPDF Features:
  • Company header with logo
  • Contract details table
  • Client, user, and vehicle information
  • Signature section
  • Page numbering

Excel Reports

Library: Apache POI Features:
  • Formatted headers
  • Color-coded status cells
  • Formula cells for totals
  • Auto-sized columns
  • Freeze panes

CSV Reports

Format: RFC 4180 compliant Features:
  • Header row
  • Proper escaping
  • UTF-8 encoding
  • Flat structure (no nested objects)

Observability

Actuator Endpoints

  • /actuator/health: Service health with database connectivity
  • /actuator/metrics: Custom metrics for contract operations
  • /actuator/info: Application metadata

OpenAPI Documentation

Swagger UI: /swagger-ui/index.html (when enabled)

Distributed Tracing

Brave/Zipkin:
  • Configured via sgivu-config
  • Traces service-to-service calls
  • Report generation spans

Testing

./mvnw test
Test Coverage:
  • Unit tests for services and mappers
  • Repository integration tests
  • Status transition validation tests
  • Report generation tests
  • Security authorization tests

Troubleshooting

Symptoms: API requests return unauthorized or forbiddenSolutions:
  • Verify Bearer token contains required permissions
  • Check token issuer matches sgivu-auth
  • Ensure user has appropriate role assignments
  • Validate token hasn’t expired
Symptoms: Cannot fetch client/user/vehicle dataSolutions:
  • Verify sgivu-config and sgivu-discovery are accessible
  • Check related services are registered in Eureka
  • Ensure internal service key is correct
  • Review network connectivity between services
  • Check for circuit breaker open state
Symptoms: Status update rejected with validation errorSolutions:
  • Review allowed transitions in workflow
  • Cannot update terminal states (COMPLETED, REJECTED, CANCELLED)
  • Check current status before attempting transition
  • Verify user has approval permission for certain transitions
Symptoms: PDF/Excel generation fails or times outSolutions:
  • Check OpenPDF and Apache POI dependencies
  • Review memory limits (reports can be memory-intensive)
  • Limit result set size for large reports
  • Check file system write permissions
  • Review logs for library-specific errors
  • Consider async report generation for large datasets
Symptoms: Contract creation fails with FK constraint errorSolutions:
  • Verify client_id exists in client service
  • Verify user_id exists in user service
  • Verify vehicle_id exists in vehicle service
  • Check vehicle isn’t already in active contract
  • Ensure referenced entities aren’t soft-deleted

Environment Variables

VariableDescriptionExample
DB_HOSTPostgreSQL hostsgivu-postgres-purchase-sale
DB_PORTPostgreSQL port5432
DB_NAMEDatabase namesgivu_purchase_sale
DB_USERNAMEDatabase usernamesgivu_user
DB_PASSWORDDatabase passwordsecure_password
SERVICE_INTERNAL_SECRET_KEYInternal service keysecret-key-value
SPRING_SECURITY_OAUTH2_RESOURCESERVER_JWT_ISSUER_URIAuth server issuerhttp://sgivu-auth:9000

Best Practices

Status Workflow

Enforce valid status transitions to maintain data integrity

Data Enrichment

Cache frequently accessed reference data to reduce service calls

Async Reports

Generate large reports asynchronously to avoid timeouts

Audit Trail

Log all status changes with user and timestamp

Vehicle Service

Provides vehicle data for contracts

Client Service

Provides client (buyer/seller) data

User Service

Provides employee data for contracts

Gateway

Routes purchase-sale API requests

Build docs developers (and LLMs) love