Companies House for UK Customers/Vendors/CompanyInfo — surface, score, alert, audit. Officer/PSC monitoring, insolvency signals, composite risk score, bulk import, change log, self-monitor.

  • Current version: v0.4.0.0
  • AL prefix: COMPA
  • Object range: 80200-80299
  • Business Central: 27.0.0.0
  • Publisher: Amplio Solutions

Companies House Integration (ASCHI)

Auto-populates UK customer, vendor and own-company master data from the Companies House public REST API. Surfaces statutory filings, secured-lending charges, officers, PSCs and a composite credit-risk score on the FactBox; raises notifications when counterparties drift into distress; and keeps an append-only audit trail of every detected change.

  • Publisher: Amplio Solutions
  • App version: 0.4.0.0
  • Target: Business Central 26.x (Cloud / OnPrem), Country = GB
  • Object prefix: ASCHI
  • ID range: 80200–80299

What it does

  1. Lookup + refresh on Customer Card, Vendor Card and Company Information.
  2. FactBox rendering CH status, statutory filings, distress signals, risk score, and the Officer + PSC list parts on every parent record.
  3. Daily Job Queue sweep that re-pulls profile + officers + PSCs + charges + filings + risk score for stale records, writes change-log entries for every detected change, raises notifications, and (if enabled) self-monitors the company's own filings.
  4. Bulk import wizard for onboarding many customers/vendors from a CSV.
  5. Setup page holds API credentials (key in Isolated Storage), refresh schedule, self-monitor configuration, and entry points to the Risk Weights, Change Log, and Bulk Import Jobs pages.

What v0.3.0.0 adds

1. Officer + PSC change monitoring

  • New tables ASCHI Officer (80224) and ASCHI PSC (80225) — one-to-many to Customer / Vendor / Company Information via (Linked Table No, Linked PK). Append-only: rows are never deleted, only the resigned-date / ceased-date columns flip when CH reports a transition.
  • Two new API client methods (GetCompanyOfficers / GetCompanyPSCs) hit /company/{number}/officers and /company/{number}/persons-with-significant-control.
  • RefreshFromCH now syncs both into the new tables, dedupes by CH's officer-id / psc-id (with a stable name+date fallback), and emits Change Log entries for every appointment / resignation / notification / cessation.
  • Notifications fire when officers are appointed, resign, or PSCs cease — gated by the new Officer/PSC Notify Enabled setup flag so chatty boards don't spam credit control.
  • FactBox grows two list-part panels rendering the parent's officers and PSCs; rows that resigned / ceased in the last 90 days render red.

2. Insolvency + financial-distress signals

  • New API client methods GetCompanyCharges and GetCompanyFilingHistory hit /charges and /filing-history.
  • New fields on Customer + Vendor + Company Information:
Field id Field Source
80224 ASCHI Outstanding Charges Count of /charges items where status='outstanding'.
80225 ASCHI Charges Last Filed Most recent created_on across all charges.
80226 ASCHI In Liquidation True when company_status='liquidation' (or status_detail indicates winding-up).
80227 ASCHI Strike-Off Active True when filing history has an active gazetted strike-off in the last 90d.
80228 ASCHI Distress Reason One-line summary of triggered distress flags (comma-joined).
  • Any of the four boolean flags transitioning false → true emits a Change Log entry and a high-priority notification (gated by the new Distress Notify Enabled setup flag).

3. Composite risk score (0–100)

  • New singleton config table ASCHI Risk Weights (80226) — defaults: Dissolved/Liquidation=100, Strike-Off Active=80, Accounts Overdue=30, Conf Stmt Overdue=20, Dormant=25, charges 5/each (cap 30), director resignations 10/each (cap 30), recent office change=10. Score floor=0, cap=100.
  • New setup page ASCHI Risk Weights Setup (80227) — single-row card with all weights, tooltips, and a Restore defaults action.
  • New codeunit ASCHI Risk Score Calc (80228) — pure function CalculateScore(sourceTableNo, sourceKey) over the parent record + its officers + PSCs + cached distress flags. Deterministic; same input ⇒ same output.
  • New fields on Customer + Vendor + Company Information:
Field id Field Notes
80229 ASCHI Risk Score Integer 0..100. Recomputed on each refresh.
80230 ASCHI Risk Score Calc At DateTime stamp of last computation.
80231 ASCHI Risk Score Reasons Comma-joined list of triggered weights for explainability.
  • FactBox surfaces the score at the top — green 0–25, amber 26–60, red 61–100.
  • Customer List + Vendor List get a hidden-by-default CH Risk Score column. Finance teams turn it on via personalisation, then filter Risk Score >= 60 to find every high-risk record.

4. Bulk import wizard

  • New tables ASCHI Import Job (80229) and ASCHI Import Job Line (80230). Lines hold the source key, search term, status, up to three candidate matches per row with a 0–100 confidence score.
  • New page ASCHI Bulk Import Wizard (80231) — a four-step NavigatePage:
    1. Pick target — Customer or Vendor.
    2. Upload a CSV (cols: customerno/vendorno, search_term — header row optional).
    3. Preview matches — opens the Job Lines page modal so the user can pick the right candidate per row, or skip a row.
    4. Confirm + apply — writes CH numbers and calls RefreshFromCH on each line.
  • Direct CH-number rows (8-char alphanumeric or padded numeric) hit /company/{number} directly; free-text rows go through /search/companies?q= with a heuristic name-similarity score.
  • Auto-pick: if exactly one candidate has score ≥ 90, the line is auto-MATCHED.
  • New page ASCHI Import Jobs List (80232) — history of imports with drill-down to lines.
  • Customer List + Vendor List grow a "Companies House Bulk Import" action that launches the wizard.

5. Audit trail / change log

  • New table ASCHI Change Log (80233) — append-only. Each row: source table+key, CH number, field name, change type (ADDED / MODIFIED / REMOVED / THRESHOLD_CROSSED), old/new values, detected-at, refresh job id (per-sweep correlation), detected-by user.
  • The refresh codeunit emits entries for: status flips, registered-office changes, officer additions/resignations, PSC notifications/cessations, charge additions, risk-score crossings (only when the delta exceeds Setup."Risk Score Change Threshold", default ±10), and the rising edges of the four overdue/distress booleans.
  • New page ASCHI Change Log List (80234) — sortable / filterable list. THRESHOLD_CROSSED rows render red; REMOVED rows render amber.
  • "View CH Change Log" action on Customer Card, Vendor Card, Customer List, Vendor List filters the log to the relevant scope.
  • Retention is configurable via Setup."Change Log Retention Days" (default 0 = keep forever); applied at the end of each sweep by ASCHI Change Log Writer.RunRetention.

Company Information (self-monitor)

  • New table extension on table 79 mirrors every CH field added to Customer + Vendor.
  • New page extension on page 1 surfaces a "Statutory Filings & Risk (self-monitor)" group, a CH self-monitor FactBox (80239), and a "Refresh CH self-monitor" action.
  • Refresh resolves the CH number from Setup."Self-Monitor Override CH No." (preferred) → Company Information."Registration No.", then runs the full v0.3 refresh pipeline against the singleton row.
  • The daily sweep refreshes Company Information whenever Enable Self-Monitor is on, regardless of the staleness window — one record, runs daily.
  • Notifications fire to Setup."Self-Monitor Recipient" (or the session user if blank).

ID allocation (80200–80299)

Range Used for
80200, 80201 Tables: Setup, Search Result
80210, 80211 Table extensions: Customer, Vendor
80220–80223 Pages: Setup, Search Results, Customer FactBox, Vendor FactBox
80224, 80225 Tables: Officer, PSC (v0.3)
80226 Table: Risk Weights (v0.3)
80227 Page: Risk Weights Setup (v0.3)
80228 Codeunit: Risk Score Calc (v0.3)
80229, 80230 Tables: Import Job, Import Job Line (v0.3)
80230, 80231 Page extensions: Customer Card, Vendor Card
80231 Page: Bulk Import Wizard (v0.3)
80232 Page: Import Jobs List (v0.3)
80233 Table: Change Log (v0.3)
80234 Page: Change Log List (v0.3)
80235 Table extension: Company Information (v0.3)
80236 Page extension: Company Information (v0.3)
80237, 80238 Pages: Officer List Part, PSC List Part (v0.3)
80239 Page: CH Company Info FactBox (v0.3)
80240–80246 Codeunits: API Client, Lookup Mgt, Refresh JQ, Install, Change Log Writer, Bulk Import Mgt, Officer/PSC Sync
80247, 80248 Pages: Officer List, PSC List (full / drilldown)
80246 Page: Import Job Lines (drilldown)
80248, 80249 Page extensions: Customer List, Vendor List (v0.3)
80250 Permission set: Admin
Page IDs 80230 / 80231 collide-by-number with page extensions 80230 / 80231 — different object types so AL accepts them. We could renumber for visual cleanliness but that would force consumers of the extension to re-test the existing page-extension IDs.

Install / configure

  1. Publish the extension to your sandbox or production tenant.
  2. Register a Companies House API key at https://developer.company-information.service.gov.uk/get-started.
  3. Open Companies House Setup.
  4. Paste the key into the masked API Key field (held in Isolated Storage).
  5. Click Test Connection.
  6. Toggle Auto Refresh Enabled, set Refresh Frequency (Days) (default 7), pick the notify flags you want.
  7. v0.3 self-monitor: open the Self-monitor group, toggle Enable Self-Monitor, set Self-Monitor Recipient (User Name) for who receives overdue notifications, optionally set the Override CH Number.
  8. v0.3 risk score: open Risk Score Weights (action on the Setup page promoted bar). Tune to your risk appetite, or click Restore defaults.
  9. v0.3 retention: optionally set Change Log Retention Days if you don't want unbounded log growth.
  10. Provision the Job Queue entry — Codeunit 80242 "ASCHI Refresh JQ", daily, e.g. 02:00.
  11. Assign the permission set ASCHI Admin to:
    • Users who curate Customer/Vendor master data.
    • The Job Queue user account that runs the daily sweep.
ASCHI Admin does not grant rights on Customer, Vendor, Company Information, or Job Queue Entry. Those should already be granted via the user's existing role (e.g. D365 BUS PREMIUM).

Upgrade from v0.2.0.0

No data migration required — every v0.3 field is additive on the existing extensions. After publishing v0.3:

  1. Open Companies House Setup. The new sections (Self-monitor, Risk score & change log) appear with sensible defaults.
  2. The Risk Weights singleton is auto-seeded with default weights on OnInstallAppPerCompany. If you publish via AppSource without triggering install (rare), it auto-seeds the first time the Setup page or a refresh reads it.
  3. Run a manual Refresh Now (Setup page → Run Refresh Now). Each customer/vendor + company info will re-fetch and populate the new fields.
  4. Optionally personalise the Customer / Vendor list to show the CH Risk Score column.

Manual test plan (extended)

The v0.2 scenarios still apply (active company, recently-incorporated company, forced overdue transition, notification idempotency). v0.3 adds the following:

  1. Officer change. Look up SIR ROBERT MCALPINE LIMITED (00006245) on a Customer record, run Refresh from CH. Confirm the Officers list-part populates. Manually edit one row's Resigned Date to today (via the full Officer List page or page inspection), then refresh again — the row stays (append-only) but the new Resigned Date is preserved. A change-log row should record the resignation. A "director resignation" notification should fire — gated by Officer/PSC Notify Enabled.
  2. Insolvency. Pick a known-recently-dissolved or in-liquidation company (search "in liquidation" via CH). Apply it to a test Customer. Run Refresh. Expect: In Liquidation = TRUE, distress reason populated, risk score jumps to 100. A high-priority notification fires. A change-log entry records In Liquidation: false → true with Change Type = MODIFIED.
  3. Risk score. Open CH Risk Score Weights, change Outstanding Charge Weight from 5 to 10. Run Refresh against a customer with 3 outstanding charges and an overdue confirmation statement. Expect score = 30 (charges, capped) + 20 (conf stmt) = 50. Reasons string lists "Outstanding Charges (3) +30, Confirmation Stmt Overdue +20". Restore defaults and re-refresh — score returns to 35.
  4. Bulk import. Prepare a 10-row CSV: 5 valid CH numbers, 3 ambiguous names ("Acme Ltd"), 2 deliberately wrong. Open Customer List → Companies House Bulk Import. Walk Steps 1–4. Expect: 5 auto-MATCHED, 3 MULTIPLEMATCHES (pick or skip in the lines page), 2 NOMATCH. After Finish, the 5 matched customers have CH metadata populated, and 8/10 success on the Job Header.
  5. Change log. With no recent changes, run Refresh on a stable record — change log should remain empty. In the BC Customer table, manually edit the cached ASCHI Company Status to a different value (via page inspection / a dev test), then run Refresh — the next refresh sees the discrepancy and writes a status-change row.
  6. Self-monitor. On Company Information, populate Registration No. with a valid CH number (e.g. your own company). Open Setup, confirm Enable Self-Monitor = TRUE and pick a Self-Monitor Recipient. Run Run Refresh Now. Expect Company Information to populate with CH metadata + risk score; subsequent overdue transitions raise notifications addressed to the configured user.

Stubs & TODOs left in code

  • ASCHI API Client.InvokeGet — 429 surfaces as an error; v1 should honour Retry-After.
  • ASCHI API Client.UriEncode — non-ASCII behaviour unverified.
  • ASCHI Lookup Mgt.IsLiquidationStatus — currently checks company_status = 'liquidation'. CH's status_detail (winding-up, in-administration) is not yet exposed by the API client; OR-in here when it is.
  • ASCHI Officer PSC Sync.ComputeChargeAggregates — does NOT yet emit a "new charge filed" change-log row when MostRecentFiled advances. Needed for v1 KYC overlay.
  • ASCHI Risk Score Calc.RegisteredOfficeChangedRecently — relies on Change Log having a row. First-ever refresh of a fresh record never trips this signal (correct behaviour, but worth flagging for testers).
  • ASCHI Bulk Import Mgt.ScheduleApply — still inline-synchronous. v1 should drop a Job Queue Entry so large CSV imports don't block the wizard.
  • ASCHI Refresh JQ.SafeRefreshCustomer/Vendor — has the existing commented Sleep(50). v0.3 issues 5 calls per record (profile + officers + PSCs + charges + filing-history); a tenant with 1000 customers is well inside the CH 600/5min ceiling but a 10k-customer tenant should add the soft brake.
  • Job Queue dependency for v0.2 overdue notifications — the same caveat applies to all v0.3 transition notifications: they fire only when the daily refresh runs against the record. There is no separate scheduler.

What Lee still needs to ship

  1. Production CH API key.
  2. Tune the refresh schedule for the largest tenants (5 calls per record × 1000+ records may bite the ceiling).
  3. Decide CH-vs-curated-data overwrite policy — still v0.1 behaviour: we don't auto-merge Name/Address from CH onto the standard fields.
  4. Consider Teams / email notification channels for the rising-edge alerts (currently in-client only).
  5. Add structured telemetry (Session.LogMessage) for refresh outcomes + risk-score deltas.
  6. Sibling AL test app with mocked HTTP fixtures so the JSON parsing has regression coverage.

Source layout

08-Companies-House-Integration/
  app.json
  README.md
  src/
    Tables/
      ASCHISetup.Table.al
      ASCHISearchResult.Table.al
      ASCHIOfficer.Table.al                    (v0.3)
      ASCHIPSC.Table.al                        (v0.3)
      ASCHIRiskWeights.Table.al                (v0.3)
      ASCHIImportJob.Table.al                  (v0.3)
      ASCHIImportJobLine.Table.al              (v0.3)
      ASCHIChangeLog.Table.al                  (v0.3)
    TableExtensions/
      ASCHICustomer.TableExt.al
      ASCHIVendor.TableExt.al
      ASCHICompanyInfo.TableExt.al             (v0.3)
    Pages/
      ASCHISetup.Page.al
      ASCHISearchResults.Page.al
      ASCHICustomerFactBox.Page.al
      ASCHIVendorFactBox.Page.al
      ASCHIRiskWeightsSetup.Page.al            (v0.3)
      ASCHIBulkImportWizard.Page.al            (v0.3)
      ASCHIImportJobsList.Page.al              (v0.3)
      ASCHIImportJobLines.Page.al              (v0.3)
      ASCHIChangeLogList.Page.al               (v0.3)
      ASCHIOfficerListPart.Page.al             (v0.3)
      ASCHIOfficerList.Page.al                 (v0.3)
      ASCHIPSCListPart.Page.al                 (v0.3)
      ASCHIPSCList.Page.al                     (v0.3)
      ASCHICompanyInfoFactBox.Page.al          (v0.3)
    PageExtensions/
      ASCHICustomerCard.PageExt.al
      ASCHIVendorCard.PageExt.al
      ASCHICompanyInfo.PageExt.al              (v0.3)
      ASCHICustomerList.PageExt.al             (v0.3)
      ASCHIVendorList.PageExt.al               (v0.3)
    Codeunits/
      ASCHIAPIClient.Codeunit.al
      ASCHILookupMgt.Codeunit.al
      ASCHIRefreshJQ.Codeunit.al
      ASCHIInstall.Codeunit.al
      ASCHIRiskScoreCalc.Codeunit.al           (v0.3)
      ASCHIChangeLogWriter.Codeunit.al         (v0.3)
      ASCHIBulkImportMgt.Codeunit.al           (v0.3)
      ASCHIOfficerPSCSync.Codeunit.al          (v0.3)
    PermissionSets/
      ASCHIAdmin.PermissionSet.al