Table 951 Time Sheet Line, source in 29

Source29

src/Layers/W1/BaseApp/Projects/TimeSheet/TimeSheetLine.Table.al582 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.Projects.TimeSheet;

using Microsoft.Assembly.Document;
using Microsoft.HumanResources.Absence;
using Microsoft.HumanResources.Employee;
using Microsoft.Projects.Project.Job;
using Microsoft.Projects.Resources.Resource;
using Microsoft.Projects.Resources.Setup;
using Microsoft.Utilities;
using System.Security.User;

table 951 "Time Sheet Line"
{
    Caption = 'Time Sheet Line';
    Permissions = TableData Employee = r;
    DataClassification = CustomerContent;

    fields
    {
        field(1; "Time Sheet No."; Code[20])
        {
            Caption = 'Time Sheet No.';
            ToolTip = 'Specifies the number of the related document.';
            TableRelation = "Time Sheet Header";
        }
        field(2; "Line No."; Integer)
        {
            Caption = 'Line No.';
        }
        field(3; "Time Sheet Starting Date"; Date)
        {
            Caption = 'Time Sheet Starting Date';
            ToolTip = 'Specifies the starting date for a time sheet.';
            Editable = false;
        }
        field(5; Type; Enum "Time Sheet Line Type")
        {
            Caption = 'Type';
            ToolTip = 'Specifies the type of time sheet line.';

            trigger OnValidate()
            begin
                TestStatus();
                if Type = Type::"Assembly Order" then
                    FieldError(Type);
                if Type <> xRec.Type then begin
                    TimeSheetDetail.SetRange("Time Sheet No.", "Time Sheet No.");
                    TimeSheetDetail.SetRange("Time Sheet Line No.", "Line No.");
                    if not TimeSheetDetail.IsEmpty() then
                        TimeSheetDetail.DeleteAll();
                    "Job No." := '';
                    Clear("Job Id");
                    "Job Task No." := '';
                    "Cause of Absence Code" := '';
                    Description := '';
                    "Assembly Order No." := '';
                    "Assembly Order Line No." := 0;
                    OnValidateTypeOnAfterClearFields(Rec);

                    UpdateApproverID();
                    if Type = Type::Absence then
                        CheckIsEmployeeLinkedToResource();
                end;
            end;
        }
        field(6; "Job No."; Code[20])
        {
            Caption = 'Project No.';
            ToolTip = 'Specifies the number for the project that is associated with the time sheet line.';
            TableRelation = Job where(Status = filter(Open));

            trigger OnValidate()
            begin
                if "Job No." <> '' then begin
                    TestField(Type, Type::Job);
                    Job.Get("Job No.");
                    if Job.Blocked = Job.Blocked::All then
                        Job.TestBlocked();
                    if Job.Status = Job.Status::Completed then
                        Job.TestStatusCompleted();
                end;
                Validate("Job Task No.", '');
                UpdateApproverID();
                UpdateJobId();
            end;
        }
        field(7; "Job Task No."; Code[20])
        {
            Caption = 'Project Task No.';
            ToolTip = 'Specifies the number of the related project task.';
            TableRelation = "Job Task"."Job Task No." where("Job No." = field("Job No."), "Job Task Type" = filter(Posting));

            trigger OnValidate()
            begin
                if "Job Task No." <> '' then begin
                    TestField(Type, Type::Job);
                    JobTask.Get("Job No.", "Job Task No.");
                    JobTask.TestField("Job Task Type", JobTask."Job Task Type"::Posting);
                    Description := JobTask.Description;
                end;
            end;
        }
        field(9; "Cause of Absence Code"; Code[10])
        {
            Caption = 'Cause of Absence Code';
            ToolTip = 'Specifies a list of standard absence codes, from which you may select one.';
            TableRelation = "Cause of Absence";

            trigger OnValidate()
            var
                CauseOfAbsence: Record "Cause of Absence";
            begin
                if "Cause of Absence Code" <> '' then begin
                    TestField(Type, Type::Absence);
                    CauseOfAbsence.Get("Cause of Absence Code");
                    Description := CauseOfAbsence.Description;
                    CheckIsEmployeeLinkedToResource();
                end;
            end;
        }
        field(10; Description; Text[100])
        {
            Caption = 'Description';
            ToolTip = 'Specifies a description of the time sheet line.';

            trigger OnValidate()
            begin
                TestStatus();
            end;
        }
        field(11; "Work Type Code"; Code[10])
        {
            Caption = 'Work Type Code';
            ToolTip = 'Specifies which work type the resource applies to. Prices are updated based on this entry.';
            TableRelation = "Work Type";

            trigger OnValidate()
            begin
                if ("Work Type Code" <> xRec."Work Type Code") and ("Work Type Code" <> '') then
                    CheckWorkType();
            end;
        }
        field(12; "Approver ID"; Code[50])
        {
            Caption = 'Approver ID';
            DataClassification = EndUserIdentifiableInformation;
            Editable = false;
            TableRelation = "User Setup";
        }
        field(13; "Service Order No."; Code[20])
        {
            Caption = 'Service Order No.';
            ToolTip = 'Specifies the service order number that is associated with the time sheet line.';
        }
        field(14; "Service Order Line No."; Integer)
        {
            Caption = 'Service Order Line No.';
        }
        field(15; "Total Quantity"; Decimal)
        {
            AutoFormatType = 0;
            CalcFormula = sum("Time Sheet Detail".Quantity where("Time Sheet No." = field("Time Sheet No."),
                                                                  "Time Sheet Line No." = field("Line No.")));
            Caption = 'Total Quantity';
            ToolTip = 'Specifies the total number of hours that have been entered on a time sheet.';
            Editable = false;
            FieldClass = FlowField;
        }
        field(17; Chargeable; Boolean)
        {
            Caption = 'Chargeable';
            ToolTip = 'Specifies if the usage that you are posting is chargeable.';
            InitValue = true;
        }
        field(18; "Assembly Order No."; Code[20])
        {
            Caption = 'Assembly Order No.';
            ToolTip = 'Specifies the assembly order number that is associated with the time sheet line.';
            Editable = false;
            TableRelation = if (Posted = const(false)) "Assembly Header"."No." where("Document Type" = const(Order));
        }
        field(19; "Assembly Order Line No."; Integer)
        {
            Caption = 'Assembly Order Line No.';
            Editable = false;
        }
        field(20; Status; Enum "Time Sheet Status")
        {
            Caption = 'Status';
            ToolTip = 'Specifies information about the status of a time sheet line.';
            Editable = false;
        }
        field(21; "Approved By"; Code[50])
        {
            Caption = 'Approved By';
            DataClassification = EndUserIdentifiableInformation;
            Editable = false;
            TableRelation = "User Setup";
        }
        field(22; "Approval Date"; Date)
        {
            Caption = 'Approval Date';
            Editable = false;
        }
        field(23; Posted; Boolean)
        {
            Caption = 'Posted';
            ToolTip = 'Specifies whether a time sheet line has been posted completely.';
            Editable = false;
        }
        field(26; Comment; Boolean)
        {
            CalcFormula = exist("Time Sheet Comment Line" where("No." = field("Time Sheet No."),
                                                                 "Time Sheet Line No." = field("Line No.")));
            Caption = 'Comment';
            ToolTip = 'Specifies that a comment about this document has been entered.';
            Editable = false;
            FieldClass = FlowField;
        }
        field(480; "Dimension Set ID"; Integer)
        {
            Caption = 'Dimension Set ID';
            DataClassification = SystemMetadata;
            Editable = false;
        }
        field(8002; "Job Id"; Guid)
        {
            Caption = 'Project Id';
            DataClassification = SystemMetadata;
            TableRelation = Job.SystemId;
        }
    }

    keys
    {
        key(Key1; "Time Sheet No.", "Line No.")
        {
            Clustered = true;
        }
        key(Key2; Type)
        {
        }
        key(Key3; "Time Sheet No.", Status, Posted)
        {
        }
        key(Key4; Type, "Job No.")
        {
        }
    }

    fieldgroups
    {
        fieldgroup(DropDown; Type, Description, "Total Quantity", Status)
        {
        }
        fieldgroup(Brick; Type, Description, "Total Quantity", Status)
        {
        }
    }

    trigger OnDelete()
    var
        TimeSheetCommentLine: Record "Time Sheet Comment Line";
        Resource: Record Resource;
    begin
        TestStatus();

        GetTimeSheetResource(Resource);
        CheckResourcePrivacyBlocked(Resource);
        Resource.TestField(Blocked, false);

        TimeSheetDetail.SetRange("Time Sheet No.", "Time Sheet No.");
        TimeSheetDetail.SetRange("Time Sheet Line No.", "Line No.");
        TimeSheetDetail.DeleteAll();

        TimeSheetCommentLine.SetRange("No.", "Time Sheet No.");
        TimeSheetCommentLine.SetRange("Time Sheet Line No.", "Line No.");
        TimeSheetCommentLine.DeleteAll();
    end;

    trigger OnInsert()
    var
        Resource: Record Resource;
    begin
        GetTimeSheetResource(Resource);
        CheckResourcePrivacyBlocked(Resource);
        Resource.TestField(Blocked, false);

        UpdateApproverID();
        "Time Sheet Starting Date" := TimeSheetHeader."Starting Date";
    end;

    trigger OnModify()
    var
        Resource: Record Resource;
    begin
        GetTimeSheetResource(Resource);
        CheckResourcePrivacyBlocked(Resource);
        Resource.TestField(Blocked, false);

        UpdateDetails();
    end;

    var
        ResourcesSetup: Record "Resources Setup";
        Job: Record Job;
        JobTask: Record "Job Task";
        TimeSheetHeader: Record "Time Sheet Header";
        TimeSheetDetail: Record "Time Sheet Detail";
#pragma warning disable AA0074
#pragma warning disable AA0470
        Text001: Label 'There is no employee linked with resource %1.';
        Text002: Label 'Status must be Open or Rejected in line with Time Sheet No.=''%1'', Line No.=''%2''.';
#pragma warning restore AA0470
        Text005: Label 'Select a type before you enter an activity.';
#pragma warning restore AA0074
        PrivacyBlockedErr: Label 'You cannot use resource %1 because they are marked as blocked due to privacy.', Comment = '%1=resource no.';

    procedure TestStatus()
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeTestStatus(IsHandled, Rec);
        if IsHandled then
            exit;

        if not (Status in [Status::Open, Status::Rejected]) then
            Error(
              Text002,
              "Time Sheet No.",
              "Line No.");
    end;

    local procedure CheckIsEmployeeLinkedToResource()
    var
        Resource: Record Resource;
        Employee: Record Employee;
    begin
        GetTimeSheetResource(Resource);
        Resource.TestField("Base Unit of Measure");
        Resource.TestField(Type, Resource.Type::Person);
        Employee.Reset();
        Employee.SetRange("Resource No.", TimeSheetHeader."Resource No.");
        if Employee.IsEmpty() then
            Error(Text001, TimeSheetHeader."Resource No.");
    end;

    local procedure UpdateDetails()
    var
        TimeSheetDetail: Record "Time Sheet Detail";
    begin
        TimeSheetDetail.SetRange("Time Sheet No.", "Time Sheet No.");
        TimeSheetDetail.SetRange("Time Sheet Line No.", "Line No.");
        if TimeSheetDetail.FindSet(true) then
            repeat
                TimeSheetDetail.CopyFromTimeSheetLine(Rec);
                TimeSheetDetail.Modify(true);
            until TimeSheetDetail.Next() = 0;
    end;

    local procedure GetTimeSheetResource(var Resource: Record Resource)
    begin
        TimeSheetHeader.Get("Time Sheet No.");
        Resource.Get(TimeSheetHeader."Resource No.");
    end;

    local procedure GetJobApproverID() ApproverID: Code[50]
    var
        Resource: Record Resource;
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeGetJobApproverID(ApproverID, IsHandled);
        if IsHandled then
            exit(ApproverID);

        Job.Get("Job No.");
        Job.TestField("Person Responsible");
        Resource.Get(Job."Person Responsible");
        Resource.TestField("Time Sheet Owner User ID");
        exit(Resource."Time Sheet Owner User ID");
    end;

    procedure UpdateApproverID()
    var
        Resource: Record Resource;
    begin
        ResourcesSetup.Get();
        GetTimeSheetResource(Resource);
        if (Type = Type::Job) and ("Job No." <> '') and
           (((Resource.Type = Resource.Type::Person) and
             (ResourcesSetup."Time Sheet by Job Approval" = ResourcesSetup."Time Sheet by Job Approval"::Always)) or
            ((Resource.Type = Resource.Type::Machine) and
             (ResourcesSetup."Time Sheet by Job Approval" in [ResourcesSetup."Time Sheet by Job Approval"::"Machine Only",
                                                              ResourcesSetup."Time Sheet by Job Approval"::Always])))
        then
            "Approver ID" := GetJobApproverID()
        else
            SetApproverIDFromResource(Resource);
        OnAfterUpdateApproverID(Rec);
    end;

    local procedure SetApproverIDFromResource(Resource: Record Resource)
    var
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeSetApproverIDFromResource(Resource, IsHandled);
        if IsHandled then
            exit;

        Resource.TestField("Time Sheet Approver User ID");
        "Approver ID" := Resource."Time Sheet Approver User ID";
    end;

    local procedure CheckWorkType()
    var
        Resource: Record Resource;
        WorkType: Record "Work Type";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeCheckWorkType(Rec, IsHandled);
        if IsHandled then
            exit;

        if WorkType.Get("Work Type Code") then begin
            GetTimeSheetResource(Resource);
            WorkType.TestField("Unit of Measure Code", Resource."Base Unit of Measure");
        end;
    end;

    procedure ShowLineDetails(ManagerRole: Boolean)
    var
        TimeSheetLineResDetail: Page "Time Sheet Line Res. Detail";
        TimeSheetLineJobDetail: Page "Time Sheet Line Job Detail";
        TimeSheetLineAssembDetail: Page "Time Sheet Line Assemb. Detail";
        TimeSheetLineAbsenceDetail: Page "Time Sheet Line Absence Detail";
        IsHandled: Boolean;
    begin
        IsHandled := false;
        OnBeforeShowLineDetails(Rec, IsHandled, ManagerRole);
        if IsHandled then
            exit;

        case Type of
            Type::Resource:
                begin
                    TimeSheetLineResDetail.SetParameters(Rec, ManagerRole);
                    if TimeSheetLineResDetail.RunModal() = ACTION::OK then
                        TimeSheetLineResDetail.GetRecord(Rec);
                end;
            Type::Job:
                begin
                    TimeSheetLineJobDetail.SetParameters(Rec, ManagerRole);
                    if TimeSheetLineJobDetail.RunModal() = ACTION::OK then
                        TimeSheetLineJobDetail.GetRecord(Rec);
                end;
            Type::Absence:
                begin
                    TimeSheetLineAbsenceDetail.SetParameters(Rec, ManagerRole);
                    if TimeSheetLineAbsenceDetail.RunModal() = ACTION::OK then
                        TimeSheetLineAbsenceDetail.GetRecord(Rec);
                end;
            Type::"Assembly Order":
                begin
                    TimeSheetLineAssembDetail.SetParameters(Rec);
                    if TimeSheetLineAssembDetail.RunModal() = ACTION::OK then
                        TimeSheetLineAssembDetail.GetRecord(Rec);
                end;
            else
                Error(Text005);
        end;
        Modify();
    end;

    procedure GetAllowEdit(FldNo: Integer; ManagerRole: Boolean) AllowEdit: Boolean
    begin
        if ManagerRole then
            AllowEdit := (FldNo in [FieldNo("Work Type Code"), FieldNo(Chargeable)]) and (Status = Status::Submitted)
        else
            AllowEdit := Status in [Status::Open, Status::Rejected];

        OnAfterGetAllowEdit(FldNo, ManagerRole, AllowEdit);
    end;

    local procedure CheckResourcePrivacyBlocked(Resource: Record Resource)
    begin
        if Resource."Privacy Blocked" then
            Error(PrivacyBlockedErr, Resource."No.");
    end;

    procedure CheckIfTimeSheetLineLinkExist(Job: Record Job)
    var
        TimeSheetLineExistsForJobErr: Label 'One or more unposted Time Sheet lines exists for the project %1.\\You must post or delete the time sheet lines before you can change the project status.', Comment = '%1 = Project No.';
    begin
        if Job.Status = Job.Status::Open then
            exit;

        SetCurrentKey(Type, "Job No.");
        SetRange(Type, Type::Job);
        SetRange("Job No.", Job."No.");
        SetRange(Posted, false);
        if not IsEmpty() then
            Error(TimeSheetLineExistsForJobErr, Job."No.");
    end;

    [Scope('OnPrem')]
    procedure UpdateJobId()
    var
        Job: Record Job;
    begin
        if "Job No." = '' then begin
            Clear("Job Id");
            exit;
        end;

        if not Job.Get("Job No.") then
            exit;

        "Job Id" := Job.SystemId;
    end;

    procedure SetExclusionTypeFilter()
    begin
        SetFilter(Type, '<>%1', Type::"Assembly Order");

        OnAfterSetExclusionTypeFilter(Rec);
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterUpdateApproverID(var TimeSheetLine: Record "Time Sheet Line")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnBeforeTestStatus(var IsHandled: Boolean; var TimeSheetLine: Record "Time Sheet Line")
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnBeforeGetJobApproverID(var ApproverID: Code[50]; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnBeforeSetApproverIDFromResource(Resource: Record Resource; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeShowLineDetails(var TimeSheetLine: Record "Time Sheet Line"; var IsHandled: Boolean; ManagerRole: Boolean)
    begin
    end;

    [IntegrationEvent(true, false)]
    local procedure OnAfterGetAllowEdit(FldNo: Integer; ManagerRole: Boolean; var AllowEdit: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnBeforeCheckWorkType(var TimeSheetLine: Record "Time Sheet Line"; var IsHandled: Boolean)
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnValidateTypeOnAfterClearFields(var TimeSheetLine: Record "Time Sheet Line")
    begin
    end;

    [IntegrationEvent(false, false)]
    local procedure OnAfterSetExclusionTypeFilter(var TimeSheetLine: Record "Time Sheet Line")
    begin
    end;
}