# Bc Pricing Refactor

> >

- **Type:** Skill
- **Install:** `agentstack add skill-fernandoartalf-al-copilot-skills-collection-bc-pricing-refactor`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [fernandoartalf](https://agentstack.voostack.com/s/fernandoartalf)
- **Installs:** 0
- **Category:** [Developer Tools](https://agentstack.voostack.com/c/developer-tools)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [fernandoartalf](https://github.com/fernandoartalf)
- **Source:** https://github.com/fernandoartalf/AL-Copilot-Skills-Collection/tree/main/skills/bc-pricing-refactor
- **Website:** https://alcopilotskills.com/

## Install

```sh
agentstack add skill-fernandoartalf-al-copilot-skills-collection-bc-pricing-refactor
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# BC Pricing Refactor Skill

## Purpose

This skill provides structured guidance for migrating Business Central
customizations from the legacy pricing model (pre-2020) to the
New Sales Pricing Experience.

The guidance is intentionally version-agnostic. Before implementing code,
verify object names, event publishers, interface signatures, and page IDs
against the symbols of your target runtime.

Treat rollout timing and feature availability as environment-specific. Confirm
the current platform behavior and deprecation state in the official Microsoft
documentation for the target runtime before planning a migration.

---

## Context: What Changed and Why

| Concept | Legacy ( Active -> Inactive |

### Official Microsoft References
- Functional guide: https://learn.microsoft.com/en-us/dynamics365/business-central/across-prices-and-discounts
- Release Plan Wave 2 2020: https://learn.microsoft.com/en-us/dynamics365-release-plan/2020wave2/smb/dynamics365-business-central/use-new-sales-pricing-experience-
- Release Plan Wave 1 2020 (engine): https://learn.microsoft.com/en-us/dynamics365-release-plan/2020wave1/dynamics365-business-central/extend-price-calculation
- Developer - Extending Price Calculations: https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-extending-best-price-calculations
- Extensibility Overview: https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/devenv-extensibility-overview

---

## Step-by-Step Refactoring Workflow

Follow these steps in order. Do not skip Step 0.

### Step 0 - Audit existing customizations

Before writing a single line of AL, produce an inventory:

1. Search the codebase for direct references to legacy objects:
   - Table `"Sales Price"` (ID 7002)
   - Table `"Sales Line Discount"` (ID 7004)
   - Codeunit `"Sales Price Calc. Mgt."` (ID 7000)
   - Page `"Sales Prices"` (ID 7002)
   - Page `"Sales Line Discounts"` (ID 7004)
2. Document each reference: object type, purpose, events subscribed, fields used.
3. Classify each as: Read, Write, UI Extension, or Business Logic.
4. Check if the Feature Update is already enabled in the target environment
   (`Feature Management` page -> search "New sales pricing experience").

Once the Feature Update is enabled it cannot be disabled.
Always test first in a sandbox environment.

See `references/audit-checklist.md` for a printable checklist.

---

### Step 1 - Map legacy objects to new objects

| Legacy object | New equivalent |
|---|---|
| `Sales Price` (7002) | `Price List Line` (7001) with `Price Type = Sale` |
| `Sales Line Discount` (7004) | `Price List Line` (7001) with `Amount Type = Discount` |
| `Sales Price Calc. Mgt.` (7000) | `Price Calculation - V16` codeunit + `Price Calculation` interface |
| `Customer Price Group` | `Price Source Type` enum value `Customer Price Group` |
| `Campaign` | `Price Source Type` enum value `Campaign` |

New key fields on `Price List Header` (7000):
- `Status`: Draft / Active / Inactive
- `Price Type`: Sale / Purchase
- `Source Type` / `Source No.`: who the list applies to
- `Allow Updating Defaults`: controls line-level overrides
- `Currency Code`: one per header (lines cannot mix currencies)

New key fields on `Price List Line` (7001):
- `Amount Type`: Price / Discount / Any
- `Asset Type`: Item / Resource / G/L Account / Item Discount Group
- `Asset No.`
- `Minimum Quantity`
- `Unit Price` / `Line Discount %` / `Unit Cost`

---

### Step 2 - Extend the Price Calculation engine (if needed)

Use this step only if your customization adds a new pricing method
(for example, contract pricing, tiered pricing, or location-based pricing).
For simple data migrations, skip to Step 3.

#### 2a. Implement the `Price Calculation` interface

Use the following snippet as a structural template only. Interface
signatures can vary between runtimes, so first inspect the interface in your
target symbols and then mirror that exact contract.

```al
codeunit 50100 "My Price Calculation" implements "Price Calculation"
{
    procedure FindPrice(Line: Variant; AmountType: Enum "Price Amount Type"): Boolean
    begin
        // Your custom price lookup logic here
    end;

    procedure ApplyPrice(AmountType: Enum "Price Amount Type"; var Line: Variant)
    begin
        // Apply the found price to the document line
    end;

    procedure ApplyDiscount(var Line: Variant)
    begin
        // Apply the found discount to the document line
    end;

    procedure CountDiscount(ShowAll: Boolean): Integer
    begin
        exit(0);
    end;

    procedure CountPrice(ShowAll: Boolean): Integer
    begin
        exit(0);
    end;

    procedure GetLine(var Line: Variant)
    begin
    end;

    procedure Init(NewSalesHeader: Variant; var PriceCalculationSetup: Record "Price Calculation Setup")
    begin
    end;

    procedure IsDiscountAllowed(FieldName: Text; var IsHandled: Boolean)
    begin
    end;

    procedure PickDiscount()
    begin
    end;

    procedure PickPrice()
    begin
    end;

    procedure ShowPrices(Line: Variant)
    begin
    end;
}
```

#### 2b. Extend the `Price Calculation Method` enum

```al
enumextension 50100 "My Price Method Ext" extends "Price Calculation Method"
{
    value(50100; "My Custom Method")
    {
        Caption = 'My Custom Method';
        Implementation = "Price Calculation" = "My Price Calculation";
    }
}
```

#### 2c. Register the new method in Price Calculation Setup

```al
codeunit 50101 "My Price Setup Subscriber"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Price Calculation Mgt.",
        'OnFindSupportedSetup', '', false, false)]
    local procedure OnFindSupportedSetup(
        var TempPriceCalculationSetup: Record "Price Calculation Setup" temporary)
    begin
        TempPriceCalculationSetup.InsertDuplicate(
            Enum::"Price Calculation Method"::"My Custom Method",
            Enum::"Price Type"::Sale,
            Enum::"Price Asset Type"::Item,
            Codeunit::"My Price Calculation",
            true);
    end;
}
```

See `references/custom-price-method.al` for a version-agnostic scaffold.

---

### Step 3 - Extend the `Price Source Type` enum (if needed)

Use this step if your customization introduces a new applies-to dimension
(for example, Location, Department, or Contract).

```al
enumextension 50102 "My Source Type Ext" extends "Price Source Type"
{
    value(50102; "My Location")
    {
        Caption = 'Location';
        Implementation = "Price Source" = "My Location Price Source";
    }
}

enumextension 50103 "My Sales Source Type Ext" extends "Sales Price Source Type"
{
    value(50102; "My Location") { }
}
```

The base extension points you typically need are:
- `Price Source Type` (Enum)
- `Sales Price Source Type` (Enum)
- `Price Source` (Interface)

Do not scaffold the `Price Source` implementation from memory. Inspect the
exact procedure contract in your target symbol package first and mirror that
signature set in your codeunit implementation.

---

### Step 4 - Handle data migration

When the Feature Update is enabled, BC can auto-migrate existing data from
tables 7002 and 7004 into the new Price List structure. However, custom
fields added by extensions must be handled separately.

#### Auto-migration (standard data)
BC creates a default price list from existing `Sales Price` and
`Sales Line Discount` records automatically on Feature Update enablement.
No code needed for standard fields.

#### Custom field migration
```al
codeunit 50104 "My Pricing Data Migration"
{
    [EventSubscriber(ObjectType::Codeunit, Codeunit::"Price List Management",
        '', '', false, false)]
    local procedure HandleVerifiedMigrationEvent(
        SalesPrice: Record "Sales Price";
        var PriceListLine: Record "Price List Line")
    begin
        PriceListLine."My Custom Field" := SalesPrice."My Legacy Field";
        PriceListLine.Modify(false);
    end;
}
```

See `references/migration-events.md` for the extensibility points and the
discovery workflow to confirm migration publishers with AL symbols.

---

### Step 5 - Update UI extensions

Legacy pages `"Sales Prices"` (7002) and `"Sales Line Discounts"` (7004)
are no longer shown when the new experience is active.
Replace page extensions targeting those pages with extensions on the current
price list UI objects in your runtime, typically:
- `"Sales Price List"` - main list page
- `"Price List Lines"` - lines subpage
- `"Price Worksheet"` - bulk update page

```al
pageextension 50201 "My Price List Line Ext" extends "Price List Lines"
{
    layout
    {
        addafter("Unit Price")
        {
            field("My Field"; Rec."My Field") { }
        }
    }
}
```

---

### Step 6 - Validation and testing checklist

Before deploying to production, verify:

- [ ] Feature Update enabled in sandbox - no compilation errors
- [ ] Standard sales orders calculate prices correctly (unit price, discount %)
- [ ] Customer-specific prices are found and applied
- [ ] Customer Price Group prices are applied when no customer-specific price exists
- [ ] Campaign prices activate/deactivate correctly by date
- [ ] Purchase price lists work independently from sales
- [ ] Custom price method appears in `Price Calculation Method` lookup
- [ ] Custom Source Type appears in Price List Header `Source Type` field
- [ ] Price list Status workflow: Draft -> Active -> (optionally) Inactive
- [ ] `Verify Lines` action finds no duplicates or conflicts
- [ ] `Price Worksheet` page correctly suggests and updates lines
- [ ] Custom fields migrated from legacy tables are present and correct
- [ ] AL tests pass (see `references/price-calculation-test.al`)

---

## Common Mistakes to Avoid

| Mistake | Correct approach |
|---|---|
| Subscribing to events on `Sales Price Calc. Mgt.` (7000) | Implement the `Price Calculation` interface instead |
| Reading directly from table 7002 or 7004 | Query `Price List Line` (7001) filtered by `Price Type` |
| Enabling Feature Update in production before testing | Always test in sandbox first - it is irreversible |
| Creating price list lines with different currency codes on same header | One currency code per `Price List Header` |
| Forgetting to set `Status = Active` on new price lists | Inactive lists are silently ignored by the engine |
| Extending `Sales Price Source Type` without also extending `Price Source Type` | Both enums must be extended simultaneously |

---

## Quick Reference: Key AL Objects

Use this table as a discovery shortcut, not as a substitute for symbol
verification in the target environment.

| Object | Type | ID | Purpose |
|---|---|---|---|
| `Price List Header` | Table | 7000 | Price list metadata and defaults |
| `Price List Line` | Table | 7001 | Individual price/discount rules |
| `Price Calculation` | Interface | - | Contract for all price engines |
| `Price Calculation Method` | Enum | - | Selects which engine to use |
| `Price Source Type` | Enum | - | Defines applies-to dimension |
| `Price Calculation - V16` | Codeunit | runtime-specific | Standard new-engine implementation |
| `Price Calculation Mgt.` | Codeunit | runtime-specific | Orchestrates engine selection |
| `Price List Management` | Codeunit | runtime-specific | Migration and management helpers |
| `Sales Price List` | Page | runtime-specific | Main UI for sales price lists |
| `Price List Lines` | Page | runtime-specific | Lines subpage |
| `Price Worksheet` | Page | runtime-specific | Bulk price update tool |

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [fernandoartalf](https://github.com/fernandoartalf)
- **Source:** [fernandoartalf/AL-Copilot-Skills-Collection](https://github.com/fernandoartalf/AL-Copilot-Skills-Collection)
- **License:** MIT
- **Homepage:** https://alcopilotskills.com/

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-fernandoartalf-al-copilot-skills-collection-bc-pricing-refactor
- Seller: https://agentstack.voostack.com/s/fernandoartalf
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
