Aggregates VAT across BC companies sharing one HMRC VRN and submits a single MTD VAT return.

  • Current version: v0.1.2.0
  • AL prefix: AMPUK
  • Object range: 80000-80099
  • Business Central: 26.0.0.0
  • Publisher: Amplio Solutions

AMP UK Group VAT MTD Bridge

Aggregate VAT entries across multiple Microsoft Dynamics 365 Business Central companies that share a single HMRC VAT Registration Number (a "VAT group") and submit one consolidated MTD VAT return on behalf of the group.

  • Publisher: Amplio Solutions
  • Object name prefix: AMP
  • Country: GB
  • Target: BC application 26.0.0.0, platform 26.0.0.0 (Cloud / Sandbox)
  • Object ID range: 80000..80099
  • Version: 0.1.2.0

What this MVP does

  1. Provides a singleton Group VAT Setup card holding the VRN, HMRC environment (Sandbox/Live), OAuth client ID, redirect URL and pointers to secret values stored in Isolated Storage.
  2. Lets you list the BC companies that form the VAT group on the same setup card (Members subpart). Members can be flagged active/inactive and one is marked as the representative member.
  3. From any VAT Return Periods row (or the new Group VAT Submissions list), creates a draft Group VAT submission for that period.
  4. Aggregates VAT entries cross-company using ChangeCompany against each active member, sums them into the nine HMRC boxes, and writes a per-member breakdown row for audit. Re-aggregation is idempotent.
  5. Submits to HMRC via the MTD VAT API (POST /organisations/vat/{vrn}/returns) with the standard Gov-Client-* / Gov-Vendor-* fraud-prevention headers, an OAuth2 bearer token, and the JSON payload HMRC expects.
  6. Stores the HMRC receipt (form bundle number, charge reference, processing date, payment indicator) on the submission row, plus a stable cross-company audit reference (GRP-VAT-{entry}-{company}) on each breakdown line so member finance teams can tie the consolidated return back to their books.
  7. Ships a printable Group VAT Member Breakdown report (RDLC-ready dataset) for the workpaper attached to the internal sign-off pack.

File map

01-UK-Group-VAT-MTD-Bridge/
├─ app.json                                  # publisher, version, dependencies, idRanges
├─ .vscode/launch.json                       # local + cloud sandbox launch templates
├─ .gitignore                                # AL build / VS Code noise
├─ .editorconfig                             # 4-space AL, CRLF, UTF-8
├─ README.md                                 # this file
└─ src/
   ├─ Tables/
   │  ├─ AMPGroupVATSetup.Table.al           (80000)
   │  ├─ AMPGroupVATMember.Table.al          (80001)
   │  ├─ AMPGroupVATSubmissionLog.Table.al   (80002)
   │  └─ AMPGroupVATMemberBreakdown.Table.al (80003)
   ├─ Enums/
   │  ├─ AMPMTDEnvironment.Enum.al           (80000)
   │  └─ AMPGroupVATSubmissionStat.Enum.al   (80001)
   ├─ Codeunits/
   │  ├─ AMPGroupVATAggregator.Codeunit.al   (80020)  cross-company VAT entry aggregator
   │  ├─ AMPHMRCMTDCommunicator.Codeunit.al  (80021)  HttpClient + OAuth2 + MTD endpoints
   │  ├─ AMPGroupVATWorkpaperMgt.Codeunit.al (80022)  reconciliation workpaper generator
   │  └─ AMPGroupVATSubmissionMgt.Codeunit.al(80023)  orchestrator: aggregate → submit
   ├─ Pages/
   │  ├─ AMPGroupVATSetup.Page.al            (80050)  Card  — Group VAT Setup
   │  ├─ AMPGroupVATMembers.Page.al          (80051)  ListPart — members on Setup card
   │  ├─ AMPGroupVATSubmissions.Page.al      (80052)  List  — Group VAT Submissions
   │  ├─ AMPGroupVATSubmissionCard.Page.al   (80053)  Card  — one submission, with actions
   │  ├─ AMPGroupVATMemberBreakdown.Page.al  (80054)  ListPart — breakdown on Submission card
   │  └─ AMPSecretInput.Page.al              (80055)  StandardDialog — masked secret entry
   ├─ PageExtensions/
   │  └─ AMPVATReturnPeriodsExt.PageExt.al   (80080)  adds "Create Group VAT Return" action
   ├─ Reports/
   │  └─ AMPGroupVATMemberBreakdown.Report.al(80090)  per-member workpaper
   └─ PermissionSets/
      └─ AMPGroupVATMTD.PermissionSet.al     (80095)  single assignable set

How it fits together

┌─────────────────────────┐    ChangeCompany(...)    ┌─────────────────────┐
│ AMP Group VAT Aggregator│ ───────────────────────▶ │  Member companies   │
│  (codeunit 80020)       │                          │  VAT Entry tables   │
└──────────┬──────────────┘                          └─────────────────────┘
           │ writes
           ▼
┌────────────────────────────┐    ┌───────────────────────────────────┐
│ Submission Log (table 80002)│ ◀──│ Member Breakdown (table 80003)   │
│  9 boxes + receipt          │    │  per-member contribution + ref   │
└──────────┬─────────────────┘    └───────────────────────────────────┘
           │
           ▼
┌─────────────────────────┐  HTTPS  ┌──────────────────────────────────┐
│ AMP HMRC MTD Communicator│ ─────▶ │ HMRC MTD VAT API                 │
│  (codeunit 80021)        │ ◀───── │ /organisations/vat/{vrn}/returns │
└─────────────────────────┘ receipt └──────────────────────────────────┘

What you get out of the box

  • End-to-end "happy path" from VAT Return Period → draft submission → cross-company aggregation → HMRC POST → stored receipt.
  • No hardcoded credentials. URLs come from the Setup table; client ID is on the table; client secret and tokens live in Isolated Storage and are referenced only by GUID key.
  • Real HttpClient request/response shape that matches HMRC's published contract (Accept header version, fraud-prevention headers, JSON payload).
  • Audit trail linking the consolidated return back to each member.
  • Read-only against VAT Entry. This MVP does not post to the GL or close VAT entries in member companies — that's intentionally deferred.

What you (Lee) need to do to ship

The numbered items below are the last mile between this MVP and a customer-ready release. They're roughly ordered by what blocks pilot use vs. what blocks GA.

Blocking pilot use (sandbox)

  1. Register for an HMRC developer account and create an MTD VAT app at https://developer.service.hmrc.gov.uk/. Pick the read:vat and write:vat scopes.
  2. Configure Group VAT Setup in BC:
    • Set the representative company.
    • Enter the VRN (no spaces).
    • Pick environment = Sandbox.
    • Paste the OAuth Client ID.
    • Click Set Client Secret and paste the secret into the masked dialog (it lands in Isolated Storage; the table only stores the GUID key).
  3. Implement the OAuth2 authorisation-code flow end-to-end. Today the communicator's EnsureAccessToken will throw TokenRefreshNotImplementedErr if there is no live cached token, because the consent + token-exchange round-trip isn't wired up yet. To finish:
    • Add a Get Authorisation URL action on the Setup card that builds {base}/oauth/authorize?... and opens it in the browser.
    • Add an Exchange Code for Tokens action that takes the redirect-URL code, POSTs to {base}/oauth/token and writes access + refresh tokens to Isolated Storage (keys = Access Token Key, Refresh Token Key), then sets Token Expiry.
    • In EnsureAccessToken, when the cached token is missing or near expiry, POST grant_type=refresh_token to {base}/oauth/token and rotate the cached pair instead of erroring.
  4. Replace the stubbed fraud-prevention headers in AddFraudPreventionHeaders with values reflecting the real client environment. HMRC documents the precise expected values per connection method here: https://developer.service.hmrc.gov.uk/guides/fraud-prevention/. Sandbox accepts the stubs but production will reject calls that don't carry plausible values.
  5. Add Members to the group (the active list that the aggregator will walk). One must be flagged Representative.
  6. Test the round-trip in the HMRC sandbox with the https://test-api.service.hmrc.gov.uk base URL and known test data. HMRC publishes test scenarios you can use to assert specific responses.

Before production

  1. Period key handling. The page extension on VAT Return Periods currently derives a placeholder period key from the period dates (e.g. 202504-202506). The user must overwrite this on the Submission Card with the real HMRC obligation key (e.g. 18A1) returned from /organisations/vat/{vrn}/obligations. A small action that calls RetrieveObligations and lets the user pick the key would close this gap.
  2. Box 2, 8, 9 (EU acquisitions / EC supplies). The aggregator currently leaves Box 2 empty and infers Box 8 from EU 3-Party Trade. Confirm the customer's VAT Posting Group conventions before going live and replace the heuristic with explicit detection (VAT Calculation Type = Reverse Charge VAT, NI/EU posting groups etc.).
  3. Sign / rounding edge cases. HMRC requires Box 6/7/8/9 as whole pounds rounded down and Box 1–5 as 2dp. The communicator does this, but spot-check against a known-good return when you have real test data.
  4. Partial exemption. Out of scope for MVP. The aggregator currently treats every VAT entry as fully recoverable. If the customer is partially exempt, you'll need an apportionment step before the roll-up.
  5. Closed VAT entries. The aggregator filters Closed = false to avoid double-reporting once a member's local VAT settlement has been run. Decide with the customer whether the group return should itself close those entries (a posting step we deliberately defer).
  6. Permission set in production. Assign AMP Group VAT MTD to the finance roles that need it. Cross-company VAT Entry reads still depend on the user having read access to each member company's VAT data — that permission is not re-issued from this extension.
  7. Translation file. app.json declares TranslationFile but no .xlf is shipped — the next compile in VS Code AL will produce the base en-US file under Translations/. Consider en-GB before customer ship.
  8. App ID. The placeholder 0a1c2b3d-... GUID in app.json should be swapped for a real one (uuidgen) before publishing to AppSource or a customer tenant.

Quick start (developer)

# from the extension folder
code .
# in VS Code AL: Ctrl+Shift+P → "AL: Download symbols"
# F5 to publish to the local sandbox configured in .vscode/launch.json

The launch profile starts on page 80050 (Group VAT Setup). From there:

  1. Fill in VRN, Client ID, Client Secret, members.
  2. Open Group VAT Submissions (search bar) → New Group Return, or from a VAT Return Period row use Create Group VAT Return.
  3. On the Submission Card: Aggregate Member VAT to roll up the boxes, then Submit to HMRC to POST. (You'll hit the OAuth TODO on the first submit until step 3 of the production checklist is done.)

Known limitations (MVP, by design)

  • Single VAT group per tenant ('DEFAULT' group code is hardcoded in a few places — the schema supports N groups, the UI does not yet).
  • No GL posting / VAT settlement closing.
  • No partial exemption.
  • OAuth refresh flow is a TODO (EnsureAccessToken errors instead).
  • Fraud-prevention headers are stubbed.
  • Period key is a placeholder until obligations lookup is wired into the UI.
  • No automated tests yet — recommend a test app at IDs 80100..80199 to exercise the aggregator with mocked VAT Entry data and the communicator with a stub HttpClient handler.
Follow on LinkedIn