From d21949c1c6f51373c1d823732cb286b370bb6a7a Mon Sep 17 00:00:00 2001 From: kb-dynaway <106300783+kb-dynaway@users.noreply.github.com> Date: Mon, 20 Nov 2023 14:09:08 +0100 Subject: [PATCH] APIs for Fixed Assets and Fixed Asset Locations (#24169) For one of the community project related to the PowerApp it is needed to have a APIs for Fixed Assets and Fixed Asset Locations. Those APIs are needed in standard API collection since. It will also allow more interact with the date inside Business Central (for example create Power Automate flows based on Fixed Assets) --------- Co-authored-by: Bartosz Rudzinski --- .../app/src/pages/APIV2FALocations.Page.al | 40 ++++ .../app/src/pages/APIV2FixedAssets.Page.al | 69 +++++++ .../test/src/APIV2FALocationsE2E.Codeunit.al | 161 ++++++++++++++++ .../test/src/APIV2FixedAssetsE2E.Codeunit.al | 177 ++++++++++++++++++ 4 files changed, 447 insertions(+) create mode 100644 Apps/W1/APIV2/app/src/pages/APIV2FALocations.Page.al create mode 100644 Apps/W1/APIV2/app/src/pages/APIV2FixedAssets.Page.al create mode 100644 Apps/W1/APIV2/test/src/APIV2FALocationsE2E.Codeunit.al create mode 100644 Apps/W1/APIV2/test/src/APIV2FixedAssetsE2E.Codeunit.al diff --git a/Apps/W1/APIV2/app/src/pages/APIV2FALocations.Page.al b/Apps/W1/APIV2/app/src/pages/APIV2FALocations.Page.al new file mode 100644 index 0000000000..4fdc9420d6 --- /dev/null +++ b/Apps/W1/APIV2/app/src/pages/APIV2FALocations.Page.al @@ -0,0 +1,40 @@ +page 30097 "APIV2 - FA Locations" +{ + DelayedInsert = true; + APIVersion = 'v2.0'; + EntityCaption = 'Fixed Asset Location'; + EntitySetCaption = 'Fixed Asset Locations'; + PageType = API; + ODataKeyFields = SystemId; + EntityName = 'fixedAssetLocation'; + EntitySetName = 'fixedAssetLocations'; + SourceTable = "FA Location"; + Extensible = false; + + layout + { + area(Content) + { + repeater(Group) + { + field(id; Rec.SystemId) + { + Caption = 'Id'; + Editable = false; + } + field(code; Rec.Code) + { + Caption = 'Code'; + } + field(displayName; Rec.Name) + { + Caption = 'Name'; + } + field(lastModifiedDateTime; Rec.SystemModifiedAt) + { + Caption = 'Last Modified Date'; + } + } + } + } +} diff --git a/Apps/W1/APIV2/app/src/pages/APIV2FixedAssets.Page.al b/Apps/W1/APIV2/app/src/pages/APIV2FixedAssets.Page.al new file mode 100644 index 0000000000..68e2c6673e --- /dev/null +++ b/Apps/W1/APIV2/app/src/pages/APIV2FixedAssets.Page.al @@ -0,0 +1,69 @@ +page 30098 "APIV2 - Fixed Assets" +{ + APIVersion = 'v2.0'; + EntityCaption = 'Fixed Asset'; + EntitySetCaption = 'Fixed Assets'; + ChangeTrackingAllowed = true; + DelayedInsert = true; + EntityName = 'fixedAsset'; + EntitySetName = 'fixedAssets'; + ODataKeyFields = SystemId; + PageType = API; + SourceTable = "Fixed Asset"; + Extensible = false; + + layout + { + area(content) + { + repeater(Group) + { + field(id; Rec.SystemId) + { + Caption = 'Id'; + Editable = false; + } + field(number; Rec."No.") + { + Caption = 'No.'; + } + field(displayName; Rec.Description) + { + Caption = 'Description'; + } + field(faLocationCode; Rec."FA Location Code") + { + Caption = 'FA Location Code'; + } + field(faClassCode; Rec."FA Class Code") + { + Caption = 'FA Class Code'; + } + field(faSubclassCode; Rec."FA Subclass Code") + { + Caption = 'FA Subclass Code'; + } + field(blocked; Rec.Blocked) + { + Caption = 'Blocked'; + } + field(serialNo; Rec."Serial No.") + { + Caption = 'Serial No.'; + } + field(responsibleEmployee; Rec."Responsible Employee") + { + Caption = 'Responsible Employee'; + } + field(underMaintenance; Rec."Under Maintenance") + { + Caption = 'Under Maintenance'; + } + field(lastModifiedDateTime; Rec.SystemModifiedAt) + { + Caption = 'Last Modified Date'; + } + } + } + } +} diff --git a/Apps/W1/APIV2/test/src/APIV2FALocationsE2E.Codeunit.al b/Apps/W1/APIV2/test/src/APIV2FALocationsE2E.Codeunit.al new file mode 100644 index 0000000000..bc2d29c274 --- /dev/null +++ b/Apps/W1/APIV2/test/src/APIV2FALocationsE2E.Codeunit.al @@ -0,0 +1,161 @@ +codeunit 139900 "APIV2 - FA Locations E2E" +{ + // version Test,W1,All + + Subtype = Test; + TestPermissions = Disabled; + + trigger OnRun() + begin + // [FEATURE] [Api] [Location] + end; + + var + Assert: Codeunit Assert; + LibraryGraphMgt: Codeunit "Library - Graph Mgt"; + LibraryUtility: Codeunit "Library - Utility"; + IsInitialized: Boolean; + EmptyJSONErr: Label 'JSON should not be empty.'; + ServiceNameTxt: Label 'fixedAssetLocations'; + + local procedure Initialize() + begin + if IsInitialized then + exit; + + IsInitialized := true; + Commit(); + end; + + [Test] + procedure TestGetFALocation() + var + FALocation: Record "FA Location"; + Response: Text; + TargetURL: Text; + begin + // [SCENARIO] User can get a Fixed Asset Location with a GET request to the service. + Initialize(); + + // [GIVEN] A Fixed Asset Location exists in the system. + CreateFALocation(FALocation); + + // [WHEN] The user makes a GET request for a given Fixed Asset Location. + TargetURL := LibraryGraphMgt.CreateTargetURL(FALocation.SystemId, Page::"APIV2 - FA Locations", ServiceNameTxt); + LibraryGraphMgt.GetFromWebService(Response, TargetURL); + + // [THEN] The response text contains the Fixed Asset Location information. + VerifyProperties(Response, FALocation); + end; + + [Test] + procedure TestCreateFALocation() + var + FALocation: Record "FA Location"; + TempFALocation: Record "FA Location" temporary; + FALocationJSON: Text; + TargetURL: Text; + Response: Text; + begin + // [SCENARIO] User can create a new Fixed Asset Location through a POST method. + Initialize(); + + // [GIVEN] The user has constructed a Fixed Asset Location JSON object to send to the service. + CreateFALocation(TempFALocation); + FALocationJSON := GetFALocationJSON(TempFALocation); + + // [WHEN] The user posts the JSON to the service. + TargetURL := LibraryGraphMgt.CreateTargetURL('', Page::"APIV2 - FA Locations", ServiceNameTxt); + LibraryGraphMgt.PostToWebService(TargetURL, FALocationJSON, Response); + + // [THEN] The Fixed Asset Location has been created in the database with all the details. + FALocation.Get(TempFALocation.Code); + VerifyProperties(Response, FALocation); + end; + + [Test] + procedure TestModifyFALocation() + var + FALocation: Record "FA Location"; + TempFALocation: Record "FA Location" temporary; + RequestBody: Text; + Response: Text; + TargetURL: Text; + begin + // [SCENARIO] User can modify name in a Fixed Asset Location through a PATCH request. + Initialize(); + + // [GIVEN] A Fixed Asset Location exists with a name. + CreateFALocation(FALocation); + TempFALocation.TransferFields(FALocation); + FALocation.Code := LibraryUtility.GenerateRandomCodeWithLength(FALocation.FieldNo(Code), Database::"FA Location", 10); + TempFALocation.Name := LibraryUtility.GenerateGUID(); + RequestBody := GetFALocationJSON(TempFALocation); + + // [WHEN] The user makes a patch request to the service and specifies name field. + TargetURL := LibraryGraphMgt.CreateTargetURL(FALocation.SystemId, Page::"APIV2 - FA Locations", ServiceNameTxt); + LibraryGraphMgt.PatchToWebService(TargetURL, RequestBody, Response); + + // [THEN] The response contains the new values. + VerifyProperties(Response, TempFALocation); + + // [THEN] The Fixed Asset Location in the database contains the updated value. + FALocation.Get(FALocation.Code); + Assert.AreEqual(FALocation.Name, TempFALocation.Name, 'Names should be equal.'); + end; + + [Test] + procedure TestDeleteFALocation() + var + FALocation: Record "FA Location"; + FALocationCode: Code[10]; + TargetURL: Text; + Response: Text; + begin + // [SCENARIO] User can delete a Fixed Asset Location by making a DELETE request. + Initialize(); + + // [GIVEN] A Fixed Asset Location exists. + CreateFALocation(FALocation); + FALocationCode := FALocation.Code; + + // [WHEN] The user makes a DELETE request to the endpoint for the Fixed Asset Location. + TargetURL := LibraryGraphMgt.CreateTargetURL(FALocation.SystemId, Page::"APIV2 - FA Locations", ServiceNameTxt); + LibraryGraphMgt.DeleteFromWebService(TargetURL, '', Response); + + // [THEN] The response is empty. + Assert.AreEqual('', Response, 'DELETE response should be empty.'); + + // [THEN] The Fixed Asset Location is no longer in the database. + FALocation.SetRange(Code, FALocationCode); + Assert.IsTrue(FALocation.IsEmpty(), 'Fixed Asset Location should be deleted.'); + end; + + local procedure CreateFALocation(var FALocation: Record "FA Location") + begin + FALocation.Init(); + FALocation.Code := LibraryUtility.GenerateRandomCodeWithLength(FALocation.FieldNo(Code), Database::"FA Location", 10); + FALocation.Name := LibraryUtility.GenerateGUID(); + FALocation.Insert(true); + Commit(); + end; + + local procedure GetFALocationJSON(var FALocation: Record "FA Location") FALocationJSON: Text + begin + if FALocation.Code = '' then + FALocation.Code := LibraryUtility.GenerateRandomCodeWithLength(FALocation.FieldNo(Code), Database::"FA Location", 10); + if FALocation.Name = '' then + FALocation.Name := LibraryUtility.GenerateGUID(); + FALocationJSON := LibraryGraphMgt.AddPropertytoJSON(FALocationJSON, 'code', FALocation.Code); + FALocationJSON := LibraryGraphMgt.AddPropertytoJSON(FALocationJSON, 'displayName', FALocation.Name); + exit(FALocationJSON); + end; + + local procedure VerifyProperties(JSON: Text; FALocation: Record "FA Location") + begin + Assert.AreNotEqual('', JSON, EmptyJSONErr); + LibraryGraphMgt.VerifyIDInJSON(JSON); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'code', FALocation.Code); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'displayName', FALocation.Name); + end; +} \ No newline at end of file diff --git a/Apps/W1/APIV2/test/src/APIV2FixedAssetsE2E.Codeunit.al b/Apps/W1/APIV2/test/src/APIV2FixedAssetsE2E.Codeunit.al new file mode 100644 index 0000000000..cb4070c3bd --- /dev/null +++ b/Apps/W1/APIV2/test/src/APIV2FixedAssetsE2E.Codeunit.al @@ -0,0 +1,177 @@ +codeunit 139901 "APIV2 - Fixed Assets E2E" +{ + // version Test,W1,All + + Subtype = Test; + TestPermissions = Disabled; + + trigger OnRun() + begin + // [FEATURE] [Graph] [Customer] + end; + + var + Assert: Codeunit Assert; + LibraryGraphMgt: Codeunit "Library - Graph Mgt"; + LibraryUtility: Codeunit "Library - Utility"; + LibraryFixedAsset: Codeunit "Library - Fixed Asset"; + IsInitialized: Boolean; + FixedAssetNoPrefixTxt: Label 'FA'; + EmptyJSONErr: Label 'JSON should not be empty.'; + ServiceNameTxt: Label 'fixedAssets'; + + local procedure Initialize() + begin + if IsInitialized then + exit; + + IsInitialized := true; + Commit(); + end; + + [Test] + procedure TestGetFixedAsset() + var + FixedAsset: Record "Fixed Asset"; + Response: Text; + TargetURL: Text; + begin + // [SCENARIO] User can get a Fixed Asset with a GET request to the service. + Initialize(); + + // [GIVEN] A Fixed Asset exists in the system. + LibraryFixedAsset.CreateFixedAsset(FixedAsset); + + // [WHEN] The user makes a GET request for a given Fixed Asset. + TargetURL := LibraryGraphMgt.CreateTargetURL(FixedAsset.SystemId, Page::"APIV2 - Fixed Assets", ServiceNameTxt); + LibraryGraphMgt.GetFromWebService(Response, TargetURL); + + // [THEN] The response text contains the Fixed Asset information. + VerifyProperties(Response, FixedAsset); + end; + + [Test] + procedure TestCreateFixedAsset() + var + FixedAsset: Record "Fixed Asset"; + Response: Text; + TargetURL: Text; + begin + // [SCENARIO] User can create a new Fixed Asset through a POST method. + Initialize(); + + // [GIVEN] The user has constructed a Fixed Asset JSON object to send to the service. + LibraryFixedAsset.CreateFixedAsset(FixedAsset); + + // [WHEN] The user posts the JSON to the service. + TargetURL := LibraryGraphMgt.CreateTargetURL('', Page::"APIV2 - Fixed Assets", ServiceNameTxt); + LibraryGraphMgt.PostToWebService(TargetURL, GetFixedAssetJSON(FixedAsset), Response); + + // [THEN] The Fixed Asset has been created in the database with all the details. + FixedAsset.Get(FixedAsset."No."); + VerifyProperties(Response, FixedAsset); + end; + + [Test] + procedure TestDeleteFixedAsset() + var + FixedAsset: Record "Fixed Asset"; + FixedAssetNo: Code[20]; + TargetURL: Text; + Response: Text; + begin + // [SCENARIO] User can delete a Fixed Asset by making a DELETE request. + Initialize(); + + // [GIVEN] A Fixed Asset exists. + LibraryFixedAsset.CreateFixedAsset(FixedAsset); + FixedAssetNo := FixedAsset."No."; + + // [WHEN] The user makes a DELETE request to the endpoint for the Fixed Asset. + TargetURL := LibraryGraphMgt.CreateTargetURL(FixedAsset.SystemId, Page::"APIV2 - Fixed Assets", ServiceNameTxt); + LibraryGraphMgt.DeleteFromWebService(TargetURL, '', Response); + + // [THEN] The response is empty. + Assert.AreEqual('', Response, 'DELETE response should be empty.'); + + // [THEN] The Fixed Asset is no longer in the database. + FixedAsset.SetRange("No.", FixedAssetNo); + Assert.IsTrue(FixedAsset.IsEmpty(), 'Fixed Asset should be deleted.'); + end; + + [Test] + procedure TestModifyFixedAsset() + var + FixedAsset: Record "Fixed Asset"; + TempFixedAsset: Record "Fixed Asset" temporary; + RequestBody: Text; + Response: Text; + TargetURL: Text; + begin + // [SCENARIO] User can modify a Fixed Asset through a PATCH request. + Initialize(); + + // [GIVEN] A Fixed Asset exists. + LibraryFixedAsset.CreateFixedAsset(FixedAsset); + TempFixedAsset.TransferFields(FixedAsset); + TempFixedAsset.Description := LibraryUtility.GenerateGUID(); + RequestBody := GetFixedAssetJSON(TempFixedAsset); + + // [WHEN] The user makes a patch request to the service. + TargetURL := LibraryGraphMgt.CreateTargetURL(FixedAsset.SystemId, Page::"APIV2 - Fixed Assets", ServiceNameTxt); + LibraryGraphMgt.PatchToWebService(TargetURL, RequestBody, Response); + + // [THEN] The response text contains the new values. + VerifyProperties(Response, TempFixedAsset); + + // [THEN] The record in the database contains the new values. + FixedAsset.Get(FixedAsset."No."); + VerifyProperties(Response, FixedAsset); + end; + + local procedure GetFixedAssetJSON(var FixedAsset: Record "Fixed Asset"): Text + var + FixedAssetJson: Text; + begin + if FixedAsset."No." = '' then + FixedAsset."No." := NextFixedAssetNo(); + if FixedAsset.Description = '' then + FixedAsset.Description := LibraryUtility.GenerateGUID(); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'number', FixedAsset."No."); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'displayName', FixedAsset.Description); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'faLocationCode', FixedAsset."FA Location Code"); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'faClassCode', FixedAsset."FA Class Code"); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'faSubclassCode', FixedAsset."FA Subclass Code"); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'blocked', FixedAsset.Blocked); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'serialNo', FixedAsset."Serial No."); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'responsibleEmployee', FixedAsset."Responsible Employee"); + FixedAssetJson := LibraryGraphMgt.AddPropertytoJSON(FixedAssetJson, 'underMaintenance', FixedAsset."Under Maintenance"); + exit(FixedAssetJson) + end; + + local procedure NextFixedAssetNo(): Code[20] + var + FixedAsset: Record "Fixed Asset"; + begin + FixedAsset.SetFilter("No.", StrSubstNo('%1*', FixedAssetNoPrefixTxt)); + if FixedAsset.FindLast() then + exit(IncStr(FixedAsset."No.")); + + exit(CopyStr(FixedAssetNoPrefixTxt + '0001', 1, 20)); + end; + + local procedure VerifyProperties(JSON: Text; FixedAsset: Record "Fixed Asset") + begin + Assert.AreNotEqual('', JSON, EmptyJSONErr); + LibraryGraphMgt.VerifyIDInJson(JSON); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'number', FixedAsset."No."); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'displayName', FixedAsset.Description); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'faLocationCode', FixedAsset."FA Location Code"); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'faClassCode', FixedAsset."FA Class Code"); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'faSubclassCode', FixedAsset."FA Subclass Code"); + Assert.AreEqual(false, FixedAsset.Blocked, 'Fixed Asset should have the correct ''blocked'' information.'); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'serialNo', FixedAsset."Serial No."); + LibraryGraphMgt.VerifyPropertyInJSON(JSON, 'responsibleEmployee', FixedAsset."Responsible Employee"); + Assert.AreEqual(false, FixedAsset."Under Maintenance", 'Fixed Asset should have the correct ''under maintenance'' information.'); + end; +} \ No newline at end of file