Skip to main content

Convert compiled Microsoft Dynamics 365 Business Central AL packages (.app) into DBML schemas.

Project description

AL-to-DBML

PyPI version Python versions CI License: MIT Ruff

al2dbml turns a compiled Microsoft Dynamics 365 Business Central AL package (.app) into a DBML schema you can paste straight into dbdiagram.io or push to dbdocs.io. It reads SymbolReference.json from the archive (tolerating AL's 40-byte header and Ready-To-Run wrappers), normalises tables, table extensions, enums, and TableRelations, and emits one valid DBML document with Project, Table, Ref, Enum, and TableGroup blocks.

Sample output:

Project "MyApp" {
    database_type: 'MSSQL'
    Note { '''MyApp 1.0.0.0 by ACME''' }
}

Table "dbo"."Customer" {
    "No." varchar(20) [pk, note: '"No."']
    "Name" varchar(100)
    "Customer Posting Group" varchar(20)
}

Ref { "dbo"."Customer"."Customer Posting Group" > "dbo"."Customer Posting Group"."Code" }

TableGroup "Sales" { "Sales Header" "Sales Line" }

Release notes live in CHANGELOG.md.

Install

Python 3.10+. Runtime depends only on click, pydbml, and PyYAML (for the optional aldoc overlay).

uv tool install al2dbml     # recommended — isolated environment, on PATH
pipx install al2dbml        # also fine
pip install al2dbml         # works inside an activated venv

If you don't have uv:

sudo dnf install uv                              # Fedora / RHEL
brew install uv                                  # macOS
curl -LsSf https://astral.sh/uv/install.sh | sh  # anywhere

Upgrade with uv tool upgrade al2dbml. Verify with al2dbml --version.

Quickstart

al2dbml MyApp.app -o schema.dbml

That's the whole workflow. Drop schema.dbml into https://dbdiagram.io, or push it to dbdocs:

npx -y dbdocs build schema.dbml

Without -o the DBML streams to stdout so you can pipe it anywhere.

CLI options

Run al2dbml --help for the full list. The flags group by purpose:

Output

  • -o, --output FILE — write DBML to FILE instead of stdout.
  • --stats — print object counts (tables, enums, refs, groups) to stderr. With no -o, skips the (expensive) render entirely for a fast probe.

Filtering

  • --include PATTERN — keep only tables matching PATTERN (fnmatch). Repeatable.
  • --exclude PATTERN — drop tables matching PATTERN. Applied after --include. Repeatable.

Grouping

Tables are bucketed into TableGroups by the last segment of their AL namespace (so Microsoft.Finance.GeneralLedger → group GeneralLedger); un-namespaced tables fall back to their first whitespace-separated word (Sales Header + Sales LineSales).

  • -g, --group NAME=PATTERN[,PATTERN...] — explicit rule. Repeatable.
  • --group-by namespace|word|none — change the auto-source (default namespace).
  • --no-groups — emit no TableGroup blocks at all.
  • --min-group-size N — drop groups smaller than N tables (default 2; use 1 to keep singletons).

Schemas

  • --table-schema NAME — schema for Table blocks (default dbo, matching BC's SQL Server).
  • --enum-schema NAME — schema for Enum blocks (default meta, since BC enums are AL-language metadata, not SQL objects).

dbdocs header

  • --database-type NAME — value for the DBML Project { database_type: ... } line (default MSSQL). Pass "" to omit it. dbdocs.io uses this as the engine label on the rendered schema.

Extensions

  • --no-merge-extensions — emit TableExtensions as separate <Target> (Extension) tables instead of merging their fields into the base table.

Rich descriptions

  • -d, --docs DIR — overlay aldoc-generated YAML field descriptions and table summaries onto the diagram (see next section).

Rich field descriptions (aldoc overlay)

Default column notes come from the AL Caption property — usually just the field name. The actual BC documentation (the "Specifies the customer number..." sentences from Microsoft Learn) lives in the AL ToolTip property and /// <summary> XML doc comments, neither of which the compiled .app keeps.

Microsoft's aldoc tool (bundled with the AL Language VS Code extension) does keep them. Run it once per release, then point al2dbml at the output:

aldoc generate MyApp.app -o ./myapp-docs/                       # slow, once per release
al2dbml MyApp.app --docs ./myapp-docs/ -o schema.dbml            # fast

You get:

  • Each Table block gains a Note { ... } body from the AL /// <summary> of the table.
  • Each column note leads with the AL ToolTip text instead of the bare caption.
  • Existing **Condition:** / **References** sections still follow.

Coverage is uneven. Active-document tables (Customer, Item, Sales Header) are richly documented; history and buffer tables often have nothing. Missing entries fall back to the Caption-based note.

Python API

from al2dbml import Diagram, generate, GroupingConfig

# One-shot
dbml = generate("MyApp.app", output_path="schema.dbml", database_type="MSSQL")

# Step-by-step with custom config
diagram = Diagram.from_app(
    "MyApp.app",
    grouping=GroupingConfig(rules={"Documents": ["Sales*", "Purch*"]}),
    includes=["Sales*", "Customer"],
    docs=...,                            # optional AldocDocs from al2dbml.aldoc.load_docs
)
print(diagram.dbml())                    # build + render
print(diagram.stats())                   # {'tables': N, 'columns': N, ...}
print(diagram.context.tables.keys())     # inspect the live BuildContext

Diagram is a single-shot dataclass: build() is cached, so mutating its fields after the first call has no effect. Construct a new instance to rebuild with different settings.

A second console script, al2dbml-validate FILE.dbml, parse-checks a DBML file via pydbml. Useful as a smoke test in CI, though it doesn't match dbdiagram.io's parser exactly — see Limitations.

Limitations

  • Render time scales quadratically with table count inside pydbml. Up to a few hundred tables: sub-second. Microsoft's full Base Application (~1,500 tables) currently takes several minutes. A custom emitter is on the roadmap.
  • FlowFields are treated as regular fields; the underlying CalcFormula is not interpreted.
  • Obsolete fields are emitted alongside active ones; no filtering by ObsoleteState.
  • Multi-field primary keys become multiple [pk] flags (DBML's single-PK convention) rather than a composite index.
  • Multi-column secondary keys are not yet emitted as DBML indexes blocks; only single-column secondary keys surface (as [unique]).
  • Cross-package references (relations pointing outside the current .app) degrade to **References** \Target` (cross-package)` notes on the source column, since the target table is absent from the diagram.
  • Conditional IF/ELSE TableRelations produce one Ref per resolved branch, with the branch's condition recorded as a // when (...) comment on the Ref. Branches whose target is missing degrade to a column note.
  • al2dbml-validate uses pydbml's parser, which is not byte-identical to dbdiagram.io's parser. For an authoritative check matching dbdiagram exactly: dbml2sql FILE --postgres from @dbml/cli.

Development

python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q
.venv/bin/ruff check . && .venv/bin/ruff format --check .

Tags matching v* trigger a PyPI Trusted Publisher upload via .github/workflows/publish.yml.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

al2dbml-0.9.0.tar.gz (52.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

al2dbml-0.9.0-py3-none-any.whl (38.0 kB view details)

Uploaded Python 3

File details

Details for the file al2dbml-0.9.0.tar.gz.

File metadata

  • Download URL: al2dbml-0.9.0.tar.gz
  • Upload date:
  • Size: 52.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for al2dbml-0.9.0.tar.gz
Algorithm Hash digest
SHA256 3c0908603f06fea246132a5fef9f9cb2eb82e4cd5daaa2cd5dab0dd53acd3df6
MD5 8ac2f680433252433441e9f6cb98e517
BLAKE2b-256 eed68c0c3913628a2d5c6579cb9d49f0cbc10d66a446a19d046064fc0cf67b7a

See more details on using hashes here.

Provenance

The following attestation bundles were made for al2dbml-0.9.0.tar.gz:

Publisher: publish.yml on mykola-kharchenko/al2dbml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file al2dbml-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: al2dbml-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 38.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for al2dbml-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0f6308639e218c52d68a17fefd43138ba32b355301ef894264585f62ab9ebb70
MD5 5306939b0da0852dc17c5a3435157111
BLAKE2b-256 98bfbd976b933a149470257b6cea7fd2a6dac621b98a9ec3c37cc5aab2502a51

See more details on using hashes here.

Provenance

The following attestation bundles were made for al2dbml-0.9.0-py3-none-any.whl:

Publisher: publish.yml on mykola-kharchenko/al2dbml

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page