Skip to main content

Change Document — Bestway CSM API Toolkit v1.2.0.0

Date: 2026-03-05 Extension: Bestway CSM API Toolkit Previous Version: 1.1.0.0 New Version: 1.2.0.0


Background

The Failed CSM Submissions Resolver is a recurring workflow that identifies service orders where CSM quality data was not successfully submitted and facilitates manual resolution. Historically, the data extraction step relied on exporting configpacks from Business Central — a manual, error-prone process that requires direct database access and produces large, unwieldy XML files.

The bc-csm-api extension already provides OData v4 API access to service orders, service order lines, fault tables, and posted document lines. However, two gaps blocked the migration from configpacks to API-based extraction:

  1. No Posted Service Invoice Header endpoint. The resolver spreadsheet requires fields from the Service Invoice Header table — specifically the invoice number (used as service_order_id), order date (claim date), CSM Quality Sent flag, and Location Code — none of which are available through existing endpoints.

  2. No computed fields on Posted Service Invoice Lines. The spreadsheet requires item_num (Item No. from the linked Service Item) and purchase_date (Sales Date from the linked Service Item). These fields are not on the Service Invoice Line table itself — they require the same Service Item lookup that already exists on the Service Order Line API page.

Summary of Changes

#CategorySeverityDescription
1AddedStandardPosted Svc. Invoice Header API (Page 56123)
2AddedStandarditemNo and salesDate computed fields on Posted Svc. Invoice Line API (Page 56121)
3AddedStandardSubpage expansion linking invoice headers to invoice lines
4AddedStandardfileUrls computed field on Posted Svc. Invoice Header API — Record Link URLs as JSON
5AddedStandardGetRecordLinkUrlsJson procedure on CSM API Helper (Codeunit 56104)

Detailed Changes

1. Posted Svc. Invoice Header API (Page 56123)

New read-only API page exposing the Service Invoice Header table (5992) under /postedServiceInvoiceHeaders.

Fields exposed:

FieldSourceTypeUsage in Resolver
idSystemIdGUIDOData key
noNo.Code[20]Invoice document number
customerNoCustomer No.Code[20]customer_id column
orderNoOrder No.Code[20]service_order_id column
orderDateOrder DateDateclaim_date column
postingDatePosting DateDateReference
locationCodeLocation CodeCode[10]Filter: Location Code = 'CS'
csmQualitySentINVC CSM Quality SentBooleanFilter: CSM Quality Sent = false

The csmQualitySent field is a direct table binding to the INVC CSM Quality Sent custom field from the BestwayUSA extension — unlike the Service Order API (Page 56101), which uses a computed field via CsmApiHelper. On the posted invoice header, the field exists directly on the table, so no helper lookup is needed.

Subpage part: The page includes a subpage linking to Posted Svc. Invoice Line API (Page 56121) via "Document No." = field("No."). This enables OData $expand=postedServiceInvoiceLines to return all invoice lines for a given invoice header in a single request.

File: Page/PostedSvcInvoiceHeaderAPI.Page.al (new)

2. Computed Fields on Posted Svc. Invoice Line API (Page 56121)

Two computed fields added to the existing posted invoice line page:

FieldVariableLookupHelper Procedure
itemNoItemNoValueService Item → Item No.CsmApiHelper.GetServiceItemNo
salesDateSalesDateValueService Item → Sales DateCsmApiHelper.GetServiceItemSalesDate

These follow the exact pattern established on the Service Order Line API page (56102), which already uses the same CsmApiHelper procedures. The implementation adds an OnAfterGetRecord trigger and two page-level variables.

No changes to CsmApiHelper (Codeunit 56104) are required — both GetServiceItemNo and GetServiceItemSalesDate already exist and handle null-safety (empty Service Item No. returns blank/zero date).

File: Page/PostedSvcInvoiceLineAPI.Page.al (modified)

3. Subpage Expansion

The subpage link uses "Document No." = field("No."), joining invoice lines to their invoice header by the posted invoice document number. This is the correct FK relationship for the posted document tables — each posted invoice line's Document No. field references the posted invoice header's No. field.

For service-order-level queries (all lines across all invoices for a service order), the existing flat endpoint /postedServiceInvoiceLines?$filter=orderNo eq 'SO-001234' remains the better approach.

The Innovia CSM Quality submission (INVCCSMQualityandStockJob codeunit) sends photo URLs to CSM 1.0 by reading Record Link records attached to each Service Invoice Header. The URLs are packaged as a JSON array in the files field of the submission payload:

[{"url": "https://blob.example.com/photo1.jpg", "type": "1"}, ...]

To enable the Python client to replicate this pattern, a new helper procedure GetRecordLinkUrlsJson was added to CsmApiHelper (Codeunit 56104). It accepts a Service Invoice Header No., finds all Record Links attached to that header's RecordId, and returns the URLs as a JSON array string in the same {"url", "type"} format used by Innovia.

The procedure is exposed on the Posted Svc. Invoice Header API page as a computed fileUrls field. This means a single OData query to /postedServiceInvoiceHeaders returns both the invoice metadata and the associated photo URLs — no separate API call needed.

Why a computed field instead of a subpage? The BC Record Link system table (2000000068) is keyed by RecordId, a composite type that cannot be used in OData SubPageLink field mappings or $filter expressions. Embedding the URLs as a JSON text field on the header page avoids this limitation entirely.

Files:

  • Codeunit/CsmApiHelper.Codeunit.al (modified — new GetRecordLinkUrlsJson procedure)
  • Page/PostedSvcInvoiceHeaderAPI.Page.al (modified — new fileUrls computed field)

Object Inventory

Object IDTypeNameSource TableStatus
56101PageService Order APIService HeaderExisting
56102PageService Order Line APIService LineExisting
56103PageServ. Order Attachment APIDocument AttachmentExisting
56104CodeunitCSM API HelperModified
56105PageFault Area APIFault Area (5915)Existing
56106PageSymptom Code APISymptom Code (5916)Existing
56107PageFault Code APIFault Code (5918)Existing
56108PageResolution Code APIResolution Code (5919)Existing
56109PageFault Resol. Relation APIFault/Resol. Cod. Relationship (5920)Existing
56120PageFA Symptom Relation APIINVC FA Symptom Code Relation (50113)Existing
56121PagePosted Svc. Invoice Line APIService Invoice Line (5993)Modified
56122PagePosted Svc. Shipment Line APIService Shipment Line (5991)Existing
56123PagePosted Svc. Invoice Header APIService Invoice Header (5992)New

File Structure

bc-csm-api/
├── app.json
├── README.md
├── BestwayCSM.postman_collection.json
├── Codeunit/
│ └── CsmApiHelper.Codeunit.al (56104)
├── Page/
│ ├── ServiceOrderAPI.Page.al (56101)
│ ├── ServiceOrderLineAPI.Page.al (56102)
│ ├── ServiceOrderAttachmentAPI.Page.al (56103)
│ ├── FaultAreaAPI.Page.al (56105)
│ ├── SymptomCodeAPI.Page.al (56106)
│ ├── FaultCodeAPI.Page.al (56107)
│ ├── ResolutionCodeAPI.Page.al (56108)
│ ├── FaultResolRelationAPI.Page.al (56109)
│ ├── FASymptomRelationAPI.Page.al (56120)
│ ├── PostedSvcInvoiceLineAPI.Page.al (56121) ← modified
│ ├── PostedSvcShipmentLineAPI.Page.al (56122)
│ └── PostedSvcInvoiceHeaderAPI.Page.al (56123) ← new
└── docs/
├── CHANGELOG.md
├── CHANGE-v1.1.0.0.md
└── CHANGE-v1.2.0.0.md ← new

Known Limitations

  1. Computed fields execute per-row. The itemNo and salesDate lookups on Pages 56121 and 56102 call CsmApiHelper.GetServiceItemFields for every row returned. This performs one Service Item Get call per row (reduced from two in the initial implementation). For large result sets (e.g., unfiltered queries returning thousands of lines), OData queries should always include filters ($filter, $top) to bound the result set.

Deployment Notes

Pre-Deployment

  1. Ensure BestwayUSA extension v27.0.0.0 is published (dependency).

Deployment

  1. Build and publish bc-csm-api v1.2.0.0 to the sandbox via VS Code (AL: Publish / F5).
  2. Verify all 13 pages are accessible in the API endpoint listing.

Post-Deployment Verification

  1. Query /postedServiceInvoiceHeaders to confirm data returns.
  2. Query /postedServiceInvoiceHeaders?$filter=csmQualitySent eq false and locationCode eq 'CS' to verify the primary resolver filter works.
  3. Query /postedServiceInvoiceHeaders({id})?$expand=postedServiceInvoiceLines to verify subpage expansion.
  4. Query /postedServiceInvoiceLines and verify itemNo and salesDate fields are populated for lines with a Service Item.

Testing

Automated tests: Test codeunit 56115 (Posted Inv. Header API Test) covers GetRecordLinkUrlsJson (URL retrieval, empty results, Note-type exclusion, non-existent records) and GetServiceItemFields (combined lookup, blank handling). Combined with existing test codeunits 56111–56114, the bc-csm-api-test extension now covers all CsmApiHelper procedures.

UAT test plan: See docs/UAT Test Plan - Bestway CSM API Toolkit v1.2.0.0.docx for manual verification steps covering all API endpoints, computed fields, OData query options, and error handling.

Manual verification: Postman or OData queries as described in Post-Deployment Verification above.