Skip to content

[Event Requests] - Codeunit 7000 "Sales Price Calc. Mgt." #29942

@exnihiloo

Description

@exnihiloo

Why do you need this change?

  1. Procedure CalcBestUnitPrice
    We request extending the integration event
    OnCalcBestUnitPriceOnBeforeCalcBestUnitPriceConvertPrice
    with additional by‑reference parameters and quantity context needed for custom pricing logic inside the unit‑price evaluation loop.
    Our extension adds custom logic inside the CalcBestUnitPrice repeat loop and relies on the standard local procedure IsInMinQty, which internally uses global variables Qty and QtyPerUOM.
    Currently, QtyPerUOM is not available to event subscribers, making it impossible to reuse standard logic without duplicating code.
    Additionally, we have modified the case true of evaluation logic, which requires updating BestSalesPrice and BestSalesPriceFound. Without passing these by reference, subscribers cannot safely influence the best‑price selection.
    Alternatives Evaluated : Reimplementing min‑qty logic - Duplicates standard behavior and risks divergence from base application.
    Performance Considerations: The event is already invoked inside the pricing loop. Adding parameters does not introduce additional iterations, calculations, or database access. Performance impact is negligible.
    Data Sensitivity Review: Only existing pricing calculation data (Sales Price, quantities) is exposed. No new sensitive data, no security or privacy implications.

  2. Procedure CalcBestUnitPrice
    Extend OnAfterCalcBestUnitPrice
    Our custom logic also executes after the repeat loop, but only when FoundSalesPrice = true.
    This flag is currently local to CalcBestUnitPrice and not exposed, forcing subscribers to infer state indirectly. Passing it explicitly ensures correct and maintainable post‑processing logic.
    Alternatives Evaluated: we could use OnBeforeCalcBestUnitPrice to pass FoundSalesPrice := SalesPrice.FindSet(); to single instance but to prevent run time errors it's better to have parameter on event.
    Performance Considerations: These events already execute as part of the pricing loop. Adding parameters does not introduce extra iterations, calculations, or database access. Performance impact is negligible.
    Data Sensitivity Review: The added parameters expose only existing pricing calculation context (quantities and interim price records). No new sensitive data is introduced, and there are no security or privacy implications.

  3. Procedure FindSalesLineDisc
    We request adding a new integration event inside the FindSalesLineDisc repeat loop, after copying discounts for Item Discount Groups, to allow extensions to apply additional filtering and copying logic safely.
    Our extension needs to apply additional filtering on FromSalesLineDisc and selectively copy records into ToSalesLineDisc after Item Discount Group discounts are copied, but still within the active repeat loop and current sales‑type context.
    Currently, there is no event at this exact execution point. Later events (OnAfterFindSalesLineDisc) are triggered after the entire loop has completed and do not allow modifying the intermediate dataset used during discount collection.
    Alternatives Evaluated: OnFindSalesLineDiscOnAfterSetFilters: Fires too early, before sales‑type, item, and item‑discount‑group logic is applied. OnAfterFindSalesLineDisc: Fires too late; the repeat loop has finished, and intermediate filtering decisions cannot be influenced.
    Performance Considerations: The event would execute only within the existing repeat loop and introduces no additional filtering or database access by default. Performance impact is negligible.
    Data Sensitivity Review: The event exposes only existing discount calculation records already in context. No new data, no security or privacy implications.

Code example:

procedure FindSalesLineDisc(var ToSalesLineDisc: Record "Sales Line Discount"; CustNo: Code[20]; ContNo: Code[20]; CustDiscGrCode: Code[20]; CampaignNo: Code[20]; ItemNo: Code[20]; ItemDiscGrCode: Code[20]; VariantCode: Code[10]; UOM: Code[10]; CurrencyCode: Code[10]; StartingDate: Date; ShowAll: Boolean)
    var
        FromSalesLineDisc: Record "Sales Line Discount";
        TempCampaignTargetGr: Record "Campaign Target Group" temporary;
        InclCampaigns: Boolean;
    begin
      ...
     repeat
                    FromSalesLineDisc.SetRange(Type, FromSalesLineDisc.Type::Item);
                    FromSalesLineDisc.SetRange(Code, ItemNo);
                    CopySalesDiscToSalesDisc(FromSalesLineDisc, ToSalesLineDisc);

                    if ItemDiscGrCode <> '' then begin
                        FromSalesLineDisc.SetRange(Type, FromSalesLineDisc.Type::"Item Disc. Group");
                        FromSalesLineDisc.SetRange(Code, ItemDiscGrCode);
                        CopySalesDiscToSalesDisc(FromSalesLineDisc, ToSalesLineDisc);
                    end;
      
                   OnFindSalesLineDiscOnAfterCopyItemDiscGroup(FromSalesLineDisc, ToSalesLineDisc); //New event 

                    if InclCampaigns then begin
                        InclCampaigns := TempCampaignTargetGr.Next() <> 0;
                        FromSalesLineDisc.SetRange("Sales Code", TempCampaignTargetGr."Campaign No.");
                    end;
                until not InclCampaigns;
        ...
  1. Procedure CopySalesDiscToSalesDisc
    We request making procedure CopySalesDiscToSalesDisc global, to allow reuse without code duplication, adding an integration event with IsHandled, enabling extensions to replace the default copy behavior. This is required to support custom discount filtering logic that cannot currently be implemented safely or cleanly with existing events.
    Business logic requires conditional copying, for example:
if (FromSalesLineDisc."Line Discount %" <> 0) or
   (FromSalesLineDisc."Rabatt (MW)" <> 0) then begin
    ToSalesLineDisc := FromSalesLineDisc;
    Insert();
end;

Alternatives Evaluated: one of the existing events allow replacing the core copy‑and‑insert behavior, which is the required customization point.
A standard event (without IsHandled) is insufficient because:

The base logic must be fully bypassed
Partial extension would cause duplicate or invalid discount lines
Custom logic applies business‑critical filtering rules that conflict with the standard “copy all” behavior
The ability to skip insertion entirely is essential to ensure correctness and avoid post‑cleanup workarounds.
Performance Considerations:
The procedure is executed during sales line discount calculation
Frequency depends on document size and discount complexity
The event adds only a boolean check and a method call
No additional database access is introduced
Data Sensitivity Review:
Only Sales Line Discount records already in scope are exposed
No new data or cross‑company access is introduced
No personal or sensitive data involved
Multi-Extension Interaction: Extensions that rely on this event must coordinate via dependency declarations. Partners can use EventSubscriberInstance = Manual to control execution order. The pattern is consistent with other core IsHandled events, so developers are familiar with the implications.

Describe the request

  1. Event OnCalcBestUnitPriceOnBeforeCalcBestUnitPriceConvertPrice
    before modification:
[IntegrationEvent(false, false)]
    local procedure OnCalcBestUnitPriceOnBeforeCalcBestUnitPriceConvertPrice(var SalesPrice: Record "Sales Price"; Qty: Decimal; var IsHandled: Boolean; var Item: Record Item)
    begin
    end;

after modification :

[IntegrationEvent(false, false)]
    local procedure OnCalcBestUnitPriceOnBeforeCalcBestUnitPriceConvertPrice(var SalesPrice: Record "Sales Price"; Qty: Decimal; var IsHandled: Boolean; var Item: Record Item; QtyPerUOM: Decimal; var BestSalesPrice: Record "Sales Price"; var BestSalesPriceFound: Boolean)
    begin
    end;
  1. Event OnAfterCalcBestUnitPrice
    before modification:
[IntegrationEvent(false, false)]
    local procedure OnAfterCalcBestUnitPrice(var SalesPrice: Record "Sales Price"; var BestSalesPrice: Record "Sales Price")
    begin
    end;

after modification :

[IntegrationEvent(false, false)]
    local procedure OnAfterCalcBestUnitPrice(var SalesPrice: Record "Sales Price"; var BestSalesPrice: Record "Sales Price"; FoundSalesPrice: Boolean)
    begin
    end;
  1. New event OnFindSalesLineDiscOnAfterCopyItemDiscGroup
[IntegrationEvent(false, false)]
    local procedure OnFindSalesLineDiscOnAfterCopyItemDiscGroup(var FromSalesLineDisc: Record "Sales Line Discount"; var ToSalesLineDisc: Record "Sales Line Discount")
    begin
    end;
  1. New event OnBeforeCopySalesDiscToSalesDisc and change procedure to global
procedure CopySalesDiscToSalesDisc(var FromSalesLineDisc: Record "Sales Line Discount"; var ToSalesLineDisc: Record "Sales Line Discount")
   var
       IsHandled: Boolean;
begin
      if FromSalesLineDisc.FindSet() then
          repeat 
              OnBeforeCopySalesDiscToSalesDisc(IsHandled, FromSalesLineDisc, ToSalesLineDisc);
               if not IsHandled then begin
                  ToSalesLineDisc := FromSalesLineDisc;
                  ToSalesLineDisc.Insert();
               end;
          until FromSalesLineDisc.Next() = 0;
    end;
[IntegrationEvent(false, false)]
    local procedure OnBeforeCopySalesDiscToSalesDisc(var FromSalesLineDisc: Record "Sales Line Discount"; var ToSalesLineDisc: Record "Sales Line Discount"; var IsHandled: Boolean)
    begin
    end;

Metadata

Metadata

Assignees

No one assigned

    Labels

    missing-infoThe issue misses information that prevents it from completion.

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions