diff --git a/Apps/W1/Shopify/app/app.json b/Apps/W1/Shopify/app/app.json index 88d5415c82..aa85c47434 100644 --- a/Apps/W1/Shopify/app/app.json +++ b/Apps/W1/Shopify/app/app.json @@ -17,7 +17,7 @@ "idRanges": [ { "from": 30100, - "to": 30370 + "to": 30380 } ], "internalsVisibleTo": [ diff --git a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyAPI.Codeunit.al index 6d5a83e720..ede700bdb6 100644 --- a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyAPI.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyAPI.Codeunit.al @@ -293,8 +293,10 @@ codeunit 30286 "Shpfy Company API" internal procedure UpdateShopifyCompanyFields(var ShopifyCompany: Record "Shpfy Company"; JCompany: JsonObject) Result: Boolean var CompanyLocation: Record "Shpfy Company Location"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; UpdatedAt: DateTime; JLocations: JsonArray; + JMetafields: JsonArray; JItem: JsonToken; OutStream: OutStream; PhoneNo: Text; @@ -343,5 +345,7 @@ codeunit 30286 "Shpfy Company API" CompanyLocation."Tax Registration Id" := CopyStr(JsonHelper.GetValueAsText(JItem, 'node.taxRegistrationId', MaxStrLen(CompanyLocation."Tax Registration Id")), 1, MaxStrLen(CompanyLocation."Tax Registration Id")); CompanyLocation.Modify(); end; + if JsonHelper.GetJsonArray(JCompany, JMetafields, 'metafields.edges') then + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Company", ShopifyCompany.Id); end; } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al index cf65199341..761d48979c 100644 --- a/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Companies/Codeunits/ShpfyCompanyExport.Codeunit.al @@ -193,6 +193,15 @@ codeunit 30284 "Shpfy Company Export" ShopifyCompany.Modify(); CompanyLocation.Modify(); end; + + UpdateMetafields(ShopifyCompany.Id); + end; + + local procedure UpdateMetafields(ComppanyId: BigInteger) + var + MetafieldAPI: Codeunit "Shpfy Metafield API"; + begin + MetafieldAPI.CreateOrUpdateMetafieldsInShopify(Database::"Shpfy Company", ComppanyId); end; internal procedure SetCreateCompanies(NewCustomers: Boolean) diff --git a/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanies.Page.al b/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanies.Page.al index 39fd16175a..8f93f6885f 100644 --- a/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanies.Page.al +++ b/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanies.Page.al @@ -171,6 +171,24 @@ page 30156 "Shpfy Companies" end; } + action(Metafields) + { + ApplicationArea = All; + Caption = 'Metafields'; + Image = PriceAdjustment; + Promoted = true; + PromotedCategory = Process; + PromotedIsBig = true; + PromotedOnly = true; + ToolTip = 'Add metafields to a company. This can be used for adding custom data fields to companies in Shopify.'; + + trigger OnAction() + var + Metafields: Page "Shpfy Metafields"; + begin + Metafields.RunForResource(Database::"Shpfy Company", Rec.Id, Rec."Shop Code"); + end; + } } } } diff --git a/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanyCard.Page.al b/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanyCard.Page.al index 8980b7073f..f0605a99ee 100644 --- a/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanyCard.Page.al +++ b/Apps/W1/Shopify/app/src/Companies/Pages/ShpfyCompanyCard.Page.al @@ -163,6 +163,24 @@ page 30157 "Shpfy Company Card" RunPageLink = "Company SystemId" = field(SystemId); ToolTip = 'View a list of Shopify catalogs for the company.'; } + action(Metafields) + { + ApplicationArea = All; + Caption = 'Metafields'; + Image = PriceAdjustment; + Promoted = true; + PromotedCategory = Category4; + PromotedIsBig = true; + PromotedOnly = true; + ToolTip = 'Add metafields to a company. This can be used for adding custom data fields to compoanies in Shopify.'; + + trigger OnAction() + var + Metafields: Page "Shpfy Metafields"; + begin + Metafields.RunForResource(Database::"Shpfy Company", Rec.Id, Rec."Shop Code"); + end; + } } } diff --git a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerAPI.Codeunit.al index fe21f1f938..448c3b7f4b 100644 --- a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerAPI.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerAPI.Codeunit.al @@ -356,12 +356,14 @@ codeunit 30114 "Shpfy Customer API" internal procedure UpdateShopifyCustomerFields(var ShopifyCustomer: Record "Shpfy Customer"; JCustomer: JsonObject) Result: Boolean var CustomerAddress: Record "Shpfy Customer Address"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; NodeId: BigInteger; UpdatedAt: DateTime; JAddresses: JsonArray; JTags: JsonArray; JAddress: JsonObject; JItem: JsonToken; + JMetafields: JsonArray; Ids: List of [BigInteger]; OutStream: OutStream; StateString: Text; @@ -462,6 +464,9 @@ codeunit 30114 "Shpfy Customer API" CustomerAddress.Modify(false); end; end; + + if JsonHelper.GetJsonArray(JCustomer, JMetafields, 'metafields.edges') then + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Customer", ShopifyCustomer.Id); end; end; diff --git a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al index d11fea569e..a370bf2db3 100644 --- a/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Customers/Codeunits/ShpfyCustomerExport.Codeunit.al @@ -42,42 +42,6 @@ codeunit 30116 "Shpfy Customer Export" CreateCustomers: Boolean; CountyCodeTooLongLbl: Label 'Can not export customer %1 %2. The length of the string is %3, but it must be less than or equal to %4 characters. Value: %5, field: %6', Comment = '%1 - Customer No., %2 - Customer Name, %3 - Length, %4 - Max Length, %5 - Value, %6 - Field Name'; - /// - /// Add Or Update Metadata. - /// - /// Parameter of type Record "Shopify Customer". - /// Parameter of type FieldRef. - internal procedure AddOrUpdateMetadata(ShopifyCustomer: Record "Shpfy Customer"; MetadataFieldRef: FieldRef) - var - Metafield: Record "Shpfy Metafield"; - Name: Text; - begin - Metafield.SetRange("Parent Table No.", Database::"Shpfy Customer"); - Metafield.SetRange("Owner Id", ShopifyCustomer.Id); - Metafield.SetRange(Namespace, 'Microsoft.Dynamics365.BusinessCentral'); - Name := CleanName(MetadataFieldRef); - Metafield.SetRange(Name, Name); - if Metafield.FindFirst() then begin - if Metafield.Value <> Format(MetadataFieldRef.Value) then; - end else begin - Clear(Metafield); - Metafield.Namespace := 'Microsoft.Dynamics365.BusinessCentral'; - Metafield.Validate("Parent Table No.", Database::"Shpfy Customer"); - Metafield."Owner Id" := ShopifyCustomer.Id; - Metafield.Type := Metafield.Type::single_line_text_field; - Metafield.Value := Format(MetadataFieldRef.Value); - end; - end; - - /// - /// Clean Name. - /// - /// Parameter of type FieldRef. - /// Return value of type Text. - local procedure CleanName(FieldRef: FieldRef): Text - begin - exit(DelChr(FieldRef.Record().Name, '=', ' %.-+') + '.' + DelChr(FieldRef.Name, '=', ' %-+')); - end; /// /// Create Shopify Customer. @@ -101,7 +65,8 @@ codeunit 30116 "Shpfy Customer Export" ShopifyCustomer.Insert(); CustomerAddress.Insert(); end; - MetadataFields(Customer, ShopifyCustomer); + + UpdateMetafields(ShopifyCustomer.Id); end; /// @@ -224,25 +189,6 @@ codeunit 30116 "Shpfy Customer Export" exit(true); end; - /// - /// Metadata Fields. - /// - /// Parameter of type Record Customer. - /// Parameter of type Record "Shopify Customer". - local procedure MetadataFields(Customer: Record Customer; ShopifyCustomer: Record "Shpfy Customer") - var - RecordRef: RecordRef; - begin - RecordRef.GetTable(Customer); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("No."))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("VAT Bus. Posting Group"))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("VAT Registration No."))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo(SystemId))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("Customer Disc. Group"))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("Customer Price Group"))); - AddOrUpdateMetadata(ShopifyCustomer, RecordRef.Field(Customer.FieldNo("Customer Posting Group"))); - end; - /// /// Set Shop. /// @@ -313,10 +259,19 @@ codeunit 30116 "Shpfy Customer Export" ShopifyCustomer.Modify(); CustomerAddress.Modify(); end; + + UpdateMetafields(ShopifyCustomer.Id); end; internal procedure SetCreateCustomers(NewCustomers: Boolean) begin CreateCustomers := NewCustomers; end; + + local procedure UpdateMetafields(CustomerId: BigInteger) + var + MetafieldAPI: Codeunit "Shpfy Metafield API"; + begin + MetafieldAPI.CreateOrUpdateMetafieldsInShopify(Database::"Shpfy Customer", CustomerId); + end; } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomerCard.Page.al b/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomerCard.Page.al index 7a2258b0e9..5a01f9bf01 100644 --- a/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomerCard.Page.al +++ b/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomerCard.Page.al @@ -186,6 +186,27 @@ page 30106 "Shpfy Customer Card" RunPageLink = "Customer Id" = Field(Id); ToolTip = 'View a list of Shopify orders for the customer.'; } + action(Metafields) + { + ApplicationArea = All; + Caption = 'Metafields'; + Image = PriceAdjustment; + Promoted = true; + PromotedCategory = Category4; + PromotedIsBig = true; + PromotedOnly = true; + ToolTip = 'Add metafields to a customer. This can be used for adding custom data fields to customers in Shopify.'; + + trigger OnAction() + var + Shop: Record "Shpfy Shop"; + Metafields: Page "Shpfy Metafields"; + begin + Shop.SetRange("Shop Id", Rec."Shop Id"); + Shop.FindFirst(); + Metafields.RunForResource(Database::"Shpfy Customer", Rec.Id, Shop.Code); + end; + } } } diff --git a/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomers.Page.al b/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomers.Page.al index 69f037fc5e..075a52361d 100644 --- a/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomers.Page.al +++ b/Apps/W1/Shopify/app/src/Customers/Pages/ShpfyCustomers.Page.al @@ -187,7 +187,27 @@ page 30107 "Shpfy Customers" BackgroundSyncs.CustomerSync(Shop.Code); end; end; + } + action(Metafields) + { + ApplicationArea = All; + Caption = 'Metafields'; + Image = PriceAdjustment; + Promoted = true; + PromotedCategory = Process; + PromotedIsBig = true; + PromotedOnly = true; + ToolTip = 'Add metafields to a customer. This can be used for adding custom data fields to customers in Shopify.'; + trigger OnAction() + var + Shop: Record "Shpfy Shop"; + Metafields: Page "Shpfy Metafields"; + begin + Shop.SetRange("Shop Id", Rec."Shop Id"); + Shop.FindFirst(); + Metafields.RunForResource(Database::"Shpfy Customer", Rec.Id, Shop.Code); + end; } } } diff --git a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompany.Codeunit.al b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompany.Codeunit.al index e6a5806e2a..595dbe2cda 100644 --- a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompany.Codeunit.al +++ b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompany.Codeunit.al @@ -13,7 +13,7 @@ codeunit 30302 "Shpfy GQL Company" implements "Shpfy IGraphQL" /// Return value of type Text. internal procedure GetGraphQL(): Text begin - exit('{"query":"{company(id: \"gid://shopify/Company/{{CompanyId}}\") {name id note createdAt updatedAt mainContact { id customer { id firstName lastName email phone}} locations(first:1, sortKey: CREATED_AT ) {edges { node { id name billingAddress {address1 address2 city countryCode phone province zip zoneCode} taxRegistrationId}}}}}"}'); + exit('{"query":"{company(id: \"gid://shopify/Company/{{CompanyId}}\") {name id note createdAt updatedAt mainContact { id customer { id firstName lastName email phone}} locations(first:1, sortKey: CREATED_AT ) {edges { node { id name billingAddress {address1 address2 city countryCode phone province zip zoneCode} taxRegistrationId }}} metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId key value type}}}}}"}'); end; /// @@ -22,6 +22,6 @@ codeunit 30302 "Shpfy GQL Company" implements "Shpfy IGraphQL" /// Return value of type Integer. internal procedure GetExpectedCost(): Integer begin - exit(7); + exit(10); end; } diff --git a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompanyMetafieldIds.Codeunit.al b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompanyMetafieldIds.Codeunit.al new file mode 100644 index 0000000000..88dc348e25 --- /dev/null +++ b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCompanyMetafieldIds.Codeunit.al @@ -0,0 +1,28 @@ +namespace Microsoft.Integration.Shopify; + +/// +/// Codeunit Shpfy GQL CompanyMetafieldIds (ID 30373) implements Interface Shpfy IGraphQL. +/// +codeunit 30373 "Shpfy GQL CompanyMetafieldIds" implements "Shpfy IGraphQL" +{ + Access = Internal; + + /// + /// GetGraphQL. + /// + /// Return value of type Text. + procedure GetGraphQL(): Text + begin + exit('{"query":"{company(id: \"gid://shopify/Company/{{CompanyId}}\") {metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId }}}}}"}'); + end; + + /// + /// GetExpectedCost. + /// + /// Return value of type Integer. + procedure GetExpectedCost(): Integer + begin + exit(50); + end; + +} diff --git a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomer.Codeunit.al b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomer.Codeunit.al index 01f216dbbe..cacc1bd2ca 100644 --- a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomer.Codeunit.al +++ b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomer.Codeunit.al @@ -13,7 +13,7 @@ codeunit 30127 "Shpfy GQL Customer" implements "Shpfy IGraphQL" /// Return value of type Text. internal procedure GetGraphQL(): Text begin - exit('{"query":"{customer(id: \"gid://shopify/Customer/{{CustomerId}}\") {legacyResourceId firstName lastName email phone taxExempt taxExemptions verifiedEmail state note createdAt updatedAt tags emailMarketingConsent {consentUpdatedAt marketingState} addresses {id company firstName lastName address1 address2 zip city countryCodeV2 country provinceCode province phone} defaultAddress {id} metafields(namespace: \"Microsoft.Dynamics365.BusinessCentral\" first: 10) {edges {node {id namespace ownerType legacyResourceId key value}}}}}"}'); + exit('{"query":"{customer(id: \"gid://shopify/Customer/{{CustomerId}}\") {legacyResourceId firstName lastName email phone taxExempt taxExemptions verifiedEmail state note createdAt updatedAt tags emailMarketingConsent {consentUpdatedAt marketingState} addresses {id company firstName lastName address1 address2 zip city countryCodeV2 country provinceCode province phone} defaultAddress {id} metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId key value type}}}}}"}'); end; /// diff --git a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomerMetafieldIds.Codeunit.al b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomerMetafieldIds.Codeunit.al new file mode 100644 index 0000000000..2dc821806a --- /dev/null +++ b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLCustomerMetafieldIds.Codeunit.al @@ -0,0 +1,27 @@ +namespace Microsoft.Integration.Shopify; +/// +/// Codeunit Shpfy GQL Customer Metafield Ids (ID 30374) implements Interface Shpfy IGraphQL. +/// +codeunit 30374 "Shpfy GQL CustomerMetafieldIds" implements "Shpfy IGraphQL" +{ + Access = Internal; + + /// + /// GetGraphQL. + /// + /// Return value of type Text. + procedure GetGraphQL(): Text + begin + exit('{"query":"{customer(id: \"gid://shopify/Customer/{{CustomerId}}\") { metafields(first: 50) {edges {node {legacyResourceId updatedAt}}}}}"}'); + end; + + /// + /// GetExpectedCost. + /// + /// Return value of type Integer. + procedure GetExpectedCost(): Integer + begin + exit(50); + end; + +} diff --git a/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al b/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al index dab96ed8ab..29e3cc1f99 100644 --- a/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al +++ b/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al @@ -490,4 +490,14 @@ enum 30111 "Shpfy GraphQL Type" implements "Shpfy IGraphQL" Caption = 'Get Product Image'; Implementation = "Shpfy IGraphQL" = "Shpfy GQL GetProductImage"; } + value(103; CustomerMetafieldIds) + { + Caption = 'Customer Metafield Ids'; + Implementation = "Shpfy IGraphQL" = "Shpfy GQL CustomerMetafieldIds"; + } + value(104; CompanyMetafieldIds) + { + Caption = 'Company Metafield Ids'; + Implementation = "Shpfy IGraphQL" = "Shpfy GQL CompanyMetafieldIds"; + } } diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCompany.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCompany.Codeunit.al new file mode 100644 index 0000000000..44ef1d693a --- /dev/null +++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCompany.Codeunit.al @@ -0,0 +1,49 @@ +namespace Microsoft.Integration.Shopify; + +codeunit 30366 "Shpfy Metafield Owner Company" implements "Shpfy IMetafield Owner Type" +{ + + procedure GetTableId(): Integer + begin + exit(Database::"Shpfy Company"); + end; + + procedure RetrieveMetafieldIdsFromShopify(OwnerId: BigInteger) MetafieldIds: Dictionary of [BigInteger, DateTime] + var + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + JsonHelper: Codeunit "Shpfy Json Helper"; + Parameters: Dictionary of [Text, Text]; + GraphQLType: Enum "Shpfy GraphQL Type"; + JResponse: JsonToken; + JMetafields: JsonArray; + JNode: JsonObject; + JItem: JsonToken; + Id: BigInteger; + UpdatedAt: DateTime; + begin + Parameters.Add('CompanyId', Format(OwnerId)); + GraphQLType := GraphQLType::CompanyMetafieldIds; + JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters); + if JsonHelper.GetJsonArray(JResponse, JMetafields, 'data.company.metafields.edges') then + foreach JItem in JMetafields do + if JsonHelper.GetJsonObject(JItem.AsObject(), JNode, 'node') then begin + Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JNode, 'legacyResourceId')); + UpdatedAt := JsonHelper.GetValueAsDateTime(JNode, 'updatedAt'); + MetafieldIds.Add(Id, UpdatedAt); + end; + end; + + procedure GetShopCode(OwnerId: BigInteger): Code[20] + var + Company: Record "Shpfy Company"; + begin + Company.Get(OwnerId); + exit(Company."Shop Code"); + end; + + procedure CanEditMetafields(Shop: Record "Shpfy Shop"): Boolean + begin + exit((Shop."Can Update Shopify Companies") and (Shop."Company Import From Shopify" <> Enum::"Shpfy Company Import Range"::AllCompanies)); + end; + +} diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCustomer.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCustomer.Codeunit.al index 98646f1731..779fd82d80 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCustomer.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCustomer.Codeunit.al @@ -7,13 +7,44 @@ codeunit 30333 "Shpfy Metafield Owner Customer" implements "Shpfy IMetafield Own exit(Database::"Shpfy Customer"); end; - procedure RetrieveMetafieldIdsFromShopify(OwnerId: BigInteger): Dictionary of [BigInteger, DateTime] + procedure RetrieveMetafieldIdsFromShopify(OwnerId: BigInteger) MetafieldIds: Dictionary of [BigInteger, DateTime] + var + CommunicationMgt: Codeunit "Shpfy Communication Mgt."; + JsonHelper: Codeunit "Shpfy Json Helper"; + Parameters: Dictionary of [Text, Text]; + GraphQLType: Enum "Shpfy GraphQL Type"; + JResponse: JsonToken; + JMetafields: JsonArray; + JNode: JsonObject; + JItem: JsonToken; + Id: BigInteger; + UpdatedAt: DateTime; begin - Error('Not implemented'); + Parameters.Add('CustomerId', Format(OwnerId)); + GraphQLType := GraphQLType::CustomerMetafieldIds; + JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters); + if JsonHelper.GetJsonArray(JResponse, JMetafields, 'data.customer.metafields.edges') then + foreach JItem in JMetafields do + if JsonHelper.GetJsonObject(JItem.AsObject(), JNode, 'node') then begin + Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JNode, 'legacyResourceId')); + UpdatedAt := JsonHelper.GetValueAsDateTime(JNode, 'updatedAt'); + MetafieldIds.Add(Id, UpdatedAt); + end; end; procedure GetShopCode(OwnerId: BigInteger): Code[20] + var + Customer: Record "Shpfy Customer"; + Shop: Record "Shpfy Shop"; begin - exit('Not implemented'); + Customer.Get(OwnerId); + Shop.SetRange("Shop Id", Customer."Shop Id"); + Shop.FindFirst(); + exit(Shop.Code); + end; + + procedure CanEditMetafields(Shop: Record "Shpfy Shop"): Boolean + begin + exit((Shop."Can Update Shopify Customer") and (Shop."Customer Import From Shopify" <> Enum::"Shpfy Customer Import Range"::AllCustomers)); end; } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerProduct.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerProduct.Codeunit.al index 3998b83f29..998a98036d 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerProduct.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerProduct.Codeunit.al @@ -39,4 +39,9 @@ codeunit 30334 "Shpfy Metafield Owner Product" implements "Shpfy IMetafield Owne Product.Get(OwnerId); exit(Product."Shop Code"); end; + + procedure CanEditMetafields(Shop: Record "Shpfy Shop"): Boolean + begin + exit((Shop."Sync Item" = Shop."Sync Item"::"To Shopify") and (Shop."Can Update Shopify Products")); + end; } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerVariant.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerVariant.Codeunit.al index 3a9ff1e4b0..66b98ebf03 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerVariant.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerVariant.Codeunit.al @@ -39,4 +39,9 @@ codeunit 30335 "Shpfy Metafield Owner Variant" implements "Shpfy IMetafield Owne Variant.Get(OwnerId); exit(Variant."Shop Code"); end; + + procedure CanEditMetafields(Shop: Record "Shpfy Shop"): Boolean + begin + exit((Shop."Sync Item" = Shop."Sync Item"::"To Shopify") and (Shop."Can Update Shopify Products")); + end; } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/ShpfyMetafieldAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/ShpfyMetafieldAPI.Codeunit.al index bd3ba3ad8d..24d5015a4b 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Codeunits/ShpfyMetafieldAPI.Codeunit.al +++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/ShpfyMetafieldAPI.Codeunit.al @@ -156,7 +156,7 @@ codeunit 30316 "Shpfy Metafield API" MetafieldIds: List of [BigInteger]; MetafieldId: BigInteger; begin - CollectMetafieldIds(OwnerId, MetafieldIds); + CollectMetafieldIds(ParentTableNo, OwnerId, MetafieldIds); foreach JItem in JMetafields do begin JsonHelper.GetJsonObject(JItem.AsObject(), JNode, 'node'); @@ -211,12 +211,12 @@ codeunit 30316 "Shpfy Metafield API" exit(true); end; - local procedure CollectMetafieldIds(ProductId: BigInteger; MetafieldIds: List of [BigInteger]) + local procedure CollectMetafieldIds(ParentTableId: Integer; OwnerId: BigInteger; MetafieldIds: List of [BigInteger]) var Metafield: Record "Shpfy Metafield"; begin - MetaField.SetRange("Parent Table No.", Database::"Shpfy Product"); - Metafield.SetRange("Owner Id", ProductId); + MetaField.SetRange("Parent Table No.", ParentTableId); + Metafield.SetRange("Owner Id", OwnerId); if Metafield.FindSet() then repeat MetafieldIds.Add(Metafield.Id); diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al index 48f187b095..6af17e2441 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al +++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al @@ -19,4 +19,10 @@ enum 30156 "Shpfy Metafield Owner Type" implements "Shpfy IMetafield Owner Type" Caption = 'Variant'; Implementation = "Shpfy IMetafield Owner Type" = "Shpfy Metafield Owner Variant"; } + + value(3; Company) + { + Caption = 'Company'; + Implementation = "Shpfy IMetafield Owner Type" = "Shpfy Metafield Owner Company"; + } } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldOwnerType.Interface.al b/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldOwnerType.Interface.al index f11f3540f7..eb2e7708bd 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldOwnerType.Interface.al +++ b/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldOwnerType.Interface.al @@ -26,4 +26,11 @@ interface "Shpfy IMetafield Owner Type" /// Id of the owner resource. /// Shop code. procedure GetShopCode(OwnerId: BigInteger): Code[20] + + /// + /// Indicates if metafields can be edited. + /// + /// Shop record. + /// Boolean value which is true if metafields for the owner can be edited. + procedure CanEditMetafields(Shop: Record "Shpfy Shop"): Boolean } \ No newline at end of file diff --git a/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafields.Page.al b/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafields.Page.al index fe8979c843..dcf63aba86 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafields.Page.al +++ b/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafields.Page.al @@ -91,9 +91,12 @@ page 30163 "Shpfy Metafields" internal procedure RunForResource(ParentTableId: Integer; OwnerId: BigInteger; ShopCode: Code[20]) var Metafield: Record "Shpfy Metafield"; + IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type"; begin Shop.Get(ShopCode); - IsPageEditable := (Shop."Sync Item" = Shop."Sync Item"::"To Shopify") and (Shop."Can Update Shopify Products"); + + IMetafieldOwnerType := Metafield.GetOwnerType(ParentTableId); + IsPageEditable := IMetafieldOwnerType.CanEditMetafields(Shop); Metafield.SetRange("Parent Table No.", ParentTableId); Metafield.SetRange("Owner Id", OwnerId); diff --git a/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al index 8912e46968..4ffd3f0ccd 100644 --- a/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al +++ b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al @@ -186,6 +186,8 @@ table 30101 "Shpfy Metafield" exit("Owner Type"::Product); Database::"Shpfy Variant": exit("Owner Type"::ProductVariant); + Database::"Shpfy Company": + exit("Owner Type"::Company); end; end; diff --git a/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al b/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al new file mode 100644 index 0000000000..e32831dd81 --- /dev/null +++ b/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsSubs.Codeunit.al @@ -0,0 +1,69 @@ +codeunit 139541 "Shpfy Company Metafields Subs" +{ + EventSubscriberInstance = Manual; + + var + GQLQueryTxt: Text; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] + local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) + begin + MakeResponse(HttpRequestMessage, HttpResponseMessage); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] + local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) + begin + HttpResponseMessage.Content.ReadAs(Response); + end; + + local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) + var + Uri: Text; + GraphQlQuery: Text; + ModifyCompanyLocationGQLStartTok: Label '{"query":"mutation {companyLocationAssignAddress(locationId: \"gid://shopify/CompanyLocation/', Locked = true; + ModifyCompanyGQLStartTok: Label '{"query":"mutation {companyUpdate(companyId: \"gid://shopify/Company/', Locked = true; + GetCompanyMetafieldsGQLStartTok: Label '{"query":"{company(id: \"gid://shopify/Company/', Locked = true; + GetCompanyMetafieldsGQLEndTok: Label '\") {metafields(first: 50) {edges {node {id namespace ownerType legacyResourceId }}}}}"}', Locked = true; + CreateMetafieldsGQLStartTok: Label '{"query": "mutation { metafieldsSet(metafields: ', Locked = true; + GraphQLCmdTxt: Label '/graphql.json', Locked = true; + begin + case HttpRequestMessage.Method of + 'POST': + begin + Uri := HttpRequestMessage.GetRequestUri(); + if Uri.EndsWith(GraphQLCmdTxt) then + if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then + case true of + GraphQlQuery.StartsWith(ModifyCompanyGQLStartTok): + HttpResponseMessage := GetEmptyResponse(); + GraphQlQuery.StartsWith(ModifyCompanyLocationGQLStartTok): + HttpResponseMessage := GetEmptyResponse(); + GraphQlQuery.StartsWith(GetCompanyMetafieldsGQLStartTok) and GraphQlQuery.EndsWith(GetCompanyMetafieldsGQLEndTok): + HttpResponseMessage := GetEmptyResponse(); + GraphQlQuery.StartsWith(CreateMetafieldsGQLStartTok): + begin + HttpResponseMessage := GetEmptyResponse(); + GQLQueryTxt := GraphQlQuery; + end; + end; + end; + end; + end; + + local procedure GetEmptyResponse(): HttpResponseMessage + var + HttpResponseMessage: HttpResponseMessage; + Body: Text; + begin + Body := '{}'; + HttpResponseMessage.Content.WriteFrom(Body); + exit(HttpResponseMessage); + end; + + internal procedure GetGQLQuery(): Text + begin + exit(GQLQueryTxt); + end; + +} diff --git a/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al b/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al new file mode 100644 index 0000000000..576648bdc1 --- /dev/null +++ b/Apps/W1/Shopify/test/Metafields/ShpfyCompanyMetafieldsTest.Codeunit.al @@ -0,0 +1,261 @@ +codeunit 139543 "Shpfy Company Metafields Test" +{ + Subtype = Test; + TestPermissions = Disabled; + + var + Shop: Record "Shpfy Shop"; + ShpfyCompany: Record "Shpfy Company"; + ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; + LibraryAssert: Codeunit "Library Assert"; + Any: Codeunit Any; + IsInitialized: Boolean; + + trigger OnRun() + begin + IsInitialized := false; + end; + + [Test] + procedure UnitTestGetMetafieldOwnerTypeFromCompanyMetafield() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + ShpfyMetafieldOwnerType: Enum "Shpfy Metafield Owner Type"; + begin + // [SCENARIO] Get Metafield Owner Type from Company Metafield. + + // [GIVEN] Shopify Metafield created for Company. + ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, Database::"Shpfy Company", Any.IntegerInRange(10000, 99999)); + + // [WHEN] Invoke Metafield.GetOwnerType(); + ShpfyMetafieldOwnerType := ShpfyMetafield.GetOwnerType(Database::"Shpfy Company"); + + // [THEN] ShpfyMetafieldOwnerType = Enum::"Shpfy Metafield Owner Type"::Company; + LibraryAssert.AreEqual(ShpfyMetafieldOwnerType, Enum::"Shpfy Metafield Owner Type"::Company, 'Metafield Owner Type is different than Company'); + end; + + [Test] + procedure UnitTestGetMetafieldOwnerTableId() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type"; + TableId: Integer; + begin + // [SCENARIO] Get Metafield Owner Values from Metafield Owner Company codeunit + Initialize(); + + // [GIVEN] Shopify Metafield created for Company. + ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, Any.IntegerInRange(100000, 99999), Database::"Shpfy Company"); + // [GIVEN] IMetafieldOwnerType + IMetafieldOwnerType := ShpfyMetafield.GetOwnerType(Database::"Shpfy Company"); + + // [WHEN] Invoke IMetafieldOwnerType.GetTableId + TableId := IMetafieldOwnerType.GetTableId(); + + // [THEN] TableId = Database::"Shpfy Company"; + LibraryAssert.AreEqual(TableId, Database::"Shpfy Company", 'Table Id is different than Company'); + end; + + [Test] + procedure UnitTestImportCompanyMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + MetafieldId: BigInteger; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + JMetafields: JsonArray; + begin + // [SCENARIO] Import Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Response Json with metafield + JMetafields := CreateCompanyMetafieldsResponse(MetafieldId, Namespace, MetafieldKey, MetafieldValue); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Company", ShpfyCompany.Id); + + // [THEN] Metafield with MetafieldId, Namespace, MetafieldKey, MetafieldValue is imported to Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange("Owner Id", ShpfyCompany.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Company"); + + LibraryAssert.IsTrue(ShpfyMetafield.FindFirst(), 'Metafield is not imported to Business Central'); + + LibraryAssert.AreEqual(ShpfyMetafield.Id, MetafieldId, 'Metafield Id is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Namespace, Namespace, 'Namespace is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Name, MetafieldKey, 'Metafield Key is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Value, MetafieldValue, 'Metafield Value is different than imported'); + end; + + [Test] + procedure UnitTestUpdateRemovedCompanyMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + MetafieldId: BigInteger; + JMetafields: JsonArray; + begin + // [SCENARIO] Update Removed Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Shopify Metafield created for Company. + MetafieldId := ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShpfyCompany.Id, Database::"Shpfy Company"); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify with empty JMetafields + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Company", ShpfyCompany.Id); + + // [THEN] Metafield is removed from Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange(Id, MetafieldId); + ShpfyMetafield.SetRange("Owner Id", ShpfyCompany.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Company"); + LibraryAssert.IsTrue(ShpfyMetafield.IsEmpty(), 'Metafield is not removed from Business Central'); + end; + + [Test] + procedure UnitTestUpdateCompanyMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + MetafieldId: BigInteger; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + JMetafields: JsonArray; + begin + // [SCENARIO] Update Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Shopify Metafield with values created for Company. + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + MetafieldId := ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShpfyCompany.Id, Database::"Shpfy Company", Namespace, MetafieldKey, MetafieldValue); + // [GIVEN] Response Json with metafield updated value + JMetafields := ShpfyMetafieldsHelper.CreateMetafieldsResult(MetafieldId, Namespace, 'COMPANY', MetafieldKey, Any.AlphabeticText(10)); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Company", ShpfyCompany.Id); + + // [THEN] Metafield with MetafieldId, Namespace, MetafieldKey, MetafieldValue is updated in Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange(Id, MetafieldId); + ShpfyMetafield.SetRange("Owner Id", ShpfyCompany.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Company"); + LibraryAssert.IsTrue(ShpfyMetafield.FindFirst(), 'Metafield is not updated in Business Central'); + LibraryAssert.AreEqual(ShpfyMetafield.Id, MetafieldId, 'Metafield Id is different than updated'); + LibraryAssert.AreEqual(ShpfyMetafield.Namespace, Namespace, 'Namespace is different than updated'); + LibraryAssert.AreEqual(ShpfyMetafield.Name, MetafieldKey, 'Metafield Key is different than updated'); + LibraryAssert.AreNotEqual(ShpfyMetafield.Value, MetafieldValue, 'Metafield Value is different than updated'); + end; + + [Test] + procedure UnitTestExportCompanyMetafieldToShopify() + var + Customer: Record Customer; + ShpfyMetafield: Record "Shpfy Metafield"; + ShopifyCompany: Record "Shpfy Company"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + MetafieldId: BigInteger; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + ActualQueryTxt: Text; + begin + // [SCENARIO] Export Metafield from Business Central to Shopify + Initialize(); + + // [GIVEN] Shop Can Update Shopify Companies = true + Shop."Can Update Shopify Companies" := true; + Shop.Modify(false); + + // [GIVEN] Customer + Customer := ShpfyInitializeTest.GetDummyCustomer(); + + // [GIVEN] Shopify Company , crea for Customer + CreateShopifyCompany(ShopifyCompany, Shop."Shop Id", Shop.Code, Customer.SystemId); + + // [GIVEN] Shopify Company Location + CreateShopifyCompanyLocation(ShopifyCompany); + + // [GIVEN] Shopify Metafield with values created for Company. + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + MetafieldId := ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShopifyCompany.Id, Database::"Shpfy Company", Namespace, MetafieldKey, MetafieldValue); + + // [WHEN] Invoke ExportCompany codeunit for company + InvokeExportCompany(Customer, ActualQueryTxt); + + // [THEN] Correct GraphQL query is created and sent to Shopify + LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo('key: \"%1\"', MetafieldKey)), 'Query does not contain Metafield Key'); + LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo('value: \"%1\"', MetafieldValue)), 'Query does not contain Metafield Value'); + LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo('namespace: \"%1\"', Namespace)), 'Query does not contain Namespace'); + LibraryAssert.IsTrue(ActualQueryTxt.Contains(StrSubstNo('ownerId: \"gid://shopify/Company/%1\"', ShopifyCompany.Id)), 'Query does not contain Owner Id'); + end; + + local procedure Initialize() + begin + Any.SetDefaultSeed(); + + if IsInitialized then + exit; + Shop := ShpfyInitializeTest.CreateShop(); + CreateShopifyCompany(ShpfyCompany, Shop."Shop Id", Shop.Code, CreateGuid()); + + Commit(); + + IsInitialized := true; + end; + + local procedure CreateCompanyMetafieldsResponse(var MetafieldId: BigInteger; var Namespace: Text; var MetafieldKey: Text; var MetafieldValue: Text): JsonArray + var + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + begin + MetafieldId := Any.IntegerInRange(100000, 999999); + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + exit(ShpfyMetafieldsHelper.CreateMetafieldsResult(MetafieldId, Namespace, 'COMPANY', MetafieldKey, MetafieldValue)); + end; + + local procedure CreateShopifyCompany(var ShopifyCompany: Record "Shpfy Company"; ShopId: BigInteger; ShopCode: Code[20]; CustomerSystemId: Guid) + begin + ShopifyCompany.Init(); + ShopifyCompany.Id := Any.IntegerInRange(100000, 999999); + ShopifyCompany."Shop Id" := ShopId; + ShopifyCompany."Shop Code" := ShopCode; + ShopifyCompany.Name := Any.AlphabeticText(10); + ShopifyCompany."Customer SystemId" := CustomerSystemId; + ShopifyCompany.Insert(false); + end; + + local procedure CreateShopifyCompanyLocation(ShopifyCompany: Record "Shpfy Company") + var + ShpfyCompanyLocation: Record "Shpfy Company Location"; + begin + ShpfyCompanyLocation.Init(); + ShpfyCompanyLocation.Id := Any.IntegerInRange(100000, 999999); + ShpfyCompanyLocation."Company SystemId" := ShopifyCompany.SystemId; + ShpfyCompanyLocation.Insert(false); + end; + + local procedure InvokeExportCompany(var Customer: Record Customer; var ActualQueryTxt: Text) + var + CompanyMetafieldsSubs: Codeunit "Shpfy Company Metafields Subs"; + CompanyExport: Codeunit "Shpfy Company Export"; + begin + BindSubscription(CompanyMetafieldsSubs); + Customer.SetRange("No.", Customer."No."); + CompanyExport.SetShop(Shop.Code); + CompanyExport.Run(Customer); + ActualQueryTxt := CompanyMetafieldsSubs.GetGQLQuery(); + UnbindSubscription(CompanyMetafieldsSubs); + end; +} diff --git a/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al b/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al new file mode 100644 index 0000000000..877f3c0ec4 --- /dev/null +++ b/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsSubs.Codeunit.al @@ -0,0 +1,92 @@ +codeunit 139547 "Shpfy Customer Metafields Subs" +{ + EventSubscriberInstance = Manual; + + var + ShopifyCustomerId: BigInteger; + GQLQueryTxt: Text; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnClientSend', '', true, false)] + local procedure OnClientSend(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) + begin + MakeResponse(HttpRequestMessage, HttpResponseMessage); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Communication Events", 'OnGetContent', '', true, false)] + local procedure OnGetContent(HttpResponseMessage: HttpResponseMessage; var Response: Text) + begin + HttpResponseMessage.Content.ReadAs(Response); + end; + + [EventSubscriber(ObjectType::Codeunit, Codeunit::"Shpfy Customer Events", OnBeforeFindMapping, '', true, false)] + local procedure OnBeforeFindMapping(var Handled: Boolean; var ShopifyCustomer: Record "Shpfy Customer") + begin + ShopifyCustomer.Id := ShopifyCustomerId; + Handled := true; + end; + + local procedure MakeResponse(HttpRequestMessage: HttpRequestMessage; var HttpResponseMessage: HttpResponseMessage) + var + Uri: Text; + GraphQlQuery: Text; + GetCustomersGQLMsg: Label '{"query":"{customers(first:100){pageInfo{endCursor hasNextPage} nodes{ legacyResourceId }}}"}', Locked = true; + ModifyCustomerGQLStartTok: Label '{"query":"mutation {customerUpdate(input: {id: \"gid://shopify/Customer/', Locked = true; + GetCustomerMetafieldsGQLStartTok: Label '{"query":"{customer(id: \"gid://shopify/Customer/', Locked = true; + GetCustomerMetafieldsGQLEndTok: Label '\") { metafields(first: 50) {edges {node {legacyResourceId updatedAt}}}}}"}', Locked = true; + CreateMetafieldsGQLStartTok: Label '{"query": "mutation { metafieldsSet(metafields: ', Locked = true; + GraphQLCmdTxt: Label '/graphql.json', Locked = true; + begin + case HttpRequestMessage.Method of + 'POST': + begin + Uri := HttpRequestMessage.GetRequestUri(); + if Uri.EndsWith(GraphQLCmdTxt) then + if HttpRequestMessage.Content.ReadAs(GraphQlQuery) then + case true of + GraphQlQuery.Contains(GetCustomersGQLMsg): + HttpResponseMessage := GetCustomersResult(); + GraphQlQuery.StartsWith(ModifyCustomerGQLStartTok): + HttpResponseMessage := GetEmptyResponse(); + GraphQlQuery.StartsWith(GetCustomerMetafieldsGQLStartTok) and GraphQlQuery.EndsWith(GetCustomerMetafieldsGQLEndTok): + HttpResponseMessage := GetEmptyResponse(); + GraphQlQuery.StartsWith(CreateMetafieldsGQLStartTok): + begin + HttpResponseMessage := GetEmptyResponse(); + GQLQueryTxt := GraphQlQuery; + end; + end; + end; + end; + end; + + local procedure GetCustomersResult(): HttpResponseMessage + var + HttpResponseMessage: HttpResponseMessage; + Body: Text; + begin + Body := '{ "data": { "customers": { "pageInfo": { "hasNextPage": false }, "edges": [] } }, "extensions": { "cost": { "requestedQueryCost": 12, "actualQueryCost": 2, "throttleStatus": { "maximumAvailable": 2000, "currentlyAvailable": 1998, "restoreRate": 100 } } } }'; + HttpResponseMessage.Content.WriteFrom(Body); + exit(HttpResponseMessage); + end; + + local procedure GetEmptyResponse(): HttpResponseMessage + var + HttpResponseMessage: HttpResponseMessage; + Body: Text; + begin + Body := '{}'; + HttpResponseMessage.Content.WriteFrom(Body); + exit(HttpResponseMessage); + end; + + internal procedure SetShopifyCustomerId(Id: BigInteger) + begin + ShopifyCustomerId := Id; + end; + + internal procedure GetGQLQuery(): Text + begin + exit(GQLQueryTxt); + end; + +} diff --git a/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al b/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al new file mode 100644 index 0000000000..6d096b602d --- /dev/null +++ b/Apps/W1/Shopify/test/Metafields/ShpfyCustomerMetafieldsTest.Codeunit.al @@ -0,0 +1,258 @@ +codeunit 139548 "Shpfy Customer Metafields Test" +{ + Subtype = Test; + TestPermissions = Disabled; + + var + Shop: Record "Shpfy Shop"; + ShpfyCustomer: Record "Shpfy Customer"; + ShpfyInitializeTest: Codeunit "Shpfy Initialize Test"; + LibraryAssert: Codeunit "Library Assert"; + Any: Codeunit Any; + IsInitialized: Boolean; + + trigger OnRun() + begin + IsInitialized := false; + end; + + [Test] + procedure UnitTestGetMetafieldOwnerTypeFromCustomerMetafield() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + ShpfyMetafieldOwnerType: Enum "Shpfy Metafield Owner Type"; + begin + // [SCENARIO] Get Metafield Owner Type from Customer Metafield. + + // [GIVEN] Shopify Metafield created for Customer. + ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, Database::"Shpfy Customer", Any.IntegerInRange(10000, 99999)); + + // [WHEN] Invoke Metafield.GetOwnerType(); + ShpfyMetafieldOwnerType := ShpfyMetafield.GetOwnerType(Database::"Shpfy Customer"); + + // [THEN] ShpfyMetafieldOwnerType = Enum::"Shpfy Metafield Owner Type"::Customer; + LibraryAssert.AreEqual(ShpfyMetafieldOwnerType, Enum::"Shpfy Metafield Owner Type"::Customer, 'Metafield Owner Type is different than Customer'); + end; + + [Test] + procedure UnitTestGetMetafieldOwnerTableId() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type"; + TableId: Integer; + begin + // [SCENARIO] Get Metafield Owner Values from Metafield Owner Customer codeunit + Initialize(); + + // [GIVEN] Shopify Metafield created for Customer. + ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, Any.IntegerInRange(100000, 99999), Database::"Shpfy Customer"); + // [GIVEN] IMetafieldOwnerType + IMetafieldOwnerType := ShpfyMetafield.GetOwnerType(Database::"Shpfy Customer"); + + // [WHEN] Invoke IMetafieldOwnerType.GetTableId + TableId := IMetafieldOwnerType.GetTableId(); + + // [THEN] TableId = Database::"Shpfy Customer"; + LibraryAssert.AreEqual(TableId, Database::"Shpfy Customer", 'Table Id is different than Customer'); + end; + + [Test] + procedure UnitTestImportCustomerMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + MetafieldId: BigInteger; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + JMetafields: JsonArray; + begin + // [SCENARIO] Import Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Response Json with metafield + JMetafields := CreateCustomerMetafieldsResponse(MetafieldId, Namespace, MetafieldKey, MetafieldValue); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Customer", ShpfyCustomer.Id); + + // [THEN] Metafield with MetafieldId, Namespace, MetafieldKey, MetafieldValue is imported to Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange("Owner Id", ShpfyCustomer.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Customer"); + + LibraryAssert.IsTrue(ShpfyMetafield.FindFirst(), 'Metafield is not imported to Business Central'); + + LibraryAssert.AreEqual(ShpfyMetafield.Id, MetafieldId, 'Metafield Id is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Namespace, Namespace, 'Namespace is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Name, MetafieldKey, 'Metafield Key is different than imported'); + LibraryAssert.AreEqual(ShpfyMetafield.Value, MetafieldValue, 'Metafield Value is different than imported'); + end; + + [Test] + procedure UnitTestUpdateRemovedCustomerMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + MetafieldId: BigInteger; + JMetafields: JsonArray; + begin + // [SCENARIO] Update Removed Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Shopify Metafield created for Customer. + MetafieldId := ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShpfyCustomer.Id, Database::"Shpfy Customer"); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify with empty JMetafields + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Customer", ShpfyCustomer.Id); + + // [THEN] Metafield is removed from Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange(Id, MetafieldId); + ShpfyMetafield.SetRange("Owner Id", ShpfyCustomer.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Customer"); + LibraryAssert.IsTrue(ShpfyMetafield.IsEmpty(), 'Metafield is not removed from Business Central'); + end; + + [Test] + procedure UnitTestUpdateCustomerMetafieldFromShopify() + var + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + MetafieldAPI: Codeunit "Shpfy Metafield API"; + MetafieldId: BigInteger; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + JMetafields: JsonArray; + begin + // [SCENARIO] Update Metafield from Shopify to Business Central + Initialize(); + + // [GIVEN] Shopify Metafield with values created for Customer. + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + MetafieldId := ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShpfyCustomer.Id, Database::"Shpfy Customer", Namespace, MetafieldKey, MetafieldValue); + // [GIVEN] Response Json with metafield updated value + JMetafields := ShpfyMetafieldsHelper.CreateMetafieldsResult(MetafieldId, Namespace, 'CUSTOMER', MetafieldKey, Any.AlphabeticText(10)); + + // [WHEN] Invoke MetafieldAPI.UpdateMetafieldsFromShopify + MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Customer", ShpfyCustomer.Id); + + // [THEN] Metafield with MetafieldId, Namespace, MetafieldKey, MetafieldValue is updated in Business Central + ShpfyMetafield.Reset(); + ShpfyMetafield.SetRange(Id, MetafieldId); + ShpfyMetafield.SetRange("Owner Id", ShpfyCustomer.Id); + ShpfyMetafield.SetRange("Parent Table No.", Database::"Shpfy Customer"); + LibraryAssert.IsTrue(ShpfyMetafield.FindFirst(), 'Metafield is not updated in Business Central'); + LibraryAssert.AreEqual(ShpfyMetafield.Id, MetafieldId, 'Metafield Id is different than updated'); + LibraryAssert.AreEqual(ShpfyMetafield.Namespace, Namespace, 'Namespace is different than updated'); + LibraryAssert.AreEqual(ShpfyMetafield.Name, MetafieldKey, 'Metafield Key is different than updated'); + LibraryAssert.AreNotEqual(ShpfyMetafield.Value, MetafieldValue, 'Metafield Value is different than updated'); + end; + + [Test] + procedure UnitTestUpdateCustomerMetafieldInShopfiy() + var + Customer: Record Customer; + ShopifyCustomer: Record "Shpfy Customer"; + ShpfyMetafield: Record "Shpfy Metafield"; + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + CustomerInitTest: Codeunit "Shpfy Customer Init Test"; + Namespace: Text; + MetafieldKey: Text; + MetafieldValue: Text; + ActualQuery: Text; + begin + // [SCENARIO] Update Metafield from Business Central to Shopify + Initialize(); + + //[GIVEN] Shop with Can update Shopify Customer = true + Shop."Can Update Shopify Customer" := true; + Shop.Modify(false); + // [GIVEN] Customer + Customer := ShpfyInitializeTest.GetDummyCustomer(); + // [GIVEN] Shopify Customer + CreateShopifyCustomer(Customer, ShopifyCustomer); + // [GIVEN] Shopify Customer Address + CustomerInitTest.CreateShopifyCustomerAddress(ShopifyCustomer); + // [GIVEN] Shopify Metafield with values created for Customer. + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + ShpfyMetafieldsHelper.CreateMetafield(ShpfyMetafield, ShopifyCustomer.Id, Database::"Shpfy Customer", Namespace, MetafieldKey, MetafieldValue); + + // [WHEN] Invoke ShopifyCustomerExport + InvokeShopifyCustomerExport(Customer, ShopifyCustomer, ActualQuery); + + // [THEN] Correct Query for updating metafields in shopify is sent + LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo('key: \"%1\"', MetafieldKey)), 'Query does not contain Metafield Key'); + LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo('value: \"%1\"', MetafieldValue)), 'Query does not contain Metafield Value'); + LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo('namespace: \"%1\"', Namespace)), 'Query does not contain Namespace'); + LibraryAssert.IsTrue(ActualQuery.Contains(StrSubstNo('ownerId: \"gid://shopify/Customer/%1\"', ShopifyCustomer.Id)), 'Query does not contain Owner Id'); + end; + + local procedure Initialize() + begin + Any.SetDefaultSeed(); + + if IsInitialized then + exit; + Shop := ShpfyInitializeTest.CreateShop(); + CreateShopifyCustomer(ShpfyCustomer, Shop."Shop Id"); + + Commit(); + + IsInitialized := true; + end; + + local procedure CreateCustomerMetafieldsResponse(var MetafieldId: BigInteger; var Namespace: Text; var MetafieldKey: Text; var MetafieldValue: Text): JsonArray + var + ShpfyMetafieldsHelper: Codeunit "Shpfy Metafields Helper"; + begin + MetafieldId := Any.IntegerInRange(100000, 999999); + Namespace := Any.AlphabeticText(10); + MetafieldKey := Any.AlphabeticText(10); + MetafieldValue := Any.AlphabeticText(10); + exit(ShpfyMetafieldsHelper.CreateMetafieldsResult(MetafieldId, Namespace, 'CUSTOMER', MetafieldKey, MetafieldValue)); + end; + + local procedure CreateShopifyCustomer(var ShopifyCustomer: Record "Shpfy Customer"; ShopId: BigInteger) + begin + Any.SetDefaultSeed(); + ShopifyCustomer.Init(); + ShopifyCustomer.Id := Any.IntegerInRange(100000, 999999); + ShopifyCustomer."Shop Id" := ShopId; + ShopifyCustomer.Insert(false); + end; + + local procedure CreateShopifyCustomer(var Customer: Record Customer; var ShopifyCustomer: Record "Shpfy Customer") + begin + ShopifyCustomer.Init(); + ShopifyCustomer.Id := Any.IntegerInRange(100000, 999999); + ShopifyCustomer."Shop Id" := Shop."Shop Id"; + ShopifyCustomer."Customer SystemId" := Customer.SystemId; + ShopifyCustomer."First Name" := Any.AlphabeticText(100); + ShopifyCustomer."Last Name" := Any.AlphabeticText(100); + ShopifyCustomer.Email := Customer."E-Mail"; + ShopifyCustomer.Insert(false); + end; + + local procedure InvokeShopifyCustomerExport(var Customer: Record Customer; var ShopifyCustomer: Record "Shpfy Customer"; var ActualQuery: Text) + var + ShpfyCustomerExport: Codeunit "Shpfy Customer Export"; + CustomerMetafieldsSubs: Codeunit "Shpfy Customer Metafields Subs"; + begin + BindSubscription(CustomerMetafieldsSubs); + CustomerMetafieldsSubs.SetShopifyCustomerId(ShopifyCustomer.Id); + ShpfyCustomerExport.SetShop(Shop); + Customer.SetRange("No.", Customer."No."); + ShpfyCustomerExport.Run(Customer); + ActualQuery := CustomerMetafieldsSubs.GetGQLQuery(); + UnbindSubscription(CustomerMetafieldsSubs); + end; +} diff --git a/Apps/W1/Shopify/test/Metafields/ShpfyMetafieldsHelper.Codeunit.al b/Apps/W1/Shopify/test/Metafields/ShpfyMetafieldsHelper.Codeunit.al new file mode 100644 index 0000000000..963311de64 --- /dev/null +++ b/Apps/W1/Shopify/test/Metafields/ShpfyMetafieldsHelper.Codeunit.al @@ -0,0 +1,38 @@ +codeunit 139549 "Shpfy Metafields Helper" +{ + procedure CreateMetafieldsResult(ResourceID: BigInteger; Namespace: Text; OwnerType: Text; MetafieldKey: Text; MetafieldValue: Text): JsonArray + var + JNode: JsonObject; + JObject: JsonObject; + JMetafields: JsonArray; + begin + JNode.Add('id', StrSubstNo('gid://shopify/Metafield/%2', ResourceID)); + JNode.Add('namespace', Namespace); + JNode.Add('ownerType', OwnerType); + JNode.Add('legacyResourceId', ResourceID); + JNode.Add('key', MetafieldKey); + JNode.Add('value', MetafieldValue); + JNode.Add('type', 'string'); + JObject.Add('node', JNode); + JMetafields.Add(JObject); + exit(JMetafields); + end; + + procedure CreateMetafield(var ShpfyMetafield: Record "Shpfy Metafield"; OwnerId: BigInteger; ParentTableId: Integer): BigInteger + begin + exit(CreateMetafield(ShpfyMetafield, OwnerId, ParentTableId, '', '', '')); + end; + + procedure CreateMetafield(var ShpfyMetafield: Record "Shpfy Metafield"; OwnerId: BigInteger; ParentTableId: Integer; Namespace: Text; Name: Text; Value: Text): BigInteger + begin + ShpfyMetafield.Init(); + ShpfyMetafield."Owner Id" := OwnerId; + ShpfyMetafield.Validate("Parent Table No.", ParentTableId); + ShpfyMetafield.Namespace := Namespace; + ShpfyMetafield.Name := Name; + ShpfyMetafield.Value := Value; + ShpfyMetafield.Insert(true); + exit(ShpfyMetafield.Id); + end; + +} diff --git a/Apps/W1/Shopify/test/app.json b/Apps/W1/Shopify/test/app.json index ba1deea22a..b12dfca8c5 100644 --- a/Apps/W1/Shopify/test/app.json +++ b/Apps/W1/Shopify/test/app.json @@ -56,6 +56,10 @@ "from": 139560, "to": 139574 }, + { + "from": 139541, + "to": 139549 + }, { "from": 139576, "to": 139589