Document Line Service
Overview
Document lines represent individual items within a document (sales order, credit note, quote, etc.). Each line captures product, quantity, pricing, and fulfillment details.
All document line operations are tenant-aware and scoped to the current tenant.
Endpoints
GET
/api/v1/DocumentLines/document/{documentId}
Returns all lines for a specific document.
Route parameters:
documentId(long, required) - parent document identifier
Behavior:
- Returns
404if no lines exist
Response:
List<DocumentLineModel>
Authorization:
- Requires Bearer Token
- Permission:
FullRead
GET
/api/v1/DocumentLines/{id}
Returns a single document line by its numeric identifier.
Route parameters:
id(long, required) - line identifier
Behavior:
- Returns
404if not found
Response:
DocumentLineModel
Authorization:
- Requires Bearer Token
- Permission:
FullRead
POST
/api/v1/DocumentLines
Creates a new document line.
Request body (DocumentLineModel):
DocumentId(long, required)LineNumber(int, required)ProductId(long, required)Quantity(decimal, required — must be > 0)UnitPrice(decimal, required — must be >= 0)LineTotal(decimal, required — must be >= 0)- Other fields optional — see DocumentLineModel
Behavior:
- Validation is handled in the service layer
- Successful creation writes an audit log entry
Response:
201 Created—DocumentLineModel
Authorization:
- Requires Bearer Token
- Permission:
FullManage
PUT
/api/v1/DocumentLines/{id}
Updates an existing document line.
Route parameters:
id(long, required) - line identifier
Request body (DocumentUpdate):
Id(long, required, must be > 0)Quantity(decimal?, optional)UnitPrice(decimal?, optional)DefaultSalePrice(decimal?, optional)DiscountAmount(decimal?, optional)TaxAmount(decimal?, optional)IsCustomised(bool?, optional) — mark line as requiring customisationIsPicked(bool?, optional) — mark line as picked in warehouseIsPacked(bool?, optional) — mark line as packed for dispatch
Behavior:
- Only provided (non-null) fields are updated
- Line total is recalculated from quantity, unit price, discount and tax
- Document subtotal, tax total and grand total are recalculated after each line update
- Successful update writes an audit log entry
Response:
200 OK—DocumentLineModel
Authorization:
- Requires Bearer Token
- Permission:
FullManage
DELETE
/api/v1/DocumentLines/{id}
Deletes a document line by its identifier.
Route parameters:
id(long, required) - line identifier
Behavior:
- Performs a hard delete
- Successful deletion writes an audit log entry
Response:
204 No Content
Authorization:
- Requires Bearer Token
- Permission:
FullManage
DocumentLineModel Fields
Returned by GET and POST endpoints:
Id (type: long)— internal identifierDocumentId (type: long?)— parent documentLineNumber (type: int?)— position within the documentProductId (type: long?)— product FKProductVariantId (type: long?)— product variant FKSku (type: string?)— SKU at time of orderTitle (type: string?)— product title at time of orderDescription (type: string?)— description at time of orderQuantity (type: decimal?)— ordered quantityUnitPrice (type: decimal?)— unit priceDefaultSalePrice (type: decimal?)— RRP/sale priceDiscountAmount (type: decimal?)— discount appliedTaxRate (type: decimal?)— tax rate percentageTaxAmount (type: decimal?)— calculated taxLineTotal (type: decimal?)— calculated line totalWarehouseId (type: long?)— fulfillment warehouseEntityUserId (type: long?)— entity user who placed the lineNotes (type: string?)— free-text notesQuantityFulfilled (type: decimal?)— quantity dispatchedQuantityReturned (type: decimal?)— quantity returnedReturnedDate (type: DateTime?)— date of returnIsPicked (type: bool)— whether the item has been picked from the warehouseIsPacked (type: bool)— whether the item has been packed for dispatchIsCustomised (type: bool)— whether the item requires customisation
Notes
- Validation is enforced in the service layer, not via model attributes
- All operations are tenant-aware
- All state-changing operations are audited
- Internal errors are logged but not exposed to clients