diff --git a/src/Apps/W1/Quality Management/app/src/API/QltyCreateInspectionAPI.Page.al b/src/Apps/W1/Quality Management/app/src/API/QltyCreateInspectionAPI.Page.al new file mode 100644 index 0000000000..9db6492530 --- /dev/null +++ b/src/Apps/W1/Quality Management/app/src/API/QltyCreateInspectionAPI.Page.al @@ -0,0 +1,149 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.QualityManagement.API; +using Microsoft.Utilities; +using Microsoft.QualityManagement.Utilities; +using Microsoft.QualityManagement.Document; + +/// +/// Power automate friendly web service for quality inspections. +/// This web service is used to help create tests. +/// +page 20415 "Qlty. Create Inspection API" +{ + APIVersion = 'v2.0'; + APIGroup = 'qualityinspection'; + APIPublisher = 'insightworks'; + Caption = 'qltyCreateInspection', Locked = true; + DelayedInsert = true; + DeleteAllowed = false; + Editable = false; + EntityName = 'qltyCreateInspectionOnRecord'; + EntitySetName = 'qltyCreateInspectionOnRecords'; + EntityCaption = 'Any Record in Business Central'; + EntitySetCaption = 'Records'; + InsertAllowed = false; + ModifyAllowed = false; + PageType = API; + RefreshOnActivate = true; + SourceTable = "Name/Value Buffer"; + SourceTableTemporary = true; + ODataKeyFields = SystemId; + layout + { + area(Content) + { + repeater(rptTests) + { + ShowCaption = false; + field(qltySystemIDOfAnyRecord; Rec.SystemId) + { + Caption = 'qltySystemIDOfAnyRecord', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the system id of the record to create a test for.'; + } + } + } + } + + var + systemRecord: Guid; + currentTable: Integer; + NoSystemIDRecordErr: Label 'Business Central cannot find a record for the system id of %1', Locked = true; + OnlyOneRecordForTableAndFilterErr: Label 'Please check your PowerAutomate configuration. 1 record should have been found, but %1 records were found for table %2 and filter %3.', Comment = '%1=the count, %2=the table, %3=the filter'; + + trigger OnFindRecord(Which: Text): Boolean + var + FilterGroupIterator: Integer; + begin + FilterGroupIterator := 4; + repeat + Rec.FilterGroup(FilterGroupIterator); + if Rec.GetFilter(SystemId) <> '' then + systemRecord := Rec.GetRangeMin(SystemId); + + if Rec.GetFilter(ID) <> '' then + currentTable := Rec.GetRangeMin(ID); + + FilterGroupIterator -= 1; + until (FilterGroupIterator < 0); + Rec.FilterGroup(0); + Rec.ID := currentTable; + Rec.SystemId := systemRecord; + if Rec.Insert() then; // this is to work around BC needing the system id on any page action when used as a webservice. + exit(Rec.Find(Which)); + end; + + // Min of BC 16 for the system ID and GetBySystemId + /// + /// Minimum of BC 16 is needed. + /// Create a test from a known table. + /// + /// The table ID or table name to create a test + /// + [ServiceEnabled] + procedure CreateInspectionFromRecordID(var ActionContext: WebServiceActionContext; tableName: Text) + var + CreatedInspection: Record "Qlty. Inspection Header"; + QltyInspectionCreate: Codeunit "Qlty. Inspection - Create"; + QltyFilterHelpers: Codeunit "Qlty. Filter Helpers"; + AnyInputRecord: RecordRef; + begin + Rec.ID := QltyFilterHelpers.IdentifyTableIDFromText(tableName); + + AnyInputRecord.Open(Rec.ID); + + if not AnyInputRecord.GetBySystemId(Rec.SystemId) then + Error(NoSystemIDRecordErr, Rec.SystemId); + + if QltyInspectionCreate.CreateInspection(AnyInputRecord, true) then begin + QltyInspectionCreate.GetCreatedInspection(CreatedInspection); + ActionContext.SetObjectType(ObjectType::Table); + ActionContext.SetObjectId(Database::"Name/Value Buffer"); + ActionContext.AddEntityKey(CreatedInspection.FieldNo(SystemId), CreatedInspection.SystemId); + ActionContext.SetResultCode(WebServiceActionResultCode::Created); + if Rec.IsTemporary then Rec.DeleteAll(); + Rec.SystemId := CreatedInspection.SystemId; + if Rec.Insert() then; + end else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + end; + + /// + /// Creates a test with a table and table filter to identify a record. + /// + /// VAR WebServiceActionContext. + /// Text. The table ID, or table name, or table caption. + /// The table filter that can identify a specific record. + [ServiceEnabled] + procedure CreateInspectionFromTableIDAndFilter(var ActionContext: WebServiceActionContext; tableName: Text; tableNameFilter: Text) + var + CreatedInspection: Record "Qlty. Inspection Header"; + QltyInspectionCreate: Codeunit "Qlty. Inspection - Create"; + QltyFilterHelpers: Codeunit "Qlty. Filter Helpers"; + AnyInputRecord: RecordRef; + begin + Rec.ID := QltyFilterHelpers.IdentifyTableIDFromText(tableName); + AnyInputRecord.Open(Rec.ID); + AnyInputRecord.SetView(tableNameFilter); + if not AnyInputRecord.FindSet(false) then + Error(OnlyOneRecordForTableAndFilterErr, 0, Rec.ID, tableNameFilter); + + if AnyInputRecord.Count() <> 1 then + Error(OnlyOneRecordForTableAndFilterErr, AnyInputRecord.Count(), Rec.ID, tableNameFilter); + + if QltyInspectionCreate.CreateInspection(AnyInputRecord, true) then begin + QltyInspectionCreate.GetCreatedInspection(CreatedInspection); + ActionContext.SetObjectType(ObjectType::Table); + ActionContext.SetObjectId(Database::"Name/Value Buffer"); + ActionContext.AddEntityKey(CreatedInspection.FieldNo(SystemId), CreatedInspection.SystemId); + ActionContext.SetResultCode(WebServiceActionResultCode::Created); + if Rec.IsTemporary then Rec.DeleteAll(); + Rec.SystemId := CreatedInspection.SystemId; + if Rec.Insert() then; + end else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + end; +} diff --git a/src/Apps/W1/Quality Management/app/src/API/QltyEventCategory.EnumExt.al b/src/Apps/W1/Quality Management/app/src/API/QltyEventCategory.EnumExt.al new file mode 100644 index 0000000000..faa66ddfcb --- /dev/null +++ b/src/Apps/W1/Quality Management/app/src/API/QltyEventCategory.EnumExt.al @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.QualityManagement.API; + +using System.Integration; + +/// +/// Used for external business events, such as power automate integration. +/// +enumextension 20403 QltyEventCategory extends EventCategory +{ + value(20400; QltyEventCategory) + { + Caption = 'Quality Management'; + } +} diff --git a/src/Apps/W1/Quality Management/app/src/API/QltyInspectionsAPI.Page.al b/src/Apps/W1/Quality Management/app/src/API/QltyInspectionsAPI.Page.al new file mode 100644 index 0000000000..c6d473b574 --- /dev/null +++ b/src/Apps/W1/Quality Management/app/src/API/QltyInspectionsAPI.Page.al @@ -0,0 +1,712 @@ +// ------------------------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. +// ------------------------------------------------------------------------------------------------ +namespace Microsoft.QualityManagement.API; +using Microsoft.QualityManagement.Document; +using Microsoft.QualityManagement.Utilities; +using Microsoft.QualityManagement.Dispositions.ItemTracking; +using Microsoft.QualityManagement.Dispositions.Transfer; +using Microsoft.QualityManagement.Dispositions.InventoryAdjustment; +using Microsoft.QualityManagement.Dispositions.Move; +using Microsoft.QualityManagement.Dispositions.PutAway; +using Microsoft.QualityManagement.Integration.Inventory; +using Microsoft.QualityManagement.Dispositions; + +/// +/// Power automate friendly web service for quality inspections. +/// +page 20414 "Qlty. Inspections API" +{ + APIVersion = 'v2.0'; + APIGroup = 'qualityinspection'; + APIPublisher = 'microsoft'; + Caption = 'qltyInspections', Locked = true; + DelayedInsert = true; + DeleteAllowed = false; + Editable = false; + EntityName = 'qltyInspection'; + EntitySetName = 'qltyInspections'; + EntityCaption = 'Quality Inspection'; + EntitySetCaption = 'Quality Inspections'; + InsertAllowed = false; + ModifyAllowed = false; + PageType = API; + RefreshOnActivate = true; + SourceTable = "Qlty. Inspection Header"; + + ODataKeyFields = SystemId; + + layout + { + area(Content) + { + repeater(rptTests) + { + ShowCaption = false; + field(qltySystemIDOfTest; Rec.SystemId) + { + Caption = 'qltySystemIDOfTest', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the system id of the record this test refers to. The Quality inspection document no.'; + } + field(qltyTestNo; Rec."No.") + { + Caption = 'qltyTestNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the quality inspection document no.'; + } + field(qltyTestRetestNo; Rec."Re-inspection No.") + { + Caption = 'qltyTestRetestNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies which retest this is for.'; + } + field(qltyTemplate; Rec."Template Code") + { + Caption = 'qltyTemplate', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies which template this test was created from.'; + } + field(qltyDescription; Rec.Description) + { + Caption = 'qltyDescription', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies a description of the test itself.'; + } + field(qltyInspectionStatus; Rec.Status) + { + Caption = 'qltyInspectionStatus', Locked = true; + ApplicationArea = All; + + ToolTip = 'Specifies the status of the test. No additional changes can be made to a finished Quality Inspection.'; + } + + field(qltyResultCode; Rec."Result Code") + { + Caption = 'qltyResultCode', Locked = true; + ApplicationArea = All; + + ToolTip = 'Specifies the result is automatically determined based on the test value and result configuration.'; + } + field(qltyResultDescription; Rec."Result Description") + { + Caption = 'qltyResultDescription', Locked = true; + ApplicationArea = All; + + ToolTip = 'Specifies the result description for this test result. The result is automatically determined based on the test value and result configuration.'; + } + field(qltyFinishedDate; Rec."Finished Date") + { + Caption = 'qltyFinishedDate', Locked = true; + ApplicationArea = All; + + ToolTip = 'Specifies the date that the test was finished.'; + } + field(qltyResultPriority; Rec."Evaluation Sequence") + { + Caption = 'qltyResultPriority', Locked = true; + ApplicationArea = All; + + ToolTip = 'Specifies the associated result priority for this test result. The result is automatically determined based on the test value and result configuration.'; + } + field(qltySourceTableNo; Rec."Source Table No.") + { + Caption = 'qltySourceTableNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies a reference to the table that the quality inspection is for. '; + + } + field(qltySourceDocumentNo; Rec."Source Document No.") + { + Caption = 'qltySourceDocumentNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies a reference to the source that this Quality Inspection is referring to. This typically refers to a production order document number.'; + + } + field(qltySourceDocumentLineNo; Rec."Source Document Line No.") + { + Caption = 'qltySourceDocumentLineNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies a reference to the source line no. that this Quality Inspection is referring to. This typically refers to a production order line no.'; + + } + + field(qltySourceItemNo; Rec."Source Item No.") + { + Caption = 'qltySourceItemNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the item that the Quality Inspection is for. When used with production orders this typically refers to the item being produced.'; + } + field(qltySourceVariantCode; Rec."Source Variant Code") + { + Caption = 'qltySourceVariantCode', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the item variant that the Quality Inspection is for. When used with production orders this typically refers to the item being produced.'; + } + + field(qltySourceSerialNo; Rec."Source Serial No.") + { + Caption = 'qltySourceSerialNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the serial number that the quality inspection is for. This is only used for serial tracked items.'; + } + field(qltySourceLotNo; Rec."Source Lot No.") + { + Caption = 'qltySourceLotNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the lot number that the quality inspection is for. This is only used for lot tracked items.'; + } + field(qltySourcePackageNo; Rec."Source Package No.") + { + Caption = 'qltySourcePackageNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the package number that the quality inspection is for. This is only used for package tracked items.'; + } + field(qltySourceQuantity; Rec."Source Quantity (Base)") + { + Caption = 'qltySourceQuantity', Locked = true; + ApplicationArea = All; + ToolTip = 'Source Quantity when configured.'; + + } + field(qltySourceRecordID; Rec."Source RecordId") + { + Caption = 'qltySourceRecordID', Locked = true; + ApplicationArea = All; + ToolTip = 'Source record ID.'; + } + field(qltySourceRecordTableNo; Rec."Source Record Table No.") + { + Caption = 'qltySourceRecordTableNo', Locked = true; + ApplicationArea = All; + ToolTip = 'Source record Table No.'; + } + field(qltyAssignedUserID; Rec."Assigned User ID") + { + Caption = 'qltyAssignedUserID', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the user this test is assigned to.'; + } + field(qltySystemCreatedAt; Rec.SystemCreatedAt) + { + Caption = 'qltySystemCreatedAt', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the date the test was created.'; + } + field(qltySystemCreatedBy; Rec.SystemCreatedBy) + { + Caption = 'qltySystemCreatedBy', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies which User ID made the test.'; + } + field(qltySystemModifiedAt; Rec.SystemModifiedAt) + { + Caption = 'qltySystemModifiedAt', Locked = true; + ApplicationArea = All; + ToolTip = 'Specifies the last modified date of the test.'; + } + field(qltySystemModifiedBy; Rec.SystemModifiedBy) + { + Caption = 'qltySystemModifiedBy'; + ApplicationArea = All; + ToolTip = 'Specifies the last Modified By User ID'; + } + } + } + } + var + QltyMiscHelpers: Codeunit "Qlty. Misc Helpers"; + CannotConvertDateErr: Label 'Could not convert date %1. Use ISO 8601 (YYYY-MM-DD) date format.', Comment = '%1=date'; + + + /// + /// Use with web services and power automate to Finish the Inspection + /// + /// + [ServiceEnabled] + procedure FinishInspection(var ActionContext: WebServiceActionContext) + begin + Rec.FinishInspection(); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Creates a Reinspection. + /// + /// + [ServiceEnabled] + procedure CreateReinspection(var ActionContext: WebServiceActionContext) + begin + Rec.CreateReinspection(); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Reopens an inspection + /// + /// + [ServiceEnabled] + procedure ReopenInspection(var ActionContext: WebServiceActionContext) + begin + Rec.ReopenInspection(); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Sets a test value + /// + /// + /// Text. The field code to set. + /// Text. The field value to set. + [ServiceEnabled] + procedure SetTestValue(var ActionContext: WebServiceActionContext; testCode: Text; testValue: Text) + begin + Rec.SetTestValue(testCode, testValue); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Assigns the test. + /// + /// + /// Text. The user id to assign the test to. + [ServiceEnabled] + procedure AssignTo(var ActionContext: WebServiceActionContext; assignToUser: Text) + begin + Rec."Assigned User ID" := CopyStr(assignToUser, 1, MaxStrLen(Rec."Assigned User ID")); + Rec.Modify(false); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Blocks the lot + /// + /// + [ServiceEnabled] + procedure BlockLot(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetLotBlockState(Rec, true); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Un-Blocks the lot + /// + /// + [ServiceEnabled] + procedure UnBlockLot(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetLotBlockState(Rec, false); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + + /// + /// Blocks the serial + /// + /// + [ServiceEnabled] + procedure BlockSerial(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetSerialBlockState(Rec, true); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Un-Blocks the serial + /// + /// + [ServiceEnabled] + procedure UnBlockSerial(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetSerialBlockState(Rec, false); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Blocks the serial + /// + /// + [ServiceEnabled] + procedure BlockPackage(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetPackageBlockState(Rec, true); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Un-Blocks the serial + /// + /// + [ServiceEnabled] + procedure UnBlockPackage(var ActionContext: WebServiceActionContext) + var + QltyItemTracking: Codeunit "Qlty. Item Tracking"; + begin + QltyItemTracking.SetPackageBlockState(Rec, false); + ActionContext.SetResultCode(WebServiceActionResultCode::Updated); + end; + + /// + /// Uses an inventory movement to move inventory + /// + /// + /// + /// + /// + /// + /// + /// + [ServiceEnabled] + procedure CreateMovement(var ActionContext: WebServiceActionContext; optionalDestinationLocation: Text; binCode: Text; optionalSpecificQuantity: Text; moveEntireLot: Text; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + TempInstructionQltyDispositionBuffer: Record "Qlty. Disposition Buffer" temporary; + QltyDispInternalMove: Codeunit "Qlty. Disp. Internal Move"; + begin + + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + binCode := DelChr(binCode, '<>', ' '); + optionalDestinationLocation := DelChr(optionalDestinationLocation, '<>', ' '); + + if QltyMiscHelpers.GetBooleanFor(moveEntireLot) then + TempInstructionQltyDispositionBuffer."Quantity Behavior" := TempInstructionQltyDispositionBuffer."Quantity Behavior"::"Item Tracked Quantity"; + + TempInstructionQltyDispositionBuffer."Disposition Action" := TempInstructionQltyDispositionBuffer."Disposition Action"::"Move with Internal Movement"; + + if optionalSpecificQuantity <> '' then + Evaluate(TempInstructionQltyDispositionBuffer."Qty. To Handle (Base)", optionalSpecificQuantity); + + TempInstructionQltyDispositionBuffer."Location Filter" := CopyStr(optionalSourceLocationFilter, 1, MaxStrLen(TempInstructionQltyDispositionBuffer."Location Filter")); + TempInstructionQltyDispositionBuffer."Bin Filter" := CopyStr(optionalSourceBinFilter, 1, MaxStrLen(TempInstructionQltyDispositionBuffer."Bin Filter")); + TempInstructionQltyDispositionBuffer."New Location Code" := CopyStr(optionalDestinationLocation, 1, 10); + TempInstructionQltyDispositionBuffer."New Bin Code" := CopyStr(binCode, 1, 20); + + if QltyDispInternalMove.PerformDisposition( + Rec, + TempInstructionQltyDispositionBuffer + ) then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + end; + + local procedure ConvertTextToQuantityBehaviorEnum(TextToConvert: Text) QltyQuantityBehavior: Enum "Qlty. Quantity Behavior" + var + IndexOfText: Integer; + OrdinalOfEnum: Integer; + begin + IndexOfText := QltyQuantityBehavior.Names.IndexOf(TextToConvert); + if IndexOfText = 0 then + QltyQuantityBehavior := QltyQuantityBehavior::"Specific Quantity" + else begin + OrdinalOfEnum := QltyQuantityBehavior.Ordinals.Get(IndexOfText); + QltyQuantityBehavior := Enum::"Qlty. Quantity Behavior".FromInteger(OrdinalOfEnum); + end; + end; + + /// + /// Creates a Warehouse Internal Put-away document. + /// This feature can be used with directed pick and put locations with lot warehouse tracked items. + /// + /// + /// When non zero this indicates the quantity to move. + /// When set to TRUE, will release the internal put-away + /// Optionally restrict the locations to move from. + /// Optionally restrict the specific bins to move from. + /// Valid options are: SpecificQuantity (quantity defined in optionalSpecificQuantity), TrackedQuantity (quantity of lot/package/serial), SampleQuantity (sample size), FailQuantity (number of failed samples), PassQuantity (number of passed samples) + [ServiceEnabled] + procedure CreateWarehouseInternalPutaway(var ActionContext: WebServiceActionContext; optionalSpecificQuantity: Text; releaseImmediately: Text; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; moveBehavior: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + QltyDispInternalPutAway: Codeunit "Qlty. Disp. Internal Put-away"; + OverrideQuantity: Decimal; + ShouldReleaseImmediately: Boolean; + QuantityBehavior: Enum "Qlty. Quantity Behavior"; + begin + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + + if optionalSpecificQuantity <> '' then + Evaluate(OverrideQuantity, optionalSpecificQuantity); + + ShouldReleaseImmediately := QltyMiscHelpers.GetBooleanFor(releaseImmediately); + QuantityBehavior := ConvertTextToQuantityBehaviorEnum(moveBehavior); + + if QltyDispInternalPutAway.PerformDisposition( + Rec, + OverrideQuantity, + optionalSourceLocationFilter, + optionalSourceBinFilter, + ShouldReleaseImmediately, + QuantityBehavior + ) then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + end; + + /// + /// Creates a Warehouse Put-away document. + /// This feature can be used with directed pick and put locations with lot warehouse tracked items. + /// + /// + /// Quantity to move, if updating a specific quantity + /// Optionally restrict the locations to move from. + /// Optionally restrict the specific bins to move from. + /// valid options are KEEPOPEN (create internal put-away), RELEASE (create and release internal put-away), or CREATEPUTAWAY (create and release internal put-away and create warehouse put-away) + /// Valid options are: SpecificQuantity (quantity defined in optionalSpecificQuantity), TrackedQuantity (quantity of lot/package/serial), SampleQuantity (sample size), FailQuantity (number of failed samples), PassQuantity (number of passed samples) + [ServiceEnabled] + procedure CreateWarehousePutAway(var ActionContext: WebServiceActionContext; optionalSpecificQuantity: Text; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; putAwayBehavior: Text; moveBehavior: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + QltyDispInternalPutAway: Codeunit "Qlty. Disp. Internal Put-away"; + QltyDispWarehousePutAway: Codeunit "Qlty. Disp. Warehouse Put-away"; + OverrideQuantity: Decimal; + QuantityBehavior: Enum "Qlty. Quantity Behavior"; + Success: Boolean; + begin + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + putAwayBehavior := DelChr(putAwayBehavior, '<>', ' ').ToUpper(); + + if optionalSpecificQuantity <> '' then + Evaluate(OverrideQuantity, optionalSpecificQuantity); + + QuantityBehavior := ConvertTextToQuantityBehaviorEnum(moveBehavior); + + if putAwayBehavior.Contains('CREATEPUTAWAY') then + Success := QltyDispWarehousePutAway.PerformDisposition( + Rec, + OverrideQuantity, + optionalSourceLocationFilter, + optionalSourceBinFilter, + QuantityBehavior) + else + Success := QltyDispInternalPutAway.PerformDisposition( + Rec, + OverrideQuantity, + optionalSourceLocationFilter, + optionalSourceBinFilter, + putAwayBehavior.Contains('RELEASE'), + QuantityBehavior); + + if Success then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + end; + + /// + /// Uses an item/warehouse reclassification journal or movement worksheet to move the inventory. + /// + /// + /// When left blank this assumes the same location as the from location. + /// The target bin to move to. + /// Quantity to move, if updating a specific quantity + /// When set to TRUE this will post journals immediately or create the warehouse movement. Verify you have sufficient licensing to use this flag. + /// Optionally restrict the locations to move from. + /// Optionally restrict the specific bins to move from. + /// When set to TRUE, will use the Movement Worksheet instead of a reclassification journal. + /// Valid options are: SpecificQuantity (quantity defined in optionalSpecificQuantity), TrackedQuantity (quantity of lot/package/serial) SampleQuantity (sample size), FailQuantity (number of failed samples), PassQuantity (number of passed samples) + [ServiceEnabled] + procedure MoveInventory(var ActionContext: WebServiceActionContext; optionalDestinationLocation: Text; optionalDestinationBin: Text; optionalSpecificQuantity: Text; postImmediately: Text; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; useMoveSheet: Text; moveBehavior: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + TempInstructionQltyDispositionBuffer: Record "Qlty. Disposition Buffer" temporary; + InventoryQltyDispMoveAutoChoose: Codeunit "Qlty. Disp. Move Auto Choose"; + UseMovement: Boolean; + begin + + optionalDestinationBin := DelChr(optionalDestinationBin, '<>', ' '); + optionalDestinationLocation := DelChr(optionalDestinationLocation, '<>', ' '); + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + + + TempInstructionQltyDispositionBuffer."Quantity Behavior" := ConvertTextToQuantityBehaviorEnum(moveBehavior); + + TempInstructionQltyDispositionBuffer."Disposition Action" := TempInstructionQltyDispositionBuffer."Disposition Action"::"Move with automatic choice"; + + if optionalSpecificQuantity <> '' then + Evaluate(TempInstructionQltyDispositionBuffer."Qty. To Handle (Base)", optionalSpecificQuantity); + + TempInstructionQltyDispositionBuffer."Location Filter" := CopyStr(optionalSourceLocationFilter, 1, MaxStrLen(TempInstructionQltyDispositionBuffer."Location Filter")); + TempInstructionQltyDispositionBuffer."Bin Filter" := CopyStr(optionalSourceBinFilter, 1, MaxStrLen(TempInstructionQltyDispositionBuffer."Bin Filter")); + if QltyMiscHelpers.GetBooleanFor(postImmediately) then + TempInstructionQltyDispositionBuffer."Entry Behavior" := TempInstructionQltyDispositionBuffer."Entry Behavior"::Post; + + TempInstructionQltyDispositionBuffer."New Location Code" := CopyStr(optionalDestinationLocation, 1, 10); + TempInstructionQltyDispositionBuffer."New Bin Code" := CopyStr(optionalDestinationBin, 1, 20); + + + if InventoryQltyDispMoveAutoChoose.MoveInventory( + Rec, + TempInstructionQltyDispositionBuffer, + UseMovement + ) then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + + end; + + /// + /// Uses the information from a Quality Inspection to process a negative adjustment for the tested item. + /// + /// + /// Optional additional location filter for item on test + /// Optional additional bin filter for item on test + /// Quantity to remove, if moving a specific quantity + /// Optional Reason Code + /// Remove a specific quantity, tracked quantity, sample size, or sample pass/fail quantity + /// Whether to create journal entries, register a warehouse item journal, or post an item journal + [ServiceEnabled] + procedure CreateNegativeAdjustment(var ActionContext: WebServiceActionContext; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; optionalSpecificQuantity: Text; optionalReasonCode: Text; adjustmentBehavior: Text; postingBehavior: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + QltyDispNegAdjustInv: Codeunit "Qlty. Disp. Neg. Adjust Inv."; + SpecificQuantity: Decimal; + begin + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + + if optionalSpecificQuantity <> '' then + Evaluate(SpecificQuantity, optionalSpecificQuantity); + + if QltyDispNegAdjustInv.PerformDisposition( + Rec, + SpecificQuantity, + ConvertTextToQuantityBehaviorEnum(adjustmentBehavior), + optionalSourceLocationFilter, + optionalSourceBinFilter, + ConvertTextToItemAdjPostBehaviorEnum(postingBehavior), + CopyStr(optionalReasonCode, 1, 10)) + then + ActionContext.SetResultCode(WebServiceActionResultCode::Created) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + + end; + + local procedure ConvertTextToItemAdjPostBehaviorEnum(InputText: Text) QltyItemAdjPostBehavior: Enum "Qlty. Item Adj. Post Behavior" + var + IndexOfText: Integer; + OrdinalOfEnum: Integer; + begin + IndexOfText := QltyItemAdjPostBehavior.Names.IndexOf(InputText); + if IndexOfText = 0 then + QltyItemAdjPostBehavior := QltyItemAdjPostBehavior::"Prepare only" + else begin + OrdinalOfEnum := QltyItemAdjPostBehavior.Ordinals.Get(IndexOfText); + QltyItemAdjPostBehavior := Enum::"Qlty. Item Adj. Post Behavior".FromInteger(OrdinalOfEnum); + end; + end; + + /// + /// Uses the information from a Quality Inspection to update item tracking information for the tested item. + /// + /// + /// Optional additional location filter for item on test + /// Optional additional bin filter for item on test + /// Quantity to update, if updating a specific quantity + /// Valid options are: SpecificQuantity (quantity defined in optionalSpecificQuantity), TrackedQuantity (quantity of lot/package/serial) + /// SampleQuantity (sample size), FailQuantity (number of failed samples), PassQuantity (number of passed samples) + /// Boolean value signifying whether to create the journal entry or create and post the journal + /// New lot no. + /// New serial no. + /// New package no. + /// New expiration date + [ServiceEnabled] + procedure ChangeItemTracking(var ActionContext: WebServiceActionContext; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; optionalSpecificQuantity: Text; quantityChoice: Text; postImmediately: Text; + newLotNo: Text; newSerialNo: Text; newPackageNo: Text; newExpirationDate: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + TempInstructionQltyDispositionBuffer: Record "Qlty. Disposition Buffer" temporary; + QltyDispChangeTracking: Codeunit "Qlty. Disp. Change Tracking"; + SpecificQuantity: Decimal; + DesiredExpirationDate: Date; + begin + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + newExpirationDate := DelChr(newExpirationDate, '<>', ' '); + + if optionalSpecificQuantity <> '' then + Evaluate(SpecificQuantity, optionalSpecificQuantity); + TempInstructionQltyDispositionBuffer."Qty. To Handle (Base)" := SpecificQuantity; + TempInstructionQltyDispositionBuffer."Quantity Behavior" := ConvertTextToQuantityBehaviorEnum(quantityChoice); + if QltyMiscHelpers.GetBooleanFor(postImmediately) then + TempInstructionQltyDispositionBuffer."Entry Behavior" := TempInstructionQltyDispositionBuffer."Entry Behavior"::Post; + + TempInstructionQltyDispositionBuffer."New Lot No." := CopyStr(DelChr(newLotNo, '<>', ' '), 1, MaxStrLen(TempInstructionQltyDispositionBuffer."New Lot No.")); + TempInstructionQltyDispositionBuffer."New Serial No." := CopyStr(DelChr(newSerialNo, '<>', ' '), 1, MaxStrLen(TempInstructionQltyDispositionBuffer."New Serial No.")); + TempInstructionQltyDispositionBuffer."New Package No." := CopyStr(DelChr(newPackageNo, '<>', ' '), 1, MaxStrLen(TempInstructionQltyDispositionBuffer."New Package No.")); + if newExpirationDate <> '' then + if not Evaluate(DesiredExpirationDate, Format(newExpirationDate, 0, 9)) then + Error(CannotConvertDateErr, newExpirationDate); + + TempInstructionQltyDispositionBuffer."New Expiration Date" := DesiredExpirationDate; + if QltyDispChangeTracking.PerformDisposition(Rec, TempInstructionQltyDispositionBuffer) then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + + end; + + /// + /// Uses the information from a Quality Inspection to create a transfer order for the tested item. + /// + /// + /// Optional additional location filter for item on test + /// Optional additional bin filter for item on test + /// Destination location for the transfer + /// Quantity to transfer, if using the specific quantity choice + /// Transfer a specific quantity (SpecificQuantity), item tracked quantity (TrackedQuantity), sample size (SampleQuantity), or sample pass/fail quantity (PassQuantity or FailQuantity) + /// Boolean defining whether the transfer is direct + /// The in-transit location to use + [ServiceEnabled] + procedure CreateTransferOrder(var ActionContext: WebServiceActionContext; optionalSourceLocationFilter: Text; optionalSourceBinFilter: Text; destinationLocation: Text; optionalSpecificQuantity: Text; quantityChoice: Text; + directTransfer: Text; inTransitLocation: Text) // Text to work around limitations in MS power automate integration as of 2023/April/17 + var + QltyDispTransfer: Codeunit "Qlty. Disp. Transfer"; + SpecificQuantity: Decimal; + QuantityBehavior: Enum "Qlty. Quantity Behavior"; + IsDirectTransfer: Boolean; + DestinationLocationCode: Code[10]; + InTransitLocationCode: Code[10]; + begin + optionalSourceLocationFilter := DelChr(optionalSourceLocationFilter, '<>', ' '); + optionalSourceBinFilter := DelChr(optionalSourceBinFilter, '<>', ' '); + + if optionalSpecificQuantity <> '' then + Evaluate(SpecificQuantity, optionalSpecificQuantity); + QuantityBehavior := ConvertTextToQuantityBehaviorEnum(quantityChoice); + IsDirectTransfer := QltyMiscHelpers.GetBooleanFor(directTransfer); + DestinationLocationCode := CopyStr(destinationLocation, 1, MaxStrLen(DestinationLocationCode)); + InTransitLocationCode := CopyStr(inTransitLocation, 1, MaxStrLen(InTransitLocationCode)); + if IsDirectTransfer then + InTransitLocationCode := ''; + + if QltyDispTransfer.PerformDisposition( + Rec, + SpecificQuantity, + QuantityBehavior, + optionalSourceLocationFilter, + optionalSourceBinFilter, + DestinationLocationCode, + InTransitLocationCode + ) + then + ActionContext.SetResultCode(WebServiceActionResultCode::Updated) + else + ActionContext.SetResultCode(WebServiceActionResultCode::None); + + end; + +} diff --git a/src/Apps/W1/Quality Management/app/src/Workflow/QltyStartWorkflow.Codeunit.al b/src/Apps/W1/Quality Management/app/src/Workflow/QltyStartWorkflow.Codeunit.al index 1df9ca00d7..9f70f4e035 100644 --- a/src/Apps/W1/Quality Management/app/src/Workflow/QltyStartWorkflow.Codeunit.al +++ b/src/Apps/W1/Quality Management/app/src/Workflow/QltyStartWorkflow.Codeunit.al @@ -11,6 +11,7 @@ using Microsoft.QualityManagement.Setup; using Microsoft.QualityManagement.Utilities; using System.Automation; using System.Environment.Configuration; +using System.Integration; using System.Security.User; /// @@ -37,16 +38,46 @@ codeunit 20426 "Qlty. Start Workflow" internal procedure StartWorkflowInspectionCreated(var QltyInspectionHeader: Record "Qlty. Inspection Header") begin WorkflowManagement.HandleEvent(QltyWorkflowSetup.GetInspectionCreatedEvent(), QltyInspectionHeader); + OnInspectionCreated( + QltyInspectionHeader.SystemId, + QltyInspectionHeader."No.", + QltyInspectionHeader.GetReferenceRecordId(), + QltyInspectionHeader."Source Document No.", + QltyInspectionHeader."Source Item No.", + QltyInspectionHeader."Source Variant Code", + QltyInspectionHeader."Source Lot No.", + QltyInspectionHeader."Source Serial No.", + QltyInspectionHeader."Result Code"); end; internal procedure StartWorkflowInspectionFinished(var QltyInspectionHeader: Record "Qlty. Inspection Header") begin WorkflowManagement.HandleEvent(QltyWorkflowSetup.GetInspectionFinishedEvent(), QltyInspectionHeader); + OnInspectionFinished( + QltyInspectionHeader.SystemId, + QltyInspectionHeader."No.", + QltyInspectionHeader.GetReferenceRecordId(), + QltyInspectionHeader."Source Document No.", + QltyInspectionHeader."Source Item No.", + QltyInspectionHeader."Source Variant Code", + QltyInspectionHeader."Source Lot No.", + QltyInspectionHeader."Source Serial No.", + QltyInspectionHeader."Result Code"); end; internal procedure StartWorkflowInspectionReopens(var QltyInspectionHeader: Record "Qlty. Inspection Header") begin WorkflowManagement.HandleEvent(QltyWorkflowSetup.GetInspectionReopenedEvent(), QltyInspectionHeader); + OnInspectionReOpened( + QltyInspectionHeader.SystemId, + QltyInspectionHeader."No.", + QltyInspectionHeader.GetReferenceRecordId(), + QltyInspectionHeader."Source Document No.", + QltyInspectionHeader."Source Item No.", + QltyInspectionHeader."Source Variant Code", + QltyInspectionHeader."Source Lot No.", + QltyInspectionHeader."Source Serial No.", + QltyInspectionHeader."Result Code"); end; internal procedure StartWorkflowInspectionChanged(var QltyInspectionHeader: Record "Qlty. Inspection Header"; xQltyInspectionHeader: Record "Qlty. Inspection Header") @@ -77,10 +108,93 @@ codeunit 20426 "Qlty. Start Workflow" WorkflowManagement.HandleEventWithxRec(CopyStr(QltyWorkflowSetup.GetInspectionHasChangedEvent(), 1, 128), QltyInspectionHeader, xQltyInspectionHeader); RecursionDetectionQltySessionHelper.SetSessionValue('StartWorkflowInspectionChanged-Time', ''); RecursionDetectionQltySessionHelper.SetSessionValue('StartWorkflowInspectionChanged-Record', ''); + + OnInspectionChanged( + QltyInspectionHeader.SystemId, + QltyInspectionHeader."No.", + QltyInspectionHeader.GetReferenceRecordId(), + QltyInspectionHeader."Source Document No.", + QltyInspectionHeader."Source Item No.", + QltyInspectionHeader."Source Variant Code", + QltyInspectionHeader."Source Lot No.", + QltyInspectionHeader."Source Serial No.", + QltyInspectionHeader."Result Code"); end; local procedure RecursionThrottleMilliseconds(): Integer begin exit(5000); end; + + /// + /// This action will occur when a new Quality Inspection has been created. + /// This is exposed with ExternalBusinessEvent and intended to be used in PowerAutomate + /// + /// The system record id of the newly created test + /// The test document no. + /// The source record id of the record that triggered the test + /// The source document no. + /// The source item no. + /// The source variant code. + /// The source lot number. + /// The source serial number. + /// The current grade of the test + [ExternalBusinessEvent('QltyOnInspectionCreated', 'Quality Inspection Created', 'This action will occur when a new Quality Inspection has been created.', EventCategory::QltyEventCategory)] + procedure OnInspectionCreated(inspectionIdentifier: guid; inspectionNo: code[20]; sourceRecordIdentifier: Guid; sourceDocumentNo: code[20]; sourceItemNo: Code[20]; sourceVariantCode: code[10]; sourceLotNo: Code[50]; sourceSerialNo: Code[50]; resultCode: Code[20]) + begin + end; + + /// + /// This action will occur when a Quality Inspection has changed to the finished state. + /// This is exposed with ExternalBusinessEvent and intended to be used in PowerAutomate + /// + /// The system ID of the quality inspection test + /// The quality inspection test no. + /// The system ID of the source record + /// The source document no. from the test + /// The source item no. associated with the test + /// If variants are used then the source variant on the test + /// The lot number associated with the test + /// The serial number associated with the test + /// The current grade of the test + [ExternalBusinessEvent('QltyOnInspectionFinished', 'Quality Inspection Finished', 'This action will occur when a Quality Inspection has changed to the finished state.', EventCategory::QltyEventCategory)] + procedure OnInspectionFinished(inspectionIdentifier: guid; inspectionNo: code[20]; sourceRecordIdentifier: Guid; sourceDocumentNo: code[20]; sourceItemNo: Code[20]; sourceVariantCode: code[10]; sourceLotNo: Code[50]; sourceSerialNo: Code[50]; resultCode: Code[20]) + begin + end; + + /// + /// This action will occur when a Quality Inspection has been re-opened. + /// This is exposed with ExternalBusinessEvent and intended to be used in PowerAutomate + /// + /// The system ID of the quality inspection test + /// The quality inspection test no. + /// The system ID of the source record + /// The source document no. from the test + /// The source item no. associated with the test + /// If variants are used then the source variant on the test + /// The lot number associated with the test + /// The serial number associated with the test + /// The current grade of the test + [ExternalBusinessEvent('QltyOnInspectionReOpened', 'Quality Inspection Re-Opened', 'This action will occur when a Quality Inspection has been re-opened.', EventCategory::QltyEventCategory)] + procedure OnInspectionReOpened(inspectionIdentifier: guid; inspectionNo: code[20]; sourceRecordIdentifier: Guid; sourceDocumentNo: code[20]; sourceItemNo: Code[20]; sourceVariantCode: code[10]; sourceLotNo: Code[50]; sourceSerialNo: Code[50]; resultCode: Code[20]) + begin + end; + + /// + /// This action will occur when a Quality Inspection has changed. + /// This is exposed with ExternalBusinessEvent and intended to be used in PowerAutomate + /// + /// The system ID of the quality inspection test + /// The quality inspection test no. + /// The system ID of the source record + /// The source document no. from the test + /// The source item no. associated with the test + /// If variants are used then the source variant on the test + /// The lot number associated with the test + /// The serial number associated with the test + /// The current grade of the test + [ExternalBusinessEvent('QltyOnTestChanged', 'Quality Inspection Changed', 'This action will occur when a Quality Inspection has changed.', EventCategory::QltyEventCategory)] + procedure OnInspectionChanged(inspectionIdentifier: guid; inspectionNo: code[20]; sourceRecordIdentifier: Guid; sourceDocumentNo: code[20]; sourceItemNo: Code[20]; sourceVariantCode: code[10]; sourceLotNo: Code[50]; sourceSerialNo: Code[50]; resultCode: Code[20]) + begin + end; }