Solution Design Document vs. Delivered Code — Line-by-Line Mapping
Solution Design: CSM 1.0 Fault Detail Sync — Solution Design Document Prepared by: Sataware Technologies LLC SDD Date: December 16, 2025 Approved by: Phil McCaffrey, December 16, 2025 Source Reviewed: v1.0.0.73 (V23 archive, March 5, 2026) Mapping Date: March 24, 2026
This document takes every commitment Sataware made in the Solution Design Document and maps it directly to what exists — or does not exist — in the delivered source code. Nothing is added, interpreted, or inferred. If the SDD says it, we check the code.
SDD Section 1 — Introduction
"This Solution Design document outlines the technical approach for synchronizing fault-related issue data from the FAQ API into Microsoft Dynamics 365 Business Central (BC) for the CSM 1.0 Fault Detail Sync initiative."
"The design builds upon previously shared artifacts and discussions and is intended to clearly define the approved scope, execution behavior, and boundaries for implementation."
"The solution is designed to ensure alignment between Business Central and the FAQ system while preserving historical integrity, avoiding disruption to posted documents, and maintaining consistency with existing BC extensions."
Three commitments are embedded in this introduction:
| SDD Commitment | Delivered? | What the code shows |
|---|---|---|
| Ensure alignment between BC and the FAQ system | Partial | Codeunit 65500 fetches data from the FAQ API and writes it into BC. But only 4 of 6 in-scope tables receive data, and alignment is one-directional with no mechanism to detect when BC and the FAQ system have drifted |
| Preserve historical integrity | Partial | No code touches posted documents. However, codeunit 65500 lines 209–212 delete and re-insert Fault/Resol. Relationship records, which changes their SystemId and SystemCreatedAt — breaking any external system referencing those records by ID |
| Avoid disruption to posted documents | Yes | No code in the extension reads, writes, or references any posted document table |
| Maintain consistency with existing BC extensions | Yes | The extension respects the BestwayUSA dependency and does not conflict with other installed extensions |
SDD Section 2 — Objectives
"Synchronize fault-related master data from the FAQ API into Business Central"
Partial. Codeunit 65500 ProcessStandardFormatResponse (lines 61–234) synchronizes four tables: Fault Area, Symptom Code, Fault Code, and Resolution Code. Two in-scope tables are not synchronized: Fault Area/Symptom Relationship (table 50113) is never referenced in any source file. Fault/Resolution Relationship (table 5920) receives data but through a different path — it is populated as a byproduct of the main sync loop, not as a standalone entity sync.
"Ensure BC uses only valid and active codes for new transactions"
Partial. Two of six tables have a Status field (Fault Area via TableExt 50076, Fault/Resol. Relationship via TableExt 65500). Page extensions filter these two tables on Status = "True" when opened. The remaining four tables — Symptom Code, Fault Code, Resolution Code, and Fault Area/Symptom Relationship — have no Status field. Every record on those tables remains selectable for new transactions regardless of whether it is current in the FAQ system.
"Preserve historical Service Orders, PSI records, and posted data"
Yes. No source file in the extension references any posted document table (Posted Service Invoice, Posted Service Shipment, Service Ledger Entry, etc.).
"Introduce inactive handling without deleting existing records"
Violated. Codeunit 65500 lines 209–212:
FaultResolutionRel.Delete(true);
NewFaultResolutionRel.Validate("Service Item Group Code", SerItemGrCodeBuff."Service Item Group ID");
NewFaultResolutionRel.Insert(true);
When a Fault/Resol. Relationship record needs its Service Item Group Code updated, the existing record is deleted and a new one is inserted. This is a deletion of an existing record — directly contrary to this objective.
"Provide controlled synchronization mechanisms as defined in this document"
Partial. The SDD defines three synchronization mechanisms in Section 7: a one-time full rebuild, an automated incremental sync, and stakeholder notifications. The full rebuild is partially implemented. The incremental sync does not exist — every execution is a full sync. Stakeholder notifications are implemented.
SDD Section 3 — Scope
"This Solution Design covers the synchronization of fault-related data entities between the FAQ API and Business Central, including fault areas, symptom codes, fault codes, resolution codes, and their defined relationships."
The SDD explicitly names five categories of data. Here is what the code does with each:
| Data Entity | Synced from FAQ API? | Receives Status field? | Can be marked Inactive? | Filtered in UI? |
|---|---|---|---|---|
| Fault Areas | Yes — codeunit 65500 line 108 | Yes — TableExt 50076, field 50000 | Yes | Yes — PageExt 50001 OnOpenPage filter |
| Symptom Codes | Yes — codeunit 65500 line 141 | No | No | No |
| Fault Codes | Yes — codeunit 65500 line 153 | No | No | No |
| Resolution Codes | Yes — codeunit 65500 line 172 | No | No | No |
| Fault/Resolution Relationship | Yes — codeunit 65500 line 185 | Yes — TableExt 65500, field 65500 | Yes | Yes — PageExt 65501 OnOpenPage filter |
| Fault Area/Symptom Relationship | No — not referenced in any source file | No | No | No |
One of six data entities is completely absent. Four of six have no inactive handling capability.
SDD Section 4 — Deliverables
"Updated BC extension logic to support FAQ API synchronization"
Partially delivered. Extension logic exists (codeunit 65500, page 65504, table extensions, page extensions). It synchronizes 4 of 6 data entities. It does not support incremental sync, inactive marking on 4 of 6 tables, or code generation.
"One-time full rebuild synchronization capability"
Partially delivered. The "First Time Sync" action on page 65504 (line 949) runs Report 65501, which iterates all Fault/Resol. Relationship records and sets their Status to "false" (Report/FaultResolCodRelationship.Report.al, line 16). It also sets the corresponding Fault Area Status to "false" (line 22). After the report runs, the user clicks "Sync Now" to import fresh data from the FAQ API. However, the rebuild only marks 2 of 6 tables as inactive — the other 4 tables have no Status field and cannot participate in the rebuild.
"Automated incremental synchronization capability"
Not delivered. There is no incremental sync in the extension. The Job Queue scheduling (page 65504, line 1005) exists and can schedule recurring executions — but every execution calls GetStandardFormat('en') which fetches the complete dataset from https://faq.bestwaycorp.com/ExternalAPI/api/1.0/standardFormat?lang=en. No "last modified" or "since" parameter is passed. No comparison between API data and existing BC data occurs. No records are marked Inactive when absent from the API response. Every scheduled run is a full rebuild, not an incremental sync.
"Stakeholder notification summary for detected changes"
Delivered. Two email notification procedures exist on page 65504:
SendReportByEmail(line 1165) — manual sync variant, lists all current active recordsSendReportByEmailJQ(line 1302) — scheduled sync variant, lists records where Status changed from False to True since the last sync
Both generate styled HTML emails and send to recipients configured in the Email Recipient Setup table (65550) via BC's built-in Email codeunit. The implementation satisfies 3 of 4 notification requirements (see Section 7.3 mapping below).
"Final source code delivery"
Delivered. The V23 archive contains v1.0.0.73 source that compiles successfully and matches the version of the compiled .app binary in the sandbox.
"Supporting technical documentation"
Not delivered. No documentation exists in the delivered archive — no README, no setup guide, no API reference, no changelog, no deployment instructions, no architecture description.
SDD Section 5 — Data Entities & Tables
"The following BC tables are involved in storing and relating fault-related data, as provided in the configuration artifacts supplied by Bestway:"
- Fault Area
- Symptom Code
- Fault Code
- Resolution Code
- Fault/Resolution Relationship
- Fault Area/Symptom Relationship
This section lists 6 tables as in-scope. The delivered code extends 2 of them.
| SDD Table | BC Table ID | Table Extension Delivered? | Extension ID | Fields Added |
|---|---|---|---|---|
| Fault Area | 5915 | Yes | TableExt 50076 | Status (Option: "true","false") |
| Symptom Code | 5916 | No | — | — |
| Fault Code | 5918 | No | — | — |
| Resolution Code | 5919 | No | — | — |
| Fault/Resolution Relationship | 5920 | Yes | TableExt 65500 | Status (Option: "True","False"), Source (Text[10]) |
| Fault Area/Symptom Relationship | 50113 | No | — | — |
4 of 6 tables listed in the SDD have no extension at all.
SDD Section 6 — Core Design Principles
"No deletion of records once used in posted transactions"
Violated. Codeunit 65500 lines 209–212 delete Fault/Resol. Relationship records and re-insert them when the Service Item Group Code mapping changes. The code does not check whether the record being deleted is referenced by any posted transaction before deleting it.
"Inactive records retained for historical reference"
Partially met. Records on Fault Area and Fault/Resol. Relationship can be set to Status "false" and will remain in the database. Records on Symptom Code, Fault Code, Resolution Code, and Fault Area/Symptom Relationship have no Status field — there is no concept of "inactive" on those tables, so all records are treated as active regardless of their standing in the FAQ system.
"Only active values available for new transactions"
Partially met. Three page extensions add filtering:
- PageExt 50001 (Fault Areas page): OnOpenPage sets filter Status = "true" — line 16
- PageExt 65501 (Fault/Resol. Relationship page): OnOpenPage sets filter Status = "True" — line 42
- PageExt 50000 (Service Order Subform): OnLookup for Fault Area Code filters Fault/Resol. Relationships by Status = "True" and Service Item Group Code — line 15
PageExt 50005 (Service Item Worksheet Subform) has the Service Item Group Code filter commented out on line 44:
// FaultAreaRel.SetRange("Service Item Group Code", Rec.ser);
This means the lookup shows all active fault areas across all product categories, ignoring the service item context.
There are no page extensions for Symptom Code, Fault Code, or Resolution Code pages. Users can select any record from those tables — active or not — on new transactions.
"Synchronization logic must be safe to re-run"
Violated. Two issues:
-
The delete-and-reinsert pattern (codeunit 65500, lines 209–212) means re-running the sync can delete a record and recreate it with a new
SystemId,SystemCreatedAt, andSystemModifiedAt. Any system that referenced the oldSystemIdwill have an orphaned reference. -
The
OnAfterInserttrigger on Job Queue Log Entry (TableExt 50001, line 14) callsFAQIntegration.GetStandardFormat('en')after the Job Queue has already executed codeunit 65500 viaOnRun. Every scheduled execution runs the sync twice. The second run sees the first run's inserts as "already exists" and skips them — but it re-evaluates every existing record for modification, doubling the write load and producing different insert/modify counts than the first run.
"Existing extension boundaries must be respected"
Met. The extension declares its dependency on BestwayUSA (Innovia Consulting) v27.0.4.2 in app.json. No object ID conflicts with other extensions in the repository. The ID range (50000–999999) is broad but does not overlap with other extensions' reserved ranges.
SDD Section 7.1 — One-Time Full Rebuild Sync
"The one-time full rebuild sync establishes a clean baseline within Business Central by aligning all fault-related data with the FAQ API."
The "First Time Sync" action exists on page 65504 (line 949). It prompts for admin credentials via page 50001, then runs Report 65501.
"All existing fault-related records will be marked Inactive"
Partially met. Report 65501 (Report/FaultResolCodRelationship.Report.al) iterates all Fault/Resol. Relationship records and sets Status = "false" (line 16). For each record, it also looks up the corresponding Fault Area and sets its Status to match (lines 18–27).
Records on Symptom Code (5916), Fault Code (5918), Resolution Code (5919), and Fault Area/Symptom Relationship (50113) are not marked inactive because those tables have no Status field. The word "all" in the SDD means all six tables. The code marks two.
"A complete import will be performed from the FAQ API"
Met. Codeunit 65500 GetStandardFormat (line 38) calls https://faq.bestwaycorp.com/ExternalAPI/api/1.0/standardFormat?lang=en and processes the full response array. Every record in the API response is upserted into BC.
"FAQ API identifiers will be persisted as the primary reference"
Not met. The FAQ API integer IDs (e.g., csMainCategoryId = "7", issueTypeId = "184") are written directly into the BC Code field as the record's primary key. There is no separate "FAQ ID" column on Fault Area, Symptom Code, Fault Code, or Resolution Code. The BC Code field and the FAQ API identifier are the same value.
A ServiceItemGroupCodeBuffer table (50001) does store a "FAQ ID" field — but this is a manual mapping table for associating Fault Area codes with Service Item Groups. It is not a comprehensive FAQ identifier store for all six tables.
The SDD says identifiers "will be persisted as the primary reference," implying a dedicated field to store the external ID. Instead, the external ID replaced the BC code entirely. This means:
- BC cannot have its own meaningful code values (e.g., "MOTOR-FAIL") — the codes are API integers ("7", "44")
- If the FAQ API changes its ID scheme, every BC record's primary key must change
- There is no way to look up a BC record by its own code — the FAQ API's ID is the code
"No legacy-to-API mapping logic will be maintained"
Met. No mapping from old BC codes to new FAQ API codes exists. The full rebuild replaces codes directly.
"Historical and posted data will remain unchanged"
Met. No code in the extension reads or writes any posted document table.
SDD Section 7.2 — Automated Incremental Sync
"Following the initial rebuild, an automated incremental synchronization process will be used to keep BC aligned with the FAQ API over time."
Not delivered. No incremental synchronization exists. What exists is a scheduled full sync. The distinction matters: the SDD describes a process that detects and applies only changes. The delivered code fetches everything and overwrites everything, every time.
"Periodic unattended execution"
Met. The ScheduleMyCodeunit action on page 65504 (line 1005) creates a BC Job Queue Entry that runs codeunit 65500 on a configurable schedule (Daily or Weekly). The Job Queue executes unattended.
"Detection of changes between API data and BC cache"
Not delivered. There is no change detection logic anywhere in the source code. Codeunit 65500 GetStandardFormat (line 48) fetches the complete dataset from /api/1.0/standardFormat — the same endpoint used for the full rebuild. No "last modified" timestamp, no "since" parameter, no pagination token, and no hash comparison is sent to the API or computed locally. The code does check whether individual field values (e.g., Description) differ before issuing a Modify (e.g., line 116–117), but this is field-level optimization within a full dataset — not change detection between the API and BC.
"Insert new records"
Met. ProcessStandardFormatResponse (codeunit 65500) checks if not FaultArea.Get(MainCategoryId) (line 108) and inserts new records when they don't exist in BC.
"Update modified records"
Met. For records that already exist, the code compares Description and Status values and issues Modify(true) when they differ (e.g., lines 116–124 for Fault Area, lines 195–214 for Fault/Resol. Relationship).
"Mark records Inactive when removed from the API"
Not delivered. There is no logic to compare the set of records returned by the API against the set of records in BC. If a Fault Area exists in BC but is absent from the API response, nothing happens — it remains active (Status = "True") in BC forever. The SDD commits to marking such records Inactive. The code does not do this.
"Maintain sync state across executions"
Partially met. A FAQ Sync Log table (50012) records each sync execution with a timestamp, sync type (Manual/Scheduled), records activated count, and user ID. The UpdateSyncLogJQ procedure (page 65504, line 1495) writes a log entry after each sync.
However, there is no per-record sync state. The log records "a sync happened at this time" — it does not record which specific records were added, modified, or should have been deactivated. There is no "last synced" timestamp on individual records. This makes it impossible to answer "when was this specific record last verified against the FAQ API?"
SDD Section 7.3 — Stakeholder Notification
"After each automated incremental sync, a notification summary will be generated to inform stakeholders of detected changes."
Notification emails are generated after both manual and scheduled syncs. Since no incremental sync exists (see Section 7.2), the notification reports are based on full sync results.
"Configurable recipient list"
Met. Table 65550 (Email Recipient Setup) stores recipient entries with User ID, User Name, and Email. Page 65551 (Email Recipient Lines) provides a lookup against BC's Users table. Recipients are loaded in both SendReportByEmail (line 1190) and SendReportByEmailJQ (line 1341).
"Summary of newly added records"
Met. Codeunit 65500 tracks FaultResolutionRelInsertCount (line 295). This count is passed to the email body via FAQ Sync Counter Manager (codeunit 65501) and displayed in the notification email's summary cards as "New (This Sync)."
"Summary of updated records"
Met. Codeunit 65500 tracks FaultResolutionRelModifyCount (line 296). This count is displayed in the notification email as "Modified (This Sync)."
"Summary of records marked Inactive"
Not delivered. No logic exists to mark records Inactive (see Section 7.2 — "Mark records Inactive when removed from the API" is not delivered). Because no records are ever deactivated by the sync, there is nothing to summarize. The JQ email reports records where Status changed from False to True (newly activated), but never the reverse (deactivated). The SDD specifically requires a summary of records marked Inactive — this cannot exist without the underlying deactivation logic.
SDD Section 8 — UI & Validation Behavior
"User-facing pages and validation logic will be updated so that only Active fault-related values are available for selection in new Service Orders, PSIs, and related transactions."
Partially met. Three page extensions add filtering for Fault Area and Fault/Resol. Relationship:
| Page Extension | Extends | Filter Applied | Scope |
|---|---|---|---|
| PageExt 50001 | Fault Areas | OnOpenPage: Status = "true" | Hides inactive fault areas when the list page is opened |
| PageExt 65501 | Fault/Resol. Cod. Relationship | OnOpenPage: Status = "True" | Hides inactive relationships when the list page is opened |
| PageExt 50000 | Service Order Subform | OnLookup: Fault Area Code filtered by Status = "True" on related Fault/Resol. records and by Service Item Group Code | Limits fault area selection on Service Order lines to active, relevant records |
| PageExt 50005 | Service Item Worksheet Subform | OnLookup: Status = "True" on Fault/Resol. records, but Service Item Group Code filter is commented out (line 44) | Shows all active fault areas regardless of product — overly broad |
No page extensions exist for:
- Symptom Code selection — all records selectable
- Fault Code selection — all records selectable
- Resolution Code selection — all records selectable
- Any PSI-related pages — no extensions delivered
- Any lookup validation that prevents saving an inactive code on a new transaction
The OnOpenPage filters are soft filters — a user can clear them from the filter pane and see all records including inactive ones. There is no hard validation (e.g., an OnValidate trigger that rejects an inactive code).
"Inactive values will remain stored for historical reference but will not be selectable."
Partially met. Records with Status = "false" are retained (not deleted) on the two tables that have Status. They are hidden by the OnOpenPage filter but remain selectable if the filter is cleared. On the four tables without Status, all records are always selectable — there is no inactive state to enforce.
SDD Section 9 — Data Safety & Integrity
"No modification of posted documents"
Met. No source file references any posted document table.
"Inactive handling ensures backward compatibility"
Partially met. On Fault Area and Fault/Resol. Relationship, inactive records remain in the database and historical transactions referencing them will continue to display correctly. On the four unextended tables, there is no inactive handling — backward compatibility is maintained by default (no schema change), but there is also no ability to distinguish current codes from stale ones.
"Partial updates are avoided"
Not met. The sync loop in ProcessStandardFormatResponse (codeunit 65500, lines 96–234) processes the API response array sequentially. Each record insert or modify is committed individually — there is no wrapping transaction or rollback mechanism. If the API returns 1,000 records and an error occurs on record 500, the first 499 are committed and the remaining 501 are not. The Commit() call in UpdateSyncLogJQ (page 65504, line 1508) forces a transaction boundary that prevents rollback of the partial sync.
Additionally, the sync only operates on Fault/Resol. Relationship insert/modify counts — the insert/modify of Fault Area, Symptom Code, Fault Code, and Resolution Code records are not counted, logged, or reported. A partial failure affecting those tables would be invisible.
"Repeat executions do not create duplicate or invalid data"
Not fully met. The upsert pattern (check if exists, insert or modify) prevents simple duplicates. However:
-
The delete-and-reinsert pattern (codeunit 65500, lines 209–212) creates a new record with a different
SystemIdandSystemCreatedAteach time the Service Item Group mapping changes and the sync re-runs. Any system referencing the oldSystemIdwill hold an orphaned reference. -
The double execution via
OnAfterInsert(TableExt 50001, line 14) means every scheduled sync runs twice. The second run's modify count includes records modified by the first run (whoseSystemModifiedAtnow differs from the API value check), potentially triggering unnecessary writes.
SDD Section 10 — Estimated Effort
"Total estimated effort for the scope defined in this document:"
- ~134 hours (approximate)
- Hourly rate: $28/hr
"This estimate reflects the combined effort for core sync functionality, one-time rebuild, automated incremental synchronization, and stakeholder notification behavior."
The estimate explicitly names four work streams:
| Work Stream | Delivered? |
|---|---|
| Core sync functionality | Partially — 4 of 6 tables synced, 2 of 6 tables extended, 0 of 36 specified fields |
| One-time rebuild | Partially — marks 2 of 6 tables inactive |
| Automated incremental synchronization | Not delivered — every run is a full sync |
| Stakeholder notification behavior | Delivered — email notifications with configurable recipients |
Invoice #466051 was submitted for the full estimated amount.
SDD Section 11 — Delivery Model
"The implementation will follow an outcome-based delivery model:"
"Requirements aligned via this Solution Design"
The Solution Design was approved December 16, 2025. It defines the scope, deliverables, and design principles. The delivered code does not align with this document in the areas detailed above.
"Effort estimate approval"
~134 hours at $28/hr was approved by Rob Chowdhury in December 2025.
"Final source code delivery with supporting documentation"
Source code was delivered (v1.0.0.73, March 20, 2026). Supporting documentation was not delivered — no README, no setup guide, no API documentation, no deployment instructions, no changelog, no architecture description.
Summary — Commitment Scorecard
The SDD contains 33 distinct, verifiable commitments across Sections 2–9 and 11. Each commitment is scored below.
| # | SDD Section | Commitment | Delivered? |
|---|---|---|---|
| 1 | 2 | Synchronize fault-related master data from FAQ API into BC | Partial |
| 2 | 2 | Ensure BC uses only valid and active codes for new transactions | Partial |
| 3 | 2 | Preserve historical Service Orders, PSI records, and posted data | Yes |
| 4 | 2 | Introduce inactive handling without deleting existing records | No — records are deleted (line 210) |
| 5 | 2 | Provide controlled synchronization mechanisms as defined in this document | Partial |
| 6 | 4 | Updated BC extension logic to support FAQ API synchronization | Partial |
| 7 | 4 | One-time full rebuild synchronization capability | Partial |
| 8 | 4 | Automated incremental synchronization capability | No |
| 9 | 4 | Stakeholder notification summary for detected changes | Yes |
| 10 | 4 | Final source code delivery | Yes |
| 11 | 4 | Supporting technical documentation | No |
| 12 | 6 | No deletion of records once used in posted transactions | No — Delete(true) on line 210 |
| 13 | 6 | Inactive records retained for historical reference | Partial — 2 of 6 tables |
| 14 | 6 | Only active values available for new transactions | Partial — 2 of 6 tables |
| 15 | 6 | Synchronization logic must be safe to re-run | No — delete-reinsert + double execution |
| 16 | 6 | Existing extension boundaries must be respected | Yes |
| 17 | 7.1 | All existing fault-related records will be marked Inactive | Partial — 2 of 6 tables |
| 18 | 7.1 | A complete import will be performed from the FAQ API | Yes |
| 19 | 7.1 | FAQ API identifiers will be persisted as the primary reference | No — API IDs used as BC Code values, no separate field |
| 20 | 7.1 | No legacy-to-API mapping logic will be maintained | Yes |
| 21 | 7.1 | Historical and posted data will remain unchanged | Yes |
| 22 | 7.2 | Periodic unattended execution | Yes |
| 23 | 7.2 | Detection of changes between API data and BC cache | No |
| 24 | 7.2 | Insert new records | Yes |
| 25 | 7.2 | Update modified records | Yes |
| 26 | 7.2 | Mark records Inactive when removed from the API | No |
| 27 | 7.2 | Maintain sync state across executions | Partial — log exists but no per-record state |
| 28 | 7.3 | Configurable recipient list | Yes |
| 29 | 7.3 | Summary of newly added records | Yes |
| 30 | 7.3 | Summary of updated records | Yes |
| 31 | 7.3 | Summary of records marked Inactive | No — deactivation logic does not exist |
| 32 | 8 | Only Active values available for selection in new Service Orders, PSIs, and related transactions; inactive values not selectable | Partial — 2 of 6 tables, soft filters only |
| 33 | 9 | No modification of posted documents | Yes |
| 34 | 9 | Inactive handling ensures backward compatibility | Partial — 2 of 6 tables |
| 35 | 9 | Partial updates are avoided | No — no transaction wrapping |
| 36 | 9 | Repeat executions do not create duplicate or invalid data | No — delete-reinsert changes SystemId |
| 37 | 11 | Final source code delivery with supporting documentation | Partial — source delivered, documentation not delivered |
Totals
| Verdict | Count | % |
|---|---|---|
| Yes — fully delivered as committed | 13 | 35% |
| Partial — partially delivered, material gaps remain | 13 | 35% |
| No — not delivered or directly violated | 11 | 30% |
| Total commitments | 37 |