CMS FAQ Management Extension — Delivery Review v3.0
Extension: FAQ Management (Epic 193) Source Version Reviewed: v1.0.0.73 (V23 archive, dated March 5, 2026) Compiled Version in Sandbox: v1.0.0.73 Vendor: Sataware Technologies LLC Solution Design Date: December 16, 2025 Original Review Date: March 6, 2026 (v1.1 — symbol extraction only) First Source Review Date: March 19, 2026 (v2.0 — v1.0.0.57 source) Current Review Date: March 24, 2026 (v3.0 — v1.0.0.73 source) Prepared by: Phil McCaffrey, ERP Specialist Organization: Bestway (USA) Inc.
Executive Summary
This is the third Delivery Review of the CMS FAQ Management extension (Epic 193). The v2.0 review (March 19, 2026) was based on v1.0.0.57 source code — 16 revisions behind the v1.0.0.73 binary deployed in the sandbox. On March 20, 2026, Sataware delivered a zip archive ("V23 FAQ Management 05-03-2026.zip") containing source code labeled v1.0.0.73, closing the version number gap identified in v2.0.
This review is based on a complete reading of all 17 AL source files in the V23 archive and a successful compilation using the AL Language compiler (v16.3.31.33499). The source compiles with warnings only (no errors), confirming it can produce a functional .app binary.
Key findings in this review:
-
The core SDD gap analysis from v2.0 is unchanged. The v1.0.0.73 source addresses none of the structural gaps: 2 of 6 tables extended, 0 of 36 required fields delivered, no incremental sync, no code generation, no CSM logging (Story 7), and the same security vulnerabilities (hardcoded credentials).
-
The FAQ Management page was refactored, not removed. The v2.0 review identified the page as potentially non-functional based on a partial reading. The V23 source reveals the file contains two versions: an 834-line commented-out old version followed by an active rewrite (lines 838–1662). The active version compiles and is functional.
-
Changes from v1.0.0.57 to v1.0.0.73 are cosmetic. The 16-revision gap produced UI cleanup, email formatting improvements, and minor refactoring — not gap remediation.
-
All v2.0 recommendations remain in effect. Do not deploy to production. Revoke the hardcoded FAQ API credentials. Determine the path forward (vendor remediation or in-house completion).
1. Background
1.1. Review History
| Version | Date | Basis | Key Finding |
|---|---|---|---|
| v1.1 | March 6, 2026 | Symbol extraction from compiled .app | Significant scope gaps visible even through symbols |
| v2.0 | March 19, 2026 | v1.0.0.57 source code (16 revisions behind) | Full gap analysis; security findings; 5 of 26 acceptance criteria satisfied |
| v3.0 | March 24, 2026 | v1.0.0.73 source code (version-matched) | v2.0 findings confirmed; 16-revision gap contained only UI polish |
1.2. Source Code Version Gap — Resolved
| Artifact | v2.0 Status | v3.0 Status |
|---|---|---|
| Source code version | 1.0.0.57 | 1.0.0.73 |
| Compiled .app version | 1.0.0.73 | 1.0.0.73 |
| Gap | 16 revisions unaccounted for | Version numbers now match |
The V23 archive app.json specifies version 1.0.0.73, matching the compiled binary in the sandbox. The source compiles successfully, producing only warnings (AL0482: invalid image "none" on two actions). The version gap that qualified every finding in v2.0 is now closed — the assessments in this document are based on the actual deployed code.
1.3. Compilation Verification
Microsoft (R) AL Compiler version 16.3.31.33499
Compilation started for project 'FAQ Management 1.0' containing '17' files.
Page/FAQManagement.Page.al(954,25): warning AL0482: The image none is not valid
Page/FAQManagement.Page.al(985,25): warning AL0482: The image none is not valid
Compilation ended.
Result: Compiles with warnings only. No errors. The source can produce the deployed binary.
1.4. Project Timeline (updated)
| Date | Event |
|---|---|
| Dec 16, 2025 | Solution Design approved (~134 hours at $28/hr) |
| Feb 28, 2026 | v1.0.0.58 installs in sandbox |
| Mar 5, 2026 | v1.0.0.73 uploaded; source withheld pending payment |
| Mar 6, 2026 | Delivery Review v1.1 (symbol extraction) |
| Mar 19, 2026 | v1.0.0.57 source delivered; Delivery Review v2.0 |
| Mar 20, 2026 | v1.0.0.73 source delivered (V23 archive) |
| Mar 24, 2026 | Delivery Review v3.0 (this document) |
2. What Changed Between v1.0.0.57 and v1.0.0.73
The 16-revision gap between the v2.0 review source and the V23 source produced the following changes:
2.1. FAQ Management Page Refactored
The Pag65504.FAQManagement.al file (now Page/FAQManagement.Page.al) was rewritten. The old version was commented out in place (lines 1–834) and a new version written below it (lines 838–1662). The file is 1,662 lines total; approximately 830 lines are dead commented-out code.
Changes in the rewrite:
| Area | v1.0.0.57 (old version) | v1.0.0.73 (new version) |
|---|---|---|
| Job Queue setup | Inline code duplicated in Daily and Weekly branches | Extracted to GetOrCreateJobQueueEntry() helper (line 1515) |
| Sync log tracking | UpdateSyncLogJQ(DateTime, Integer) | UpdateSyncLogJQ(DateTime, Integer, Boolean) — pIsManual parameter distinguishes manual vs. scheduled syncs |
| Manual sync email | Showed active/inactive counts and new/modified counts | Now includes a full table of all active records after sync |
| JQ sync email | Showed False→True status changes since last sync | Same behavior, minor formatting refinements |
| Email HTML | Basic styled HTML | More polished layout with card-style summary boxes, row numbering, alternating row colors |
2.2. FAQ Sync Counter Manager Added
A new SingleInstance codeunit (65501 "FAQ Sync Counter Manager") was added to pass insert/modify counts from the Job Queue execution context to the page. This addresses a real problem — the Job Queue runs the codeunit in a separate session, so the page cannot directly read the codeunit's counters. The SingleInstance pattern persists the counts in memory within the same server session.
2.3. No Structural Changes
The following areas are unchanged between v1.0.0.57 and v1.0.0.73:
- API client (codeunit 65500) — same endpoints, same hardcoded credentials, same full-fetch logic
- Table extensions — same 2 tables (Fault Area, Fault/Resol. Relationship), same fields
- Page extensions — same filters, same commented-out Service Item Group filter
- Job Queue Log Entry trigger — same double-execution pattern
- Reports — same Report 65501 (mark all as false) and Report 51000 (Excel export)
- Admin Permission page — same hardcoded admin/12345 credentials
- API Setup table — still exists, still ignored by the codeunit
- All custom tables — unchanged
3. SDD Compliance — Section-by-Section Mapping
3.1. Section 2: Objectives
| # | Objective | Status | Evidence |
|---|---|---|---|
| 1 | Synchronize fault-related master data from the FAQ API into BC | Partial | Codeunit 65500 fetches and upserts 4 of 6 tables. Fault Area/Symptom Relationship (50113) not addressed. Symptom Code, Fault Code, and Resolution Code synced without Status fields |
| 2 | Ensure BC uses only valid and active codes for new transactions | Partial | Only 2 of 6 tables have Status fields. Fault Code, Symptom Code, Resolution Code, and Fault Area/Symptom Relationship have no active/inactive distinction — all records remain selectable |
| 3 | Preserve historical Service Orders, PSI records, and posted data | Compliant | No code touches posted documents |
| 4 | Introduce inactive handling without deleting existing records | Partial | Report 65501 marks Fault/Resol. Relationship and Fault Area records as "false." Remaining 4 tables have no Status field. Additionally, the delete-and-reinsert pattern in codeunit 65500 (line 210) violates the "without deleting" requirement |
| 5 | Provide controlled synchronization mechanisms as defined in this document | Partial | Codeunit and UI exist, but only full sync is implemented — no incremental sync per Section 7.2 |
3.2. Section 5: Data Entities & Tables
| BC Table | Table ID | SDD In Scope | Extended? | Fields Added | SDD Required |
|---|---|---|---|---|---|
| Fault Area | 5915 | Yes | Yes (TableExt 50076) | Status (Option "true"/"false") | 6 fields per Epic (FAQ ID, Active, Created in FAQ, Updated in FAQ, Created in BC, Updated in BC) |
| Symptom Code | 5916 | Yes | NO | — | 6 fields |
| Fault Code | 5918 | Yes | NO | — | 6 fields |
| Resolution Code | 5919 | Yes | NO | — | 6 fields |
| Fault/Resol. Relationship | 5920 | Yes | Yes (TableExt 65500) | Status (Option), Source (Text) | 6 fields |
| Fault Area/Symptom Rel. | 50113 | Yes | NO | — | 6 fields |
Result: 2 of 6 tables extended. 0 of 36 required fields (per Epic Story 2) delivered. The Status fields on the 2 extended tables use Option type with "true"/"false" members — not the Boolean "Active" field specified in the Epic.
3.3. Section 6: Core Design Principles
| # | Principle | Status | Evidence |
|---|---|---|---|
| 1 | No deletion of records once used in posted transactions | VIOLATED | Codeunit 65500, lines 209–212: FaultResolutionRel.Delete(true) followed by NewFaultResolutionRel.Insert(true). When a Fault/Resol. Relationship record's Service Item Group Code changes, the existing record is deleted and a new one inserted. If the original record was referenced by a posted Service Order, the reference is now orphaned |
| 2 | Inactive records retained for historical reference | Partial | Status field exists on Fault Area and Fault/Resol. Relationship only. 4 of 6 tables have no inactive capability |
| 3 | Only active values available for new transactions | Partial | OnOpenPage filters on Fault Areas (PageExt 50001, line 16) and Fault/Resol. Relationship page (PageExt 65501, line 42). OnLookup filter on Service Order Subform (PageExt 50000, line 15). Service Item Worksheet Subform (PageExt 50005, line 44) has the Service Item Group Code filter commented out — shows all active fault areas regardless of product category |
| 4 | Synchronization logic must be safe to re-run | VIOLATED | The delete-and-reinsert pattern means re-running the sync can delete and recreate records with different Service Item Group mappings. The OnAfterInsert trigger on Job Queue Log Entry (line 20) calls GetStandardFormat after the Job Queue has already executed the codeunit, causing double execution per scheduled run |
| 5 | Existing extension boundaries must be respected | Compliant | No cross-extension ID conflicts observed |
3.4. Section 7.1: One-Time Full Rebuild Sync
| # | Requirement | Status | Evidence |
|---|---|---|---|
| 1 | All existing fault-related records will be marked Inactive | Partial | Report 65501 sets Status = "false" on Fault/Resol. Relationship records (line 16) and Fault Area records (lines 18–27). Symptom Code, Fault Code, Resolution Code, and Fault Area/Symptom Relationship have no Status field and cannot be marked |
| 2 | A complete import will be performed from the FAQ API | Implemented | Codeunit 65500 GetStandardFormat fetches the full dataset from /api/1.0/standardFormat |
| 3 | FAQ API identifiers will be persisted as the primary reference | NOT implemented | API integer IDs (e.g., "7", "44", "56") are used directly as BC Code field values. No separate FAQ ID field exists on the fault master tables. The ServiceItemGroupCodeBuffer table (50001) has a "FAQ ID" field, but this is a manual mapping table for Fault Area → Service Item Group, not a comprehensive FAQ identifier store |
| 4 | No legacy-to-API mapping logic will be maintained | Compliant | No legacy mapping code exists |
| 5 | Historical and posted data will remain unchanged | Compliant | No code modifies posted documents. However, the delete-and-reinsert pattern (codeunit 65500, line 210) can orphan references from posted Service Orders to Fault/Resol. Relationship records |
3.5. Section 7.2: Automated Incremental Sync
| # | Requirement | Status | Evidence |
|---|---|---|---|
| 1 | Periodic unattended execution | Implemented | Job Queue scheduling via ScheduleMyCodeunit action (page 65504, line 1005). Supports Daily and Weekly cadences |
| 2 | Detection of changes between API data and BC cache | NOT implemented | GetStandardFormat always fetches the entire dataset. No "last modified" parameter is sent to the API. No per-record change detection against BC cache |
| 3 | Insert new records | Implemented | Upsert logic in ProcessStandardFormatResponse (codeunit 65500, lines 61–234) |
| 4 | Update modified records | Implemented | Description changes detected and applied (e.g., line 116–124 for Fault Area) |
| 5 | Mark records Inactive when removed from the API | NOT implemented | No logic exists to detect records present in BC but absent from the API response. Records that are removed from the FAQ API will remain active in BC indefinitely |
| 6 | Maintain sync state across executions | Partial | FAQ Sync Log table (50012) tracks sync timestamps and record counts. UpdateSyncLogJQ writes a log entry after each sync. However, no per-record sync state exists — there is no way to determine which specific records were added, modified, or should be deactivated in a given sync |
3.6. Section 7.3: Stakeholder Notification
| # | Requirement | Status | Evidence |
|---|---|---|---|
| 1 | Configurable recipient list | Implemented | Email Recipient Setup table (65550) with user lookup page (65551). Recipients selected from BC Users list via OnLookup |
| 2 | Summary of newly added records | Implemented | Insert count tracked in codeunit 65500 (FaultResolutionRelInsertCount), passed to page via FAQ Sync Counter Manager SingleInstance codeunit, displayed in email body |
| 3 | Summary of updated records | Implemented | Modify count tracked similarly and included in email summary cards |
| 4 | Summary of records marked Inactive | NOT implemented | No logic exists to mark records Inactive (see Section 3.5 #5), so no summary of deactivated records is possible. The JQ email reports records where Status changed from False → True, but not the reverse |
3.7. Section 8: UI & Validation Behavior
| Requirement | Status | Evidence |
|---|---|---|
| Active fault-related values available for selection in new Service Orders, PSIs, and related transactions | Partial | OnLookup on Service Order Subform (PageExt 50000, line 15) filters Fault Area Code by Fault/Resol. Relationship Status = "True" and Service Item Group Code. This works for the one table that has both Status and Service Item Group mapping |
| Inactive values will remain stored for historical reference but will not be selectable | Partial | OnOpenPage filters hide inactive records on Fault Areas and Fault/Resol. Relationship pages. However, these are soft filters — users can clear them. Service Item Worksheet Subform (PageExt 50005, line 44) has the Service Item Group Code filter commented out, showing all active fault areas regardless of product |
| UI updated across all fault-related pages | Incomplete | Only 3 page extensions delivered (Fault Areas, Service Order Subform, Service Item Worksheet Subform). No extensions for Symptom Code, Fault Code, or Resolution Code pages — those tables have no Status field to filter on |
3.8. Section 9: Data Safety & Integrity
| # | Commitment | Status | Evidence |
|---|---|---|---|
| 1 | No modification of posted documents | Compliant | No code references posted document tables |
| 2 | Inactive handling ensures backward compatibility | Partial | Only 2 of 6 tables participate in inactive handling. Historical Service Orders referencing records on the 4 unextended tables will continue to display correctly (no schema change), but those records cannot be marked inactive when they become stale |
| 3 | Partial updates are avoided | VIOLATED | No transaction control wraps the sync loop (codeunit 65500, lines 96–234). If the API call succeeds but processing fails mid-array (e.g., a record insert fails on row 500 of 1,000), the first 499 records are committed while the remaining 501 are not. The Commit() in UpdateSyncLogJQ (page 65504, line 1508) forces a commit boundary that prevents rollback of partial data |
| 4 | Repeat executions do not create duplicate or invalid data | VIOLATED | The delete-and-reinsert pattern (codeunit 65500, lines 209–212) means a re-run can delete a Fault/Resol. Relationship record and recreate it with a different SystemCreatedAt timestamp, invalidating any SystemModifiedAt-based change detection. The double execution via OnAfterInsert (Job Queue Log Entry ext, line 20) means every scheduled sync runs twice, with the second execution seeing the first execution's modifications as "already exists" rather than "new" |
4. Deliverable Status
| # | Deliverable | v1.1 (Mar 6) | v2.0 (Mar 19) | v3.0 (Mar 24) |
|---|---|---|---|---|
| D1 | BC extension logic for FAQ sync | Partially delivered | Partially delivered | Partially delivered — unchanged. Core upsert works for 4 tables. Missing: 4 table extensions, 36 fields, incremental logic, inactive marking, code generation |
| D2 | One-time full rebuild sync | Executed but unverifiable | Partially delivered | Partially delivered — unchanged. Report 65501 marks 2 of 6 tables |
| D3 | Automated incremental sync | Unknown | NOT delivered | NOT delivered — confirmed. Every scheduled run is a full sync. No change detection, no API-side removal detection, no per-record sync state |
| D4 | Stakeholder notification | Unknown | Delivered | Delivered — confirmed. Email notifications work with configurable recipients, styled HTML reports, and manual/scheduled variants. Minor improvements in v1.0.0.73 (better formatting, row numbering, manual/scheduled distinction in sync log) |
| D5 | Final source code delivery | NOT delivered | Partially delivered (v1.0.0.57) | Delivered — v1.0.0.73 source matches compiled binary. Source compiles successfully |
| D6 | Supporting technical documentation | NOT delivered | NOT delivered | NOT delivered — no documentation of any kind |
5. Security Findings
All security findings from v2.0 remain present and unchanged in v1.0.0.73.
5.1. Hardcoded FAQ API Credentials (CRITICAL)
File: Codeunit/FAQAPIIntegration.Codeunit.al, line 24
Code: 'https://faq.bestwaycorp.com/ExternalAPI/api/Login/token?username=BWUFAQ&password=BWUFAQ123'
The API Setup table (65503) exists with Login and URL fields but is never read. The codeunit calls RecAPISetup.Get() (line 23) but only to verify the record exists — the actual URL, username, and password are hardcoded.
5.2. Hardcoded Admin Password (CRITICAL)
File: Page/AdminPermission.Page.al, lines 35–36
Code: ExpectedUser := 'admin'; ExpectedPass := '12345';
The "Admin Permission" dialog (page 50001) gates the "First Time Sync" action behind a hardcoded username/password check. This is not BC security — it is a custom cleartext credential check that provides no real access control. Any user who reads the source or decompiles the .app can bypass it.
5.3. Credentials in Source Control
Both sets of credentials are now in source control (this repository). They must be considered compromised regardless of access restrictions on the repo.
6. Code Quality Findings (New in v1.0.0.73)
6.1. Dead Code — 834 Lines of Commented-Out Page
File: Page/FAQManagement.Page.al, lines 1–834
The entire old version of page 65504 is commented out in place rather than removed. This is approximately 50% of the file by line count. The old version and new version are functionally similar — the new version has minor improvements (helper extraction, email formatting) but the same architecture.
This dead code:
- Doubles the file size with no functional benefit
- Creates confusion during review (the v2.0 review initially assessed only the commented-out version)
- Indicates source control was not used to track revisions
6.2. Double Execution — OnAfterInsert Trigger
File: TableExt/JobQueueLogEntryExt.TableExt.al, lines 14–25
The OnAfterInsert trigger on Job Queue Log Entry fires after the Job Queue has already executed codeunit 65500. The trigger then calls FAQIntegration.GetStandardFormat('en') again (line 20), causing the entire sync to run twice per scheduled execution. The second execution creates a new FAQ API Integration codeunit instance (line 11), so it has its own counters — the insert/modify counts reported by the second run do not include the first run's work.
6.3. Delete-and-Reinsert Pattern
File: Codeunit/FAQAPIIntegration.Codeunit.al, lines 209–212
When a Fault/Resol. Relationship record's Service Item Group Code needs to change, the code deletes the existing record and inserts a new one:
FaultResolutionRel.Delete(true);
NewFaultResolutionRel.Validate("Service Item Group Code", ...);
NewFaultResolutionRel.Insert(true);
This pattern:
- Violates SDD Section 6 principle #1 (no deletion of records used in posted transactions)
- Violates SDD Section 9 commitment #4 (repeat executions safe)
- Changes
SystemCreatedAtandSystemIdon re-insert, breaking any external references - Fires OnDelete and OnInsert triggers, which may have side effects from other extensions
6.4. Report Layout Path Reference
File: Report/FaultResolCodRelationshiprep.Report.al, line 7
Code: RDLCLayout = 'src/FaultResolCodRelationshiprepnew.rdl';
The report references src/FaultResolCodRelationshiprepnew.rdl, but the file is located at ReportLayout/FaultResolCodRelationshiprepnew.rdl in the organized folder structure. This path mismatch would cause a runtime error when the report is run. The file must be at the path specified in the RDLCLayout property relative to the project root, or the property must be updated.
7. Gap Analysis Summary
7.1. Story-Level Completion (unchanged from v2.0)
| Story | Acceptance Criteria | Satisfied | Partially | Not Implemented |
|---|---|---|---|---|
| Story 1: API Client | 3 | 1 | 1 | 1 |
| Story 2: Sync Fields | 3 | 0 | 1 | 2 |
| Story 3: Periodic Job | 3 | 2 | 0 | 1 |
| Story 4: Code Generation | 2 | 0 | 0 | 2 |
| Story 5: Active Filtering | 2 | 0 | 2 | 0 |
| Story 6: Email Notifications | 2 | 2 | 0 | 0 |
| Story 7: CSM Logging | 3 | 0 | 0 | 3 |
| Total | 18 | 5 | 4 | 9 |
7.2. Required Fields Gap (unchanged from v2.0)
| Field | Epic Spec | Tables Required | Tables Delivered | Gap |
|---|---|---|---|---|
| FAQ ID (Int) | Story 2 | 6 | 0 | 6 missing |
| Active (Boolean) | Story 2 | 6 | 0 | 6 missing |
| Created in FAQ (Timestamp) | Story 2 | 6 | 0 | 6 missing |
| Updated in FAQ (Timestamp) | Story 2 | 6 | 0 | 6 missing |
| Created in BC (Timestamp) | Story 2 | 6 | 0 | 6 missing |
| Updated in BC (Timestamp) | Story 2 | 6 | 0 | 6 missing |
| Total | 36 | 0 | 36 missing |
7.3. Missing Capabilities (unchanged from v2.0)
- Incremental sync does not exist. Every run is a full sync. No differential fetch, no API-side removal detection, no per-record sync state.
- Inactive marking is incomplete. Only 2 of 6 tables have a Status field.
- Code generation does not exist. FAQ API integer IDs used directly as BC Code values.
- CSM Quality Report logging is entirely missing. Story 7 has zero implementation.
- FAQ API identifiers are not persisted separately. The API ID is the Code field — no separate FAQ ID on fault master tables.
8. Effort vs. Delivery Assessment
| Metric | Agreed | Delivered |
|---|---|---|
| Estimated effort | Invoice #466051 submitted for full amount | |
| User stories addressed | 7 | 4 partially, 1 fully (notifications), 2 not addressed (code gen, CSM logging) |
| Acceptance criteria | 18 | 5 fully satisfied, 4 partially, 9 not implemented |
| Required field implementations | 36 (6 fields x 6 tables) | 0 of the specified fields |
| Table extensions | 6 tables | 2 tables |
| Deliverables | 6 | 1 fully delivered (notifications), 1 delivered (source code), 2 partially delivered, 2 not delivered |
| Core sync capabilities | 3 (rebuild, incremental, notification) | 1 partially delivered (rebuild), 1 delivered (notification), 1 not delivered (incremental) |
9. What v1.0.0.73 Resolved (Relative to v1.0.0.57)
The 16-revision gap between v1.0.0.57 and v1.0.0.73 resolved:
- Source code delivery. Bestway now has version-matched source code. D5 is delivered.
- FAQ Management page refactoring. The management UI was rewritten with minor improvements (helper extraction, email formatting, manual/scheduled sync log distinction).
- Sync counter passing. The
FAQ Sync Counter ManagerSingleInstance codeunit addresses the cross-session counter problem between Job Queue execution and the management page.
The 16-revision gap did not resolve:
- Any of the 36 missing field implementations
- Any of the 4 unextended tables
- Incremental sync
- Code generation
- CSM Quality Report logging (Story 7)
- Hardcoded credentials (FAQ API or admin)
- Double execution via OnAfterInsert
- Delete-and-reinsert pattern
- Missing documentation (D6)
10. Recommendations
10.1. Immediate Actions (unchanged from v2.0)
-
Do not deploy to production. The security vulnerabilities (hardcoded credentials), data integrity defects (double execution, delete-and-reinsert, no transaction control), and incomplete scope make this extension unsafe for production use.
-
Revoke the FAQ API credentials (BWUFAQ / BWUFAQ123) immediately. These credentials are in source control and must be considered compromised.
-
Fix the report layout path.
Report/FaultResolCodRelationshiprep.Report.alreferencessrc/FaultResolCodRelationshiprepnew.rdlbut the file is atReportLayout/FaultResolCodRelationshiprepnew.rdl. This will cause a runtime error.
10.2. Path Forward — Decision Required (unchanged from v2.0)
Option A: Require Sataware to remediate.
- Pro: Vendor accountability.
- Con: The v1.0.0.57 → v1.0.0.73 gap shows 16 revisions of UI polish with no structural gap remediation. This suggests the vendor either does not acknowledge the gaps or lacks the capability to address them.
- Requirement: Concrete remediation plan with dates, milestones, and acceptance criteria per story.
Option B: Bring the extension in-house for completion.
- Pro: Full control over quality, timeline, and architecture. Bestway has the BC development capability and standards infrastructure.
- Con: Additional internal effort. The existing code requires substantial rework (credential management, transaction safety, inactive handling, 4 additional table extensions, incremental sync, code generation, CSM logging).
- Consideration: Invoice reconciliation — the delivered extension satisfies 5 of 18 acceptance criteria.
10.3. Discussion Points for Sataware (updated)
- Source code is now delivered. Acknowledge receipt of v1.0.0.73 source. This closes D5.
- Scope acknowledgment: The 16 revisions between v1.0.0.57 and v1.0.0.73 contain only UI polish. Does Sataware acknowledge the 18 acceptance criteria and 36 field implementations remain unaddressed?
- Invoice reconciliation: 5 of 18 acceptance criteria are satisfied. 1 of 6 deliverables is fully delivered. How should Invoice #466051 be reconciled against this delivery?
- Security: The hardcoded credentials (FAQ API and admin) are now in source control. Sataware must acknowledge this exposure and participate in credential rotation.
Appendix A: Object Inventory
| Object Type | ID | Name | File | Status |
|---|---|---|---|---|
| Codeunit | 65500 | FAQ API Integration | Codeunit/FAQAPIIntegration.Codeunit.al | Active — core sync logic |
| Codeunit | 65501 | FAQ Sync Counter Manager | Codeunit/FAQAPIIntegration.Codeunit.al | Active — new in v1.0.0.73 |
| Table | 50001 | ServiceItemGroupCodeBuffer | Table/ServiceItemGroupCodeBuffer.Table.al | Active — FAQ ID → Service Item Group mapping |
| Table | 50012 | FAQ Sync Log | TableExt/Faultcoderelationext.TableExt.al | Active — sync history |
| Table | 65503 | API Setup | Table/APISetup.Table.al | Active — unused by codeunit |
| Table | 65550 | Email Recipient Setup | Table/EmailRecipientSetup.Table.al | Active — notification recipients |
| TableExt | 50076 | FaultAreaExt (extends 5915) | PageExt/FaultAreasExt.PageExt.al | Active — adds Status field |
| TableExt | 65500 | Faultcoderelationext (extends 5920) | TableExt/Faultcoderelationext.TableExt.al | Active — adds Status, Source |
| TableExt | 50001 | JobQueueLogEntryExt | TableExt/JobQueueLogEntryExt.TableExt.al | Active — OnAfterInsert trigger |
| Page | 50001 | AdminPermission | Page/AdminPermission.Page.al | Active — hardcoded credential check |
| Page | 50002 | ServiceItemGroupCodeBufferPage | Page/ServiceItemGroupCodeBufferPage.Page.al | Active — mapping UI |
| Page | 65503 | API Setup | Page/APISetup.Page.al | Active — config UI (unused by codeunit) |
| Page | 65504 | FAQ Management | Page/FAQManagement.Page.al | Active — main management page (lines 838–1662) |
| Page | 65551 | Email Recipient Lines | Page/EmailRecipientLines.Page.al | Active — recipient subpage |
| PageExt | 50000 | ServiceSubformFilterExt (extends Service Order Subform) | PageExt/ServiceSubformFilterExt.PageExt.al | Active — Fault Area lookup filter |
| PageExt | 50001 | FaultAreasExt (extends Fault Areas) | PageExt/FaultAreasExt.PageExt.al | Active — Status column + filter |
| PageExt | 50005 | ServiceItemformFilterExt (extends Service Item Worksheet Subform) | PageExt/ServiceSubformFilterExt.PageExt.al | Active — Fault Area lookup (Service Item Group filter commented out) |
| PageExt | 65501 | Faultcoderelationextpage (extends Fault/Resol. Cod. Relationship) | TableExt/Faultcoderelationext.TableExt.al | Active — Status column + filter |
| Report | 65501 | Fault/ResolCodRelationship | Report/FaultResolCodRelationship.Report.al | Active — marks all records as "false" |
| Report | 51000 | FaultResolCodRelationshiprep | Report/FaultResolCodRelationshiprep.Report.al | Active — Excel export (broken layout path) |
| Report | 50003 | AttachmentPurpose | Report/AttachmentPurpose.Report.al | Dead code — entirely commented out |
| PermissionSet | 50012 | NC_Extension_Admin | Table/APISetup.Table.al | Active — FAQ Sync Log access |
| PermissionSet | 50100 | SatawareExtPermiss | PermissionSet/SatawareExtPermiss.PermissionSet.al | Active — extension data access |
| PermissionSet | 55101 | API Setup Perm | Table/APISetup.Table.al | Active — API Setup table access |
Appendix B: Reference Documents
| Document | Date | Location |
|---|---|---|
| Solution Design | Dec 16, 2025 | docs/V1_CSM_1_0_Fault_Detail_Sync_Solution_Design (2).pdf |
| Epic 193 Requirements | Nov 2025 | docs/Epic - Sync Business Central with FAQ API.docx |
| FAQ API to BC Mapping | Dec 2025 | docs/FAQ API to BC Mapping.png |
| Delivery Review v1.1 | Mar 6, 2026 | docs/CMS FAQ Management - Delivery Review v1.1.docx |
| Code Quality Review v1.0 | Mar 19, 2026 | docs/CMS FAQ Management - Code Quality Review v1.0.docx |
| Delivery Review v2.0 | Mar 19, 2026 | docs/DELIVERY-REVIEW-v2.0.md |
| Delivery Review v3.0 | Mar 24, 2026 | docs/DELIVERY-REVIEW-v3.0.md (this document) |
Appendix C: Acceptance Criteria — Detailed Status
| Story | # | Acceptance Criterion | Status | Evidence |
|---|---|---|---|---|
| 1 | 1 | API client supports authentication with FAQ API | Partial | Auth works but uses hardcoded credentials (codeunit 65500, line 24) instead of API Setup table |
| 1 | 2 | Can fetch data from FAQ API | Yes | GetStandardFormat() fetches /api/1.0/standardFormat |
| 1 | 3 | Can retrieve updates from FAQ API | No | No incremental fetch — full dataset every time |
| 2 | 1 | Fields added to BC tables (6 fields x 6 tables) | No | 0 of 36 specified fields. 2 non-matching Status fields on 2 tables |
| 2 | 2 | Tables updated (6 specified) | Partial | 2 of 6 tables have extensions |
| 2 | 3 | Mapping logic between FAQ API and BC tables | Partial | Mapping exists for 4 tables; missing for Fault Area/Symptom Relationship (50113) |
| 3 | 1 | Job can be scheduled and run periodically | Yes | Daily/Weekly Job Queue scheduling (page 65504, line 1005) |
| 3 | 2 | Job fetches only updated records since last sync | No | Full dataset fetched every run |
| 3 | 3 | Scheduling is configurable | Yes | Time and frequency options on management page |
| 4 | 1 | Code generation ensures uniqueness | No | No code generation — API integer IDs used directly as BC Code values |
| 4 | 2 | Code maps correctly to FAQ API ID | No | API ID is the Code — no separate mapping field |
| 5 | 1 | UI filters out inactive records | Partial | Works on 2 tables with Status; 4 tables have no Status to filter |
| 5 | 2 | Validation logic respects Active flag | Partial | Service Order Subform lookup filters by Status on Fault/Resol. Rel. Service Item Worksheet Subform has group filter commented out |
| 6 | 1 | Email includes list of changes synced | Yes | HTML email with active/inactive counts, change details, record-level tables |
| 6 | 2 | Email sent to configured stakeholders | Yes | Email Recipient Setup with user lookup |
| 7 | 1 | Log includes failure code | No | Story 7 entirely unimplemented |
| 7 | 2 | Log includes Service Item ID from PSI | No | Story 7 entirely unimplemented |
| 7 | 3 | Log includes Fault Area, Symptom, Fault, Resolution | No | Story 7 entirely unimplemented |