Skip to main content

Suppliers Module

Overview

The Suppliers module manages vendor/business partner records for purchasing and accounts payable workflows. Every supplier is scoped to a businessId for multi-tenant isolation.

Suppliers are referenced by:

  • Purchase Orders (supplierId FK)
  • Goods Received Notes (supplierId FK)
  • Accounts Payable Bills (supplierId FK)
  • Replenishment Settings (supplierId FK)
  • Contractor Assignments (supplierId FK)

Architecture

suppliers/
├── suppliers.module.ts # NestJS module
├── domain/
│ └── suppliers-repository.domain.ts # Repository port (interface)
├── application/
│ └── suppliers.service.ts # Use cases
├── infrastructure/
│ └── suppliers.repository.ts # Kysely adapter
└── interfaces/
├── suppliers.controller.ts # HTTP routes
├── dtos/
│ ├── create-supplier.dto.ts # Create validation + Swagger
│ └── update-supplier.dto.ts # Partial update validation
└── query/
└── paginate-suppliers.query.ts # Pagination + sort + search

Layer responsibilities

LayerResponsibility
DomainISuppliersRepository interface — no framework dependencies
ApplicationSuppliersService orchestrates CRUD + pagination
InfrastructureSuppliersRepository implements Kysely queries with multi-tenant scoping
InterfacesController maps HTTP requests; DTOs validate input

Database Schema

Table: supplier

ColumnTypeRequiredDescription
idUUIDautoPrimary key
businessIdUUIDyesMulti-tenant scope (FK → business)
nameVARCHARnoDisplay name
taxIdVARCHARnoTax identification number
taxNameVARCHARnoLegal/tax registered name
taxAddressVARCHARnoTax-registered address
contactJSONnoContact person details
emailVARCHARnoEmail address
phoneVARCHARnoPhone number
addressVARCHARnoPhysical address
cityVARCHARnoCity
postalCodeVARCHARnoPostal/ZIP code
supplierCodeVARCHARnoUnique supplier code (import upsert key)
countryIdVARCHAR(3)default GTISO country code
stateNameVARCHARnoState/region
departmentNameVARCHARnoDepartment
municipalityNameVARCHARnoMunicipality
taxpayerTypeVARCHARnoTax classification
isActiveBOOLEANdefault trueActive flag
createdAtTIMESTAMPTZautoCreation timestamp
createdByUUIDyesFK → user
updatedAtTIMESTAMPTZnoLast update timestamp
updatedByUUIDnoFK → user

API Endpoints

All endpoints require Bearer token authentication and Supplier resource permissions.

POST /suppliers

Create a new supplier. createdBy is resolved from the authenticated Firebase user.

GET /suppliers?businessId=<uuid>

Paginated list with search across 9 fields (name, email, phone, taxId, taxName, taxAddress, address, city, postalCode). Supports orderBy, order, page, size query params.

GET /suppliers/:id?businessId=<uuid>

Get a single supplier by ID, scoped by business.

PATCH /suppliers/:id

Partial update. updatedBy is resolved from the authenticated Firebase user. Body must include businessId.

DELETE /suppliers/:id?businessId=<uuid>

Permanently delete a supplier.

Data Import

Suppliers can be bulk-imported via the Data Import module. The import handler:

  • Requires name + either supplierCode or email for upsert matching
  • Resolves country names/aliases to ISO codes (US, GT, SV, HN, MX, CA)
  • Auto-detects create vs update based on existing records

Handler: apps/backend/src/data-import/application/handlers/supplier-import.handler.ts

Design Decisions

  1. No custom enums — Suppliers use plain string fields (taxpayerType, supplierCode) rather than PostgreSQL enums, allowing flexibility across different tax jurisdictions.
  2. Upsert via supplierCode or email — The import system uses these as natural keys for matching, avoiding duplicate suppliers during bulk imports.
  3. Country defaults to GT — The system defaults to Guatemala (countryId = 'GT') reflecting the primary market.
  4. Hard delete — Suppliers are permanently deleted (not soft-deleted). Consider deactivating (isActive = false) before deleting if the supplier has related purchase orders or AP bills.