Skip to main content
Version: MyBestDealNow

Dealer Management API

Authors

  • Sanket Mal
  • Ayan Ghosh
  • Ashik Ikbal

Last Updated Date

2025-11-16


SRS References


Version History

VersionDateChangesAuthor
1.02025-11-16Initial draft - Dealer API designAshik
1.12025-11-17Added latitude/longitude to DealerAddress, auto-populate from zipcodedetailsAshik

Feature Overview

Objective: Provide secure, role-based APIs for managing dealer entities in the MBDN platform. The Dealer Management API enables creation, retrieval, updating, and address suggestion for dealer records, supporting administrative workflows and dealer self-service.

Scope:

  • Dealer creation, update, and retrieval
  • Paginated dealer listing with advanced filtering
  • Dealer address suggestion for onboarding and updates
  • Strict access control based on user roles and claims
  • Comprehensive error handling and status reporting
  • Geo-coordinates (latitude, longitude) for dealer addresses auto-populated from zipcodedetails

Dependencies:

  • IAMService - For authentication, JWT validation, and user claims
  • DealerManagementService.Helper - Business logic for dealer operations
  • PostgreSQL Database - For dealer data persistence
  • CommonLibrary/ContractLibrary - For shared models and enums
  • zipcodedetails table - for address geo-coordinates

Request & Response Models

Request Models

Dealer (ContractLibrary.Dealer.Dealer)

public record Dealer
{
public Guid DealerId { get; set; }
public string? DealerCode { get; set; }
public required string PrimaryEmail { get; init; }
public required string CompanyName { get; init; }
public string? ContactPersonName { get; init; }
public required string Phone { get; init; }
public string? Website { get; init; }
public List<DealershipType>? DealershipType { get; init; }
public List<Brand>? Brand { get; init; }
public Status Status { get; set; } = Status.Active;
public string? DealerLogoUrl { get; set; }
public List<DealerAddress>? Addresses { get; init; } = new List<DealerAddress>();
public DateTimeOffset CreatedAt { get; init; } = DateTimeOffset.UtcNow;
public DateTimeOffset UpdatedAt { get; set; } = DateTimeOffset.UtcNow;
public string? LogUsername { get; set; }
public DateTimeOffset LogDts { get; set; } = DateTimeOffset.UtcNow;
}

DealerAddress (ContractLibrary.Dealer.DealerAddress)

public record DealerAddress
{
public AddressType AddressType { get; init; }
public string? AddressLine1 { get; init; }
public string? City { get; init; }
public string? State { get; init; }
public string? ZipCode { get; init; }
public double? Latitude { get; set; } // auto-populated from zipcodedetails
public double? Longitude { get; set; } // auto-populated from zipcodedetails
public bool IsPrimary { get; set; } = false; // indicates primary address for dealer
public double? Radius { get; set; } // optional radius for this address
}

DealerFilter (ContractLibrary.Dealer.DealerFilter)

public record DealerFilter
{
public string? DealerCode { get; set; }
public string? CompanyName { get; set; }
public string? ContactPersonName { get; set; }
public string? PrimaryEmail { get; set; }
public string? Phone { get; set; }
public List<Status>? Status { get; set; }
public List<DealershipType>? DealershipTypes { get; set; }
public List<Brand>? Brands { get; set; }
public string? Website { get; set; }
public DateTimeOffset? CreatedAtFrom { get; set; }
public DateTimeOffset? CreatedAtTo { get; set; }
public DateTimeOffset? UpdatedAtFrom { get; set; }
public DateTimeOffset? UpdatedAtTo { get; set; }
public string? SearchKeyword { get; set; }
public DealerSortBy SortBy { get; set; }
public SortOrder SortDirection { get; set; }
[Range(1, int.MaxValue, ErrorMessage = "Page number must be greater than 0")]
public int PageNumber { get; set; }
[Range(1,100, ErrorMessage = "Page size must be between 1 and 100")]
public int RowsPerPage { get; set; }
}

UpdateDealerRadiusRequest (ContractLibrary.Dealer.UpdateDealerRadiusRequest)

public record UpdateDealerRadiusRequest
{
[Range(1, 1000, ErrorMessage = "Radius must be between 1 and 1000 KM")]
public required double Radius { get; init; }
}

Response Models

CommonResponse (ContractLibrary.Common.CommonResponse)

public record CommonResponse
{
public int Status { get; init; }
public string? Message { get; init; }
}

ServerPaginatedData<T>

From ContractLibrary.Common.ServerPaginatedData<T>

public class ServerPaginatedData<T>
{
public List<T> Data { get; set; }
public int TotalNumber { get; set; }
public bool HasPreviousPage { get; set; }
public bool HasNextPage { get; set; }
public int TotalPages { get; set; }
public int PageNumber { get; set; }
public int RowsPerPage { get; set; }
}

Dealer (Response)

  • Same as request model, with all properties populated.

List<DealerAddress> (Response)

  • List of DealerAddress records as defined above, including Latitude and Longitude.

Geo-coordinates Handling

  • On create and update dealer, for each address:
    • The system will fetch latitude and longitude from the zipcodedetails table using the provided ZipCode.
    • These values are stored in the dealeraddress table and returned in API responses.
    • If the zip code is not found, Latitude and Longitude will be null.

Primary Address Management

  • Each dealer must have exactly one primary address (isprimary = true)
  • When updating dealer addresses:
    • If a new address is marked as primary, all other addresses for that dealer are automatically set to isprimary = false
    • If no address is marked as primary, the system will default the first address as primary
  • The primary address is used for:
    • Geo-radius filtering calculations
    • Default location for auction notifications
    • Dealer's main service location

Requirements

Functional Requirements

  1. Dealer Creation
  • Only Admin users can create new dealers
  • Validates user context and required claims
  • Returns appropriate status and error messages
  1. Dealer Listing
  • Admins can retrieve a paginated, filterable list of dealers
  • Supports filtering by code, name, status, type, brand, and date
  • Returns paginated data with total count and navigation info
  1. Dealer Details
  • Any authorized user with the correct scope can fetch full dealer details by ID
  • Returns 404 if dealer not found
  1. Dealer Update
  • Admins and Dealers can update dealer information
  • Dealers can only update their own record (validated via entity_id claim)
  • Validates user context and permissions
  • Auto-populates latitude and longitude from zipcodedetails table based on zip code
  • Ensures only one address has isprimary = true at a time
  • Updates or inserts addresses with populated geo-coordinates
  1. Address Suggestions
  • Admins can get address suggestions for any dealer (dealerId required)
  • Dealers can only get suggestions for their own entity (entity_id from JWT)
  • Returns list of suggested addresses or error if unauthorized
  1. Update Dealer Radius
  • Dealers can update their service radius
  • Dealer ID is retrieved from entity_id claim in JWT token
  • Validates radius value (must be positive number, maximum 1000 KM)
  • Updates the radius field in dealermaster table
  • Returns success message with updated radius value

Non-Functional Requirements

  1. Performance
  • API response time under 500ms for list endpoints
  • API response time under 300ms for create/update endpoints
  • Efficient database queries and pagination
  1. Security
  • All endpoints require JWT authentication
  • Role and scope-based access control
  • Input validation and error handling
  1. Scalability
  • Supports large dealer datasets with pagination and filtering
  • Designed for multi-tenant, multi-role access
  1. Reliability
  • Consistent error responses
  • Logging for all critical operations and errors
  1. Usability
  • Clear error messages and status codes
  • Swagger/OpenAPI documentation for all endpoints

Design Specifications

API Interfaces

EndpointMethodParametersResponseResponse Status Codes
/api/dealer/createPOSTDealer (body)CommonResponse201, 400, 401, 403, 500
/api/dealer/listGETDealerFilter (query)ServerPaginatedData<Dealer>200, 400, 401, 403, 500
/api/dealer/details/{dealerId}GETdealerId (route)Dealer200, 401, 404, 500
/api/dealer/update/{dealerId}PUTdealerId (route), Dealer (body)CommonResponse200, 400, 401, 403, 404, 500
/api/dealer/address-suggestionGETdealerId (query, optional)List<DealerAddress>200, 400, 401, 403, 500
/api/dealer/update-radiusPUTUpdateDealerRadiusRequest (body)CommonResponse200, 400, 401, 403, 500

API Request & Response Examples

1. Create Dealer API

Endpoint: POST /api/dealer/create

Request Headers:

Authorization: Bearer <jwt-token>
Content-Type: application/json

Request Body (Dealer):

{
"primaryEmail": "dealer@example.com",
"companyName": "ABC Motors",
"phone": "1234567890",
"dealershipType": [1],
"brand": [12],
"status":1,
"addresses": [
{
"addressType":1,
"addressLine1": "123 Main St",
"city": "Metropolis",
"state": "NY",
"zipCode": "10001"
}
]
}

Success Response (201 Created):

{
"status":0,
"message": "Dealer created successfully"
}

Error Response (400 Bad Request):

{
"status":400,
"message": "Invalid dealer data"
}

Error Response (403 Forbidden):

{
"status":403,
"message": "You are not allowed to create dealers."
}

2. Get Dealer List API

Endpoint: GET /api/dealer/list

Request Headers:

Authorization: Bearer <jwt-token>

Query Parameters (DealerFilter):

?companyName=ABC&status=1&pageNumber=1&rowsPerPage=20

Success Response (200 OK):

{
"data": [
{
"dealerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"dealerCode": "DLR-001234",
"primaryEmail": "dealer@example.com",
"companyName": "ABC Motors",
"contactPersonName": "John Doe",
"phone": "1234567890",
"website": "https://abcmotors.com",
"dealershipType": [1],
"brand": [12],
"status":1,
"dealerLogoUrl": "https://cdn.example.com/dealers/abc-motors-logo.jpg",
"addresses": [],
"createdAt": "2025-11-16T10:25:00Z",
"updatedAt": "2025-11-16T10:25:00Z"
}
],
"totalNumber":1,
"hasPreviousPage": false,
"hasNextPage": false,
"totalPages":1,
"pageNumber":1,
"rowsPerPage":20
}

Error Response (400 Bad Request):

{
"status":400,
"message": "Invalid filter parameters"
}

3. Get Dealer Details API

Endpoint: GET /api/dealer/details/{dealerId}

Request Headers:

Authorization: Bearer <jwt-token>

Success Response (200 OK):

{
"dealerId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"dealerCode": "DLR-001234",
"primaryEmail": "dealer@example.com",
"companyName": "ABC Motors",
"contactPersonName": "John Doe",
"phone": "1234567890",
"website": "https://abcmotors.com",
"dealershipType": [1],
"brand": [12],
"status":1,
"dealerLogoUrl": "https://cdn.example.com/dealers/abc-motors-logo.jpg",
"addresses": [],
"createdAt": "2025-11-16T10:25:00Z",
"updatedAt": "2025-11-16T10:25:00Z"
}

Error Response (404 Not Found):

{
"status":404,
"message": "Dealer not found"
}

4. Update Dealer API

Endpoint: PUT /api/dealer/update/{dealerId}

Request Headers:

Authorization: Bearer <jwt-token>
Content-Type: application/json

Request Body (Dealer):

{
"primaryEmail": "dealer@example.com",
"companyName": "ABC Motors",
"phone": "1234567890",
"dealershipType": [1],
"brand": [12],
"status":1,
"addresses": []
}

Success Response (200 OK):

{
"status":0,
"message": "Dealer updated successfully"
}

Error Response (403 Forbidden):

{
"status":403,
"message": "You are not allowed to update dealers."
}

Error Response (404 Not Found):

{
"status":404,
"message": "Dealer not found"
}

Implementation Logic:

  1. Receive Update Request

    • Accept dealer ID from route parameter
    • Accept dealer information including addresses from request body
    • Validate user has permission (Admin or Dealer with matching entity_id)
  2. Fetch Latitude & Longitude from zipcodedetails

    • For each address in the update request:
      • Query zipcodedetails table using the provided zip code
      • Retrieve lat and lng values
      • Auto-populate latitude and longitude fields for the address
      • If zip code not found, set lat/lng to null
  3. Manage Primary Address

    • Ensure only one address has isprimary = true
    • If a new primary address is specified:
      • Set all other addresses' isprimary = false for this dealer
      • Set the new address's isprimary = true
    • If no address is marked as primary:
      • Default the first address to isprimary = true
  4. Update Dealer and Address Records

    • Update dealer information in dealermaster table
    • Update or insert addresses in dealeraddress table with:
      • Populated lat/lng values
      • Correct isprimary flag
      • Optional radius value
    • Log the update with username and timestamp
  5. Return Response

    • Return success message confirming dealer update

5. Get Address Suggestions API

Endpoint: GET /api/dealer/address-suggestion

Request Headers:

Authorization: Bearer <jwt-token>

Query Parameters:

?dealerId=3fa85f64-5717-4562-b3fc-2c963f66afa6

Success Response (200 OK):

[
{
"addressType":1,
"addressLine1": "123 Main St",
"city": "Metropolis",
"state": "NY",
"zipCode": "10001"
}
]

Error Response (400 Bad Request):

{
"status":400,
"message": "Invalid dealer context. Entity ID claim is required."
}

6. Update Dealer Radius API

Endpoint: PUT /api/dealer/update-radius

Request Headers:

Authorization: Bearer <jwt-token>
Content-Type: application/json

Request Body (UpdateDealerRadiusRequest):

{
"radius": 150.5
}

Success Response (200 OK):

{
"status": 0,
"message": "Dealer radius updated successfully"
}

Error Response (400 Bad Request):

{
"status": 400,
"message": "Radius must be between 1 and 1000 KM"
}

Implementation Logic:

  1. Receive Radius Update Request

    • Extract dealer ID from entity_id claim in JWT token
    • Accept new radius value from request body (in KM)
    • Validate radius value (must be positive number, max 1000 KM)
  2. Update Dealer Radius

    • Update the radius field in dealermaster table for the authenticated dealer
    • Example SQL: UPDATE dealermaster SET radius = {radius} WHERE dealerid = '{dealerId}'
    • Log the update with username and timestamp
  3. Return Response

    • Return success message confirming radius update

Development Tasks & Estimates

NoTask NameEstimate (Hours)DependenciesNotes
1DealerController API implementation10NoneAll endpoints, validation, error handling (includes UpdateDealerRadius)
2DealerHelper business logic and integration7Task1Includes radius update logic
3Unit and integration tests for DealerController10Task1,2Includes tests for radius update API
4Swagger/OpenAPI documentation2Task1
Grand Total29 hours

Testing & Quality Assurance

Integration Test Scenarios:

  • Dealer creation with valid and invalid data
  • Dealer listing with various filters and pagination
  • Dealer update by admin and dealer roles
  • Dealer radius update with valid and invalid values (1-1000 KM)
  • Dealer radius update only by dealer role
  • Address suggestion for admin and dealer
  • Unauthorized and forbidden access attempts
  • Error handling for missing/invalid claims

Testing Tools

  • xUnit - Unit and integration testing
  • FluentAssertions - Test assertions
  • Moq - Mocking dependencies
  • WebApplicationFactory - API integration tests
  • Postman/Swagger - Manual API testing

Acceptance Criteria

Feature Complete When:

  1. ✅ Admins can create, update, and list dealers
  2. ✅ Dealers can update their own information
  3. ✅ Dealers can update their service radius (validated 1-1000 KM, dealer-only access)
  4. ✅ All endpoints enforce correct access control
  5. ✅ Pagination and filtering work as expected
  6. ✅ Address suggestions are accurate and secure
  7. ✅ All error cases return correct status and message
  8. ✅ Unit test coverage >85%
  9. ✅ All integration tests pass

Deployment Considerations

Configuration Changes

  • Ensure database schema for dealers and addresses is up to date
  • Configure JWT authentication and required claims in IAMService
  • Update Swagger documentation

Risks & Mitigations

RiskImpactLikelihoodMitigation Strategy
Unauthorized access to endpointsHighMediumStrict JWT and claim validation, logging, and auditing
Data inconsistency on updateMediumLowUse transactions and validation in business logic

Review & Approval

  • Reviewer: Sanket

  • Approval Date: 2025-11-17


Notes

Future Enhancements

  • Bulk dealer import/export
  • Dealer status change notifications
  • Dealer audit log endpoints
  • Advanced address validation and enrichment

Dependencies on Other Services

  • IAMService: For authentication and user claims
  • CommonLibrary/ContractLibrary: For shared models and enums