Migrate customer, vendor and item transactions for BC14 cloud migration - #9663
Migrate customer, vendor and item transactions for BC14 cloud migration#9663gggdttt wants to merge 12 commits into
Conversation
…cloud migration Adds customer ledger entry migration alongside the existing G/L-only transaction migration (work item 642787). Current path: open customer ledger entries are re-created as opening-balance general journal lines, balanced against the customer posting group's receivables control account so the G/L nets to zero (the G/L Entry migrator already carries the receivables balance) while the customer subledger detail is reconstructed. Follows the same cutoff split as the G/L Entry migrator. Historical path: pre-cutoff customer ledger entries are copied read-only into a new BC14 Old Cust. Ledg. Entry archive table (Business Central 14 Historical Data app), mirroring the Old G/L Entry archive. New objects: buffer tables 46937/46938, transaction migrator 46939, historical migrator 46940 (main app); archive table 46886 + list page 46887 (historical data app). Registered in the transaction/historical migrator enums and the runner phase population.
…14 cloud migration Replicates the customer ledger migration pattern for vendors (work item 642787): open vendor ledger entries are re-created as opening-balance journal lines balanced against the vendor posting group's payables control account (net-zero G/L), and pre-cutoff entries are archived read-only into a new BC14 Old Vendor Ledg. Entry table. New objects: buffer tables 46941/46942, transaction migrator 46943, historical migrator 46944 (main app); archive table 46888 + list page 46889 (historical data app).
…igration Item transactions are migrated read-only (work item 642787): the full item ledger, with each entry's actual cost summed from its value entries, is archived into a new BC14 Old Item Ledg. Entry table. Live on-hand inventory is not re-created (no Transaction-phase item migrator), so there is no item-journal posting subsystem. New objects: buffer tables 46945/46946, historical migrator 46947 (main app); archive table 46899 + list page 46891 (historical data app), wired into the historical migrator enum and runner.
Adds test codeunits 148915 (customer current migrator), 148916 (vendor current migrator), and 148917 (item archive migrator) covering GetDisplayName, IsEnabled module gating, net-zero control-account balancing, settled-entry skip, idempotency, and the item archive fallback path with cost summed from value entries (work item 642787).
…fer (microsoft#9700) ## Summary Renames `TempCustomMigrationTableBuffer` back to `CustomMigrationTableBuffer` in `AddCustomMigrationMapping.Page.al` (declaration + both call sites). ## Why The variable is only a handle used to call `SaveMigrationTableMapping` — its own fields are never read or written; the method persists to the `Migration Setup Table Mapping` / `Replication Table Mapping` tables. PR microsoft#9200 renamed it to `TempCustomMigrationTableBuffer`, which is misleading (it isn't used as a data buffer) and trips `AA0237` ("non temporary variables must not be prefixed with Temp") under stricter rulesets — e.g. the NAV build, where AA0237 is an error rather than a warning. This restores the pre-microsoft#9200 name, keeping `main` consistent with the NAV 28.x backport and PR microsoft#9663. ## Risk None — the change is naming-only and behavior-preserving. [AB#642621](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/642621) Co-authored-by: wenjiefan <wenjiefan@microsoft.com>
Rebuild live on-hand inventory during the transaction (current) phase, matching the customer and vendor current migrators. Open item ledger entries are staged as positive-adjustment item journal lines whose quantity is the remaining quantity and whose unit cost is the entry's actual cost spread over its original quantity, then posted by a dedicated item journal post action. Because Automatic Cost Posting is off, posting writes only the item and value ledgers (not G/L), so it does not double-count against the separate G/L Entry migrator. Changes: - New BC14 Item Ledger Migrator (46948) and BC14 Item Journal Post Action (46949). - BC14 Item Ledger Entry buffer: add Journal Batch Name staging field. - Register the migrator and post action in the runner and enums. - Clarify the historical archive migrator comment on how the two item migrators complement each other. - Add BC14 ItemLedgerMigr Tests (148918). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: aaa19873-1007-45cd-b649-a5b7913ca6de
…n tests The customer and vendor transaction migration tests called CreateJournalLine with a local record variable that was never loaded from the buffer table, so the migrator received an empty record (Entry No. 0) and skipped every line, which would fail the open-entry and idempotency assertions on CI. Load the entry with Get after inserting it so the tests exercise the real migrator path. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: aaa19873-1007-45cd-b649-a5b7913ca6de
…anch by mistake This one-line file under .github/skills was accidentally included in commit ff0d01d during the vendor migration work and is unrelated to the BC14 transaction migration. Removing it so it no longer appears in PR #9663. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: aaa19873-1007-45cd-b649-a5b7913ca6de
| SourceTable = "BC14 Old Cust. Ledg. Entry"; | ||
| Editable = false; |
There was a problem hiding this comment.
This historical ledger list page does not set a descending SourceTableView, so it will open oldest-first. Historical entry pages should default to showing the newest migrated records first.
| SourceTable = "BC14 Old Cust. Ledg. Entry"; | |
| Editable = false; | |
| SourceTable = "BC14 Old Cust. Ledg. Entry"; | |
| SourceTableView = order(descending); | |
| Editable = false; |
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Reasonable UX suggestion. These are read-only archive list pages; the default platform sort is acceptable and users can re-sort or filter as needed. Minor preference, not changing in this PR.
| SourceTable = "BC14 Old Item Ledg. Entry"; | ||
| Editable = false; |
There was a problem hiding this comment.
This historical ledger list page does not set a descending SourceTableView, so it will open oldest-first. Historical entry pages should default to showing the newest migrated records first.
| SourceTable = "BC14 Old Item Ledg. Entry"; | |
| Editable = false; | |
| SourceTable = "BC14 Old Item Ledg. Entry"; | |
| SourceTableView = order(descending); | |
| Editable = false; |
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Reasonable UX suggestion. These are read-only archive list pages; the default platform sort is acceptable and users can re-sort or filter as needed. Minor preference, not changing in this PR.
| SourceTable = "BC14 Old Vendor Ledg. Entry"; | ||
| Editable = false; |
There was a problem hiding this comment.
This historical ledger list page does not set a descending SourceTableView, so it will open oldest-first. Historical entry pages should default to showing the newest migrated records first.
| SourceTable = "BC14 Old Vendor Ledg. Entry"; | |
| Editable = false; | |
| SourceTable = "BC14 Old Vendor Ledg. Entry"; | |
| SourceTableView = order(descending); | |
| Editable = false; |
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Reasonable UX suggestion. These are read-only archive list pages; the default platform sort is acceptable and users can re-sort or filter as needed. Minor preference, not changing in this PR.
| end; | ||
|
|
||
| local procedure AssignJournalBatches() | ||
| var |
There was a problem hiding this comment.
AssignJournalBatches (and the Item-ledger equivalent AssignItemJournalBatches) loops over every open buffer record with FindSet/repeat/Next and calls Modify(false) on each row to stamp the batch name, with no Commit anywhere in the loop. For a large company this can touch hundreds of thousands of open customer/vendor/item ledger entries in a single implicit transaction, risking transaction-log growth, lock escalation, or a session timeout before the batch assignment pass completes -- and unlike TransferDataInBatches in the historical migrators, there is no bounded-checkpoint or periodic-commit strategy here at all. Consider chunking this pass with a bounded key range and a commit between chunks (per the project's own avoid-commit-inside-loops guidance for large batches), or track progress so a partial run can resume.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Fair observation. AssignJournalBatches stamps a batch code on open-only buffer rows in a single pass; the heavy work and batching happen in the subsequent per-record loop (MigrationLoop.RunRecordLoop) and journal posting. For very large open-entry counts a periodic commit here could mirror the historical migrators; noting as a possible follow-up. Not changing in this PR.
|
The assignable "BC14 Historical Data" permission set adds tabledata grants for the new historical ledger tables, but it still omits execute grants for the new list pages ("BC14 Old Cust. Ledg. List", "BC14 Old Vendor Ledg. List", and "BC14 Old Item Ledg. List") that the new tables expose through DrillDownPageId/LookupPageId. That leaves normal non-SUPER usage paths uncovered for AppSource validation. Suggested fix (apply manually — could not be anchored as a one-click suggestion): Permissions =
tabledata "BC14 Arch. Sales Inv. Header" = RIMD,
tabledata "BC14 Arch. Sales Inv. Line" = RIMD,
tabledata "BC14 Old G/L Entry" = RIMD,
tabledata "BC14 Old Cust. Ledg. Entry" = RIMD,
tabledata "BC14 Old Vendor Ledg. Entry" = RIMD,
tabledata "BC14 Old Item Ledg. Entry" = RIMD,
page "BC14 Old Cust. Ledg. List" = X,
page "BC14 Old Vendor Ledg. List" = X,
page "BC14 Old Item Ledg. List" = X;Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4 |
| @@ -0,0 +1,109 @@ | |||
| // ------------------------------------------------------------------------------------------------ | |||
There was a problem hiding this comment.
src/Apps/W1/HybridBC14/app/src/Migration/BufferTables/Transaction/BC14CustLedgerEntry.Table.al, BC14VendorLedgerEntry.Table.al, and BC14ItemLedgerEntry.Table.al copy live-table secondary keys such as Customer/Vendor/Item No., Posting Date, Open, and Transaction No. even though the new migrators only read these staging tables with SetCurrentKey("Entry No.") plus filters. On replicated migration buffers, those unused indexes add write overhead during load and batch updates without supporting any lookup or aggregation path in this app; trim the staging tables to the keys the migrators actually use.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Noted. These are write-once / read-sequential migration staging buffers processed once during migration. The primary key (Entry No., and Cust./Vendor Ledger Entry No. on the detailed buffers) drives the access paths and resume order; adding extra secondary/SIFT keys would add index-maintenance cost on every insert during the bulk load for a one-time pass. This is an acceptable trade-off for a migration buffer and does not affect correctness. Not changing.
|
|
||
| Commit(); | ||
|
|
||
| if not Codeunit.Run(Codeunit::"BC14 Old Cust. Ledger Migr.") then begin |
There was a problem hiding this comment.
This historical customer-ledger migrator wraps the entire archive transfer in one Codeunit.Run call and, on failure, logs only a table-level error with an empty source key and empty RecordId. A single bad archive row will abort the whole migrator without telling the operator which customer ledger entry failed. Route the archive through the shared per-record migration loop, or at least capture the current entry key and RecordId when logging the failure so retries can target the bad row.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Intentional for the historical archive pass. Per-record errors on the live subledger phase are already captured and continued through BC14 Migration Error Handler. The historical archive is a bulk read-only copy wrapped in one Codeunit.Run; on the rare platform failure it fails the migrator and is retried, and the resume watermark (GetLastArchivedEntryNo) means the retry continues past already-archived rows. Row-level granularity here would add overhead without changing the recovery path. Not changing.
|
|
||
| Commit(); | ||
|
|
||
| if not Codeunit.Run(Codeunit::"BC14 Old Item Ledger Migr.") then begin |
There was a problem hiding this comment.
This historical item-ledger migrator also collapses any per-row archive failure into one generic table-level error row with a blank source key and blank RecordId. One bad item ledger entry therefore aborts the whole historical archive while leaving Continue migration with no precise row to diagnose or retry. Capture the current entry identity when logging failures, or process archive rows through the existing per-record migration loop so error handling remains granular.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Intentional for the historical archive pass. Per-record errors on the live subledger phase are already captured and continued through BC14 Migration Error Handler. The historical archive is a bulk read-only copy wrapped in one Codeunit.Run; on the rare platform failure it fails the migrator and is retried, and the resume watermark (GetLastArchivedEntryNo) means the retry continues past already-archived rows. Row-level granularity here would add overhead without changing the recovery path. Not changing.
|
|
||
| Commit(); | ||
|
|
||
| if not Codeunit.Run(Codeunit::"BC14 Old Vend. Ledger Migr.") then begin |
There was a problem hiding this comment.
This historical vendor-ledger migrator has the same failure shape: one Codeunit.Run around the whole archive and a generic LogError call with an empty source key and empty RecordId. If one archived vendor ledger row fails to insert, the entire migrator stops and the error log cannot identify the offending row for remediation or targeted retry. Preserve the failing entry number and RecordId, or move the work to the shared per-record loop so failures stay attributable.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Intentional for the historical archive pass. Per-record errors on the live subledger phase are already captured and continued through BC14 Migration Error Handler. The historical archive is a bulk read-only copy wrapped in one Codeunit.Run; on the rare platform failure it fails the migrator and is retried, and the resume watermark (GetLastArchivedEntryNo) means the retry continues past already-archived rows. Row-level granularity here would add overhead without changing the recovery path. Not changing.
| end; | ||
|
|
||
| [IntegrationEvent(false, false)] | ||
| local procedure OnMigrateOldCustLedgerEntries(var IsMigrated: Boolean) |
There was a problem hiding this comment.
Several new publishers use ad-hoc names that do not encode where they fire: the OnMigrateOld*/OnMigrate* hooks at the start of Migrate(), the OnConfigureEntryDataTransfer and OnTransfer*CustomFields hooks inside the migration routines, and OnValidateBeforeMigration in the runner. Rename these publishers to OnBefore.../OnAfter... or OnOnBefore/OnOnAfter forms so subscribers can tell the publisher boundary from the event name.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.20.4
There was a problem hiding this comment.
Acknowledged as a naming-convention preference. These are new integration events that follow the existing BC14 migrator publisher style in this app, and OnAfter... variants are already present where a subscriber needs the post position (OnAfterMigrate..., OnAfterCreateJournalLine). This is a Warning-level guideline rather than a correctness issue, so we are keeping the names consistent with the surrounding app. Not changing.
|
|
||
| page 46889 "BC14 Old Vendor Ledg. List" | ||
| { | ||
| Caption = 'BC14 Old Vendor Ledger Entries'; |
There was a problem hiding this comment.
This new historical ledger list page does not set a descending default view, so it opens oldest-first instead of showing the newest migrated entries first. Add a descending SourceTableView on the page.
Suggested fix (apply manually — could not be anchored as a one-click suggestion):
Caption = 'BC14 Old Vendor Ledger Entries';
PageType = List;
ApplicationArea = All;
UsageCategory = Lists;
SourceTable = "BC14 Old Vendor Ledg. Entry";
Editable = false;
SourceTableView = order(descending);Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
|
The Historical Data app adds three user-facing list pages ( Suggested fix (apply manually — could not be anchored as a one-click suggestion): Permissions =
page "BC14 Old Cust. Ledg. List" = X,
page "BC14 Old Item Ledg. List" = X,
page "BC14 Old Vendor Ledg. List" = X,
tabledata "BC14 Old Cust. Ledg. Entry" = RIMD,
tabledata "BC14 Old Vendor Ledg. Entry" = RIMD,
tabledata "BC14 Old Item Ledg. Entry" = RIMD;Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements interface "BC14 Migrator" and every current value has an Implementation, but the enum declares no DefaultImplementation. "BC14 Migration Runner" assigns these enum values to Interface "BC14 Migrator" variables; a partner-added enum value that omits Implementation would then fail at runtime at the assignment/call site. Add a safe DefaultImplementation fallback. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements interface "BC14 Post Migration Action" and every current value has an Implementation, but the enum declares no DefaultImplementation. "BC14 Migration Runner" assigns these enum values to Interface "BC14 Post Migration Action" variables; a partner-added enum value that omits Implementation would then fail at runtime at the assignment/call site. Add a safe DefaultImplementation fallback. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements interface "BC14 Migrator" and every current value has an Implementation, but the enum declares no DefaultImplementation. "BC14 Migration Runner" assigns these enum values to Interface "BC14 Migrator" variables; a partner-added enum value that omits Implementation would then fail at runtime at the assignment/call site. Add a safe DefaultImplementation fallback. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
| // Only open entries with a positive remaining quantity make up current on-hand stock and are | ||
| // rebuilt as positive adjustments. Fully-consumed inbound entries (remaining 0) and outbound | ||
| // entries (remaining <= 0) net to zero on-hand and are left to the historical-phase archive. | ||
| if BC14ItemLedgerEntry."Remaining Quantity" <= 0 then |
There was a problem hiding this comment.
Open item ledger entries can legitimately have a negative "Remaining Quantity" (for example, negative inventory or an outbound entry still waiting to be applied). This code exits for every value <= 0, so those open negative balances are never recreated in the live item ledger and the migrated company can end up with overstated on-hand inventory. Keep the zero-remaining skip, but handle negative remaining quantities by staging the corresponding negative adjustment instead of dropping the entry.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c6a2d9b6-b23f-424c-8cb1-7fdc8368e63d
|
The assignable permission set adds tabledata grants for the three new historical-ledger tables but omits execute permissions for the three new list pages exposed with UsageCategory = Lists, so assigned non-SUPER users cannot open this app UI even though the objects are part of normal usage. Suggested fix (apply manually — could not be anchored as a one-click suggestion): Permissions =
tabledata "BC14 Arch. Sales Inv. Header" = RIMD,
tabledata "BC14 Arch. Sales Inv. Line" = RIMD,
tabledata "BC14 Old G/L Entry" = RIMD,
tabledata "BC14 Old Cust. Ledg. Entry" = RIMD,
tabledata "BC14 Old Vendor Ledg. Entry" = RIMD,
tabledata "BC14 Old Item Ledg. Entry" = RIMD,
page "BC14 Old Cust. Ledg. List" = X,
page "BC14 Old Vendor Ledg. List" = X,
page "BC14 Old Item Ledg. List" = X;Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements "BC14 Migrator" and the runner assigns enum values to an Interface "BC14 Migrator" variable, but the enum declares no DefaultImplementation. If a partner extension adds a historical migrator value without its own Implementation, the enum-to-interface assignment can fail at runtime. Add an enum-level DefaultImplementation mapped to a safe fallback migrator. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements "BC14 Post Migration Action" and the runner assigns enum values to an Interface "BC14 Post Migration Action" variable, but the enum declares no DefaultImplementation. If a partner extension adds a post-migration action without its own Implementation, the enum-to-interface assignment can fail at runtime. Add an enum-level DefaultImplementation mapped to a safe fallback action. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
This extensible enum implements "BC14 Migrator" and the runner assigns enum values to an Interface "BC14 Migrator" variable, but the enum declares no DefaultImplementation. If a partner extension adds a transaction migrator value without its own Implementation, the enum-to-interface assignment can fail at runtime. Add an enum-level DefaultImplementation mapped to a safe fallback migrator. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
BC14MigrationRunner logs ValidationErrorMessage directly inside the Session.LogMessage message string at BC14MigrationRunner.codeunit.al:125. The call already marks that payload as CustomerContent, which means validation details still ship verbatim in free-text telemetry instead of staying in a static message plus structured, classified context. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
BC14MigrationRunner interpolates CompanyName() directly into the Session.LogMessage message string on lines 143, 147, 245, and 357. Even with DataClassification::OrganizationIdentifiableInformation, the company name is still emitted as free text instead of a static telemetry message with separately reviewed context. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
| exit; | ||
| repeat | ||
| BC14OldItemLedgEntry.Init(); | ||
| BC14OldItemLedgEntry.TransferFields(BC14ItemLedgerEntry, true); |
There was a problem hiding this comment.
BC14 Old Item Ledger Migr. calls TransferFields(BC14ItemLedgerEntry, true) even though source table BC14 Item Ledger Entry uses field 100 for Journal Batch Name (Code[10]) while destination table BC14 Old Item Ledg. Entry uses field 100 for Cost Amount (Actual) (Decimal). Because TransferFields with MatchAllFields=true requires the same field ID, type, and length, this field-number/type collision will throw a runtime error during historical item-ledger migration (this would otherwise be rated major/blocker as a functional bug, but is capped at minor per agent-finding rules since it has no dedicated knowledge-file citation). Renumber the working field or replace the TransferFields call with explicit field assignments for this source/destination pair.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
Port the customer/vendor/item transaction migrators (current + historical) from features/CloudMigrationBC14-CustVendItemTransactions (PR #9663) onto the third-party PTE preview branch, applying the same reassignment transform used for this branch: publisher MS, namespace MS.DataMigration.BC14Reimplementation, object IDs +20000. Test sources are copied unchanged, matching the existing convention on this branch. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c6a2d9b6-b23f-424c-8cb1-7fdc8368e63d
…ctions Brings the feature branch up to date with main (137 commits), restoring page actions (ShowErrors / ContinueMigration / RerunHistorical on BC14 Company Upgrade Status) and other BC14 changes the stale branch was missing. Clean auto-merge, 0 conflicts; the Migration Runner is auto-merged to keep both main's RerunHistoricalForCompany and the feature's transaction/historical migrator registrations. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c6a2d9b6-b23f-424c-8cb1-7fdc8368e63d
Positive-adjustment item journal lines cannot use an in-transit location (BC restricts in-transit locations to transfer orders). Open item ledger entries whose Location Code is flagged 'Use As In-Transit' therefore failed with 'You can use In-Transit location X for transfer orders only.' during the Transaction phase. Such in-transit stock cannot be rebuilt through the inventory journal, so it is now skipped in the on-hand rebuild (the full ledger remains available read-only in the historical archive) instead of being logged as an unresolvable per-record failure. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: c6a2d9b6-b23f-424c-8cb1-7fdc8368e63d
|
The historical-data app adds three new list pages with UsageCategory = Lists, but its only assignable permission set still grants tabledata only. Users assigned BC14 Historical Data can discover "BC14 Old Cust. Ledg. List", "BC14 Old Vendor Ledg. List", and "BC14 Old Item Ledg. List" but cannot execute them, which violates the AppSource requirement that normal app usage work without SUPER. Suggested fix (apply manually — could not be anchored as a one-click suggestion): Permissions =
tabledata "BC14 Arch. Sales Inv. Header" = RIMD,
tabledata "BC14 Arch. Sales Inv. Line" = RIMD,
tabledata "BC14 Old G/L Entry" = RIMD,
tabledata "BC14 Old Cust. Ledg. Entry" = RIMD,
tabledata "BC14 Old Vendor Ledg. Entry" = RIMD,
tabledata "BC14 Old Item Ledg. Entry" = RIMD,
page "BC14 Old Cust. Ledg. List" = X,
page "BC14 Old Vendor Ledg. List" = X,
page "BC14 Old Item Ledg. List" = X;Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
| ReplicateData = false; | ||
| Extensible = false; | ||
|
|
||
| fields |
There was a problem hiding this comment.
BC14 Detailed Cust. LE drops the source table's Excluded from calculation flag, but the added customer migrators derive Remaining Amount by summing every detailed-entry amount for a ledger entry. That means detailed entries that BC excludes from the open-balance FlowField will be counted here, so migrated and archived customer balances can be wrong. Add the Boolean field with the source field id and carry it through replication so remaining-balance calculations can filter Excluded from calculation = false. This would otherwise be a higher-severity data defect, but agent findings are capped at minor until backed by curated knowledge.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
| ReplicateData = false; | ||
| Extensible = false; | ||
|
|
||
| fields |
There was a problem hiding this comment.
BC14 Detailed Vendor LE has the same schema gap: it omits Excluded from calculation, while the new vendor migrators compute Remaining Amount by summing all detailed-entry amounts for a vendor ledger entry. Any detailed entries that BC excludes from the outstanding-balance FlowField will therefore inflate migrated and archived vendor balances. Add the Boolean field with the source field id and preserve it through replication so remaining-balance calculations can filter Excluded from calculation = false. This would otherwise be a higher-severity data defect, but agent findings are capped at minor until backed by curated knowledge.
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
| if RemainingAmount = 0 then | ||
| exit; | ||
|
|
||
| if not CustomerPostingGroup.Get(BC14CustLedgerEntry."Customer Posting Group") then |
There was a problem hiding this comment.
This missing-setup failure is recoverable: the code knows exactly which Customer Posting Group record must be corrected, but it raises a plain Error with no recommended action. Prefer an ErrorInfo with navigation context (PageNo/RecordId plus AddNavigationAction) so the migration operator can jump straight to the posting-group setup and fix the receivables account.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
| if RemainingAmount = 0 then | ||
| exit; | ||
|
|
||
| if not VendorPostingGroup.Get(BC14VendorLedgerEntry."Vendor Posting Group") then |
There was a problem hiding this comment.
This missing-setup failure is recoverable: the code knows exactly which Vendor Posting Group record must be corrected, but it raises a plain Error with no recommended action. Prefer an ErrorInfo with navigation context (PageNo/RecordId plus AddNavigationAction) so the migration operator can jump straight to the posting-group setup and fix the payables account.
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
|
"BC14 Historical Migrator" is an extensible enum that implements "BC14 Migrator", but it declares no DefaultImplementation. A partner can add a new enum value and include it in PopulateHistoricalMigrators without setting Implementation; assigning that value to Interface "BC14 Migrator" will then fail at runtime. Add a safe DefaultImplementation for this extensibility point. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
"BC14 Post Migration Action" is an extensible enum that implements "BC14 Post Migration Action", but it declares no DefaultImplementation. A partner can add a new enum value and include it in the action list without setting Implementation; assigning that value to Interface "BC14 Post Migration Action" will then fail at runtime. Add a safe DefaultImplementation for this extensibility point. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
"BC14 Transaction Migrator" is an extensible enum that implements "BC14 Migrator", but it declares no DefaultImplementation. A partner can add a new enum value and include it in PopulateTransactionMigrators without setting Implementation; assigning that value to Interface "BC14 Migrator" will then fail at runtime. Add a safe DefaultImplementation for this extensibility point. Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
The new "BC14 Historical Data" permission set grants direct RIMD rights on read-only historical ledger archive tables. Because the feature exposes these records through non-editable list pages, assignees only need read access; granting insert/modify/delete lets them alter or remove archived migration history through other clients or AL entry points. Suggested fix (apply manually — could not be anchored as a one-click suggestion): Permissions =
tabledata "BC14 Arch. Sales Inv. Header" = R,
tabledata "BC14 Arch. Sales Inv. Line" = R,
tabledata "BC14 Old G/L Entry" = R,
tabledata "BC14 Old Cust. Ledg. Entry" = R,
tabledata "BC14 Old Vendor Ledg. Entry" = R,
tabledata "BC14 Old Item Ledg. Entry" = R;Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
The file Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
The file Knowledge: Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
|
The halted-phase branch is a customer-actionable migration failure: it immediately marks the company failed and tells the operator to retry, but the telemetry is emitted as Verbosity::Warning to TelemetryScope::ExtensionPublisher only. That both down-ranks a failed operation and hides it from environment telemetry, so tenant operators cannot reliably alert on the failure. Log this branch at Error with TelemetryScope::All. Suggested fix (apply manually — could not be anchored as a one-click suggestion): Session.LogMessage('0000TZ2', StrSubstNo(MigrationPhaseHaltedLbl, Format(Phase), CompanyName()), Verbosity::Error, DataClassification::OrganizationIdentifiableInformation, TelemetryScope::All, 'Category', BC14Telemetry.GetCategory());Knowledge:
Line mapping was unavailable, so this was posted as an issue comment. 👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4 |
| [Test] | ||
| procedure TestMigrate_FallbackPath_ArchivesAllEntriesWithCostSummedAndMigratedOnStamped() |
There was a problem hiding this comment.
This test exercises BC14 Old Item Ledger Migr. Migrate(), which calls Commit(), but the method is declared with only [Test]. Add [TransactionModel(TransactionModel::AutoCommit)] so the test can execute the commit path instead of failing on the first commit under the default rollback transaction.
| [Test] | |
| procedure TestMigrate_FallbackPath_ArchivesAllEntriesWithCostSummedAndMigratedOnStamped() | |
| [Test] | |
| [TransactionModel(TransactionModel::AutoCommit)] | |
| procedure TestMigrate_FallbackPath_ArchivesAllEntriesWithCostSummedAndMigratedOnStamped() |
Knowledge:
👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.26.4
Background
BC14 cloud migration previously staged only G/L Entries. For Dynamics SL and GP source data that is insufficient — partners need the customer, vendor and item subledger transactions migrated as well (open receivables / payables and on-hand inventory), for both current (open) and historical (archive) data.
Addresses work item AB#642787 — [CloudMigration][BC14] Migrate customer, vendor and item transactions (not only G/L) for current and historical data.
What this PR does
Adds dedicated buffer tables, current migrators, historical migrators and post actions for the customer ledger, vendor ledger and item ledger, wired into the BC14 migration runner through the transaction-migrator, post-migration-action and historical-migrator enums.
Automatic Cost Postingis intentionally left off so posting writes only the Item / Value ledger and does not double-count against the G/L migrator.HybridBC14HistoricalDataapp for reference; these migrators do not post.IsEnabled) so it only runs when the corresponding module is selected for migration.Changes
Registration (enums + runner)
app/src/Migration/BC14TransactionMigrator.Enum.alapp/src/Migration/BC14PostMigrationAction.Enum.alapp/src/Migration/BC14HistoricalMigrator.Enum.alapp/src/Orchestration/BC14MigrationRunner.codeunit.alBuffer tables (HybridBC14)
BufferTables/Transaction/BC14CustLedgerEntry.Table.alBufferTables/Transaction/BC14DetailedCustLE.Table.alBufferTables/Transaction/BC14VendorLedgerEntry.Table.alBufferTables/Transaction/BC14DetailedVendorLE.Table.alBufferTables/Transaction/BC14ItemLedgerEntry.Table.alBufferTables/Transaction/BC14ValueEntry.Table.alCurrent migrators + post action
Transactions/BC14CustLedgerMigrator.Codeunit.alTransactions/BC14VendorLedgerMigrator.Codeunit.alTransactions/BC14ItemLedgerMigrator.Codeunit.alTransactions/BC14ItemJournalPostAction.Codeunit.alHistorical migrators
Historical/BC14OldCustLedgerMigr.Codeunit.alHistorical/BC14OldVendLedgerMigr.Codeunit.alHistorical/BC14OldItemLedgerMigr.Codeunit.alHistorical data app (HybridBC14HistoricalData)
Tables/BC14OldCustLedgEntry.Table.al,Tables/BC14OldVendorLedgEntry.Table.al,Tables/BC14OldItemLedgEntry.Table.alPages/BC14OldCustLedgList.Page.al,Pages/BC14OldVendorLedgList.Page.al,Pages/BC14OldItemLedgList.Page.alPermissions/BC14HistoricalData.permissionset.alTests
BC14CustLedgerMigrTests.Codeunit.al,BC14VendLedgerMigrTests.Codeunit.alBC14ItemLedgerMigrTests.Codeunit.al,BC14OldItemLedgMigrTests.Codeunit.alTesting
Test codeunits for the customer, vendor and item current migrators plus the item historical migrator. They cover: display name,
IsEnabledper module flag, open-entry staging, zero-remaining entries skipped, and idempotency (re-running does not duplicate journal lines).Risk
Additive only — all new objects live in dedicated id ranges and existing shipped migration logic is untouched apart from enum registration. Posting paths were chosen specifically to avoid double-counting inventory / subledger against the existing G/L migrator; historical migration is copy-only.