Skip to content

[Event Requests] - table 36 "Sales Header" #29624

@exnihiloo

Description

@exnihiloo

Why do you need this change?

  1. Procedure UpdateSalesLinesByFieldNo, our extension must skip the SalesLine.LockTable() call, because the locking behavior causes contention and deadlocks in highΓÇævolume environments.

    Alternatives Evaluated:
    At the beggining of proceure there is an event OnBeforeUpdateSalesLinesByFieldNo with IsHandled parameter and it could exit the procedure, but then we would have to copy lots of standard code to that event just to avoid one line of the code.

    Justification for IsHandled:
    Avoiding table locks that cause blocking and deadlocks in multiΓÇæuser environments. Running custom update logic that does not require locking. Ensuring smoother concurrency for large sales order volumes. Without an IsHandled pattern, the standard lock always executes and interferes with our custom process.

    Performance Considerations:
    This procedure is triggered when certain header fields change, not continuously.Removing unnecessary locks improves concurrency and reduces blocking.

    Data Sensitivity Review:
    The event exposes only current Sales Header and IsHandled. These records do not contain sensitive data.

    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.

    Example of custom code:

    procedure UpdateSalesLinesByFieldNo(ChangedFieldNo: Integer; AskQuestion: Boolean)
       var
           "Field": Record "Field";
           JobTransferLine: Codeunit "Job Transfer Line";
           JobPostLine: Codeunit "Job Post-Line";
           Question: Text[250];
           IsHandled: Boolean;
           ShouldConfirmReservationDateConflict: Boolean;
       begin
           ...
    
           if AskQuestion then begin
               Question := StrSubstNo(Text031, Field."Field Caption");
               if GuiAllowed and not GetHideValidationDialog() then
                   if DIALOG.Confirm(Question, true) then begin
                       ShouldConfirmReservationDateConflict := ChangedFieldNo in [
                           FieldNo("Shipment Date"),
                           FieldNo("Shipping Agent Code"),
                           FieldNo("Shipping Agent Service Code"),
                           FieldNo("Shipping Time"),
                           FieldNo("Requested Delivery Date"),
                           FieldNo("Promised Delivery Date"),
                           FieldNo("Outbound Whse. Handling Time")
                       ];
                       OnUpdateSalesLinesByFieldNoOnAfterCalcShouldConfirmReservationDateConflict(Rec, ChangedFieldNo, ShouldConfirmReservationDateConflict);
                       if ShouldConfirmReservationDateConflict then
                           ConfirmReservationDateConflict();
                   end else
                       exit
           end;
    
           //SalesLine.LockTable(); Custom code
           if not Rec.Modify() then begin
               Session.LogMessage('0000G97', SalesHeaderCannotModifyLbl, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', SalesLinesCategoryLbl);
               exit;
           end;
    
           SalesLine.Reset();
    ...
    end;
  2. Procedure ConfirmCloseUnposted

    Standard procedure:

    procedure ConfirmCloseUnposted() Result: Boolean
    var
       InstructionMgt: Codeunit "Instruction Mgt.";
       IsHandled: Boolean;
    begin
       if SalesLinesExist() then begin
           IsHandled := false;
           OnConfirmCloseUnpostedOnSalesLinesExist(Rec, Result, IsHandled);
           if IsHandled then
               exit(Result);
    
           if InstructionMgt.IsUnpostedEnabledForRecord(Rec) then
               exit(InstructionMgt.ShowConfirm(DocumentNotPostedClosePageQst, InstructionMgt.QueryPostOnCloseCode()));
       end;
       exit(true)
    end;

    Customized procedure:

    procedure ConfirmCloseUnposted() Result: Boolean
    var
       InstructionMgt: Codeunit "Instruction Mgt.";
       IsHandled: Boolean;
    begin
       //Custom removed code
       exit(true)
     end;

    Alternatives Evaluated:
    We can not use OnConfirmCloseUnpostedOnSalesLinesExist because procedure SalesLinesExist would be already executed. The procedure it self has event OnBeforeSalesLinesExist where we could exit it, but this procedure is global and used in several places. We need new event publisher at the beginning of ConfirmCloseUnposted before checking SalesLinesExist. The publisher should contain these parameters Rec, Result, IsHandled or publisher with only Rec parameter would be sufficient for us, we could reimplement the rest using Single Instance codeunit.

    Justification for IsHandled:
    Avoid triggering procedure SalesLinesExist and setting filters to globalvariable.

    Performance Considerations:
    This procedure is triggered on few pages when they are closed.

    Data Sensitivity Review:
    The event exposes only current Sales Header, Result, IsHandled. These records do not contain sensitive data.

    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. Procedure UpdateSalesLinesByFieldNo

    procedure UpdateSalesLinesByFieldNo(ChangedFieldNo: Integer; AskQuestion: Boolean)
    var
       "Field": Record "Field";
       JobTransferLine: Codeunit "Job Transfer Line";
       JobPostLine: Codeunit "Job Post-Line";
       Question: Text[250];
       IsHandled: Boolean;
       ShouldConfirmReservationDateConflict: Boolean;
    begin
       ...
    
       if AskQuestion then begin
           Question := StrSubstNo(Text031, Field."Field Caption");
           if GuiAllowed and not GetHideValidationDialog() then
               if DIALOG.Confirm(Question, true) then begin
                   ShouldConfirmReservationDateConflict := ChangedFieldNo in [
                       FieldNo("Shipment Date"),
                       FieldNo("Shipping Agent Code"),
                       FieldNo("Shipping Agent Service Code"),
                       FieldNo("Shipping Time"),
                       FieldNo("Requested Delivery Date"),
                       FieldNo("Promised Delivery Date"),
                       FieldNo("Outbound Whse. Handling Time")
                   ];
                   OnUpdateSalesLinesByFieldNoOnAfterCalcShouldConfirmReservationDateConflict(Rec, ChangedFieldNo, ShouldConfirmReservationDateConflict);
                   if ShouldConfirmReservationDateConflict then
                       ConfirmReservationDateConflict();
               end else
                   exit
       end;
    
       OnBeforeSalesLineByFieldNoLock(Rec, Ishandled); //New Event
       if not Ishandled then
           SalesLine.LockTable(); 
       if not Rec.Modify() then begin
           Session.LogMessage('0000G97', SalesHeaderCannotModifyLbl, Verbosity::Warning, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', SalesLinesCategoryLbl);
           exit;
       end;
    
       SalesLine.Reset();
    ...
    end;
    
    [IntegrationEvent(false, false)]
    local procedure OnBeforeSalesLineByFieldNoLock(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
    end;
  2. Procedure ConfirmCloseUnposted

    procedure ConfirmCloseUnposted() Result: Boolean
    var
      InstructionMgt: Codeunit "Instruction Mgt.";
      IsHandled: Boolean;
    begin
      IsHandled := false;
      OnBeforeConfirmCloseUnposted(Rec, Result, IsHandled); //New Event
      if IsHandled then
          exit(Result);
      if SalesLinesExist() then begin
          IsHandled := false;
          OnConfirmCloseUnpostedOnSalesLinesExist(Rec, Result, IsHandled);
          if IsHandled then
              exit(Result);
    
          if InstructionMgt.IsUnpostedEnabledForRecord(Rec) then
              exit(InstructionMgt.ShowConfirm(DocumentNotPostedClosePageQst, InstructionMgt.QueryPostOnCloseCode()));
      end;
      exit(true)
    end;
    
    [IntegrationEvent(false, false)]
    local procedure OnBeforeConfirmCloseUnposted(var SalesHeader: Record "Sales Header"; var Result: Boolean; var IsHandled: Boolean)
    begin
    end;
    
    OR
    
    procedure ConfirmCloseUnposted() Result: Boolean
    var
      InstructionMgt: Codeunit "Instruction Mgt.";
      IsHandled: Boolean;
    begin
      OnBeforeConfirmCloseUnposted(Rec); //New Event
      if SalesLinesExist() then begin
          IsHandled := false;
          OnConfirmCloseUnpostedOnSalesLinesExist(Rec, Result, IsHandled);
          if IsHandled then
              exit(Result);
    
          if InstructionMgt.IsUnpostedEnabledForRecord(Rec) then
              exit(InstructionMgt.ShowConfirm(DocumentNotPostedClosePageQst, InstructionMgt.QueryPostOnCloseCode()));
      end;
      exit(true)
    end;
    
    [IntegrationEvent(false, false)]
    local procedure OnBeforeConfirmCloseUnposted(var SalesHeader: Record "Sales Header")
    begin
    end;

Internal work item: AB#619099

Metadata

Metadata

Assignees

No one assigned

    Labels

    SCMGitHub request for SCM areaevent-requestRequest for adding an event

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions