Saltar al contenido principal

Goods Received Notes (GRN)

Overview

The GRN module manages the lifecycle of goods received against Purchase Orders. A GRN confirms physical delivery, verifies quantities (including damaged items), updates inventory, and supports cost tracking and PDF/print output.

Module path: apps/backend/src/goods-received-notes/

Architecture

The module follows hexagonal architecture:

goods-received-notes/
├── domain/
│ └── goods-received-notes-repository.domain.ts # Port interface + injection token
├── application/
│ ├── goods-received-notes.service.ts # Use cases / business logic
│ └── events/
│ ├── on-create-goods-received-note.event.ts
│ └── on-update-goods-received-note.event.ts
├── infrastructure/
│ └── goods-received-notes.repository.ts # Kysely adapter
└── interfaces/
├── goods-received-notes.controller.ts # REST endpoints
├── dtos/
│ ├── create-goods-received-note.dto.ts
│ └── update-goods-received-note.dto.ts
└── query/
└── paginate-goods-received-notes.query.ts

Dependency flow: Controller → Service → Repository (via domain interface)

The service injects IGoodsReceivedNotesRepository via the GOODS_RECEIVED_NOTES_REPOSITORY symbol token, keeping the application layer decoupled from infrastructure.

Domain Concepts

ConceptDescription
GRNDocument confirming receipt of goods against a Purchase Order
Statusdraftsubmittedreviewed
goodsReceivedDetailJSONB containing items array with quantities, prices, and inventory details
costDetailJSONB with WAC cost calculations, computed on create/update
documentNumberAuto-generated sequential number per business

Status Lifecycle

DRAFT ──(submit)──> SUBMITTED ──(manual)──> REVIEWED
  • DRAFT: Can be edited, deleted, or submitted
  • SUBMITTED: Inventory details have been created; triggers goodsReceivedNote.submitted event
  • REVIEWED: Final state after human review

API Endpoints

MethodEndpointDescription
POST/goods-received-notesCreate a GRN (generates document number, calculates costs)
GET/goods-received-notesList GRNs with pagination and filters
GET/goods-received-notes/searchAlias for list endpoint
GET/goods-received-notes/:idGet a single GRN by UUID
GET/goods-received-notes/:id/pdfDownload PDF with optional page/margin settings
GET/goods-received-notes/:id/printHTML print preview
PUT/goods-received-notes/:id/submitTransition DRAFT → SUBMITTED, creates inventory details
PATCH/goods-received-notes/:idPartial update (recalculates costs)
DELETE/goods-received-notes/:id?businessId=Delete GRN (scoped by business)

Query Parameters (GET list)

ParameterTypeDescription
businessIdUUIDFilter by business
statusstringFilter by status (draft/submitted/reviewed)
purchaseOrderIdUUIDFilter by linked PO
createdAtFrom / createdAtToISO dateCreation date range
receivedDateFrom / receivedDateToISO dateReceived date range
searchstringText search on document number
page, sizenumberPagination
orderBy, orderstringSorting

Key Flows

Create GRN

  1. Extract items from goodsReceivedDetail, validate inventory detail quantities
  2. Group items by location + product
  3. Ensure inventory records exist (create if missing)
  4. Calculate WAC costs and populate costDetail
  5. Generate document number
  6. Insert record within a transaction
  7. Emit goodsReceivedNote.create event

Submit GRN

  1. Verify status is DRAFT
  2. For each received item: create inventory detail records (net quantity = received - damaged)
  3. Update status to SUBMITTED within a transaction
  4. Emit goodsReceivedNote.submitted event

PDF Generation

Uses the shared PDF module: GenerateGoodsReceivedNotePdfUseCaseGoodsReceivedNoteDataTransformer → Handlebars template → Puppeteer PDF.

Integration Points

  • Purchase Orders: Linked via purchaseOrderId FK
  • Inventories: Ensures inventory records exist; reads current costs for WAC
  • Inventory Details: Creates detail records on submit (batch/serial tracking)
  • Product Cost History: Cost changes tracked with source_type = 'goods_received_note'
  • PDF Module: Dedicated use case, transformer, and template
  • Document Counter: Auto-generates sequential numbers per business

Database

  • Table: goodsReceivedNote (camelCase via Kysely codegen)
  • Enum: goods_received_note_status (draft, submitted, reviewed)
  • Migration: 2024-06-02t23:35:16.678z-onboarding-tables.mjs
  • Indexes: idx_grn_business_id, idx_grn_purchase_order_id, idx_grn_status, idx_grn_received_date

Events

EventPayloadWhen
goodsReceivedNote.create{ createdGoodsReceivedNoteRecord }After creation
goodsReceivedNote.update{ originalGoodsReceivedNoteRecord, updatedGoodsReceivedNoteRecord }After update
goodsReceivedNote.submitted{ grn }After submission

Bruno API Collection

Located at api-client/flowpos/collections/goods-received-notes/. Includes requests for all endpoints including create variants (complete, partial, multi-currency), PDF generation, print preview, update, delete, and submit.