Codeunit 6638 Sales-Get Return Receipts

App
Base Application
Namespace
Microsoft.Sales.Document
Versions
17-28

Procedures, 4Events, 14

Versions171819202122232425262728

Source29

Source in 29

src/Layers/W1/BaseApp/Sales/Document/SalesGetReturnReceipts.Codeunit.al399 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.Sales.Document;

using Microsoft.Foundation.Attachment;
using Microsoft.Foundation.UOM;
using Microsoft.Sales.History;

/// <summary>
/// Retrieves return receipt lines to create credit memo lines for invoicing returned goods.
/// </summary>
codeunit 6638 "Sales-Get Return Receipts"
{
    TableNo = "Sales Line";

    trigger OnRun()
    var
        IsHandled: Boolean;
    begin
        CheckHeader(Rec);

        ReturnRcptLine.SetCurrentKey("Bill-to Customer No.");
        ReturnRcptLine.SetRange("Bill-to Customer No.", SalesHeader."Bill-to Customer No.");
        ReturnRcptLine.SetFilter("Return Qty. Rcd. Not Invd.", '<>0');
        ReturnRcptLine.SetRange("Currency Code", SalesHeader."Currency Code");
        OnRunOnAfterSetReturnRcptLineFilters(ReturnRcptLine, SalesHeader);

        IsHandled := false;
        OnRunOnBeforeGetReturnRcptLines(ReturnRcptLine, SalesHeader, IsHandled);
        if not IsHandled then begin
            GetReturnRcptLines.SetTableView(ReturnRcptLine);
            GetReturnRcptLines.LookupMode := true;
            GetReturnRcptLines.SetSalesHeader(SalesHeader);
            GetReturnRcptLines.RunModal();
        end;
    end;

    var
        SalesHeader: Record "Sales Header";
        SalesLine: Record "Sales Line";
        ReturnRcptHeader: Record "Return Receipt Header";
        ReturnRcptLine: Record "Return Receipt Line";
        UOMMgt: Codeunit "Unit of Measure Management";
        GetReturnRcptLines: Page "Get Return Receipt Lines";
        LineListHasAttachments: Dictionary of [Code[20], Boolean];

#pragma warning disable AA0074
#pragma warning disable AA0470
        Text001: Label 'The %1 on the %2 %3 and the %4 %5 must be the same.';
#pragma warning restore AA0470
#pragma warning restore AA0074

    /// <summary>
    /// Creates credit memo lines from return receipt lines.
    /// </summary>
    /// <param name="ReturnRcptLine2">The return receipt lines to create credit memo lines from.</param>
    procedure CreateInvLines(var ReturnRcptLine2: Record "Return Receipt Line")
    var
        DifferentCurrencies: Boolean;
        ShouldInsertReturnRcptLine: Boolean;
        IsHandled: Boolean;
        OrderNoList: List of [Code[20]];
    begin
        ReturnRcptLine2.SetFilter("Return Qty. Rcd. Not Invd.", '<>0');
        OnCreateInvLinesOnAfterReturnRcptLine2SetFilters(ReturnRcptLine2, SalesHeader);
        if ReturnRcptLine2.Find('-') then begin
            SalesLine.LockTable();
            SalesLine.SetRange("Document Type", SalesHeader."Document Type");
            SalesLine.SetRange("Document No.", SalesHeader."No.");
            SalesLine."Document Type" := SalesHeader."Document Type";
            SalesLine."Document No." := SalesHeader."No.";

            IsHandled := false;
            OnCreateInvLinesOnBeforeLoopReturnRcptLines(ReturnRcptLine2, SalesLine, ReturnRcptHeader, IsHandled);
            if IsHandled then
                exit;

            repeat
                if ReturnRcptHeader."No." <> ReturnRcptLine2."Document No." then begin
                    ReturnRcptHeader.Get(ReturnRcptLine2."Document No.");
                    CheckReturnReceiptBillToCustomerNo(ReturnRcptHeader, SalesHeader, ReturnRcptLine2);
                    DifferentCurrencies := false;
                    if ReturnRcptHeader."Currency Code" <> SalesHeader."Currency Code" then begin
                        Message(Text001,
                          SalesHeader.FieldCaption("Currency Code"),
                          SalesHeader.TableCaption, SalesHeader."No.",
                          ReturnRcptHeader.TableCaption(), ReturnRcptHeader."No.");
                        DifferentCurrencies := true;
                    end;
                    OnBeforeTransferLineToSalesDoc(ReturnRcptHeader, ReturnRcptLine2, SalesHeader, DifferentCurrencies);
                end;
                ShouldInsertReturnRcptLine := not DifferentCurrencies;
                OnCreateInvLinesOnAfterCalcShouldInsertReturnRcptLine(ReturnRcptHeader, ReturnRcptLine2, SalesHeader, ShouldInsertReturnRcptLine);
                if ShouldInsertReturnRcptLine then begin
                    ReturnRcptLine := ReturnRcptLine2;
                    CheckReturnReceiptLineVATBusPostingGroup(ReturnRcptLine2, SalesHeader);
                    ReturnRcptLine.InsertInvLineFromRetRcptLine(SalesLine);
                    CopyDocumentAttachments(ReturnRcptLine2, SalesLine);
                    if ReturnRcptLine2.Type = ReturnRcptLine2.Type::"Charge (Item)" then
                        GetItemChargeAssgnt(ReturnRcptLine2, SalesLine."Qty. to Invoice");
                end;
                OnCreateInvLinesOnAfterReturnRcptLoop(ShouldInsertReturnRcptLine, ReturnRcptHeader, ReturnRcptLine2, SalesHeader, SalesLine);
                if ReturnRcptLine2."Return Order No." <> '' then
                    if not OrderNoList.Contains(ReturnRcptLine2."Return Order No.") then
                        OrderNoList.Add(ReturnRcptLine2."Return Order No.");
            until ReturnRcptLine2.Next() = 0;
            CopyDocumentAttachments(OrderNoList, SalesHeader);
        end;

        OnAfterCreateInvLines(SalesHeader);
    end;

    local procedure CheckReturnReceiptLineVATBusPostingGroup(ReturnReceiptLine: Record "Return Receipt Line"; SalesHeader: Record "Sales Header")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeTestReturnReceiptLineVATBusPostingGroup(ReturnReceiptLine, SalesHeader, IsHandled);
        if IsHandled then
            exit;

        ReturnReceiptLine.TestField("VAT Bus. Posting Group", SalesHeader."VAT Bus. Posting Group");
    end;

    /// <summary>
    /// Sets the sales header for which to get return receipts.
    /// </summary>
    /// <param name="SalesHeader2">The sales credit memo header.</param>
    procedure SetSalesHeader(var SalesHeader2: Record "Sales Header")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnSetSalesHeaderOnBeforeTestIsCreditMemo(SalesHeader, IsHandled);
        if not IsHandled then begin
            SalesHeader.Get(SalesHeader2."Document Type", SalesHeader2."No.");
            SalesHeader.TestField("Document Type", SalesHeader."Document Type"::"Credit Memo");
        end;
    end;

    local procedure CheckHeader(SalesLine: Record "Sales Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckHeader(SalesHeader, SalesLine, IsHandled);
        if IsHandled then
            exit;

        SalesHeader.Get(SalesLine."Document Type", SalesLine."Document No.");
        SalesHeader.TestField("Document Type", SalesHeader."Document Type"::"Credit Memo");
        SalesHeader.TestField(Status, SalesHeader.Status::Open);
    end;

    local procedure CheckReturnReceiptBillToCustomerNo(ReturnReceiptHeader: Record "Return Receipt Header"; SalesHeader2: Record "Sales Header"; ReturnReceiptLine: Record "Return Receipt Line")
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckReturnReceiptBillToCustomerNo(ReturnReceiptHeader, SalesHeader2, ReturnReceiptLine, IsHandled);
        if IsHandled then
            exit;

        ReturnReceiptHeader.TestField("Bill-to Customer No.", ReturnReceiptLine."Bill-to Customer No.");
    end;

    /// <summary>
    /// Gets item charge assignments from the return receipt line.
    /// </summary>
    /// <param name="ReturnRcptLine">The return receipt line with item charges.</param>
    /// <param name="QtyToInv">The quantity to invoice.</param>
    procedure GetItemChargeAssgnt(var ReturnRcptLine: Record "Return Receipt Line"; QtyToInv: Decimal)
    var
        SalesOrderLine: Record "Sales Line";
        ItemChargeAssgntSales: Record "Item Charge Assignment (Sales)";
    begin
        if not SalesOrderLine.Get(SalesOrderLine."Document Type"::"Return Order", ReturnRcptLine."Return Order No.", ReturnRcptLine."Return Order Line No.") then
            exit;

        ItemChargeAssgntSales.LockTable();
        ItemChargeAssgntSales.Reset();
        ItemChargeAssgntSales.SetRange("Document Type", SalesOrderLine."Document Type");
        ItemChargeAssgntSales.SetRange("Document No.", SalesOrderLine."Document No.");
        ItemChargeAssgntSales.SetRange("Document Line No.", SalesOrderLine."Line No.");
        ItemChargeAssgntSales.CalcSums("Qty. to Assign");
        if ItemChargeAssgntSales."Qty. to Assign" <> 0 then
            CopyItemChargeAssgnt(
              SalesOrderLine, ReturnRcptLine, ItemChargeAssgntSales."Qty. to Assign",
              QtyToInv / ItemChargeAssgntSales."Qty. to Assign");
    end;

    local procedure CopyItemChargeAssgnt(SalesOrderLine: Record "Sales Line"; ReturnRcptLine: Record "Return Receipt Line"; QtyToAssign: Decimal; QtyFactor: Decimal)
    var
        ReturnRcptLine2: Record "Return Receipt Line";
        SalesLine2: Record "Sales Line";
        ItemChargeAssgntSales: Record "Item Charge Assignment (Sales)";
        ItemChargeAssgntSales2: Record "Item Charge Assignment (Sales)";
        InsertChargeAssgnt: Boolean;
    begin
        ItemChargeAssgntSales.SetRange("Document Type", SalesOrderLine."Document Type");
        ItemChargeAssgntSales.SetRange("Document No.", SalesOrderLine."Document No.");
        ItemChargeAssgntSales.SetRange("Document Line No.", SalesOrderLine."Line No.");
        if ItemChargeAssgntSales.FindSet() then
            repeat
                if ItemChargeAssgntSales."Qty. to Assign" <> 0 then begin
                    ItemChargeAssgntSales2 := ItemChargeAssgntSales;
                    ItemChargeAssgntSales2."Qty. to Assign" :=
                      Round(QtyFactor * ItemChargeAssgntSales2."Qty. to Assign", UOMMgt.QtyRndPrecision());
                    SalesLine2.SetRange("Return Receipt No.", ReturnRcptLine."Document No.");
                    SalesLine2.SetRange("Return Receipt Line No.", ReturnRcptLine."Line No.");
                    SalesLine2.SetAutoCalcFields("Qty. to Assign");
                    if SalesLine2.Find('-') then
                        repeat
                            InsertChargeAssgnt := SalesLine2."Qty. to Assign" <> SalesLine2.Quantity;
                        until (SalesLine2.Next() = 0) or InsertChargeAssgnt;

                    if InsertChargeAssgnt then begin
                        ItemChargeAssgntSales2."Document Type" := SalesLine2."Document Type";
                        ItemChargeAssgntSales2."Document No." := SalesLine2."Document No.";
                        ItemChargeAssgntSales2."Document Line No." := SalesLine2."Line No.";
                        ItemChargeAssgntSales2."Qty. Assigned" := 0;
                        if Abs(QtyToAssign) < Abs(ItemChargeAssgntSales2."Qty. to Assign") then
                            ItemChargeAssgntSales2."Qty. to Assign" := QtyToAssign;
                        if Abs(SalesLine2.Quantity - SalesLine2."Qty. to Assign") <
                           Abs(ItemChargeAssgntSales2."Qty. to Assign")
                        then
                            ItemChargeAssgntSales2."Qty. to Assign" :=
                              SalesLine2.Quantity - SalesLine2."Qty. to Assign";
                        ItemChargeAssgntSales2.Validate("Unit Cost");

                        if ItemChargeAssgntSales2."Applies-to Doc. Type" = SalesOrderLine."Document Type" then begin
                            ItemChargeAssgntSales2."Applies-to Doc. Type" := SalesLine2."Document Type";
                            ItemChargeAssgntSales2."Applies-to Doc. No." := SalesLine2."Document No.";
                            ReturnRcptLine2.SetCurrentKey("Return Order No.", "Return Order Line No.");
                            ReturnRcptLine2.SetRange("Return Order No.", ItemChargeAssgntSales."Applies-to Doc. No.");
                            ReturnRcptLine2.SetRange("Return Order Line No.", ItemChargeAssgntSales."Applies-to Doc. Line No.");
                            ReturnRcptLine2.SetFilter(Quantity, '<>0');
                            if ReturnRcptLine2.FindFirst() then begin
                                SalesLine2.SetCurrentKey("Document Type", "Shipment No.", "Shipment Line No.");
                                SalesLine2.SetRange("Document Type", SalesOrderLine."Document Type"::"Credit Memo");
                                SalesLine2.SetRange("Return Receipt No.", ReturnRcptLine2."Document No.");
                                SalesLine2.SetRange("Return Receipt Line No.", ReturnRcptLine2."Line No.");
                                if SalesLine2.FindFirst() and (SalesLine2.Quantity <> 0) then
                                    ItemChargeAssgntSales2."Applies-to Doc. Line No." := SalesLine2."Line No."
                                else
                                    InsertChargeAssgnt := false;
                            end else
                                InsertChargeAssgnt := false;
                        end;
                    end;

                    if InsertChargeAssgnt and (ItemChargeAssgntSales2."Qty. to Assign" <> 0) then begin
                        ItemChargeAssgntSales2.Insert();
                        QtyToAssign := QtyToAssign - ItemChargeAssgntSales2."Qty. to Assign";
                    end;
                end;
            until ItemChargeAssgntSales.Next() = 0;
    end;

    /// <summary>
    /// Gets posted sales credit memos related to a return order.
    /// </summary>
    /// <param name="TempSalesCrMemoHeader">Returns the temporary table with related credit memo headers.</param>
    /// <param name="ReturnOrderNo">The return order number to find credit memos for.</param>
    procedure GetSalesRetOrderCrMemos(var TempSalesCrMemoHeader: Record "Sales Cr.Memo Header" temporary; ReturnOrderNo: Code[20])
    var
        SalesCrMemoHeader: Record "Sales Cr.Memo Header";
        SalesCrMemosByRetOrder: Query "Sales Cr. Memos By Ret. Order";
    begin
        TempSalesCrMemoHeader.Reset();
        TempSalesCrMemoHeader.DeleteAll();

        SalesCrMemosByRetOrder.SetRange(Order_No_, ReturnOrderNo);
        SalesCrMemosByRetOrder.SetFilter(Quantity, '<>0');
        SalesCrMemosByRetOrder.Open();

        while SalesCrMemosByRetOrder.Read() do begin
            SalesCrMemoHeader.Get(SalesCrMemosByRetOrder.Document_No_);
            TempSalesCrMemoHeader := SalesCrMemoHeader;
            TempSalesCrMemoHeader.Insert();
        end;
    end;

    local procedure AnyLineHasAttachments(DocNo: Code[20]): boolean
    begin
        if not LineListHasAttachments.ContainsKey(DocNo) then
            LineListHasAttachments.Add(DocNo, EntityHasAttachments(DocNo, Database::"Sales Line"));
        exit(LineListHasAttachments.Get(DocNo));
    end;

    local procedure EntityHasAttachments(DocNo: Code[20]; TableNo: Integer): boolean
    var
        DocumentAttachment: Record "Document Attachment";
    begin
        DocumentAttachment.ReadIsolation := IsolationLevel::ReadUncommitted;
        DocumentAttachment.SetRange("Table ID", TableNo);
        DocumentAttachment.SetRange("Document Type", DocumentAttachment."Document Type"::"Return Order");
        DocumentAttachment.SetRange("No.", DocNo);
        exit(not DocumentAttachment.IsEmpty());
    end;

    local procedure CopyDocumentAttachments(var ReturnRcptLine2: Record "Return Receipt Line"; var SalesLine2: Record "Sales Line")
    var
        OrderSalesLine: Record "Sales Line";
        DocumentAttachmentMgmt: Codeunit "Document Attachment Mgmt";
    begin
        if (ReturnRcptLine2."Return Order No." = '') or (ReturnRcptLine2."Return Order Line No." = 0) then
            exit;
        if not AnyLineHasAttachments(ReturnRcptLine2."Return Order No.") then
            exit;
        OrderSalesLine.ReadIsolation := IsolationLevel::ReadCommitted;
        OrderSalesLine.SetLoadFields("Document Type", "Document No.", "Line No.");
        if OrderSalesLine.Get(OrderSalesLine."Document Type"::"Return Order", ReturnRcptLine2."Return Order No.", ReturnRcptLine2."Return Order Line No.") then
            DocumentAttachmentMgmt.CopyAttachments(OrderSalesLine, SalesLine2);
    end;

    local procedure CopyDocumentAttachments(OrderNoList: List of [Code[20]]; var SalesHeader2: Record "Sales Header")
    var
        OrderSalesHeader: Record "Sales Header";
        DocumentAttachmentMgmt: Codeunit "Document Attachment Mgmt";
        OrderNo: Code[20];
    begin
        OrderSalesHeader.ReadIsolation := IsolationLevel::ReadCommitted;
        OrderSalesHeader.SetLoadFields("Document Type", "No.");
        foreach OrderNo in OrderNoList do
            if OrderHasAttachments(OrderNo) then
                if OrderSalesHeader.Get(OrderSalesHeader."Document Type"::"Return Order", OrderNo) then
                    DocumentAttachmentMgmt.CopyAttachments(OrderSalesHeader, SalesHeader2);
    end;

    local procedure OrderHasAttachments(DocNo: Code[20]): boolean
    begin
        exit(EntityHasAttachments(DocNo, Database::"Sales Header"));
    end;

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

    [IntegrationEvent(false, false)]
    local procedure OnBeforeTransferLineToSalesDoc(ReturnReceiptHeader: Record "Return Receipt Header"; ReturnReceiptLine: Record "Return Receipt Line"; var SalesHeader: Record "Sales Header"; var TransferLine: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateInvLinesOnAfterCalcShouldInsertReturnRcptLine(var ReturnReceiptHeader: Record "Return Receipt Header"; var ReturnReceiptLine: Record "Return Receipt Line"; var SalesHeader: Record "Sales Header"; var ShouldInsertReturnRcptLine: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateInvLinesOnAfterReturnRcptLine2SetFilters(var ReturnReceiptLine: Record "Return Receipt Line"; var SalesHeader: Record "Sales Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateInvLinesOnAfterReturnRcptLoop(ShouldInsertReturnRcptLine: Boolean; ReturnReceiptHeader: Record "Return Receipt Header"; ReturnReceiptLine: Record "Return Receipt Line"; SalesHeader: Record "Sales Header"; SalesLine: Record "Sales Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRunOnAfterSetReturnRcptLineFilters(var ReturnReceiptLine: Record "Return Receipt Line"; SalesHeader: Record "Sales Header")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeTestReturnReceiptLineVATBusPostingGroup(ReturnReceiptLine: Record "Return Receipt Line"; SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnRunOnBeforeGetReturnRcptLines(var ReturnReceiptLine: Record "Return Receipt Line"; SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnCreateInvLinesOnBeforeLoopReturnRcptLines(var ReturnReceiptLine: Record "Return Receipt Line"; var SalesLine: Record "Sales Line"; var ReturnReceiptHeader: Record "Return Receipt Header"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckReturnReceiptBillToCustomerNo(ReturnReceiptHeader: Record "Return Receipt Header"; SalesHeader: Record "Sales Header"; var ReturnReceiptLine: Record "Return Receipt Line"; var IsHandled: Boolean)
    begin
    end;

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

    [IntegrationEvent(false, false)]
    local procedure OnSetSalesHeaderOnBeforeTestIsCreditMemo(var SalesHeader: Record "Sales Header"; var IsHandled: Boolean)
    begin
    end;
}