What this audit checks
Authentication & access
- Azure AD OAuth2 token resolves for the D365 Finance environment (correct tenant + resource URL)
- App registration granted OData access to GeneralJournalEntries, CustomersV3, SalesOrderHeadersV2, CustomerInvoiceJournalHeaders, InventOnhand
- DataAreaId (legal entity) for every active company is configured
- OData $filter + cross-company query enabled for multi-entity reads
GL health
- Unposted general journals below 50 (no stuck batch from the ecom integration)
- Zero out-of-balance vouchers persisting past posting
- Batch job / Data Management import errors cleared in the last 24h
- Manual journals under 25% of total postings (controls gap if higher)
AP/AR cycle
- AR aging 60+ days below 15% of open receivables
- DSO under 60 days and not rising > 10 days vsP
- No single customer holding > 40% of 60+ day AR (concentration risk)
- Cash application (settlement) rate at or above 95%; high-value invoices (> 50k) not overdue 60+ days
Inventory reconciliation
- ERP-vs-ecom on-hand variance below 5% on top-50 velocity SKUs
- No item on-hand gone negative in any warehouse / site
- Items below safety stock / minimum coverage flagged
- Slow-moving / dead stock under 10% of inventory value
Period close
- Ledger period not past close deadline on any legal entity (> 5 days blocks consolidation)
- Zero intercompany imbalances across the legal-entity group
- Accrual reversals from the last close within expected range
Cross-channel: revenue reconciliation
- Ecom orders with a matching D365 Sales Order + Customer Invoice of equal value within the period (sibling = shopify/bigcommerce/adobe.order)
- Ecom customers with completed orders present in the D365 customer master (CustomersV3)
- AR aging on customers with active ecom orders (credit-control gap)
- Unbilled revenue forecast (commerce cleared value minus matched customer-invoice value)
Severity thresholds
| Signal | Warn | Critical |
|---|---|---|
open_journal_count | 25 | 50 |
journal_imbalance_count | 1 | 5 |
ar_aging_60d_pct | 10 | 15 |
dso_days | 45 | 60 |
inventory_variance_pct | 3 | 5 |
period_close_overdue_days | 2 | 5 |
customer_master_drift | 5 | 10 |
Data sources
GET https://{env}.dynamics.com/data/GeneralJournalEntries- Posted + unposted journal entries, voucher balanceGET https://{env}.dynamics.com/data/CustomersV3- Customer master, credit limit, blocked statusGET https://{env}.dynamics.com/data/SalesOrderHeadersV2- Sales orders: status, hold, ecom referenceGET https://{env}.dynamics.com/data/CustomerInvoiceJournalHeaders- Customer invoices for order-to-invoice reconciliationGET https://{env}.dynamics.com/data/InventOnhand- On-hand by warehouse/site vs ecom availability