Codeunit 550 VAT Rate Change Conversion

App
Base Application
Namespace
Microsoft.Finance.VAT.RateChange
Versions
17-28

Procedures, 20Events, 68Obsolete, 5

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Finance/VAT/RateChange/VATRateChangeConversion.Codeunit.al2055 lines, Copyright (c) Microsoft Corporation. MIT

// ------------------------------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// ------------------------------------------------------------------------------------------------
namespace Microsoft.Finance.VAT.RateChange;

using Microsoft.Finance.Currency;
using Microsoft.Finance.GeneralLedger.Account;
using Microsoft.Finance.GeneralLedger.Journal;
using Microsoft.Finance.GeneralLedger.Setup;
using Microsoft.Finance.VAT.Setup;
using Microsoft.Foundation.UOM;
using Microsoft.Inventory.Item;
using Microsoft.Inventory.Journal;
using Microsoft.Inventory.Requisition;
using Microsoft.Inventory.Tracking;
using Microsoft.Projects.Project.Journal;
using Microsoft.Projects.Resources.Journal;
using Microsoft.Projects.Resources.Resource;
using Microsoft.Purchases.Document;
using Microsoft.Sales.Document;
using Microsoft.Sales.FinanceCharge;
using Microsoft.Sales.Reminder;
using Microsoft.Warehouse.Request;
using System.Reflection;

/// <summary>
/// Handles VAT rate change conversion operations across all Business Central master data and transactions.
/// Processes items, resources, G/L accounts, journals, and sales/purchase documents according to setup configuration.
/// </summary>
codeunit 550 "VAT Rate Change Conversion"
{
    Permissions = TableData "VAT Rate Change Log Entry" = i;

    trigger OnRun()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeOnRun(IsHandled);
        if IsHandled then
            exit;

        if not VATRateChangeSetup.Get() then begin
            VATRateChangeSetup.Init();
            VATRateChangeSetup.Insert();
        end;

        Convert();
    end;

    var
        VATRateChangeSetup: Record "VAT Rate Change Setup";
        VATRateChangeConversion: Record "VAT Rate Change Conversion";
        UOMMgt: Codeunit "Unit of Measure Management";
        ProgressWindow: Dialog;
#pragma warning disable AA0074
#pragma warning disable AA0470
        Text0001: Label 'Progressing Table #1#####################################  ';
        Text0002: Label 'Progressing Record #2############ of #3####################  ';
        Text0004: Label 'Order line %1 has a drop shipment purchasing code. Update the order manually.';
        Text0005: Label 'Order line %1 has a special order purchasing code. Update the order manually.';
#pragma warning restore AA0470
        Text0006: Label 'The order has a partially shipped line with link to a WHSE document. Update the order manually.';
        Text0007: Label 'There is nothing to convert. The outstanding quantity is zero.';
#pragma warning disable AA0470
        Text0008: Label 'There must be an entry in the %1 table for the combination of VAT business posting group %2 and VAT product posting group %3.';
        Text0009: Label 'Conversion cannot be performed before %1 is set to true.';
#pragma warning restore AA0470
        Text0011: Label 'Documents that have posted prepayment must be converted manually.';
#pragma warning disable AA0470
        Text0012: Label 'This line %1 has been split into two lines. The outstanding quantity will be on the new line.';
        Text0013: Label 'This line %1 has been added. It contains the outstanding quantity from line %2.';
        Text0014: Label 'The order line %1 of type %2 have been partial Shipped/Invoiced . Update the order manually.';
#pragma warning restore AA0470
        Text0015: Label 'A defined conversion does not exist. Define the conversion.';
        Text0016: Label 'Defined tables for conversion do not exist.';
#pragma warning disable AA0470
        Text0017: Label 'This line %1 will be split into two lines. The outstanding quantity will be on the new line.';
#pragma warning restore AA0470
        Text0018: Label 'This document is linked to an assembly order. You must convert the document manually.';
#pragma warning restore AA0074

    local procedure Convert()
    var
        GenProductPostingGroup: Record "Gen. Product Posting Group";
        TempGenProductPostingGroup: Record "Gen. Product Posting Group" temporary;
    begin
        OnBeforeConvert(VATRateChangeSetup);

        VATRateChangeSetup.TestField("VAT Rate Change Tool Completed", false);
        if VATRateChangeConversion.IsEmpty() then
            Error(Text0015);
        if not AreTablesSelected() then
            Error(Text0016);
        TestVATPostingSetup();
        ProgressWindow.Open(Text0001 + Text0002);
        OnConvertOnBeforeStartConvert(VATRateChangeSetup);
        ProgressWindow.Update();
        UpdateTable(
          Database::"Gen. Product Posting Group",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Gen. Prod. Post. Groups"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Gen. Prod. Post. Groups"));
        TempGenProductPostingGroup.DeleteAll();
        if GenProductPostingGroup.Find('-') then
            repeat
                TempGenProductPostingGroup := GenProductPostingGroup;
                TempGenProductPostingGroup.Insert();
                GenProductPostingGroup."Auto Insert Default" := false;
                GenProductPostingGroup.Modify();
            until GenProductPostingGroup.Next() = 0;
        UpdateItem();
        UpdateResource();
        UpdateGLAccount();
        UpdatePurchase();
        UpdateSales();
        UpdateTables();

        OnBeforeFinishConvert(VATRateChangeSetup, ProgressWindow);

        GenProductPostingGroup.DeleteAll();
        if TempGenProductPostingGroup.Find('-') then
            repeat
                GenProductPostingGroup := TempGenProductPostingGroup;
                GenProductPostingGroup.Insert();
                TempGenProductPostingGroup.Delete();
            until TempGenProductPostingGroup.Next() = 0;

        ProgressWindow.Close();

        if VATRateChangeSetup."Perform Conversion" then begin
            VATRateChangeSetup."VAT Rate Change Tool Completed" := true;
            VATRateChangeSetup.Modify();
            VATRateChangeConversion.Reset();
            if VATRateChangeConversion.FindSet(true) then
                repeat
                    VATRateChangeConversion."Converted Date" := WorkDate();
                    VATRateChangeConversion.Modify();
                until VATRateChangeConversion.Next() = 0;
        end;

        OnAfterConvert(VATRateChangeSetup);
    end;

    local procedure UpdateTables()
    begin
        UpdateTable(
          Database::"Item Templ.",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Item Templates"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Item Templates"));
        UpdateTable(
          Database::"Item Charge",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Item Charges"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Item Charges"));
        UpdateTable(
          Database::"Gen. Journal Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Gen. Journal Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Gen. Journal Lines"));
        UpdateTable(
          Database::"Gen. Jnl. Allocation",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Gen. Journal Allocation"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Gen. Journal Allocation"));
        UpdateTable(
          Database::"Standard General Journal Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Std. Gen. Jnl. Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Std. Gen. Jnl. Lines"));
        UpdateTable(
          Database::"Res. Journal Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Res. Journal Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Res. Journal Lines"));
        UpdateTable(
          Database::"Job Journal Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Job Journal Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Job Journal Lines"));
        UpdateTable(
          Database::"Requisition Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Requisition Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Requisition Lines"));
        UpdateTable(
          Database::"Standard Item Journal Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Std. Item Jnl. Lines"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Std. Item Jnl. Lines"));
        UpdateTable(
          Database::"Reminder Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Reminders"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Reminders"));
        UpdateTable(
          Database::"Finance Charge Memo Line",
          ConvertVATProdPostGrp(VATRateChangeSetup."Update Finance Charge Memos"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Finance Charge Memos"));

        OnAfterUpdateTables(VATRateChangeSetup);
    end;

    local procedure TestVATPostingSetup()
    var
        VATPostingSetupOld: Record "VAT Posting Setup";
        VATPostingSetupNew: Record "VAT Posting Setup";
        VATRateChangeConversion: Record "VAT Rate Change Conversion";
    begin
        VATRateChangeConversion.SetRange(Type, VATRateChangeConversion.Type::"VAT Prod. Posting Group");
        if VATRateChangeConversion.FindSet() then
            repeat
                VATPostingSetupOld.SetRange("VAT Prod. Posting Group", VATRateChangeConversion."From Code");
                OnTestVATPostingSetupOnAfterVATPostingSetupOldSetFilters(VATPostingSetupOld, VATRateChangeSetup);
                if VATPostingSetupOld.FindSet() then
                    repeat
                        if not VATPostingSetupNew.Get(VATPostingSetupOld."VAT Bus. Posting Group", VATRateChangeConversion."To Code") then
                            Error(
                              Text0008,
                              VATPostingSetupNew.TableCaption(),
                              VATPostingSetupOld."VAT Bus. Posting Group",
                              VATRateChangeConversion."To Code");
                        if VATPostingSetupOld."VAT Identifier" <> '' then
                            VATPostingSetupNew.TestField("VAT Identifier")
                    until VATPostingSetupOld.Next() = 0;
            until VATRateChangeConversion.Next() = 0;
    end;

    local procedure UpdateItem()
    var
        Item: Record Item;
        RecRef: RecordRef;
        IsHandled: Boolean;
    begin
        ProgressWindow.Update(1, Item.TableCaption());

        IsHandled := false;
        OnBeforeUpdateItem(Item, VATRateChangeSetup, IsHandled);
        if IsHandled then
            exit;

        if VATRateChangeSetup."Item Filter" = '' then
            UpdateTable(Database::Item, ConvertVATProdPostGrp(VATRateChangeSetup."Update Items"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Items"))
        else begin
            Item.SetFilter("No.", VATRateChangeSetup."Item Filter");
            if Item.Find('-') then
                repeat
                    RecRef.GetTable(Item);
                    UpdateRec(RecRef, ConvertVATProdPostGrp(VATRateChangeSetup."Update Items"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Items"));
                until Item.Next() = 0;
        end;
    end;

    local procedure UpdateGLAccount()
    var
        GLAccount: Record "G/L Account";
        RecRef: RecordRef;
        IsHandled: Boolean;
    begin
        ProgressWindow.Update(1, GLAccount.TableCaption());

        IsHandled := false;
        OnBeforeUpdateGLAccount(GLAccount, VATRateChangeSetup, IsHandled);
        if IsHandled then
            exit;

        if VATRateChangeSetup."Account Filter" = '' then
            UpdateTable(Database::"G/L Account", ConvertVATProdPostGrp(VATRateChangeSetup."Update G/L Accounts"), ConvertGenProdPostGrp(VATRateChangeSetup."Update G/L Accounts"))
        else begin
            GLAccount.SetFilter("No.", VATRateChangeSetup."Account Filter");
            if GLAccount.Find('-') then
                repeat
                    RecRef.GetTable(GLAccount);
                    UpdateRec(RecRef, ConvertVATProdPostGrp(VATRateChangeSetup."Update G/L Accounts"), ConvertGenProdPostGrp(VATRateChangeSetup."Update G/L Accounts"));
                until GLAccount.Next() = 0;
        end;
    end;

    local procedure UpdateResource()
    var
        Resource: Record Resource;
        RecRef: RecordRef;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeUpdateResource(Resource, VATRateChangeSetup, IsHandled);
        if IsHandled then
            exit;

        if VATRateChangeSetup."Resource Filter" = '' then
            UpdateTable(Database::Resource, ConvertVATProdPostGrp(VATRateChangeSetup."Update Resources"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Resources"))
        else begin
            Resource.SetFilter("No.", VATRateChangeSetup."Resource Filter");
            if Resource.Find('-') then
                repeat
                    RecRef.GetTable(Resource);
                    UpdateRec(RecRef, ConvertVATProdPostGrp(VATRateChangeSetup."Update Resources"), ConvertGenProdPostGrp(VATRateChangeSetup."Update Resources"));
                until Resource.Next() = 0;
        end;
    end;

    /// <summary>
    /// Determines whether VAT product posting groups should be converted based on the update option.
    /// </summary>
    /// <param name="UpdateOption">Option value indicating which posting groups to update</param>
    /// <returns>True if VAT product posting groups should be converted</returns>
    procedure ConvertVATProdPostGrp(UpdateOption: Option): Boolean
    var
        DummyVATRateChangeSetup: Record "VAT Rate Change Setup";
    begin
        if UpdateOption in [DummyVATRateChangeSetup."Update Items"::"VAT Prod. Posting Group",
                            DummyVATRateChangeSetup."Update Items"::Both]
        then
            exit(true);
        exit(false);
    end;

    /// <summary>
    /// Determines whether general product posting groups should be converted based on the update option.
    /// </summary>
    /// <param name="UpdateOption">Option value indicating which posting groups to update</param>
    /// <returns>True if general product posting groups should be converted</returns>
    procedure ConvertGenProdPostGrp(UpdateOption: Option): Boolean
    var
        DummyVATRateChangeSetup: Record "VAT Rate Change Setup";
    begin
        if UpdateOption in [DummyVATRateChangeSetup."Update Items"::"Gen. Prod. Posting Group",
                            DummyVATRateChangeSetup."Update Items"::Both]
        then
            exit(true);
        exit(false);
    end;

    /// <summary>
    /// Updates all records in a specified table with new VAT or general product posting groups.
    /// </summary>
    /// <param name="TableID">ID of the table to update</param>
    /// <param name="ConvertVATProdPostingGroup">Whether to convert VAT product posting groups</param>
    /// <param name="ConvertGenProdPostingGroup">Whether to convert general product posting groups</param>
    procedure UpdateTable(TableID: Integer; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean)
    var
        RecRef: RecordRef;
        I: Integer;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeUpdateTable(TableID, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup, IsHandled);
        if IsHandled then
            exit;

        if not ConvertVATProdPostingGroup and not ConvertGenProdPostingGroup then
            exit;
        RecRef.Open(TableID);
        ProgressWindow.Update(1, Format(RecRef.Caption));
        I := 0;
        ProgressWindow.Update(3, RecRef.Count);
        if RecRef.Find('-') then
            repeat
                I := I + 1;
                ProgressWindow.Update(2, I);
                UpdateRec(RecRef, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup);
            until RecRef.Next() = 0;
    end;

    /// <summary>
    /// Updates posting group fields in a specific record and creates log entries for the changes.
    /// </summary>
    /// <param name="RecRef">Record reference to update</param>
    /// <param name="ConvertVATProdPostingGroup">Whether to convert VAT product posting groups</param>
    /// <param name="ConvertGenProdPostingGroup">Whether to convert general product posting groups</param>
    procedure UpdateRec(var RecRef: RecordRef; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean)
    var
        "Field": Record "Field";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        FldRef: FieldRef;
        GenProdPostingGroupConverted: Boolean;
        VATProdPostingGroupConverted: Boolean;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeUpdateRec(RecRef, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup, IsHandled);
        if IsHandled then
            exit;

        VATRateChangeLogEntry.Init();
        VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
        VATRateChangeLogEntry."Table ID" := RecRef.Number;
        Field.SetRange(TableNo, RecRef.Number);
        Field.SetFilter(ObsoleteState, '<>%1', Field.ObsoleteState::Removed);
        Field.SetRange(RelationTableNo, Database::"Gen. Product Posting Group");
        Field.SetRange(Type, Field.Type::Code);
        if Field.Find('+') then
            repeat
                FldRef := RecRef.Field(Field."No.");
                GenProdPostingGroupConverted := false;
                if ConvertGenProdPostingGroup then
                    if VATRateChangeConversion.Get(VATRateChangeConversion.Type::"Gen. Prod. Posting Group", Format(FldRef.Value())) then begin
                        VATRateChangeLogEntry."Old Gen. Prod. Posting Group" := FldRef.Value();
                        IsHandled := false;
                        OnUpdateRecOnBeforeValidateGenProdPostingGroup(RecRef, FldRef, VATRateChangeConversion, IsHandled);
                        if not IsHandled then
                            FldRef.Validate(VATRateChangeConversion."To Code");
                        VATRateChangeLogEntry."New Gen. Prod. Posting Group" := FldRef.Value();
                        GenProdPostingGroupConverted := true;
                    end;
                if not GenProdPostingGroupConverted then begin
                    VATRateChangeLogEntry."Old Gen. Prod. Posting Group" := FldRef.Value();
                    VATRateChangeLogEntry."New Gen. Prod. Posting Group" := FldRef.Value();
                end;
            until Field.Next(-1) = 0;

        Field.SetRange(RelationTableNo, Database::"VAT Product Posting Group");
        if Field.Find('+') then
            repeat
                FldRef := RecRef.Field(Field."No.");
                VATProdPostingGroupConverted := false;
                if ConvertVATProdPostingGroup then
                    if VATRateChangeConversion.Get(VATRateChangeConversion.Type::"VAT Prod. Posting Group", Format(FldRef.Value())) then begin
                        VATRateChangeLogEntry."Old VAT Prod. Posting Group" := FldRef.Value();
                        FldRef.Validate(VATRateChangeConversion."To Code");
                        VATRateChangeLogEntry."New VAT Prod. Posting Group" := FldRef.Value();
                        VATProdPostingGroupConverted := true;
                    end;
                if not VATProdPostingGroupConverted then begin
                    VATRateChangeLogEntry."Old VAT Prod. Posting Group" := FldRef.Value();
                    VATRateChangeLogEntry."New VAT Prod. Posting Group" := FldRef.Value();
                end;
            until Field.Next(-1) = 0;

        VATRateChangeSetup.Get();
        if VATRateChangeSetup."Perform Conversion" then begin
            OnUpdateRecOnBeforeRecRefModify(RecRef, GenProdPostingGroupConverted, VATProdPostingGroupConverted);
            RecRef.Modify();
            OnUpdateRecOnAfterRecRefModify(RecRef, GenProdPostingGroupConverted, VATProdPostingGroupConverted);
            VATRateChangeLogEntry.Converted := true;
        end;
        if (VATRateChangeLogEntry."New Gen. Prod. Posting Group" <> VATRateChangeLogEntry."Old Gen. Prod. Posting Group") or
           (VATRateChangeLogEntry."New VAT Prod. Posting Group" <> VATRateChangeLogEntry."Old VAT Prod. Posting Group")
        then
            WriteLogEntry(VATRateChangeLogEntry);
    end;

    /// <summary>
    /// Inserts a log entry for VAT rate change conversion tracking.
    /// </summary>
    /// <param name="VATRateChangeLogEntry">Log entry record to insert</param>
    procedure WriteLogEntry(VATRateChangeLogEntry: Record "VAT Rate Change Log Entry")
    begin
        if VATRateChangeLogEntry.Converted then
            VATRateChangeLogEntry."Converted Date" := WorkDate()
        else
            if VATRateChangeLogEntry.Description = '' then
                VATRateChangeLogEntry.Description := StrSubstNo(Text0009, VATRateChangeSetup.FieldCaption("Perform Conversion"));
        VATRateChangeLogEntry.Insert();
    end;

    local procedure UpdateSales()
    var
        SalesHeader: Record "Sales Header";
        SalesHeader2: Record "Sales Header";
        SalesLine: Record "Sales Line";
        SalesLineOld: Record "Sales Line";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        SalesHeaderStatusChanged: Boolean;
        NewVATProdPotingGroup: Code[20];
        NewGenProdPostingGroup: Code[20];
        ConvertVATProdPostingGroup: Boolean;
        ConvertGenProdPostingGroup: Boolean;
        RoundingPrecision: Decimal;
        IsHandled: Boolean;
        IsModified: Boolean;
    begin
        ProgressWindow.Update(1, SalesHeader.TableCaption());
        ConvertVATProdPostingGroup := ConvertVATProdPostGrp(VATRateChangeSetup."Update Sales Documents");
        ConvertGenProdPostingGroup := ConvertGenProdPostGrp(VATRateChangeSetup."Update Sales Documents");
        if not ConvertVATProdPostingGroup and not ConvertGenProdPostingGroup then
            exit;

        IsHandled := false;
        OnBeforeUpdateSales(VATRateChangeSetup, IsHandled, SalesHeader);
        if IsHandled then
            exit;

        SalesHeader.SetFilter(
          "Document Type", '%1..%2|%3', SalesHeader."Document Type"::Quote, SalesHeader."Document Type"::Invoice,
          SalesHeader."Document Type"::"Blanket Order");
        OnUpdateSalesOnAfterSalesHeaderSetFilters(VATRateChangeSetup, SalesHeader);
        if SalesHeader.Find('-') then
            repeat
                SalesHeaderStatusChanged := false;
                if CanUpdateSales(SalesHeader, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup) then begin
                    if VATRateChangeSetup."Ignore Status on Sales Docs." then
                        if SalesHeader.Status <> SalesHeader.Status::Open then begin
                            SalesHeader2 := SalesHeader;
                            SalesHeader.Status := SalesHeader.Status::Open;
                            SalesHeader.Modify();
                            SalesHeaderStatusChanged := true;
                            OnUpdateSalesOnAfterOpenSalesHeader(SalesHeader);
                        end;
                    if SalesHeader.Status = SalesHeader.Status::Open then begin
                        SalesLine.SetRange("Document Type", SalesHeader."Document Type");
                        SalesLine.SetRange("Document No.", SalesHeader."No.");
                        OnUpdateSalesOnAfterSalesLineSetFilters(VATRateChangeSetup, SalesHeader, SalesLine);
                        if SalesLine.FindSet() then
                            repeat
                                if LineInScope(
                                     SalesLine."Gen. Prod. Posting Group", SalesLine."VAT Prod. Posting Group", ConvertGenProdPostingGroup,
                                     ConvertVATProdPostingGroup)
                                then
                                    if (SalesLine."Shipment No." = '') and (SalesLine."Return Receipt No." = '') and
                                       IncludeSalesLine(SalesLine.Type, SalesLine."No.")
                                    then
                                        if DoUpdateSalesLine(SalesLine) then begin
                                            OnUpdateSalesOnBeforeChangeSalesLine(SalesLine);

                                            RecRef.GetTable(SalesLine);
                                            if SalesHeader."Prices Including VAT" then
                                                SalesLineOld := SalesLine;

                                            UpdateRec(
                                              RecRef, ConvertVATProdPostGrp(VATRateChangeSetup."Update Sales Documents"),
                                              ConvertGenProdPostGrp(VATRateChangeSetup."Update Sales Documents"));

                                            SalesLine.Find();
                                            IsModified := false;
                                            if SalesHeader."Prices Including VAT" and VATRateChangeSetup."Perform Conversion" and
                                               (SalesLine."VAT %" <> SalesLineOld."VAT %") and
                                               UpdateUnitPriceInclVAT(SalesLine.Type)
                                            then begin
                                                RoundingPrecision := GetRoundingPrecision(SalesHeader."Currency Code");
                                                SalesLine.Validate(
                                                  "Unit Price",
                                                  Round(
                                                    SalesLineOld."Unit Price" * (100 + SalesLine."VAT %") / (100 + SalesLineOld."VAT %"), RoundingPrecision));
                                                IsModified := true;
                                            end;
                                            if SalesLine."Prepayment %" <> 0 then begin
                                                SalesLine.UpdatePrepmtSetupFields();
                                                IsModified := true;
                                            end;
                                            OnUpdateSalesOnBeforeModifySalesLine(SalesLine, IsModified);
                                            if IsModified then
                                                SalesLine.Modify(true);
                                            OnUpdateSalesOnAfterModifySalesLine(SalesLine, IsModified, VATRateChangeSetup, SalesHeader, SalesLineOld);
                                        end else
                                            if VATRateChangeSetup."Perform Conversion" and (SalesLine."Outstanding Quantity" <> 0) then begin
                                                NewVATProdPotingGroup := SalesLine."VAT Prod. Posting Group";
                                                NewGenProdPostingGroup := SalesLine."Gen. Prod. Posting Group";
                                                if VATRateChangeConversion.Get(
                                                     VATRateChangeConversion.Type::"VAT Prod. Posting Group", SalesLine."VAT Prod. Posting Group")
                                                then
                                                    NewVATProdPotingGroup := VATRateChangeConversion."To Code";
                                                if VATRateChangeConversion.Get(
                                                     VATRateChangeConversion.Type::"Gen. Prod. Posting Group", SalesLine."Gen. Prod. Posting Group")
                                                then
                                                    NewGenProdPostingGroup := VATRateChangeConversion."To Code";
                                                AddNewSalesLine(SalesLine, NewVATProdPotingGroup, NewGenProdPostingGroup);
                                            end else begin
                                                RecRef.GetTable(SalesLine);
                                                InitVATRateChangeLogEntry(
                                                  VATRateChangeLogEntry, RecRef, SalesLine."Outstanding Quantity", SalesLine."Line No.");
                                                VATRateChangeLogEntry.UpdateGroups(
                                                  SalesLine."Gen. Prod. Posting Group", SalesLine."Gen. Prod. Posting Group",
                                                  SalesLine."VAT Prod. Posting Group", SalesLine."VAT Prod. Posting Group");
                                                WriteLogEntry(VATRateChangeLogEntry);
                                            end;
                            until SalesLine.Next() = 0;
                        OnUpdateSalesOnAfterUpdateSalesLines(VATRateChangeSetup, SalesHeader, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup);
                    end;
                    if SalesHeaderStatusChanged then begin
                        SalesHeader.Status := SalesHeader2.Status;
                        SalesHeader.Modify();
                        OnUpdateSalesOnAfterResetSalesHeaderStatus(SalesHeader);
                    end;
                end;
            until SalesHeader.Next() = 0;
    end;

    local procedure CanUpdateSales(SalesHeader: Record "Sales Header"; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean) Result: Boolean
    var
        SalesLine: Record "Sales Line";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        DescriptionTxt: Text[250];
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCanUpdateSales(SalesHeader, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup, IsHandled, Result);
        if IsHandled then
            exit(Result);

        SalesLine.SetRange("Document Type", SalesHeader."Document Type");
        SalesLine.SetRange("Document No.", SalesHeader."No.");
        OnCanUpdateSalesOnAfterSalesLineSetFilters(SalesLine);
        if SalesLine.FindSet() then
            repeat
                DescriptionTxt := '';
                if LineInScope(SalesLine."Gen. Prod. Posting Group", SalesLine."VAT Prod. Posting Group", ConvertGenProdPostingGroup, ConvertVATProdPostingGroup) then begin
                    if SalesLine."Drop Shipment" and (SalesLine."Purchase Order No." <> '') then
                        DescriptionTxt := StrSubstNo(Text0004, SalesLine."Line No.");
                    if SalesLine."Special Order" and (SalesLine."Special Order Purchase No." <> '') then
                        DescriptionTxt := StrSubstNo(Text0005, SalesLine."Line No.");
                    CheckSalesLinePartlyShipped(SalesLine, DescriptionTxt);
                    if (SalesLine."Outstanding Quantity" <> SalesLine.Quantity) and (SalesLine.Type = SalesLine.Type::"Charge (Item)") then
                        DescriptionTxt := StrSubstNo(Text0014, SalesLine."Line No.", SalesLine.Type::"Charge (Item)");
                    if SalesLine."Prepmt. Amount Inv. Incl. VAT" <> 0 then
                        DescriptionTxt := Text0011;
                    if SalesLine."Qty. to Assemble to Order" <> 0 then
                        DescriptionTxt := Text0018;
                    OnCanUpdateSalesOnAfterLoopIteration(DescriptionTxt, SalesLine);
                end;
            until (SalesLine.Next() = 0) or (DescriptionTxt <> '');
        if DescriptionTxt = '' then
            exit(true);

        RecRef.GetTable(SalesHeader);
        VATRateChangeLogEntry.Init();
        VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
        VATRateChangeLogEntry."Table ID" := RecRef.Number;
        VATRateChangeLogEntry.Description := DescriptionTxt;
        WriteLogEntry(VATRateChangeLogEntry);
    end;

    local procedure CheckSalesLinePartlyShipped(var SalesLine: Record "Sales Line"; var DescriptionTxt: Text[250])
    var
        WhseValidateSourceLine: Codeunit "Whse. Validate Source Line";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckSalesLinePartlyShipped(SalesLine, DescriptionTxt, IsHandled);
        if IsHandled then
            exit;

        if (SalesLine."Outstanding Quantity" <> SalesLine.Quantity) and
               WhseValidateSourceLine.WhseLinesExist(Database::"Sales Line", SalesLine."Document Type".AsInteger(), SalesLine."Document No.", SalesLine."Line No.", 0, SalesLine.Quantity)
        then
            DescriptionTxt := Text0006;
    end;

    /// <summary>
    /// Creates a new sales line with updated posting groups when document lines need to be split during conversion.
    /// Handles complex scenarios with reservations, item charges, and blanket orders.
    /// </summary>
    /// <param name="SalesLine">Original sales line to split</param>
    /// <param name="VATProdPostingGroup">New VAT product posting group for the new line</param>
    /// <param name="GenProdPostingGroup">New general product posting group for the new line</param>
    procedure AddNewSalesLine(SalesLine: Record "Sales Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20])
    var
        NewSalesLine: Record "Sales Line";
        OldSalesLine: Record "Sales Line";
        SalesHeader: Record "Sales Header";
        OldReservationEntry: Record "Reservation Entry";
        NewReservationEntry: Record "Reservation Entry";
        OldItemChargeAssignmentSales: Record "Item Charge Assignment (Sales)";
        NewItemChargeAssignmentSales: Record "Item Charge Assignment (Sales)";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        NewLineNo: Integer;
        QtyRemainder: Decimal;
        AmountRemainder: Decimal;
        RoundingPrecision: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeAddNewSalesLine(SalesLine, VATProdPostingGroup, GenProdPostingGroup, IsHandled);
        if not IsHandled then begin
            if not GetNextSalesLineNo(SalesLine, NewLineNo) then
                exit;
            InitNewSalesLineFromSalesLine(NewSalesLine, SalesLine, NewLineNo);
            if (GenProdPostingGroup <> '') and ConvertGenProdPostGrp(VATRateChangeSetup."Update Sales Documents") then
                NewSalesLine.Validate("Gen. Prod. Posting Group", GenProdPostingGroup);
            if (VATProdPostingGroup <> '') and ConvertVATProdPostGrp(VATRateChangeSetup."Update Sales Documents") then
                NewSalesLine.Validate("VAT Prod. Posting Group", VATProdPostingGroup);
            NewSalesLine.Validate(Quantity, SalesLine."Outstanding Quantity");
            NewSalesLine.Validate("Qty. to Ship", SalesLine."Qty. to Ship");
            NewSalesLine.Validate("Return Qty. to Receive", SalesLine."Return Qty. to Receive");
            if Abs(SalesLine."Qty. to Invoice") > (Abs(SalesLine."Quantity Shipped") - Abs(SalesLine."Quantity Invoiced")) then
                NewSalesLine.Validate(
                  NewSalesLine."Qty. to Invoice", SalesLine."Qty. to Invoice" - (SalesLine."Quantity Shipped" - SalesLine."Quantity Invoiced"))
            else
                NewSalesLine.Validate("Qty. to Invoice", 0);
            SalesHeader.Get(NewSalesLine."Document Type", NewSalesLine."Document No.");
            RoundingPrecision := GetRoundingPrecision(SalesHeader."Currency Code");
            if SalesHeader."Prices Including VAT" then
                NewSalesLine.Validate("Unit Price", Round(SalesLine."Unit Price" * (100 + NewSalesLine."VAT %") / (100 + SalesLine."VAT %"), RoundingPrecision))
            else
                NewSalesLine.Validate("Unit Price", SalesLine."Unit Price");
            NewSalesLine.Validate("Line Discount %", SalesLine."Line Discount %");
            NewSalesLine.Insert();
            OnAddNewSalesLineOnAfterInsertNewLine(Salesline, NewSalesline);

            RecRef.GetTable(SalesLine);
            VATRateChangeLogEntry.Init();
            VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
            VATRateChangeLogEntry."Table ID" := RecRef.Number;
            VATRateChangeLogEntry.UpdateGroups(
              SalesLine."Gen. Prod. Posting Group", SalesLine."Gen. Prod. Posting Group",
              SalesLine."VAT Prod. Posting Group", SalesLine."VAT Prod. Posting Group");
            VATRateChangeLogEntry.Description := StrSubstNo(Text0012, Format(SalesLine."Line No."));
            VATRateChangeLogEntry.Converted := true;
            WriteLogEntry(VATRateChangeLogEntry);

            RecRef.GetTable(NewSalesLine);
            VATRateChangeLogEntry.Init();
            VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
            VATRateChangeLogEntry."Table ID" := RecRef.Number;
            VATRateChangeLogEntry.UpdateGroups(
              SalesLine."Gen. Prod. Posting Group", NewSalesLine."Gen. Prod. Posting Group",
              SalesLine."VAT Prod. Posting Group", NewSalesLine."VAT Prod. Posting Group");
            VATRateChangeLogEntry.Description := StrSubstNo(Text0013, Format(NewSalesLine."Line No."), Format(SalesLine."Line No."));
            VATRateChangeLogEntry.Converted := true;
            WriteLogEntry(VATRateChangeLogEntry);

            UpdateSalesBlanketOrder(NewSalesLine, SalesLine."Line No.");
            UpdateAttachedToLineNoSales(NewSalesLine, SalesLine."Line No.");

            OldReservationEntry.Reset();
            OldReservationEntry.SetCurrentKey("Source ID", "Source Ref. No.", "Source Type", "Source Subtype");
            OldReservationEntry.SetRange("Source ID", SalesLine."Document No.");
            OldReservationEntry.SetRange("Source Ref. No.", SalesLine."Line No.");
            OldReservationEntry.SetRange("Source Type", Database::"Sales Line");
            OldReservationEntry.SetRange("Source Subtype", SalesLine."Document Type");
            if OldReservationEntry.FindSet() then
                repeat
                    NewReservationEntry := OldReservationEntry;
                    NewReservationEntry."Source Ref. No." := NewLineNo;
                    NewReservationEntry.Modify();
                until OldReservationEntry.Next() = 0;

            case SalesLine.Type of
                SalesLine.Type::Item:
                    begin
                        OldItemChargeAssignmentSales.Reset();
                        OldItemChargeAssignmentSales.SetCurrentKey("Applies-to Doc. Type", "Applies-to Doc. No.", "Applies-to Doc. Line No.");
                        OldItemChargeAssignmentSales.SetRange("Applies-to Doc. Type", SalesLine."Document Type");
                        OldItemChargeAssignmentSales.SetRange("Applies-to Doc. No.", SalesLine."Document No.");
                        OldItemChargeAssignmentSales.SetRange("Applies-to Doc. Line No.", SalesLine."Line No.");
                        if OldItemChargeAssignmentSales.Find('-') then
                            repeat
                                QtyRemainder := OldItemChargeAssignmentSales."Qty. to Assign";
                                AmountRemainder := OldItemChargeAssignmentSales."Amount to Assign";
                                NewItemChargeAssignmentSales := OldItemChargeAssignmentSales;
                                NewItemChargeAssignmentSales."Line No." := GetNextItemChrgAssSaleLineNo(OldItemChargeAssignmentSales);
                                NewItemChargeAssignmentSales."Applies-to Doc. Line No." := NewLineNo;
                                NewItemChargeAssignmentSales."Qty. to Assign" :=
                                  Round(QtyRemainder / SalesLine.Quantity * SalesLine."Outstanding Quantity", UOMMgt.QtyRndPrecision());
                                if SalesLine."Quantity Shipped" - SalesLine."Quantity Invoiced" = 0 then
                                    NewItemChargeAssignmentSales."Qty. to Assign" := QtyRemainder;
                                NewItemChargeAssignmentSales."Amount to Assign" :=
                                  Round(NewItemChargeAssignmentSales."Qty. to Assign" * NewItemChargeAssignmentSales."Unit Cost", RoundingPrecision);
                                NewItemChargeAssignmentSales.Insert();
                                QtyRemainder := QtyRemainder - NewItemChargeAssignmentSales."Qty. to Assign";
                                AmountRemainder := AmountRemainder - NewItemChargeAssignmentSales."Amount to Assign";
                                OldItemChargeAssignmentSales."Qty. to Assign" := QtyRemainder;
                                OldItemChargeAssignmentSales."Amount to Assign" := AmountRemainder;
                                OldItemChargeAssignmentSales.Modify();
                            until OldItemChargeAssignmentSales.Next() = 0;
                    end;
            end;

            OldSalesLine.Get(SalesLine."Document Type", SalesLine."Document No.", SalesLine."Line No.");
            OldSalesLine.Validate(Quantity, SalesLine."Quantity Shipped");

            OldSalesLine.Validate("Unit Price", SalesLine."Unit Price");
            OldSalesLine.Validate("Line Discount %", SalesLine."Line Discount %");
            OldSalesLine.Validate("Qty. to Ship", 0);
            OldSalesLine.Validate("Return Qty. to Receive", 0);
            if Abs(SalesLine."Qty. to Invoice") > (Abs(SalesLine."Quantity Shipped") - Abs(SalesLine."Quantity Invoiced")) then
                OldSalesLine.Validate("Qty. to Invoice", SalesLine."Quantity Shipped" - SalesLine."Quantity Invoiced")
            else
                OldSalesLine.Validate("Qty. to Invoice", SalesLine."Qty. to Invoice");

            OnAddNewSalesLineOnBeforeOldSalesLineModify(OldSalesLine, NewSalesLine, VATProdPostingGroup, GenProdPostingGroup);
            OldSalesLine.Modify();
        end;

        OnAfterAddNewSalesLine(OldSalesLine, NewSalesLine);
    end;

    local procedure InitNewSalesLineFromSalesLine(var NewSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line"; NewLineNo: Integer)
    begin
        NewSalesLine.Init();
        NewSalesLine := SalesLine;
        NewSalesLine."Line No." := NewLineNo;
        NewSalesLine."Quantity Shipped" := 0;
        NewSalesLine."Qty. Shipped (Base)" := 0;
        NewSalesLine."Return Qty. Received" := 0;
        NewSalesLine."Return Qty. Received (Base)" := 0;
        NewSalesLine."Quantity Invoiced" := 0;
        NewSalesLine."Qty. Invoiced (Base)" := 0;
        NewSalesLine."Reserved Quantity" := 0;
        NewSalesLine."Reserved Qty. (Base)" := 0;
        NewSalesLine."Qty. to Ship" := 0;
        NewSalesLine."Qty. to Ship (Base)" := 0;
        NewSalesLine."Return Qty. to Receive" := 0;
        NewSalesLine."Return Qty. to Receive (Base)" := 0;
        NewSalesLine."Qty. to Invoice" := 0;
        NewSalesLine."Qty. to Invoice (Base)" := 0;
        NewSalesLine."Qty. Shipped Not Invoiced" := 0;
        NewSalesLine."Return Qty. Rcd. Not Invd." := 0;
        NewSalesLine."Shipped Not Invoiced" := 0;
        NewSalesLine."Return Rcd. Not Invd." := 0;
        NewSalesLine."Qty. Shipped Not Invd. (Base)" := 0;
        NewSalesLine."Ret. Qty. Rcd. Not Invd.(Base)" := 0;
        NewSalesLine."Shipped Not Invoiced (LCY)" := 0;
        NewSalesLine."Return Rcd. Not Invd. (LCY)" := 0;
        OnAfterInitNewSalesLineFromSalesLine(NewSalesLine, SalesLine);
    end;

    local procedure UpdateSalesBlanketOrder(SalesLine: Record "Sales Line"; OriginalLineNo: Integer)
    var
        SalesHeader: Record "Sales Header";
        SalesLine2: Record "Sales Line";
        SalesLine3: Record "Sales Line";
        IsHandled: Boolean;
    begin
        if SalesLine."Document Type" = SalesLine."Document Type"::"Blanket Order" then begin
            SalesLine2.SetCurrentKey("Document Type", "Blanket Order No.", "Blanket Order Line No.");
            SalesLine2.SetRange("Blanket Order No.", SalesLine."Document No.");
            SalesLine2.SetFilter("Blanket Order Line No.", '=%1', OriginalLineNo);
            SalesLine2.SetRange(Type, SalesLine.Type);
            SalesLine2.SetRange("No.", SalesLine."No.");
            SalesLine2.SetRange("Quantity Shipped", 0);
            Clear(SalesHeader);
            if SalesLine2.FindSet() then
                repeat
                    if (SalesHeader."Document Type" <> SalesLine2."Document Type") or
                       (SalesHeader."No." <> SalesLine2."Document No.")
                    then begin
                        SalesHeader.Get(SalesLine2."Document Type", SalesLine2."Document No.");
                        SalesLine3.Reset();
                        SalesLine3.SetRange("Document Type", SalesHeader."Document Type");
                        SalesLine3.SetRange("Document No.", SalesHeader."No.");
                        SalesLine3.SetRange("Blanket Order No.", SalesLine2."Blanket Order No.");
                        SalesLine3.SetRange("Blanket Order Line No.", SalesLine2."Blanket Order Line No.");
                        IsHandled := false;
                        OnUpdateSalesBlanketOrderOnBeforeChangeBlanketOrder(SalesLine3, IsHandled);
                        if not IsHandled then
                            if SalesLine3.FindLast() then begin
                                SalesLine3."Blanket Order Line No." := SalesLine."Line No.";
                                SalesLine3.Modify();
                            end;
                    end;
                until SalesLine2.Next() = 0;
        end;
    end;

    local procedure UpdateAttachedToLineNoSales(SalesLine: Record "Sales Line"; OriginalLineNo: Integer)
    var
        SalesLine2: Record "Sales Line";
    begin
        if SalesLine."Document Type" = SalesLine."Document Type"::"Blanket Order" then begin
            SalesLine2.SetRange("Document No.", SalesLine."Document No.");
            SalesLine2.SetRange(Type, SalesLine2.Type::" ");
            SalesLine2.SetRange("Attached to Line No.", OriginalLineNo);
            if not SalesLine2.IsEmpty() then
                SalesLine2.ModifyAll("Attached to Line No.", SalesLine."Line No.");
        end;
    end;

    local procedure UpdateAttachedToLineNoPurch(PurchLine: Record "Purchase Line"; OriginalLineNo: Integer)
    var
        PurchLine2: Record "Purchase Line";
    begin
        if PurchLine."Document Type" = PurchLine."Document Type"::"Blanket Order" then begin
            PurchLine2.SetRange("Document No.", PurchLine."Document No.");
            PurchLine2.SetRange(Type, PurchLine2.Type::" ");
            PurchLine2.SetRange("Attached to Line No.", OriginalLineNo);
            if not PurchLine2.IsEmpty() then
                PurchLine2.ModifyAll("Attached to Line No.", PurchLine."Line No.");
        end;
    end;

    /// <summary>
    /// Updates purchase documents with new VAT and general product posting groups during conversion.
    /// Handles document status changes, line splitting, and complex validation scenarios.
    /// </summary>
    procedure UpdatePurchase()
    var
        PurchaseHeader: Record "Purchase Header";
        PurchaseHeader2: Record "Purchase Header";
        PurchaseLine: Record "Purchase Line";
        PurchaseLineOld: Record "Purchase Line";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        NewVATProdPotingGroup: Code[20];
        NewGenProdPostingGroup: Code[20];
        StatusChanged: Boolean;
        ConvertVATProdPostingGroup: Boolean;
        ConvertGenProdPostingGroup: Boolean;
        RoundingPrecision: Decimal;
        IsHandled: Boolean;
        IsModified: Boolean;
        ShouldProcessLine: Boolean;
    begin
        ProgressWindow.Update(1, PurchaseHeader.TableCaption());

        ConvertVATProdPostingGroup := ConvertVATProdPostGrp(VATRateChangeSetup."Update Purchase Documents");
        ConvertGenProdPostingGroup := ConvertGenProdPostGrp(VATRateChangeSetup."Update Purchase Documents");
        if not ConvertVATProdPostingGroup and not ConvertGenProdPostingGroup then
            exit;

        IsHandled := false;
        OnBeforeUpdatePurchase(VATRateChangeSetup, IsHandled, PurchaseHeader);
        if IsHandled then
            exit;

        PurchaseHeader.SetFilter(
          "Document Type", '%1..%2|%3', PurchaseHeader."Document Type"::Quote, PurchaseHeader."Document Type"::Invoice,
          PurchaseHeader."Document Type"::"Blanket Order");
        OnUpdatePurchaseOnAfterPurchaseHeaderSetFilters(PurchaseHeader);
        if PurchaseHeader.Find('-') then
            repeat
                StatusChanged := false;
                if CanUpdatePurchase(PurchaseHeader, ConvertGenProdPostingGroup, ConvertVATProdPostingGroup) then begin
                    if VATRateChangeSetup."Ignore Status on Purch. Docs." then
                        if PurchaseHeader.Status <> PurchaseHeader.Status::Open then begin
                            PurchaseHeader2 := PurchaseHeader;
                            PurchaseHeader.Status := PurchaseHeader.Status::Open;
                            PurchaseHeader.Modify();
                            StatusChanged := true;
                            OnUpdatePurchaseOnAfterOpenPurchaseHeader(PurchaseHeader);
                        end;
                    if PurchaseHeader.Status = PurchaseHeader.Status::Open then begin
                        PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
                        PurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
                        OnUpdatePurchaseOnAfterPurchaseLineSetFilters(PurchaseLine, PurchaseHeader);
                        if PurchaseLine.FindSet() then
                            repeat
                                ShouldProcessLine := LineInScope(PurchaseLine."Gen. Prod. Posting Group", PurchaseLine."VAT Prod. Posting Group", ConvertGenProdPostingGroup, ConvertVATProdPostingGroup);
                                OnUpdatePurchaseOnAfterCalcShouldProcessLine(PurchaseLine, ShouldProcessLine);
                                if ShouldProcessLine then
                                    if (PurchaseLine."Receipt No." = '') and
                                       (PurchaseLine."Return Shipment No." = '') and IncludePurchLine(PurchaseLine.Type, PurchaseLine."No.")
                                    then
                                        if DoUpdatePurchaseLine(PurchaseLine) then begin
                                            OnUpdatePurchaseOnBeforeChangePurchaseLine(PurchaseLine);

                                            if PurchaseHeader."Prices Including VAT" then
                                                PurchaseLineOld := PurchaseLine;

                                            RecRef.GetTable(PurchaseLine);
                                            UpdateRec(
                                              RecRef, ConvertVATProdPostGrp(VATRateChangeSetup."Update Purchase Documents"),
                                              ConvertGenProdPostGrp(VATRateChangeSetup."Update Purchase Documents"));

                                            PurchaseLine.Find();
                                            IsModified := false;
                                            if PurchaseHeader."Prices Including VAT" and VATRateChangeSetup."Perform Conversion" and
                                               (PurchaseLine."VAT %" <> PurchaseLineOld."VAT %") then
                                                if UpdateUnitPriceInclVAT(PurchaseLine.Type) then begin
                                                    RecRef.SetTable(PurchaseLine);
                                                    RoundingPrecision := GetRoundingPrecision(PurchaseHeader."Currency Code");
                                                    PurchaseLine.Validate(
                                                      "Direct Unit Cost",
                                                      Round(
                                                        PurchaseLineOld."Direct Unit Cost" * (100 + PurchaseLine."VAT %") / (100 + PurchaseLineOld."VAT %"),
                                                        RoundingPrecision));
                                                    IsModified := true;
                                                end else
                                                    if PurchaseLine.Type in [PurchaseLine.Type::"G/L Account", PurchaseLine.Type::"Charge (Item)", PurchaseLine.Type::"Fixed Asset"] then
                                                        if PurchaseLine."Direct Unit Cost" <> PurchaseLineOld."Direct Unit Cost" then begin
                                                            PurchaseLine.Validate("Direct Unit Cost", PurchaseLineOld."Direct Unit Cost");
                                                            IsModified := true;
                                                        end;
                                            if PurchaseLine."Prepayment %" <> 0 then begin
                                                PurchaseLine.UpdatePrepmtSetupFields();
                                                IsModified := true;
                                            end;
                                            OnUpdatePurchaseOnBeforeModifyPurchaseLine(PurchaseLine, IsModified);
                                            if IsModified then begin
                                                PurchaseLine.Modify(true);
                                                OnUpdatePurchaseOnAfterPurchaseLineModify(VATRateChangeSetup, PurchaseHeader, PurchaseLine, PurchaseLineOld);
                                            end;
                                        end else
                                            if VATRateChangeSetup."Perform Conversion" and (PurchaseLine."Outstanding Quantity" <> 0) then begin
                                                NewVATProdPotingGroup := PurchaseLine."VAT Prod. Posting Group";
                                                NewGenProdPostingGroup := PurchaseLine."Gen. Prod. Posting Group";
                                                if ConvertVATProdPostingGroup then
                                                    if VATRateChangeConversion.Get(
                                                         VATRateChangeConversion.Type::"VAT Prod. Posting Group", PurchaseLine."VAT Prod. Posting Group")
                                                    then
                                                        NewVATProdPotingGroup := VATRateChangeConversion."To Code";
                                                if ConvertGenProdPostingGroup then
                                                    if VATRateChangeConversion.Get(
                                                         VATRateChangeConversion.Type::"Gen. Prod. Posting Group", PurchaseLine."Gen. Prod. Posting Group")
                                                    then
                                                        NewGenProdPostingGroup := VATRateChangeConversion."To Code";
                                                AddNewPurchaseLine(PurchaseLine, NewVATProdPotingGroup, NewGenProdPostingGroup);
                                            end else begin
                                                RecRef.GetTable(PurchaseLine);
                                                InitVATRateChangeLogEntry(
                                                  VATRateChangeLogEntry, RecRef, PurchaseLine."Outstanding Quantity", PurchaseLine."Line No.");
                                                VATRateChangeLogEntry.UpdateGroups(
                                                  PurchaseLine."Gen. Prod. Posting Group", PurchaseLine."Gen. Prod. Posting Group",
                                                  PurchaseLine."VAT Prod. Posting Group", PurchaseLine."VAT Prod. Posting Group");
                                                WriteLogEntry(VATRateChangeLogEntry);
                                            end;
                            until PurchaseLine.Next() = 0;

                        OnUpdatePurchaseOnAfterUpdatePurchaseLines(VATRateChangeSetup, PurchaseHeader, ConvertVATProdPostingGroup, ConvertGenProdPostingGroup);
                    end;
                    if StatusChanged then begin
                        PurchaseHeader.Status := PurchaseHeader2.Status;
                        PurchaseHeader.Modify();
                        OnUpdatePurchaseOnAfterResetPurchaseHeaderStatus(PurchaseHeader);
                    end;
                end;
            until PurchaseHeader.Next() = 0;
    end;

    local procedure CanUpdatePurchase(PurchaseHeader: Record "Purchase Header"; ConvertGenProdPostingGroup: Boolean; ConvertVATProdPostingGroup: Boolean) Result: Boolean
    var
        PurchaseLine: Record "Purchase Line";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        DescriptionTxt: Text[250];
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCanUpdatePurchase(PurchaseHeader, ConvertGenProdPostingGroup, ConvertVATProdPostingGroup, IsHandled, Result);
        if IsHandled then
            exit(Result);

        PurchaseLine.SetRange("Document Type", PurchaseHeader."Document Type");
        PurchaseLine.SetRange("Document No.", PurchaseHeader."No.");
        OnCanUpdatePurchaseOnAfterPurchaseLineSetFilters(PurchaseLine);
        if PurchaseLine.FindSet() then
            repeat
                DescriptionTxt := '';
                if LineInScope(PurchaseLine."Gen. Prod. Posting Group", PurchaseLine."VAT Prod. Posting Group", ConvertGenProdPostingGroup, ConvertVATProdPostingGroup) then begin
                    if PurchaseLine."Drop Shipment" and (PurchaseLine."Sales Order No." <> '') then
                        DescriptionTxt := StrSubstNo(Text0004, PurchaseLine."Line No.");
                    if PurchaseLine."Special Order" and (PurchaseLine."Special Order Sales No." <> '') then
                        DescriptionTxt := StrSubstNo(Text0005, PurchaseLine."Line No.");
                    CheckPurchaseLinePartlyShipped(PurchaseLine, DescriptionTxt);
                    if (PurchaseLine."Outstanding Quantity" <> PurchaseLine.Quantity) and (PurchaseLine.Type = PurchaseLine.Type::"Charge (Item)") then
                        DescriptionTxt := StrSubstNo(Text0014, PurchaseLine."Line No.", PurchaseLine.Type::"Charge (Item)");
                    if PurchaseLine."Prepmt. Amount Inv. (LCY)" <> 0 then
                        DescriptionTxt := Text0011;
                end;
                OnCanUpdatePurchaseOnAfterLoopIteration(DescriptionTxt, PurchaseLine);
            until (PurchaseLine.Next() = 0) or (DescriptionTxt <> '');
        if DescriptionTxt = '' then
            exit(true);

        VATRateChangeLogEntry.Init();
        RecRef.GetTable(PurchaseHeader);
        VATRateChangeLogEntry.Init();
        VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
        VATRateChangeLogEntry."Table ID" := RecRef.Number;
        VATRateChangeLogEntry.Description := DescriptionTxt;
        WriteLogEntry(VATRateChangeLogEntry);
    end;

    local procedure CheckPurchaseLinePartlyShipped(var PurchaseLine: Record "Purchase Line"; var DescriptionTxt: Text[250])
    var
        WhseValidateSourceLine: Codeunit "Whse. Validate Source Line";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckPurchaseLinePartlyShipped(PurchaseLine, DescriptionTxt, IsHandled);
        if IsHandled then
            exit;

        if (PurchaseLine."Outstanding Quantity" <> PurchaseLine.Quantity) and
               WhseValidateSourceLine.WhseLinesExist(
                 Database::"Purchase Line", PurchaseLine."Document Type".AsInteger(), PurchaseLine."Document No.", PurchaseLine."Line No.", 0, PurchaseLine.Quantity)
        then
            DescriptionTxt := Text0006;
    end;

    local procedure AddNewPurchaseLine(PurchaseLine: Record "Purchase Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20])
    var
        NewPurchaseLine: Record "Purchase Line";
        OldPurchaseLine: Record "Purchase Line";
        PurchaseHeader: Record "Purchase Header";
        OldReservationEntry: Record "Reservation Entry";
        NewReservationEntry: Record "Reservation Entry";
        OldItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)";
        NewItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)";
        VATRateChangeLogEntry: Record "VAT Rate Change Log Entry";
        RecRef: RecordRef;
        NewLineNo: Integer;
        QtyRemainder: Decimal;
        AmountRemainder: Decimal;
        RoundingPrecision: Decimal;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeAddNewPurchaseLine(PurchaseLine, VATProdPostingGroup, GenProdPostingGroup, IsHandled);
        if not IsHandled then begin
            if not GetNextPurchaseLineNo(PurchaseLine, NewLineNo) then
                exit;

            NewPurchaseLine.Init();
            NewPurchaseLine := PurchaseLine;
            NewPurchaseLine."Line No." := NewLineNo;
            NewPurchaseLine."Quantity Received" := 0;
            NewPurchaseLine."Qty. Received (Base)" := 0;
            NewPurchaseLine."Return Qty. Shipped" := 0;
            NewPurchaseLine."Return Qty. Shipped (Base)" := 0;
            NewPurchaseLine."Quantity Invoiced" := 0;
            NewPurchaseLine."Qty. Invoiced (Base)" := 0;
            NewPurchaseLine."Reserved Quantity" := 0;
            NewPurchaseLine."Reserved Qty. (Base)" := 0;
            NewPurchaseLine."Qty. Rcd. Not Invoiced" := 0;
            NewPurchaseLine."Qty. Rcd. Not Invoiced (Base)" := 0;
            NewPurchaseLine."Return Qty. Shipped Not Invd." := 0;
            NewPurchaseLine."Ret. Qty. Shpd Not Invd.(Base)" := 0;
            NewPurchaseLine."Qty. to Receive" := 0;
            NewPurchaseLine."Qty. to Receive (Base)" := 0;
            NewPurchaseLine."Return Qty. to Ship" := 0;
            NewPurchaseLine."Return Qty. to Ship (Base)" := 0;
            NewPurchaseLine."Qty. to Invoice" := 0;
            NewPurchaseLine."Qty. to Invoice (Base)" := 0;
            NewPurchaseLine."Amt. Rcd. Not Invoiced" := 0;
            NewPurchaseLine."Amt. Rcd. Not Invoiced (LCY)" := 0;
            NewPurchaseLine."Return Shpd. Not Invd." := 0;
            NewPurchaseLine."Return Shpd. Not Invd. (LCY)" := 0;
            OnAddNewPurchaseLineOnAfterNewPurchaseLineInit(OldPurchaseLine, NewPurchaseLine);

            if (GenProdPostingGroup <> '') and ConvertGenProdPostGrp(VATRateChangeSetup."Update Purchase Documents") then
                NewPurchaseLine.Validate("Gen. Prod. Posting Group", GenProdPostingGroup);
            if (VATProdPostingGroup <> '') and ConvertVATProdPostGrp(VATRateChangeSetup."Update Purchase Documents") then
                NewPurchaseLine.Validate("VAT Prod. Posting Group", VATProdPostingGroup);
            NewPurchaseLine.Validate(Quantity, PurchaseLine."Outstanding Quantity");
            NewPurchaseLine.Validate("Qty. to Receive", PurchaseLine."Qty. to Receive");
            NewPurchaseLine.Validate("Return Qty. to Ship", PurchaseLine."Return Qty. to Ship");
            if Abs(PurchaseLine."Qty. to Invoice") > (Abs(PurchaseLine."Quantity Received") - Abs(PurchaseLine."Quantity Invoiced")) then
                NewPurchaseLine.Validate(
                  NewPurchaseLine."Qty. to Invoice", PurchaseLine."Qty. to Invoice" - (PurchaseLine."Quantity Received" - PurchaseLine."Quantity Invoiced"))
            else
                NewPurchaseLine.Validate("Qty. to Invoice", 0);

            PurchaseHeader.Get(NewPurchaseLine."Document Type", NewPurchaseLine."Document No.");
            RoundingPrecision := GetRoundingPrecision(PurchaseHeader."Currency Code");

            if PurchaseHeader."Prices Including VAT" then
                NewPurchaseLine.Validate(
                  NewPurchaseLine."Direct Unit Cost",
                  Round(PurchaseLine."Direct Unit Cost" * (100 + NewPurchaseLine."VAT %") / (100 + PurchaseLine."VAT %"), RoundingPrecision))
            else
                NewPurchaseLine.Validate("Direct Unit Cost", PurchaseLine."Direct Unit Cost");

            NewPurchaseLine.Validate("Line Discount %", PurchaseLine."Line Discount %");

            OnAddNewPurchaseLineOnBeforeInsertNewLine(PurchaseLine, NewPurchaseLine);
            NewPurchaseLine.Insert();
            OnAddNewPurchaseLineOnAfterInsertNewLine(PurchaseLine, NewPurchaseLine);

            RecRef.GetTable(PurchaseLine);
            VATRateChangeLogEntry.Init();
            VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
            VATRateChangeLogEntry."Table ID" := RecRef.Number;
            VATRateChangeLogEntry.Description := StrSubstNo(Text0012, Format(PurchaseLine."Line No."));
            VATRateChangeLogEntry.UpdateGroups(
              PurchaseLine."Gen. Prod. Posting Group", PurchaseLine."Gen. Prod. Posting Group",
              PurchaseLine."VAT Prod. Posting Group", PurchaseLine."VAT Prod. Posting Group");
            VATRateChangeLogEntry.Converted := true;
            WriteLogEntry(VATRateChangeLogEntry);

            RecRef.GetTable(NewPurchaseLine);
            VATRateChangeLogEntry.Init();
            VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
            VATRateChangeLogEntry."Table ID" := RecRef.Number;
            VATRateChangeLogEntry.UpdateGroups(
              PurchaseLine."Gen. Prod. Posting Group", NewPurchaseLine."Gen. Prod. Posting Group",
              PurchaseLine."VAT Prod. Posting Group", NewPurchaseLine."VAT Prod. Posting Group");
            VATRateChangeLogEntry.Description := StrSubstNo(Text0013, Format(NewPurchaseLine."Line No."), Format(PurchaseLine."Line No."));
            VATRateChangeLogEntry.Converted := true;
            WriteLogEntry(VATRateChangeLogEntry);

            UpdatePurchaseBlanketOrder(NewPurchaseLine, PurchaseLine."Line No.");
            UpdateAttachedToLineNoPurch(NewPurchaseLine, PurchaseLine."Line No.");

            OldReservationEntry.Reset();
            OldReservationEntry.SetCurrentKey("Source ID", "Source Ref. No.", "Source Type", "Source Subtype");
            OldReservationEntry.SetRange("Source ID", PurchaseLine."Document No.");
            OldReservationEntry.SetRange("Source Ref. No.", PurchaseLine."Line No.");
            OldReservationEntry.SetRange("Source Type", Database::"Purchase Line");
            OldReservationEntry.SetRange("Source Subtype", PurchaseLine."Document Type");
            OldReservationEntry.SetFilter(
              "Reservation Status", '%1|%2',
              OldReservationEntry."Reservation Status"::Reservation,
              OldReservationEntry."Reservation Status"::Surplus);
            if OldReservationEntry.Find('-') then
                repeat
                    NewReservationEntry := OldReservationEntry;
                    NewReservationEntry."Source Ref. No." := NewLineNo;
                    NewReservationEntry.Modify();
                until OldReservationEntry.Next() = 0;

            case PurchaseLine.Type of
                PurchaseLine.Type::Item:
                    begin
                        OldItemChargeAssignmentPurch.Reset();
                        OldItemChargeAssignmentPurch.SetCurrentKey("Applies-to Doc. Type", "Applies-to Doc. No.", "Applies-to Doc. Line No.");
                        OldItemChargeAssignmentPurch.SetRange("Applies-to Doc. Type", PurchaseLine."Document Type");
                        OldItemChargeAssignmentPurch.SetRange("Applies-to Doc. No.", PurchaseLine."Document No.");
                        OldItemChargeAssignmentPurch.SetRange("Applies-to Doc. Line No.", PurchaseLine."Line No.");
                        if OldItemChargeAssignmentPurch.FindSet() then
                            repeat
                                QtyRemainder := OldItemChargeAssignmentPurch."Qty. to Assign";
                                AmountRemainder := OldItemChargeAssignmentPurch."Amount to Assign";
                                NewItemChargeAssignmentPurch := OldItemChargeAssignmentPurch;
                                NewItemChargeAssignmentPurch."Line No." := GetNextItemChrgAssPurchLineNo(OldItemChargeAssignmentPurch);
                                NewItemChargeAssignmentPurch."Applies-to Doc. Line No." := NewLineNo;
                                NewItemChargeAssignmentPurch."Qty. to Assign" :=
                                  Round(QtyRemainder / PurchaseLine.Quantity * PurchaseLine."Outstanding Quantity", UOMMgt.QtyRndPrecision());
                                if PurchaseLine."Quantity Received" - PurchaseLine."Quantity Invoiced" = 0 then
                                    NewItemChargeAssignmentPurch."Qty. to Assign" := QtyRemainder;
                                NewItemChargeAssignmentPurch."Amount to Assign" :=
                                  Round(NewItemChargeAssignmentPurch."Qty. to Assign" * NewItemChargeAssignmentPurch."Unit Cost", RoundingPrecision);
                                NewItemChargeAssignmentPurch.Insert();
                                QtyRemainder := QtyRemainder - NewItemChargeAssignmentPurch."Qty. to Assign";
                                AmountRemainder := AmountRemainder - NewItemChargeAssignmentPurch."Amount to Assign";
                                OldItemChargeAssignmentPurch."Qty. to Assign" := QtyRemainder;
                                OldItemChargeAssignmentPurch."Amount to Assign" := AmountRemainder;
                                OldItemChargeAssignmentPurch.Modify();
                            until OldItemChargeAssignmentPurch.Next() = 0;
                    end;
            end;

            OldPurchaseLine.Get(PurchaseLine."Document Type", PurchaseLine."Document No.", PurchaseLine."Line No.");
            OldPurchaseLine.Validate("Qty. to Receive", 0);
            OldPurchaseLine.Validate(Quantity, PurchaseLine."Quantity Received");

            OldPurchaseLine.Validate("Direct Unit Cost", PurchaseLine."Direct Unit Cost");

            OldPurchaseLine.Validate("Line Discount %", PurchaseLine."Line Discount %");
            OldPurchaseLine.Validate("Return Qty. to Ship", 0);
            if Abs(PurchaseLine."Qty. to Invoice") > (Abs(PurchaseLine."Quantity Received") - Abs(PurchaseLine."Quantity Invoiced")) then
                OldPurchaseLine.Validate("Qty. to Invoice", PurchaseLine."Quantity Received" - PurchaseLine."Quantity Invoiced")
            else
                OldPurchaseLine.Validate("Qty. to Invoice", PurchaseLine."Qty. to Invoice");

            OnAddNewPurchaseLineOnBeforeOldPurchaseLineModify(OldPurchaseLine, NewPurchaseLine, VATProdPostingGroup, GenProdPostingGroup);
            OldPurchaseLine.Modify();
        end;

        OnAfterAddNewPurchaseLine(OldPurchaseLine, NewPurchaseLine);
    end;

    /// <summary>
    /// Calculates the next available line number for a new purchase line.
    /// </summary>
    /// <param name="PurchaseLine">Reference purchase line to find next line number for</param>
    /// <param name="NextLineNo">Returns the next available line number</param>
    /// <returns>True if a valid next line number was found</returns>
    procedure GetNextPurchaseLineNo(PurchaseLine: Record "Purchase Line"; var NextLineNo: Integer): Boolean
    var
        PurchaseLine2: Record "Purchase Line";
    begin
        PurchaseLine2.Reset();
        PurchaseLine2.SetRange("Document Type", PurchaseLine."Document Type");
        PurchaseLine2.SetRange("Document No.", PurchaseLine."Document No.");
        PurchaseLine2 := PurchaseLine;
        if PurchaseLine2.Find('>') then
            NextLineNo := PurchaseLine."Line No." + (PurchaseLine2."Line No." - PurchaseLine."Line No.") div 2;
        if (NextLineNo = PurchaseLine."Line No.") or (NextLineNo = 0) then begin
            PurchaseLine2.FindLast();
            NextLineNo := PurchaseLine2."Line No." + 10000;
        end;
        exit(NextLineNo <> PurchaseLine."Line No.");
    end;

    local procedure UpdatePurchaseBlanketOrder(PurchaseLine: Record "Purchase Line"; OriginalLineNo: Integer)
    var
        PurchaseHeader: Record "Purchase Header";
        PurchaseLine2: Record "Purchase Line";
        PurchaseLine3: Record "Purchase Line";
        IsHandled: Boolean;
    begin
        if PurchaseLine."Document Type" = PurchaseLine."Document Type"::"Blanket Order" then begin
            PurchaseLine2.SetCurrentKey("Document Type", "Blanket Order No.", "Blanket Order Line No.");
            PurchaseLine2.SetRange("Blanket Order No.", PurchaseLine."Document No.");
            PurchaseLine2.SetFilter("Blanket Order Line No.", '=%1', OriginalLineNo);
            PurchaseLine2.SetRange(Type, PurchaseLine.Type);
            PurchaseLine2.SetRange("No.", PurchaseLine."No.");
            PurchaseLine2.SetRange("Quantity Received", 0);
            Clear(PurchaseHeader);
            if PurchaseLine2.Find('-') then
                repeat
                    if (PurchaseHeader."Document Type" <> PurchaseLine2."Document Type") or
                       (PurchaseHeader."No." <> PurchaseLine2."Document No.")
                    then begin
                        PurchaseHeader.Get(PurchaseLine2."Document Type", PurchaseLine2."Document No.");
                        PurchaseLine3.Reset();
                        PurchaseLine3.SetRange("Document Type", PurchaseHeader."Document Type");
                        PurchaseLine3.SetRange("Document No.", PurchaseHeader."No.");
                        PurchaseLine3.SetRange("Blanket Order No.", PurchaseLine2."Blanket Order No.");
                        PurchaseLine3.SetRange("Blanket Order Line No.", PurchaseLine2."Blanket Order Line No.");
                        IsHandled := false;
                        OnUpdatePurchaseBlanketOrderOnBeforeChangeBlanketOrder(PurchaseLine, IsHandled);
                        if not IsHandled then
                            if PurchaseLine3.FindLast() then begin
                                PurchaseLine3."Blanket Order Line No." := PurchaseLine."Line No.";
                                PurchaseLine3.Modify();
                            end;
                    end;
                until PurchaseLine2.Next() = 0;
        end;
    end;

    /// <summary>
    /// Determines whether unit price including VAT should be updated for the specified sales line type.
    /// Uses setup configuration to control which line types require price updates during VAT rate changes.
    /// </summary>
    /// <param name="Type">Sales line type to check for unit price update eligibility</param>
    /// <returns>True if unit price including VAT should be updated for the specified line type, false otherwise</returns>
    procedure UpdateUnitPriceInclVAT(Type: Enum "Sales Line Type"): Boolean
    var
        SalesLine: Record "Sales Line";
    begin
        exit(
          ((VATRateChangeSetup."Update Unit Price For G/L Acc." and
            (Type = SalesLine.Type::"G/L Account")) or
           (VATRateChangeSetup."Upd. Unit Price For Item Chrg." and
            (Type = SalesLine.Type::"Charge (Item)")) or
           (VATRateChangeSetup."Upd. Unit Price For FA" and (Type = SalesLine.Type::"Fixed Asset"))));
    end;

    /// <summary>
    /// Determines whether a record is in scope for conversion based on its posting group values.
    /// </summary>
    /// <param name="GenProdPostingGroup">General product posting group of the record</param>
    /// <param name="VATProdPostingGroup">VAT product posting group of the record</param>
    /// <param name="ConvertGenProdPostingGroup">Whether general product posting groups are being converted</param>
    /// <param name="ConvertVATProdPostingGroup">Whether VAT product posting groups are being converted</param>
    /// <returns>True if the record should be included in the conversion</returns>
    procedure LineInScope(GenProdPostingGroup: Code[20]; VATProdPostingGroup: Code[20]; ConvertGenProdPostingGroup: Boolean; ConvertVATProdPostingGroup: Boolean) Result: Boolean
    begin
        if ConvertGenProdPostingGroup then
            if VATRateChangeConversion.Get(VATRateChangeConversion.Type::"Gen. Prod. Posting Group", GenProdPostingGroup) then
                Result := true;
        if ConvertVATProdPostingGroup then
            if VATRateChangeConversion.Get(VATRateChangeConversion.Type::"VAT Prod. Posting Group", VATProdPostingGroup) then
                Result := true;
        OnAfterLineInScope(GenProdPostingGroup, VATProdPostingGroup, ConvertGenProdPostingGroup, ConvertVATProdPostingGroup, Result)
    end;

    /// <summary>
    /// Calculates the next available line number for inserting a new sales line in the same document.
    /// Uses spacing logic to insert between existing lines when possible, or appends with standard increment.
    /// </summary>
    /// <param name="SalesLine">Reference sales line from the document to insert into</param>
    /// <param name="NextLineNo">Returns the calculated line number for the new sales line</param>
    /// <returns>True if a valid next line number was calculated, false if calculation failed</returns>
    procedure GetNextSalesLineNo(SalesLine: Record "Sales Line"; var NextLineNo: Integer): Boolean
    var
        SalesLine2: Record "Sales Line";
    begin
        SalesLine2.Reset();
        SalesLine2.SetRange("Document Type", SalesLine."Document Type");
        SalesLine2.SetRange("Document No.", SalesLine."Document No.");
        SalesLine2 := SalesLine;
        if SalesLine2.Find('>') then
            NextLineNo := SalesLine."Line No." + (SalesLine2."Line No." - SalesLine."Line No.") div 2;
        if (NextLineNo = SalesLine."Line No.") or (NextLineNo = 0) then begin
            SalesLine2.FindLast();
            NextLineNo := SalesLine2."Line No." + 10000;
        end;
        exit(NextLineNo <> SalesLine."Line No.");
    end;

    /// <summary>
    /// Retrieves the unit amount rounding precision for the specified currency.
    /// Handles both local currency (empty code) and foreign currency scenarios with proper precision lookup.
    /// </summary>
    /// <param name="CurrencyCode">Currency code for precision lookup, or empty for local currency</param>
    /// <returns>Unit amount rounding precision decimal value for the currency</returns>
    procedure GetRoundingPrecision(CurrencyCode: Code[10]): Decimal
    var
        Currency: Record Currency;
    begin
        if CurrencyCode = '' then
            Currency.InitRoundingPrecision()
        else
            Currency.Get(CurrencyCode);
        exit(Currency."Unit-Amount Rounding Precision");
    end;

    local procedure GetNextItemChrgAssSaleLineNo(ItemChargeAssignmentSales: Record "Item Charge Assignment (Sales)"): Integer
    var
        ItemChargeAssignmentSales2: Record "Item Charge Assignment (Sales)";
        ExitValue: Integer;
    begin
        ExitValue := 10000;
        ItemChargeAssignmentSales2.Reset();
        ItemChargeAssignmentSales2.SetCurrentKey("Document Type", "Document No.", "Document Line No.");
        ItemChargeAssignmentSales2.SetRange("Document Type", ItemChargeAssignmentSales."Document Type");
        ItemChargeAssignmentSales2.SetRange("Document No.", ItemChargeAssignmentSales."Document No.");
        ItemChargeAssignmentSales2.SetRange("Document Line No.", ItemChargeAssignmentSales."Document Line No.");
        if ItemChargeAssignmentSales2.FindLast() then
            ExitValue := ItemChargeAssignmentSales2."Line No." + 10000;
        exit(ExitValue);
    end;

    local procedure GetNextItemChrgAssPurchLineNo(ItemChargeAssignmentPurch: Record "Item Charge Assignment (Purch)"): Integer
    var
        ItemChargeAssignmentPurch2: Record "Item Charge Assignment (Purch)";
        ExitValue: Integer;
    begin
        ExitValue := 10000;
        ItemChargeAssignmentPurch2.Reset();
        ItemChargeAssignmentPurch2.SetCurrentKey("Document Type", "Document No.", "Document Line No.");
        ItemChargeAssignmentPurch2.SetRange("Document Type", ItemChargeAssignmentPurch."Document Type");
        ItemChargeAssignmentPurch2.SetRange("Document No.", ItemChargeAssignmentPurch."Document No.");
        ItemChargeAssignmentPurch2.SetRange("Document Line No.", ItemChargeAssignmentPurch."Document Line No.");
        if ItemChargeAssignmentPurch2.FindLast() then
            ExitValue := ItemChargeAssignmentPurch2."Line No." + 10000;
        exit(ExitValue);
    end;

    local procedure AreTablesSelected() Result: Boolean
    begin
        if VATRateChangeSetup."Update Gen. Prod. Post. Groups" <> VATRateChangeSetup."Update Gen. Prod. Post. Groups"::No then
            exit(true);
        if VATRateChangeSetup."Update G/L Accounts" <> VATRateChangeSetup."Update G/L Accounts"::No then
            exit(true);
        if VATRateChangeSetup."Update Items" <> VATRateChangeSetup."Update Items"::No then
            exit(true);
        if VATRateChangeSetup."Update Item Templates" <> VATRateChangeSetup."Update Item Templates"::No then
            exit(true);
        if VATRateChangeSetup."Update Item Charges" <> VATRateChangeSetup."Update Item Charges"::No then
            exit(true);
        if VATRateChangeSetup."Update Resources" <> VATRateChangeSetup."Update Resources"::No then
            exit(true);
        if VATRateChangeSetup."Update Gen. Journal Lines" <> VATRateChangeSetup."Update Gen. Journal Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Gen. Journal Allocation" <> VATRateChangeSetup."Update Gen. Journal Allocation"::No then
            exit(true);
        if VATRateChangeSetup."Update Std. Gen. Jnl. Lines" <> VATRateChangeSetup."Update Std. Gen. Jnl. Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Res. Journal Lines" <> VATRateChangeSetup."Update Res. Journal Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Job Journal Lines" <> VATRateChangeSetup."Update Job Journal Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Requisition Lines" <> VATRateChangeSetup."Update Requisition Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Std. Item Jnl. Lines" <> VATRateChangeSetup."Update Std. Item Jnl. Lines"::No then
            exit(true);
        if VATRateChangeSetup."Update Sales Documents" <> VATRateChangeSetup."Update Sales Documents"::No then
            exit(true);
        if VATRateChangeSetup."Update Purchase Documents" <> VATRateChangeSetup."Update Purchase Documents"::No then
            exit(true);
        if VATRateChangeSetup."Update Reminders" <> VATRateChangeSetup."Update Reminders"::No then
            exit(true);
        if VATRateChangeSetup."Update Finance Charge Memos" <> VATRateChangeSetup."Update Finance Charge Memos"::No then
            exit(true);
        Result := false;

        OnAfterAreTablesSelected(VATRateChangeSetup, Result);
    end;

    /// <summary>
    /// Determines whether a sales line with the specified type and number should be included in VAT rate change processing.
    /// Validates line inclusion based on setup configuration and posting group assignments.
    /// </summary>
    /// <param name="Type">Sales line type to evaluate for inclusion</param>
    /// <param name="No">Item, G/L account, or resource number for the sales line</param>
    /// <returns>True if the sales line should be included in VAT rate change conversion, false otherwise</returns>
    procedure IncludeSalesLine(Type: Enum "Sales Line Type"; No: Code[20]): Boolean
    var
        IsHandled: Boolean;
        Result: Boolean;
    begin
        case Type of
            "Sales Line Type"::"G/L Account":
                exit(IncludeGLAccount(No));
            "Sales Line Type"::Item:
                exit(IncludeItem(No));
            "Sales Line Type"::Resource:
                exit(IncludeRes(No));
            else begin
                IsHandled := false;
                OnIncludeSalesLineOnTypeElse(Type, Result, IsHandled);
                if IsHandled then
                    exit(Result);
            end;
        end;
        exit(true);
    end;

    /// <summary>
    /// Determines whether a purchase line with the specified type and number should be included in VAT rate change processing.
    /// Validates line inclusion based on setup configuration and posting group assignments.
    /// </summary>
    /// <param name="Type">Purchase line type to evaluate for inclusion</param>
    /// <param name="No">Item, G/L account, or resource number for the purchase line</param>
    /// <returns>True if the purchase line should be included in VAT rate change conversion, false otherwise</returns>
    procedure IncludePurchLine(Type: Enum "Purchase Line Type"; No: Code[20]): Boolean
    var
        IsHandled: Boolean;
        Result: Boolean;
    begin
        case Type of
            "Purchase Line Type"::"G/L Account":
                exit(IncludeGLAccount(No));
            "Purchase Line Type"::Item:
                exit(IncludeItem(No));
            "Purchase Line Type"::Resource:
                exit(IncludeRes(No));
            else begin
                IsHandled := false;
                OnIncludePurchLineOnTypeElse(Type, Result, IsHandled);
                if IsHandled then
                    exit(Result);
            end;
        end;
        exit(true);
    end;

    local procedure IncludeGLAccount(No: Code[20]): Boolean
    var
        GLAccount: Record "G/L Account";
    begin
        if VATRateChangeSetup."Account Filter" = '' then
            exit(true);
        GLAccount."No." := No;
        GLAccount.SetFilter("No.", VATRateChangeSetup."Account Filter");
        exit(GLAccount.Find());
    end;

    local procedure IncludeItem(No: Code[20]): Boolean
    var
        Item: Record Item;
    begin
        if VATRateChangeSetup."Item Filter" = '' then
            exit(true);
        Item."No." := No;
        Item.SetFilter("No.", VATRateChangeSetup."Item Filter");
        exit(Item.Find());
    end;

    local procedure IncludeRes(No: Code[20]): Boolean
    var
        Res: Record Resource;
    begin
        if VATRateChangeSetup."Resource Filter" = '' then
            exit(true);
        Res."No." := No;
        Res.SetFilter("No.", VATRateChangeSetup."Resource Filter");
        exit(Res.Find());
    end;

    /// <summary>
    /// Initializes a VAT rate change log entry with standard tracking information for a record conversion.
    /// Sets up logging data including record identification, table reference, and status description.
    /// </summary>
    /// <param name="VATRateChangeLogEntry">Log entry record to initialize with tracking information</param>
    /// <param name="RecRef">Record reference for the item being converted</param>
    /// <param name="OutstandingQuantity">Outstanding quantity on the line being processed</param>
    /// <param name="LineNo">Line number for reference in case of partial conversion</param>
    procedure InitVATRateChangeLogEntry(var VATRateChangeLogEntry: Record "VAT Rate Change Log Entry"; RecRef: RecordRef; OutstandingQuantity: Decimal; LineNo: Integer)
    begin
        VATRateChangeLogEntry.Init();
        VATRateChangeLogEntry."Record ID" := RecRef.RecordId;
        VATRateChangeLogEntry."Table ID" := RecRef.Number;
        if (OutstandingQuantity = 0) and VATRateChangeSetup."Perform Conversion" then
            VATRateChangeLogEntry.Description := Text0007
        else begin
            VATRateChangeLogEntry.Description :=
              StrSubstNo(Text0009, VATRateChangeSetup.FieldCaption("Perform Conversion"));
            if OutstandingQuantity <> 0 then
                VATRateChangeLogEntry.Description := StrSubstNo(Text0017, LineNo)
        end;
    end;

    local procedure DoUpdatePurchaseLine(var PurchaseLine: Record "Purchase Line") Result: Boolean
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeDoUpdatePurchaseLine(PurchaseLine, IsHandled, Result);
        if IsHandled then
            exit(Result);

        exit(PurchaseLine.Quantity = PurchaseLine."Outstanding Quantity")
    end;

    local procedure DoUpdateSalesLine(var SalesLine: Record "Sales Line") Result: Boolean
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeDoUpdateSalesLine(SalesLine, IsHandled, Result);
        if IsHandled then
            exit(Result);

        exit(SalesLine.Quantity = SalesLine."Outstanding Quantity")
    end;

    /// <summary>
    /// Integration event raised before modifying the original purchase line during VAT rate change conversion.
    /// Allows custom modifications to both old and new purchase lines before database update.
    /// </summary>
    /// <param name="OldPurchaseLine">Original purchase line being modified during conversion</param>
    /// <param name="NewPurchaseLine">New purchase line created with updated posting groups</param>
    /// <param name="VATProdPostingGroup">New VAT product posting group being applied</param>
    /// <param name="GenProdPostingGroup">New general product posting group being applied</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddNewPurchaseLineOnBeforeOldPurchaseLineModify(var OldPurchaseLine: Record "Purchase Line"; var NewPurchaseLine: Record "Purchase Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20])
    begin
    end;

    /// <summary>
    /// Integration event raised before modifying the original sales line during VAT rate change conversion.
    /// Allows custom modifications to both old and new sales lines before database update.
    /// </summary>
    /// <param name="OldSalesLine">Original sales line being modified during conversion</param>
    /// <param name="NewSalesLine">New sales line created with updated posting groups</param>
    /// <param name="VATProdPostingGroup">New VAT product posting group being applied</param>
    /// <param name="GenProdPostingGroup">New general product posting group being applied</param>
    [IntegrationEvent(false, false)]
    local procedure OnAddNewSalesLineOnBeforeOldSalesLineModify(var OldSalesLine: Record "Sales Line"; var NewSalesLine: Record "Sales Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20])
    begin
    end;

    /// <summary>
    /// Integration event raised after initializing a new sales line from an existing sales line during VAT rate change conversion.
    /// Allows custom field copying and initialization logic for the new sales line.
    /// </summary>
    /// <param name="NewSalesLine">New sales line being initialized with updated posting groups</param>
    /// <param name="SalesLine">Source sales line providing initial field values</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterInitNewSalesLineFromSalesLine(var NewSalesLine: Record "Sales Line"; SalesLine: Record "Sales Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after updating all tables during VAT rate change conversion process.
    /// Allows custom post-processing logic after all configured tables have been updated.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration used for the conversion</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterUpdateTables(var VATRateChangeSetup: Record "VAT Rate Change Setup")
    begin
    end;

    /// <summary>
    /// Integration event raised after completing the entire VAT rate change conversion process.
    /// Allows custom finalization logic and cleanup after all conversion operations.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration used for the conversion</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterConvert(var VATRateChangeSetup: Record "VAT Rate Change Setup")
    begin
    end;

    /// <summary>
    /// Integration event raised after determining if a line is in scope for VAT rate change conversion.
    /// Allows custom logic to modify the scope determination result based on additional criteria.
    /// </summary>
    /// <param name="GenProdPostingGroup">General product posting group of the line being evaluated</param>
    /// <param name="VATProdPostingGroup">VAT product posting group of the line being evaluated</param>
    /// <param name="ConvertGenProdPostingGroup">Whether general product posting group conversion is enabled</param>
    /// <param name="ConvertVATProdPostingGroup">Whether VAT product posting group conversion is enabled</param>
    /// <param name="Result">Line scope determination result that can be modified by subscribers</param>
    [IntegrationEvent(true, false)]
    local procedure OnAfterLineInScope(GenProdPostingGroup: Code[20]; VATProdPostingGroup: Code[20]; ConvertGenProdPostingGroup: Boolean; ConvertVATProdPostingGroup: Boolean; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after checking if tables are selected for VAT rate change conversion.
    /// Allows custom logic to modify table selection validation results.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration being validated</param>
    /// <param name="Result">Table selection validation result that can be modified by subscribers</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterAreTablesSelected(var VATRateChangeSetup: Record "VAT Rate Change Setup"; var Result: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before checking if a purchase line is partly shipped during VAT rate change validation.
    /// Allows custom validation logic for purchase line shipping status.
    /// </summary>
    /// <param name="PurchaseLine">Purchase line being validated for partial shipment</param>
    /// <param name="DescriptionTxt">Error description text that can be modified by subscribers</param>
    /// <param name="IsHandled">Set to true to skip standard validation processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckPurchaseLinePartlyShipped(var PurchaseLine: Record "Purchase Line"; var DescriptionTxt: Text[250]; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before checking if a sales line is partly shipped during VAT rate change validation.
    /// Allows custom validation logic for sales line shipping status.
    /// </summary>
    /// <param name="SalesLine">Sales line being validated for partial shipment</param>
    /// <param name="DescriptionTxt">Error description text that can be modified by subscribers</param>
    /// <param name="IsHandled">Set to true to skip standard validation processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckSalesLinePartlyShipped(var SalesLine: Record "Sales Line"; var DescriptionTxt: Text[250]; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before starting the VAT rate change conversion process.
    /// Allows custom initialization and validation logic before conversion begins.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration for the conversion</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeConvert(var VATRateChangeSetup: Record "VAT Rate Change Setup")
    begin
    end;

    /// <summary>
    /// Integration event raised before finishing the VAT rate change conversion process.
    /// Allows custom cleanup and finalization logic before closing the conversion operation.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration used for the conversion</param>
    /// <param name="ProgressWindow">Progress dialog window that can be modified by subscribers</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeFinishConvert(var VATRateChangeSetup: Record "VAT Rate Change Setup"; var ProgressWindow: Dialog)
    begin
    end;

    /// <summary>
    /// Integration event raised before running the VAT rate change conversion codeunit.
    /// Allows custom logic to override or enhance the main conversion process execution.
    /// </summary>
    /// <param name="IsHandled">Set to true to skip standard conversion process execution</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeOnRun(var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before updating G/L account records during VAT rate change conversion.
    /// Allows custom validation and modification logic for G/L account updates.
    /// </summary>
    /// <param name="GLAccount">G/L account record being updated</param>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="IsHandled">Set to true to skip standard G/L account update processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateGLAccount(var GLAccount: Record "G/L Account"; var VATRateChangeSetup: Record "VAT Rate Change Setup"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before updating item records during VAT rate change conversion.
    /// Allows custom validation and modification logic for item updates.
    /// </summary>
    /// <param name="Item">Item record being updated</param>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="IsHandled">Set to true to skip standard item update processing</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeUpdateItem(var Item: Record Item; var VATRateChangeSetup: Record "VAT Rate Change Setup"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before updating resource records during VAT rate change conversion.
    /// Allows custom validation and modification logic for resource updates.
    /// </summary>
    /// <param name="Resource">Resource record being updated</param>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="IsHandled">Set to true to skip standard resource update processing</param>
    [IntegrationEvent(true, false)]
    local procedure OnBeforeUpdateResource(var Resource: Record Resource; var VATRateChangeSetup: Record "VAT Rate Change Setup"; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before updating sales documents during VAT rate change conversion.
    /// Allows custom validation and preprocessing logic for sales document updates.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="IsHandled">Set to true to skip standard sales document update processing</param>
    /// <param name="SalesHeader">Sales header record being processed</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateSales(var VATRateChangeSetup: Record "VAT Rate Change Setup"; var IsHandled: Boolean; var SalesHeader: Record "Sales Header")
    begin
    end;

    /// <summary>
    /// Integration event raised before updating a specific table during VAT rate change conversion.
    /// Allows custom logic to override or enhance table-specific update processing.
    /// </summary>
    /// <param name="TableID">ID of the table being updated</param>
    /// <param name="ConvertVATProdPostingGroup">Whether VAT product posting group conversion is enabled</param>
    /// <param name="ConvertGenProdPostingGroup">Whether general product posting group conversion is enabled</param>
    /// <param name="IsHandled">Set to true to skip standard table update processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateTable(TableID: Integer; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before updating purchase documents during VAT rate change conversion.
    /// Allows custom validation and preprocessing logic for purchase document updates.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="IsHandled">Set to true to skip standard purchase document update processing</param>
    /// <param name="PurchaseHeader">Purchase header record being processed</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdatePurchase(var VATRateChangeSetup: Record "VAT Rate Change Setup"; var IsHandled: Boolean; var PurchaseHeader: Record "Purchase Header")
    begin
    end;

    /// <summary>
    /// Integration event raised before updating a record during VAT rate change conversion.
    /// Allows custom validation and preprocessing logic for individual record updates.
    /// </summary>
    /// <param name="RecRef">Record reference for the record being updated</param>
    /// <param name="ConvertVATProdPostingGroup">Whether VAT product posting group conversion is enabled</param>
    /// <param name="ConvertGenProdPostingGroup">Whether general product posting group conversion is enabled</param>
    /// <param name="IsHandled">Set to true to skip standard record update processing</param>
    [IntegrationEvent(false, false)]
    local procedure OnBeforeUpdateRec(var RecRef: RecordRef; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean; var IsHandled: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised before starting the conversion process within the main convert operation.
    /// Allows custom initialization and validation logic at the beginning of the conversion workflow.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration for the conversion</param>
    [IntegrationEvent(false, false)]
    local procedure OnConvertOnBeforeStartConvert(var VATRateChangeSetup: Record "VAT Rate Change Setup")
    begin
    end;

    /// <summary>
    /// Integration event raised after adding a new sales line during VAT rate change conversion.
    /// Allows custom post-processing logic for the newly created sales line.
    /// </summary>
    /// <param name="OldSalesLine">Original sales line that was split or modified</param>
    /// <param name="NewSalesLine">New sales line created with updated posting groups</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterAddNewSalesLine(var OldSalesLine: Record "Sales Line"; var NewSalesLine: Record "Sales Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after adding a new purchase line during VAT rate change conversion.
    /// Allows custom post-processing logic for the newly created purchase line.
    /// </summary>
    /// <param name="OldPurchaseLine">Original purchase line that was split or modified</param>
    /// <param name="NewPurchaseLine">New purchase line created with updated posting groups</param>
    [IntegrationEvent(false, false)]
    local procedure OnAfterAddNewPurchaseLine(var OldPurchaseLine: Record "Purchase Line"; var NewPurchaseLine: Record "Purchase Line")
    begin
    end;

    /// <summary>
    /// Integration event raised after setting filters on purchase headers during purchase document update.
    /// Allows custom filter modifications for purchase header processing.
    /// </summary>
    /// <param name="PurchaseHeader">Purchase header record with applied filters</param>
    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnAfterPurchaseHeaderSetFilters(var PurchaseHeader: Record "Purchase Header")
    begin
    end;

    /// <summary>
    /// Integration event raised after setting filters on purchase lines during purchase document update.
    /// Allows custom filter modifications for purchase line processing.
    /// </summary>
    /// <param name="PurchaseLine">Purchase line record with applied filters</param>
    /// <param name="PurchaseHeader">Related purchase header record</param>
    [IntegrationEvent(true, false)]
    local procedure OnUpdatePurchaseOnAfterPurchaseLineSetFilters(var PurchaseLine: Record "Purchase Line"; PurchaseHeader: Record "Purchase Header")
    begin
    end;

    /// <summary>
    /// Integration event raised before modifying a record reference during VAT rate change conversion.
    /// Allows custom logic before database modification occurs.
    /// </summary>
    /// <param name="RecRef">Record reference being modified</param>
    /// <param name="GenProdPostingGroupConverted">Whether general product posting group was converted</param>
    /// <param name="VATProdPostingGroupConverted">Whether VAT product posting group was converted</param>
    [IntegrationEvent(false, false)]
    local procedure OnUpdateRecOnBeforeRecRefModify(var RecRef: RecordRef; GenProdPostingGroupConverted: Boolean; VATProdPostingGroupConverted: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after modifying a record reference during VAT rate change conversion.
    /// Allows custom post-processing logic after database modification.
    /// </summary>
    /// <param name="RecRef">Record reference that was modified</param>
    /// <param name="GenProdPostingGroupConverted">Whether general product posting group was converted</param>
    /// <param name="VATProdPostingGroupConverted">Whether VAT product posting group was converted</param>
    [IntegrationEvent(false, false)]
    local procedure OnUpdateRecOnAfterRecRefModify(var RecRef: RecordRef; GenProdPostingGroupConverted: Boolean; VATProdPostingGroupConverted: Boolean)
    begin
    end;

    /// <summary>
    /// Integration event raised after setting filters on sales headers during sales document update.
    /// Allows custom filter modifications for sales header processing.
    /// </summary>
    /// <param name="VATRateChangeSetup">VAT rate change setup configuration</param>
    /// <param name="SalesHeader">Sales header record with applied filters</param>
    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnAfterSalesHeaderSetFilters(VATRateChangeSetup: Record "VAT Rate Change Setup"; var SalesHeader: Record "Sales Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnAfterSalesLineSetFilters(VATRateChangeSetup: Record "VAT Rate Change Setup"; SalesHeader: Record "Sales Header"; var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnAfterModifySalesLine(var SalesLine: Record "Sales Line"; IsModified: Boolean; VATRateChangeSetup: Record "VAT Rate Change Setup"; SalesHeader: Record "Sales Header"; SalesLineOld: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnUpdateSalesOnAfterUpdateSalesLines(VATRateChangeSetup: Record "VAT Rate Change Setup"; var SalesHeader: Record "Sales Header"; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnAfterResetPurchaseHeaderStatus(var PurchaseHeader: Record "Purchase Header")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnUpdatePurchaseOnAfterCalcShouldProcessLine(var PurchaseLine: Record "Purchase Line"; var ShouldProcessLine: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnAfterResetSalesHeaderStatus(var SalesHeader: Record "Sales Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnBeforeModifySalesLine(var SalesLine: Record "Sales Line"; var IsModified: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnBeforeModifyPurchaseLine(var PurchaseLine: Record "Purchase Line"; var IsModified: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAddNewPurchaseLineOnAfterInsertNewLine(var PurchaseLine: Record "Purchase Line"; var NewPurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAddNewPurchaseLineOnBeforeInsertNewLine(var PurchaseLine: Record "Purchase Line"; var NewPurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAddNewSalesLineOnAfterInsertNewLine(var SalesLine: Record "Sales Line"; var NewSalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnBeforeChangeSalesLine(var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnBeforeChangePurchaseLine(var PurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseBlanketOrderOnBeforeChangeBlanketOrder(var PurchaseLine: Record "Purchase Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesBlanketOrderOnBeforeChangeBlanketOrder(var SalesLine: Record "Sales Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnAfterOpenPurchaseHeader(var PurchaseHeader: Record "Purchase Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdateSalesOnAfterOpenSalesHeader(var SalesHeader: Record "Sales Header")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnUpdatePurchaseOnAfterUpdatePurchaseLines(VATRateChangeSetup: Record "VAT Rate Change Setup"; var PurchaseHeader: Record "Purchase Header"; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnTestVATPostingSetupOnAfterVATPostingSetupOldSetFilters(var VATPostingSetupOld: Record "VAT Posting Setup"; VATRateChangeSetup: Record "VAT Rate Change Setup")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnUpdateRecOnBeforeValidateGenProdPostingGroup(var RecRef: RecordRef; FldRef: FieldRef; VatRateChangeConversion: Record "VAT Rate Change Conversion"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAddNewPurchaseLineOnAfterNewPurchaseLineInit(var OldPurchaseLine: Record "Purchase Line"; var NewPurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnUpdatePurchaseOnAfterPurchaseLineModify(VATRateChangeSetup: Record "VAT Rate Change Setup"; PurchaseHeader: Record "Purchase Header"; var PurchaseLine: Record "Purchase Line"; PurchaseLineOld: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnIncludePurchLineOnTypeElse(Type: Enum "Purchase Line Type"; var Result: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnIncludeSalesLineOnTypeElse(Type: Enum "Sales Line Type"; var Result: Boolean; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCanUpdatePurchaseOnAfterLoopIteration(var DescriptionTxt: Text[250]; PurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCanUpdatePurchaseOnAfterPurchaseLineSetFilters(var PurchaseLine: Record "Purchase Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCanUpdateSalesOnAfterLoopIteration(var DescriptionTxt: Text[250]; SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCanUpdateSalesOnAfterSalesLineSetFilters(var SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeDoUpdatePurchaseLine(PurchaseLine: Record "Purchase Line"; var IsHandled: Boolean; var CanUpdate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeDoUpdateSalesLine(SalesLine: Record "Sales Line"; var IsHandled: Boolean; var CanUpdate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCanUpdateSales(SalesHeader: Record "Sales Header"; ConvertVATProdPostingGroup: Boolean; ConvertGenProdPostingGroup: Boolean; var IsHandled: Boolean; var CanUpdate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCanUpdatePurchase(PurchaseHeader: Record "Purchase Header"; ConvertGenProdPostingGroup: Boolean; ConvertVATProdPostingGroup: Boolean; var IsHandled: Boolean; var CanUpdate: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAddNewSalesLine(SalesLine: Record "Sales Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20]; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeAddNewPurchaseLine(PurchaseLine: Record "Purchase Line"; VATProdPostingGroup: Code[20]; GenProdPostingGroup: Code[20]; var IsHandled: Boolean)
    begin
    end;
}