diff --git a/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLVariantMetafieldIds.Codeunit.al b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLVariantMetafieldIds.Codeunit.al
new file mode 100644
index 0000000000..4bf3e281b7
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/GraphQL/Codeunits/ShpfyGQLVariantMetafieldIds.Codeunit.al
@@ -0,0 +1,28 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Codeunit Shpfy GQL VariantMetafieldIds (ID 30336) implements Interface Shpfy IGraphQL.
+///
+codeunit 30336 "Shpfy GQL VariantMetafieldIds" implements "Shpfy IGraphQL"
+{
+ Access = Internal;
+
+ ///
+ /// GetGraphQL.
+ ///
+ /// Return value of type Text.
+ internal procedure GetGraphQL(): Text
+ begin
+ exit('{"query":"{productVariant(id: \"gid://shopify/ProductVariant/{{VariantId}}\") { metafields(first: 50) {edges{ node{legacyResourceId updatedAt}}}}}"}');
+ end;
+
+ ///
+ /// GetExpectedCost.
+ ///
+ /// Return value of type Integer.
+ internal 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 c3ee92e22b..76c143c034 100644
--- a/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al
+++ b/Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al
@@ -405,6 +405,26 @@ enum 30111 "Shpfy GraphQL Type" implements "Shpfy IGraphQL"
Caption = 'Get Order Transactions';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL OrderTransactions";
}
+ value(80; DraftOrderComplete)
+ {
+ Caption = 'Draft Order Complete';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL DraftOrderComplete";
+ }
+ value(81; FulfillOrder)
+ {
+ Caption = 'Fulfill Order';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL Fulfill Order";
+ }
+ value(82; GetPaymentTerms)
+ {
+ Caption = 'Get Payment Terms';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL Payment Terms";
+ }
+ value(83; GetFulfillmentOrderIds)
+ {
+ Caption = 'Get Fulfillments';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL Get Fulfillments";
+ }
value(85; ProductVariantDelete)
{
Caption = 'Product Variant Delete';
@@ -435,4 +455,34 @@ enum 30111 "Shpfy GraphQL Type" implements "Shpfy IGraphQL"
Caption = 'Get Next Reverse Fulfillment Order Lines';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL NextRevFulfillOrdLns";
}
+ value(91; TranslationsRegister)
+ {
+ Caption = 'Translations Register';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL TranslationsRegister";
+ }
+ value(92; ShopLocales)
+ {
+ Caption = 'Shop Locales';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL ShopLocales";
+ }
+ value(93; GetTranslResource)
+ {
+ Caption = 'Get Transl Resource';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL TranslResource";
+ }
+ value(94; MetafieldSet)
+ {
+ Caption = 'MetfieldSet';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL MetafieldsSet";
+ }
+ value(95; ProductMetafieldIds)
+ {
+ Caption = 'Product Metafield Ids';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL ProductMetafieldIds";
+ }
+ value(96; VariantMetafieldIds)
+ {
+ Caption = 'Variant Metafield Ids';
+ Implementation = "Shpfy IGraphQL" = "Shpfy GQL VariantMetafieldIds";
+ }
}
diff --git a/Apps/W1/Shopify/app/src/Integration/Codeunits/ShpfyAuthenticationMgt.Codeunit.al b/Apps/W1/Shopify/app/src/Integration/Codeunits/ShpfyAuthenticationMgt.Codeunit.al
index e3ac43a697..3770859591 100644
--- a/Apps/W1/Shopify/app/src/Integration/Codeunits/ShpfyAuthenticationMgt.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Integration/Codeunits/ShpfyAuthenticationMgt.Codeunit.al
@@ -15,7 +15,7 @@ codeunit 30199 "Shpfy Authentication Mgt."
var
// https://shopify.dev/api/usage/access-scopes
- ScopeTxt: Label 'write_orders,read_all_orders,write_assigned_fulfillment_orders,read_checkouts,write_customers,read_discounts,write_files,write_merchant_managed_fulfillment_orders,write_fulfillments,write_inventory,read_locations,read_payment_terms,write_products,write_shipping,read_shopify_payments_disputes,read_shopify_payments_payouts,write_returns,write_translations,write_third_party_fulfillment_orders,write_order_edits,write_companies,write_publications', Locked = true;
+ ScopeTxt: Label 'write_orders,read_all_orders,write_assigned_fulfillment_orders,read_checkouts,write_customers,read_discounts,write_files,write_merchant_managed_fulfillment_orders,write_fulfillments,write_inventory,read_locations,write_products,write_shipping,read_shopify_payments_disputes,read_shopify_payments_payouts,write_returns,write_translations,write_third_party_fulfillment_orders,write_order_edits,write_companies,write_publications,read_payment_terms,write_payment_terms,write_draft_orders,read_locales', Locked = true;
ShopifyAPIKeyAKVSecretNameLbl: Label 'ShopifyApiKey', Locked = true;
ShopifyAPISecretAKVSecretNameLbl: Label 'ShopifyApiSecret', Locked = true;
MissingAPIKeyTelemetryTxt: Label 'The api key has not been initialized.', Locked = true;
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al
new file mode 100644
index 0000000000..0e67fc2d40
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyDraftOrdersAPI.Codeunit.al
@@ -0,0 +1,386 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Sales.Comment;
+using Microsoft.Sales.History;
+using Microsoft.Finance.Currency;
+using Microsoft.Inventory.Item.Attribute;
+using Microsoft.Inventory.Item;
+
+///
+/// Codeunit Draft Orders API (ID 30159).
+///
+codeunit 30159 "Shpfy Draft Orders API"
+{
+ Access = Internal;
+
+ var
+ ShpfyShop: Record "Shpfy Shop";
+ ShpfyCommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ ShpfyJsonHelper: Codeunit "Shpfy Json Helper";
+
+ ///
+ /// Creates a draft order in shopify by constructing and sending a graphQL request.
+ ///
+ /// Header information for a shopify order.
+ /// Line items for a shopify order.
+ /// Tax lines for a shopify order.
+ /// Unique id of the created draft order in shopify.
+ internal procedure CreateDraftOrder(
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]
+ ): BigInteger
+ var
+ DraftOrderId: BigInteger;
+ GraphQuery: TextBuilder;
+ begin
+ GraphQuery := CreateDraftOrderGQLRequest(TempShpfyOrderHeader, TempShpfyOrderLine, ShpfyOrderTaxLines);
+ DraftOrderId := SendDraftOrderGraphQLRequest(GraphQuery);
+ exit(DraftOrderId);
+ end;
+
+ ///
+ /// Completes a draft order in shopify by converting it to an order.
+ ///
+ /// Draft order id that needs to be completed.
+ /// Json response of a created order in shopify.
+ internal procedure CompleteDraftOrder(DraftOrderId: BigInteger): JsonToken
+ var
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ Parameters: Dictionary of [Text, Text];
+ JResponse: JsonToken;
+ begin
+ GraphQLType := "Shpfy GraphQL Type"::DraftOrderComplete;
+ Parameters.Add('DraftOrderId', Format(DraftOrderId));
+ JResponse := ShpfyCommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+ exit(JResponse);
+ end;
+
+ ///
+ /// Sets a global shopify shop to be used for draft orders api functionality.
+ ///
+ /// Shopify shop code to be set.
+ internal procedure SetShop(ShopCode: Code[20])
+ begin
+ Clear(ShpfyShop);
+ ShpfyShop.Get(ShopCode);
+ ShpfyCommunicationMgt.SetShop(ShpfyShop);
+ end;
+
+ local procedure CreateDraftOrderGQLRequest(
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]
+ ): TextBuilder
+ var
+ GraphQuery: TextBuilder;
+ begin
+ GraphQuery.Append('{"query":"mutation {draftOrderCreate(input: {');
+ if TempShpfyOrderHeader.Email <> '' then begin
+ GraphQuery.Append('email: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader.Email));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Phone No." <> '' then begin
+ GraphQuery.Append('phone: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Phone No."));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Currency Code" <> '' then begin
+ GraphQuery.Append('presentmentCurrencyCode: ');
+ GraphQuery.Append(Format(GetISOCode(TempShpfyOrderHeader."Currency Code")));
+ end;
+ if TempShpfyOrderHeader."Discount Amount" <> 0 then
+ AddDiscountAmountToGraphQuery(GraphQuery, TempShpfyOrderHeader."Discount Amount", 'Invoice Discount Amount');
+
+ GraphQuery.Append(', taxExempt: true');
+
+ AddShippingAddressToGraphQuery(GraphQuery, TempShpfyOrderHeader);
+ AddBillingAddressToGraphQuery(GraphQuery, TempShpfyOrderHeader);
+ AddNote(GraphQuery, TempShpfyOrderHeader);
+ if TempShpfyOrderHeader.Unpaid then
+ AddPaymentTerms(GraphQuery, TempShpfyOrderHeader);
+
+ AddLineItemsToGraphQuery(GraphQuery, TempShpfyOrderHeader, TempShpfyOrderLine, ShpfyOrderTaxLines);
+
+ GraphQuery.Append('}) {draftOrder { legacyResourceId } userErrors {field, message}}');
+ GraphQuery.Append('}"}');
+
+ exit(GraphQuery);
+ end;
+
+ local procedure SendDraftOrderGraphQLRequest(GraphQuery: TextBuilder): BigInteger
+ var
+ DraftOrderId: BigInteger;
+ JResponse: JsonToken;
+ begin
+ JResponse := ShpfyCommunicationMgt.ExecuteGraphQL(GraphQuery.ToText());
+ DraftOrderId := ShpfyJsonHelper.GetValueAsBigInteger(JResponse, 'data.draftOrderCreate.draftOrder.legacyResourceId');
+ exit(DraftOrderId);
+ end;
+
+ local procedure AddShippingAddressToGraphQuery(var GraphQuery: TextBuilder; var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary)
+ begin
+ GraphQuery.Append(', shippingAddress: {');
+ if TempShpfyOrderHeader."Ship-to Address" <> '' then begin
+ GraphQuery.Append('address1: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to Address"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Ship-to Address 2" <> '' then begin
+ GraphQuery.Append(', address2: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to Address 2"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Ship-to City" <> '' then begin
+ GraphQuery.Append(', city: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to City"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Ship-to Country/Region Code" <> '' then begin
+ GraphQuery.Append(', countryCode: ');
+ GraphQuery.Append(TempShpfyOrderHeader."Ship-to Country/Region Code");
+ end;
+ if TempShpfyOrderHeader."Ship-to Post Code" <> '' then begin
+ GraphQuery.Append(', zip: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to Post Code"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Ship-to Name" <> '' then begin
+ GraphQuery.Append(', firstName: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to Name"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Ship-to Name 2" <> '' then begin
+ GraphQuery.Append(', lastName: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Ship-to Name 2"));
+ GraphQuery.Append('\"');
+ end;
+ GraphQuery.Append('}');
+ end;
+
+ local procedure AddBillingAddressToGraphQuery(var GraphQuery: TextBuilder; var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary)
+ begin
+ GraphQuery.Append(', billingAddress: {');
+ if TempShpfyOrderHeader."Bill-to Address" <> '' then begin
+ GraphQuery.Append('address1: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to Address"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Bill-to Address 2" <> '' then begin
+ GraphQuery.Append(', address2: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to Address 2"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Bill-to City" <> '' then begin
+ GraphQuery.Append(', city: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to City"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Bill-to Country/Region Code" <> '' then begin
+ GraphQuery.Append(', countryCode: ');
+ GraphQuery.Append(TempShpfyOrderHeader."Bill-to Country/Region Code");
+ end;
+ if TempShpfyOrderHeader."Bill-to Post Code" <> '' then begin
+ GraphQuery.Append(', zip: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to Post Code"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Bill-to Name" <> '' then begin
+ GraphQuery.Append(', firstName: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to Name"));
+ GraphQuery.Append('\"');
+ end;
+ if TempShpfyOrderHeader."Bill-to Name 2" <> '' then begin
+ GraphQuery.Append(', lastName: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderHeader."Bill-to Name 2"));
+ GraphQuery.Append('\"');
+ end;
+ GraphQuery.Append('}');
+ end;
+
+ local procedure AddLineItemsToGraphQuery(
+ var GraphQuery: TextBuilder;
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]
+ )
+ var
+ TaxTitle: Text;
+ begin
+ TempShpfyOrderLine.SetRange("Shopify Order Id", TempShpfyOrderHeader."Shopify Order Id");
+ if TempShpfyOrderLine.FindSet(false) then begin
+ GraphQuery.Append(', lineItems: [');
+ repeat
+ GraphQuery.Append('{');
+ GraphQuery.Append('title: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TempShpfyOrderLine.Description));
+ GraphQuery.Append('\"');
+
+ if TempShpfyOrderLine."Shopify Variant Id" <> 0 then begin
+ GraphQuery.Append(', variantId: \"gid://shopify/ProductVariant/');
+ GraphQuery.Append(Format(TempShpfyOrderLine."Shopify Variant Id"));
+ GraphQuery.Append('\"');
+
+ AddItemAttributes(GraphQuery, TempShpfyOrderLine."Item No.");
+ end;
+
+ GraphQuery.Append(', quantity: ');
+ GraphQuery.Append(Format(TempShpfyOrderLine.Quantity, 0, 9));
+
+ GraphQuery.Append(', originalUnitPrice :');
+ GraphQuery.Append(Format(TempShpfyOrderLine."Unit Price", 0, 9));
+
+ GraphQuery.Append('},');
+ until TempShpfyOrderLine.Next() = 0;
+
+ foreach TaxTitle in ShpfyOrderTaxLines.Keys() do begin
+ GraphQuery.Append('{');
+ GraphQuery.Append('title: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(TaxTitle));
+ GraphQuery.Append('\"');
+
+ GraphQuery.Append(', quantity: ');
+ GraphQuery.Append(Format(1, 0, 9));
+
+ GraphQuery.Append(', originalUnitPrice: ');
+ GraphQuery.Append(Format(ShpfyOrderTaxLines.Get(TaxTitle), 0, 9));
+
+ GraphQuery.Append('},');
+ end;
+ GraphQuery.Remove(GraphQuery.Length(), 1);
+ end;
+ GraphQuery.Append(']');
+ end;
+
+ local procedure AddDiscountAmountToGraphQuery(var GraphQuery: TextBuilder; DiscountAmount: Decimal; DiscountTitle: Text)
+ begin
+ GraphQuery.Append(', appliedDiscount: {');
+ GraphQuery.Append('description: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(DiscountTitle));
+ GraphQuery.Append('\"');
+
+ GraphQuery.Append(', value: ');
+ GraphQuery.Append(Format(DiscountAmount, 0, 9));
+
+ GraphQuery.Append(', valueType: ');
+ GraphQuery.Append('FIXED_AMOUNT');
+
+ GraphQuery.Append(', title: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(DiscountTitle));
+ GraphQuery.Append('\"');
+
+ GraphQuery.Append('}');
+ end;
+
+ local procedure AddNote(var GraphQuery: TextBuilder; var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary)
+ var
+ SalesCommentLine: Record "Sales Comment Line";
+ NotesTextBuilder: TextBuilder;
+ begin
+ SalesCommentLine.SetRange("Document Type", SalesCommentLine."Document Type"::"Posted Invoice");
+ SalesCommentLine.SetRange("No.", TempShpfyOrderHeader."Sales Invoice No.");
+
+ if SalesCommentLine.FindSet() then begin
+ GraphQuery.Append(', note: \"');
+ repeat
+ NotesTextBuilder.Append(SalesCommentLine.Comment + '\n');
+ until SalesCommentLine.Next() = 0;
+ GraphQuery.Append(NotesTextBuilder.ToText());
+ GraphQuery.Append('\"');
+ end;
+ end;
+
+ local procedure AddPaymentTerms(var GraphQuery: TextBuilder; var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary)
+ var
+ SalesInvoiceHeader: Record "Sales Invoice Header";
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ DueAtDateTime: DateTime;
+ IssuedAtDateTime: DateTime;
+ begin
+ if not ShopifyPaymentTermsExists(ShpfyPaymentTerms, TempShpfyOrderHeader, SalesInvoiceHeader) then
+ exit;
+
+ GraphQuery.Append(', paymentTerms: {');
+ GraphQuery.Append('paymentTermsTemplateId: \"gid://shopify/PaymentTermsTemplate/');
+ GraphQuery.Append(Format(ShpfyPaymentTerms.Id));
+ GraphQuery.Append('\"');
+
+ Evaluate(IssuedAtDateTime, Format(SalesInvoiceHeader."Document Date"));
+ Evaluate(DueAtDateTime, Format(SalesInvoiceHeader."Due Date"));
+
+ GraphQuery.Append(', paymentSchedules: {');
+ if ShpfyPaymentTerms.Type = 'FIXED' then begin
+ GraphQuery.Append('dueAt: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(Format(DueAtDateTime, 0, 9)));
+ GraphQuery.Append('\"');
+ end else
+ if ShpfyPaymentTerms.Type = 'NET' then begin
+ GraphQuery.Append(', issuedAt: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(Format(IssuedAtDateTime, 0, 9)));
+ GraphQuery.Append('\"');
+ end;
+
+ GraphQuery.Append('}}');
+ end;
+
+ local procedure ShopifyPaymentTermsExists(
+ var ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var SalesInvoiceHeader: Record "Sales Invoice Header"
+ ): Boolean
+ begin
+ SalesInvoiceHeader.Get(TempShpfyOrderHeader."Sales Invoice No.");
+ ShpfyPaymentTerms.SetRange("Payment Terms Code", SalesInvoiceHeader."Payment Terms Code");
+ ShpfyPaymentTerms.SetRange("Shop Code", ShpfyShop.Code);
+
+ if not ShpfyPaymentTerms.FindFirst() then begin
+ ShpfyPaymentTerms.SetRange("Payment Terms Code");
+ ShpfyPaymentTerms.SetRange("Is Primary", true);
+
+ if not ShpfyPaymentTerms.FindFirst() then
+ exit(false);
+ end;
+
+ exit(true);
+ end;
+
+ local procedure GetISOCode(CurrencyCode: Code[10]): Code[3]
+ var
+ Currency: Record Currency;
+ begin
+ Currency.Get(CurrencyCode);
+ exit(Currency."ISO Code");
+ end;
+
+ local procedure AddItemAttributes(var GraphQuery: TextBuilder; ItemNo: Code[20])
+ var
+ Item: Record Item;
+ ItemAttribute: Record "Item Attribute";
+ ItemAttributeValue: Record "Item Attribute Value";
+ ItemAttributeValueMapping: Record "Item Attribute Value Mapping";
+ begin
+ Item.Get(ItemNo);
+ ItemAttributeValueMapping.SetRange("Table ID", Database::Item);
+ ItemAttributeValueMapping.SetRange("No.", ItemNo);
+ if ItemAttributeValueMapping.FindSet() then begin
+ GraphQuery.Append(', customAttributes: [');
+ repeat
+ ItemAttribute.Get(ItemAttributeValueMapping."Item Attribute ID");
+ ItemAttributeValue.Get(ItemAttribute.ID, ItemAttributeValueMapping."Item Attribute Value ID");
+
+ GraphQuery.Append('{');
+ GraphQuery.Append('key: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(Format(ItemAttribute.Name)));
+ GraphQuery.Append('\"');
+
+ GraphQuery.Append(', value: \"');
+ GraphQuery.Append(ShpfyCommunicationMgt.EscapeGrapQLData(Format(ItemAttributeValue.Value)));
+ GraphQuery.Append('\"');
+ GraphQuery.Append('},')
+ until ItemAttributeValueMapping.Next() = 0;
+ GraphQuery.Append(']');
+ end;
+
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyFulfillmentAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyFulfillmentAPI.Codeunit.al
new file mode 100644
index 0000000000..e63b44d222
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyFulfillmentAPI.Codeunit.al
@@ -0,0 +1,69 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Codeunit Shpfy Fulfillment API (ID 30361).
+///
+codeunit 30361 "Shpfy Fulfillment API"
+{
+ Access = Internal;
+
+ var
+ ShpfyCommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+
+ ///
+ /// Creates a fulfillment for a provided fulfillment order id.
+ ///
+ /// Fulfillment order id.
+ internal procedure CreateFulfillment(FulfillmentOrderId: BigInteger)
+ var
+ JResponse: JsonToken;
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ Parameters: Dictionary of [Text, Text];
+ begin
+ GraphQLType := "Shpfy GraphQL Type"::FulfillOrder;
+ Parameters.Add('FulfillmentOrderId', Format(FulfillmentOrderId));
+ JResponse := ShpfyCommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+ end;
+
+ ///
+ /// Gets fulfillment order ids for a provided shopify order id.
+ ///
+ /// Shopify order id to get fulfillments from.
+ /// Number of fulfillment orders to get.
+ /// List of fulfillment order ids.
+ internal procedure GetFulfillmentOrderIds(OrderId: Text; NumberOfLines: Integer) FulfillmentOrderList: List of [BigInteger]
+ var
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ Parameters: Dictionary of [Text, Text];
+ JFulfillments: JsonToken;
+ begin
+ GraphQLType := "Shpfy GraphQL Type"::GetFulfillmentOrderIds;
+ Parameters.Add('OrderId', OrderId);
+ Parameters.Add('NumberOfOrders', Format(NumberOfLines));
+ JFulfillments := ShpfyCommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+ FulfillmentOrderList := ParseFulfillmentOrders(JFulfillments);
+
+ exit(FulfillmentOrderList);
+ end;
+
+ ///
+ /// Sets a global shopify shop to be used for fulfillment api functionality.
+ ///
+ /// Shopify shop code to be set.
+ internal procedure SetShop(ShopCode: Code[20])
+ begin
+ ShpfyCommunicationMgt.SetShop(ShopCode);
+ end;
+
+ local procedure ParseFulfillmentOrders(JFulfillments: JsonToken) FulfillmentOrderList: List of [BigInteger]
+ var
+ ShpfyJsonHelper: Codeunit "Shpfy Json Helper";
+ JArray: JsonArray;
+ JToken: JsonToken;
+ begin
+ JArray := ShpfyJsonHelper.GetJsonArray(JFulfillments, 'data.order.fulfillmentOrders.nodes');
+
+ foreach JToken in JArray do
+ FulfillmentOrderList.Add(ShpfyCommunicationMgt.GetIdOfGId(ShpfyJsonHelper.GetValueAsText(JToken, 'id')));
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyPostedInvoiceExport.Codeunit.al b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyPostedInvoiceExport.Codeunit.al
new file mode 100644
index 0000000000..bcd6981e3e
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyPostedInvoiceExport.Codeunit.al
@@ -0,0 +1,459 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Sales.History;
+using Microsoft.Finance.GeneralLedger.Setup;
+
+///
+/// Codeunit Shpfy Posted Invoice Export" (ID 30362).
+///
+codeunit 30362 "Shpfy Posted Invoice Export"
+{
+ Access = Internal;
+ TableNo = "Sales Invoice Header";
+ Permissions = tabledata "Sales Invoice Header" = m;
+
+ var
+ ShpfyShop: Record "Shpfy Shop";
+ ShpfyDraftOrdersAPI: Codeunit "Shpfy Draft Orders API";
+ ShpfyFulfillmentAPI: Codeunit "Shpfy Fulfillment API";
+ ShpfyJsonHelper: Codeunit "Shpfy Json Helper";
+
+ trigger OnRun()
+ begin
+ ExportPostedSalesInvoiceToShopify(Rec);
+ end;
+
+ ///
+ /// Sets a global shopify shop to be used for posted invoice export.
+ ///
+ /// Shopify shop code to be set.
+ internal procedure SetShop(NewShopCode: Code[20])
+ begin
+ ShpfyShop.Get(NewShopCode);
+ ShpfyDraftOrdersAPI.SetShop(ShpfyShop.Code);
+ ShpfyFulfillmentAPI.SetShop(ShpfyShop.Code);
+ end;
+
+ ///
+ /// Exports provided posted sales invoice to shopify.
+ ///
+ ///
+ /// If the posted sales invoice isn't exportable, the shopify order id is set to -2.
+ /// If shopify order creation fails, the id is set to -1.
+ ///
+ /// Posted sales invoice to be exported.
+ internal procedure ExportPostedSalesInvoiceToShopify(SalesInvoiceHeader: Record "Sales Invoice Header")
+ var
+ TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ DraftOrderId: BigInteger;
+ ShpfyOrderTaxLines: Dictionary of [Text, Decimal];
+ FulfillmentOrderIds: List of [BigInteger];
+ JResponse: JsonToken;
+ OrderId: BigInteger;
+ OrderNo: Text;
+ begin
+ if not IsInvoiceExportable(SalesInvoiceHeader) then begin
+ SetSalesInvoiceShopifyOrderInformation(SalesInvoiceHeader, -2, '');
+ exit;
+ end;
+
+ MapPostedSalesInvoiceData(SalesInvoiceHeader, TempShpfyOrderHeader, TempShpfyOrderLine, ShpfyOrderTaxLines);
+
+ DraftOrderId := ShpfyDraftOrdersAPI.CreateDraftOrder(TempShpfyOrderHeader, TempShpfyOrderLine, ShpfyOrderTaxLines);
+ JResponse := ShpfyDraftOrdersAPI.CompleteDraftOrder(DraftOrderId);
+
+ if IsSuccess(JResponse) then begin
+ OrderId := ShpfyJsonHelper.GetValueAsBigInteger(JResponse, 'data.draftOrderComplete.draftOrder.order.legacyResourceId');
+ OrderNo := ShpfyJsonHelper.GetValueAsText(JResponse, 'data.draftOrderComplete.draftOrder.order.name');
+
+ FulfillmentOrderIds := ShpfyFulfillmentAPI.GetFulfillmentOrderIds(Format(OrderId), GetNumberOfLines(TempShpfyOrderLine, ShpfyOrderTaxLines));
+ CreateFulfillmentsForShopifyOrder(FulfillmentOrderIds);
+ CreateShpfyInvoiceHeader(OrderId);
+ SetSalesInvoiceShopifyOrderInformation(SalesInvoiceHeader, OrderId, Format(OrderNo));
+ AddDocumentLinkToBCDocument(SalesInvoiceHeader);
+ end else
+ SetSalesInvoiceShopifyOrderInformation(SalesInvoiceHeader, -1, '');
+ end;
+
+ local procedure CreateFulfillmentsForShopifyOrder(FulfillmentOrderIds: List of [BigInteger])
+ var
+ FulfillmentOrderId: BigInteger;
+ begin
+ foreach FulfillmentOrderId in FulfillmentOrderIds do
+ ShpfyFulfillmentAPI.CreateFulfillment(FulfillmentOrderId);
+ end;
+
+ local procedure IsInvoiceExportable(SalesInvoiceHeader: Record "Sales Invoice Header"): Boolean
+ var
+ ShpfyCompany: Record "Shpfy Company";
+ ShpfyCustomer: Record "Shpfy Customer";
+ begin
+ ShpfyCompany.SetRange("Customer No.", SalesInvoiceHeader."Bill-to Customer No.");
+ if ShpfyCompany.IsEmpty() then begin
+ ShpfyCustomer.SetRange("Customer No.", SalesInvoiceHeader."Bill-to Customer No.");
+ if ShpfyCustomer.IsEmpty() then
+ exit(false);
+ end;
+
+ if not CurrencyCodeMatch(SalesInvoiceHeader) then
+ exit(false);
+
+ if not ShopifyPaymentTermsExists(SalesInvoiceHeader."Payment Terms Code") then
+ exit(false);
+
+ if ShpfyShop."Default Customer No." = SalesInvoiceHeader."Bill-to Customer No." then
+ exit(false);
+
+ if CheckCustomerTemplates(SalesInvoiceHeader."Bill-to Customer No.") then
+ exit(false);
+
+ if not CheckSalesInvoiceHeaderLines(SalesInvoiceHeader) then
+ exit(false);
+
+ exit(true);
+ end;
+
+ local procedure CurrencyCodeMatch(SalesInvoiceHeader: Record "Sales Invoice Header"): Boolean
+ var
+ GeneralLedgerSetup: Record "General Ledger Setup";
+ ShopifyLocalCurrencyCode: Code[10];
+ begin
+ GeneralLedgerSetup.Get();
+
+ if ShpfyShop."Currency Code" = '' then
+ ShopifyLocalCurrencyCode := GeneralLedgerSetup."LCY Code"
+ else
+ ShopifyLocalCurrencyCode := ShpfyShop."Currency Code";
+
+ if SalesInvoiceHeader."Currency Code" = '' then
+ exit(ShopifyLocalCurrencyCode = GeneralLedgerSetup."LCY Code")
+ else
+ exit(ShopifyLocalCurrencyCode = SalesInvoiceHeader."Currency Code");
+ end;
+
+ local procedure ShopifyPaymentTermsExists(PaymentTermsCode: Code[10]): Boolean
+ var
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ begin
+ ShpfyPaymentTerms.SetRange("Payment Terms Code", PaymentTermsCode);
+ ShpfyPaymentTerms.SetRange("Shop Code", ShpfyShop.Code);
+
+ if not ShpfyPaymentTerms.FindFirst() then begin
+ ShpfyPaymentTerms.SetRange("Payment Terms Code");
+ ShpfyPaymentTerms.SetRange("Is Primary", true);
+
+ if not ShpfyPaymentTerms.FindFirst() then
+ exit(false);
+ end;
+
+ exit(true);
+ end;
+
+ local procedure CheckCustomerTemplates(CustomerNo: Code[20]): Boolean
+ var
+ ShpfyCustomerTemplate: Record "Shpfy Customer Template";
+ begin
+ ShpfyCustomerTemplate.SetRange("Default Customer No.", CustomerNo);
+ ShpfyCustomerTemplate.SetRange("Shop Code", ShpfyShop.Code);
+ exit(not ShpfyCustomerTemplate.IsEmpty());
+ end;
+
+ local procedure CheckSalesInvoiceHeaderLines(SalesInvoiceHeader: Record "Sales Invoice Header"): Boolean
+ var
+ SalesInvoiceLine: Record "Sales Invoice Line";
+ begin
+ SalesInvoiceLine.SetFilter(Type, '<>%1', SalesInvoiceLine.Type::" ");
+ if SalesInvoiceLine.IsEmpty() then
+ exit(false);
+
+ SalesInvoiceLine.Reset();
+
+ SalesInvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No.");
+ SalesInvoiceLine.SetRange(Type, SalesInvoiceLine.Type::Item);
+ if SalesInvoiceLine.FindSet() then
+ repeat
+ if (SalesInvoiceLine.Quantity <> 0) and (SalesInvoiceLine.Quantity <> Round(SalesInvoiceLine.Quantity, 1)) then
+ exit(false);
+
+ if ShpfyShop."Items Mapped to Products" then
+ if not ItemIsMappedToShopifyProduct(SalesInvoiceLine) then
+ exit(false);
+
+ if (SalesInvoiceLine.Type <> SalesInvoiceLine.Type::" ") and (SalesInvoiceLine."No." = '') then
+ exit(false);
+ until SalesInvoiceLine.Next() = 0;
+
+ exit(true);
+ end;
+
+ local procedure SetSalesInvoiceShopifyOrderInformation(var SalesInvoiceHeader: Record "Sales Invoice Header"; OrderId: BigInteger; OrderNo: Code[50])
+ begin
+ SalesInvoiceHeader.Validate("Shpfy Order Id", OrderId);
+ SalesInvoiceHeader.Validate("Shpfy Order No.", OrderNo);
+ SalesInvoiceHeader.Modify(true);
+ end;
+
+ local procedure ItemIsMappedToShopifyProduct(SalesInvoiceLine: Record "Sales Invoice Line"): Boolean
+ var
+ ShpfyProduct: Record "Shpfy Product";
+ ShpfyVariant: Record "Shpfy Variant";
+ begin
+ ShpfyProduct.SetRange("Item No.", SalesInvoiceLine."No.");
+ if ShpfyProduct.IsEmpty() then
+ exit(false);
+
+ if ShpfyShop."UoM as Variant" then begin
+ if not ProductVariantExists(SalesInvoiceLine."Unit of Measure Code", SalesInvoiceLine) then
+ exit(false);
+ end else begin
+ ShpfyVariant.SetRange("Item No.", SalesInvoiceLine."No.");
+ ShpfyVariant.SetRange("Variant Code", SalesInvoiceLine."Variant Code");
+ ShpfyVariant.SetRange("Shop Code", ShpfyShop.Code);
+ if ShpfyVariant.IsEmpty() then
+ exit(false);
+ end;
+
+ exit(true);
+ end;
+
+ local procedure ProductVariantExists(UnitOfMeasure: Code[10]; SalesInvoiceLine: Record "Sales Invoice Line"): Boolean
+ var
+ ShpfyVariant: Record "Shpfy Variant";
+ begin
+ ShpfyVariant.SetRange("Item No.", SalesInvoiceLine."No.");
+ ShpfyVariant.SetRange("Shop Code", ShpfyShop.Code);
+ ShpfyVariant.SetRange("Variant Code", SalesInvoiceLine."Variant Code");
+ if ShpfyVariant.FindSet() then
+ repeat
+ case ShpfyVariant."UoM Option Id" of
+ 1:
+ if ShpfyVariant."Option 1 Value" = UnitOfMeasure then
+ exit(true);
+ 2:
+ if ShpfyVariant."Option 2 Value" = UnitOfMeasure then
+ exit(true);
+ 3:
+ if ShpfyVariant."Option 3 Value" = UnitOfMeasure then
+ exit(true);
+ end;
+ until ShpfyVariant.Next() = 0;
+ end;
+
+ local procedure MapPostedSalesInvoiceData(
+ SalesInvoiceHeader: Record "Sales Invoice Header";
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]
+ )
+ var
+ InvoiceLine: Record "Sales Invoice Line";
+ begin
+ MapSalesInvoiceHeader(SalesInvoiceHeader, TempShpfyOrderHeader);
+
+ InvoiceLine.SetRange("Document No.", SalesInvoiceHeader."No.");
+ if InvoiceLine.FindSet() then
+ repeat
+ MapSalesInvoiceLine(InvoiceLine, TempShpfyOrderHeader, TempShpfyOrderLine, ShpfyOrderTaxLines);
+ until InvoiceLine.Next() = 0;
+ end;
+
+ local procedure MapSalesInvoiceHeader(
+ SalesInvoiceHeader: Record "Sales Invoice Header";
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary
+ )
+ begin
+ TempShpfyOrderHeader.Init();
+ TempShpfyOrderHeader."Sales Invoice No." := SalesInvoiceHeader."No.";
+ TempShpfyOrderHeader."Sales Order No." := SalesInvoiceHeader."Order No.";
+ TempShpfyOrderHeader."Created At" := SalesInvoiceHeader.SystemCreatedAt;
+ TempShpfyOrderHeader.Confirmed := true;
+ TempShpfyOrderHeader."Updated At" := SalesInvoiceHeader.SystemModifiedAt;
+ TempShpfyOrderHeader."Currency Code" := MapCurrencyCode(SalesInvoiceHeader);
+ TempShpfyOrderHeader."Document Date" := SalesInvoiceHeader."Document Date";
+ SalesInvoiceHeader.CalcFields(Amount, "Amount Including VAT", "Invoice Discount Amount");
+ TempShpfyOrderHeader."VAT Amount" := SalesInvoiceHeader."Amount Including VAT" - SalesInvoiceHeader.Amount;
+ TempShpfyOrderHeader."Discount Amount" := SalesInvoiceHeader."Invoice Discount Amount";
+ TempShpfyOrderHeader."Fulfillment Status" := Enum::"Shpfy Order Fulfill. Status"::Fulfilled;
+ TempShpfyOrderHeader."Shop Code" := ShpfyShop.Code;
+ TempShpfyOrderHeader.Unpaid := IsInvoiceUnpaid(SalesInvoiceHeader);
+
+ MapBillToInformation(TempShpfyOrderHeader, SalesInvoiceHeader);
+ MapShipToInformation(TempShpfyOrderHeader, SalesInvoiceHeader);
+
+ TempShpfyOrderHeader.Insert(false);
+ end;
+
+ local procedure MapCurrencyCode(SalesInvoiceHeader: Record "Sales Invoice Header"): Code[10]
+ var
+ GeneralLedgerSetup: Record "General Ledger Setup";
+ begin
+ if SalesInvoiceHeader."Currency Code" <> '' then
+ exit(SalesInvoiceHeader."Currency Code");
+
+ GeneralLedgerSetup.Get();
+ exit(GeneralLedgerSetup."LCY Code");
+ end;
+
+ local procedure MapBillToInformation(
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ SalesInvoiceHeader: Record "Sales Invoice Header"
+ )
+ var
+ ShpfyCustomer: Record "Shpfy Customer";
+ begin
+ TempShpfyOrderHeader."Bill-to Name" := CopyStr(SalesInvoiceHeader."Bill-to Name", 1, MaxStrLen(TempShpfyOrderHeader."Bill-to Name"));
+ TempShpfyOrderHeader."Bill-to Name 2" := SalesInvoiceHeader."Bill-to Name 2";
+ TempShpfyOrderHeader."Bill-to Address" := SalesInvoiceHeader."Bill-to Address";
+ TempShpfyOrderHeader."Bill-to Address 2" := SalesInvoiceHeader."Bill-to Address 2";
+ TempShpfyOrderHeader."Bill-to Post Code" := SalesInvoiceHeader."Bill-to Post Code";
+ TempShpfyOrderHeader."Bill-to City" := SalesInvoiceHeader."Bill-to City";
+ TempShpfyOrderHeader."Bill-to County" := SalesInvoiceHeader."Bill-to County";
+ TempShpfyOrderHeader."Bill-to Country/Region Code" := SalesInvoiceHeader."Bill-to Country/Region Code";
+ TempShpfyOrderHeader."Bill-to Customer No." := SalesInvoiceHeader."Bill-to Customer No.";
+
+ ShpfyCustomer.SetRange("Customer No.", SalesInvoiceHeader."Bill-to Customer No.");
+ if ShpfyCustomer.FindFirst() then begin
+ TempShpfyOrderHeader.Email := CopyStr(ShpfyCustomer.Email, 1, MaxStrLen(TempShpfyOrderHeader.Email));
+ TempShpfyOrderHeader."Phone No." := ShpfyCustomer."Phone No.";
+ end;
+ end;
+
+ local procedure MapShipToInformation(
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ SalesInvoiceHeader: Record "Sales Invoice Header"
+ )
+ begin
+ TempShpfyOrderHeader."Ship-to Name" := CopyStr(SalesInvoiceHeader."Ship-to Name", 1, MaxStrLen(TempShpfyOrderHeader."Ship-to Name"));
+ TempShpfyOrderHeader."Ship-to Name 2" := SalesInvoiceHeader."Ship-to Name 2";
+ TempShpfyOrderHeader."Ship-to Address" := SalesInvoiceHeader."Ship-to Address";
+ TempShpfyOrderHeader."Ship-to Address 2" := SalesInvoiceHeader."Ship-to Address 2";
+ TempShpfyOrderHeader."Ship-to Post Code" := SalesInvoiceHeader."Ship-to Post Code";
+ TempShpfyOrderHeader."Ship-to City" := SalesInvoiceHeader."Ship-to City";
+ TempShpfyOrderHeader."Ship-to County" := SalesInvoiceHeader."Ship-to County";
+ TempShpfyOrderHeader."Ship-to Country/Region Code" := SalesInvoiceHeader."Ship-to Country/Region Code";
+ end;
+
+ local procedure IsInvoiceUnpaid(SalesInvoiceHeader: Record "Sales Invoice Header"): Boolean
+ begin
+ SalesInvoiceHeader.CalcFields("Remaining Amount");
+ exit(SalesInvoiceHeader."Remaining Amount" <> 0);
+ end;
+
+ local procedure MapSalesInvoiceLine(
+ SalesInvoiceLine: Record "Sales Invoice Line";
+ var TempShpfyOrderHeader: Record "Shpfy Order Header" temporary;
+ var TempShpfyOrderLine: Record "Shpfy Order Line" temporary;
+ var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]
+): BigInteger
+ var
+ ShpfyVariant: Record "Shpfy Variant";
+ begin
+ TempShpfyOrderLine.Init();
+ TempShpfyOrderLine."Line Id" := SalesInvoiceLine."Line No.";
+ TempShpfyOrderLine.Description := SalesInvoiceLine.Description;
+ TempShpfyOrderLine.Quantity := SalesInvoiceLine.Quantity;
+ TempShpfyOrderLine."Item No." := SalesInvoiceLine."No.";
+ TempShpfyOrderLine."Variant Code" := SalesInvoiceLine."Variant Code";
+ TempShpfyOrderLine."Gift Card" := false;
+ TempShpfyOrderLine.Taxable := false;
+ TempShpfyOrderLine."Unit Price" := SalesInvoiceLine."Unit Price";
+ TempShpfyOrderHeader."Discount Amount" += SalesInvoiceLine."Line Discount Amount";
+ TempShpfyOrderHeader.Modify(false);
+
+ if ShpfyShop."UoM as Variant" then
+ MapUOMProductVariants(SalesInvoiceLine, TempShpfyOrderLine)
+ else begin
+ ShpfyVariant.SetRange("Shop Code", ShpfyShop.Code);
+ ShpfyVariant.SetRange("Item No.", SalesInvoiceLine."No.");
+ ShpfyVariant.SetRange("Variant Code", SalesInvoiceLine."Variant Code");
+ if ShpfyVariant.FindFirst() then begin
+ TempShpfyOrderLine."Shopify Product Id" := ShpfyVariant."Product Id";
+ TempShpfyOrderLine."Shopify Variant Id" := ShpfyVariant.Id;
+ end;
+ end;
+
+ MapTaxLine(SalesInvoiceLine, ShpfyOrderTaxLines);
+
+ TempShpfyOrderLine.Insert(false);
+ end;
+
+ local procedure MapUOMProductVariants(SalesInvoiceLine: Record "Sales Invoice Line"; var TempShpfyOrderLine: Record "Shpfy Order Line" temporary)
+ var
+ ShpfyVariant: Record "Shpfy Variant";
+ begin
+ ShpfyVariant.SetRange("Shop Code", ShpfyShop.Code);
+ ShpfyVariant.SetRange("Item No.", SalesInvoiceLine."No.");
+ ShpfyVariant.SetRange("Variant Code", SalesInvoiceLine."Variant Code");
+ if ShpfyVariant.FindSet() then
+ repeat
+ case ShpfyVariant."UoM Option Id" of
+ 1:
+ if ShpfyVariant."Option 1 Value" = SalesInvoiceLine."Unit of Measure Code" then begin
+ TempShpfyOrderLine."Shopify Product Id" := ShpfyVariant."Product Id";
+ TempShpfyOrderLine."Shopify Variant Id" := ShpfyVariant.Id;
+ exit;
+ end;
+ 2:
+ if ShpfyVariant."Option 2 Value" = SalesInvoiceLine."Unit of Measure Code" then begin
+ TempShpfyOrderLine."Shopify Product Id" := ShpfyVariant."Product Id";
+ TempShpfyOrderLine."Shopify Variant Id" := ShpfyVariant.Id;
+ exit;
+ end;
+ 3:
+ if ShpfyVariant."Option 3 Value" = SalesInvoiceLine."Unit of Measure Code" then begin
+ TempShpfyOrderLine."Shopify Product Id" := ShpfyVariant."Product Id";
+ TempShpfyOrderLine."Shopify Variant Id" := ShpfyVariant.Id;
+ exit;
+ end;
+ end;
+ until ShpfyVariant.Next() = 0;
+ end;
+
+ local procedure MapTaxLine(var SalesInvoiceLine: Record "Sales Invoice Line" temporary; var ShpfyOrderTaxLines: Dictionary of [Text, Decimal])
+ var
+ VATAmount: Decimal;
+ TaxLineTok: Label '%1 - %2%', Comment = '%1 = VAT Calculation Type, %2 = VAT %', Locked = true;
+ TaxTitle: Text;
+ begin
+ VATAmount := SalesInvoiceLine."Amount Including VAT" - SalesInvoiceLine."VAT Base Amount";
+
+ TaxTitle := StrSubstNo(TaxLineTok, Format(SalesInvoiceLine."VAT Calculation Type"), Format(SalesInvoiceLine."VAT %"));
+ if ShpfyOrderTaxLines.ContainsKey(TaxTitle) then
+ ShpfyOrderTaxLines.Set(TaxTitle, ShpfyOrderTaxLines.Get(TaxTitle) + VATAmount)
+ else
+ ShpfyOrderTaxLines.Add(TaxTitle, VATAmount);
+ end;
+
+ local procedure IsSuccess(JsonTokenResponse: JsonToken): Boolean
+ begin
+ exit(ShpfyJsonHelper.GetJsonArray(JsonTokenResponse, 'data.draftOrderComplete.userErrors').Count() = 0);
+ end;
+
+ local procedure CreateShpfyInvoiceHeader(OrderId: BigInteger)
+ var
+ ShpfyInvoiceHeader: Record "Shpfy Invoice Header";
+ begin
+ ShpfyInvoiceHeader.Init();
+ ShpfyInvoiceHeader.Validate("Shopify Order Id", OrderId);
+ ShpfyInvoiceHeader.Insert(true);
+ end;
+
+ local procedure AddDocumentLinkToBCDocument(SalesInvoiceHeader: Record "Sales Invoice Header")
+ var
+ DocLinkToBCDoc: Record "Shpfy Doc. Link To Doc.";
+ BCDocumentTypeConvert: Codeunit "Shpfy BC Document Type Convert";
+ begin
+ DocLinkToBCDoc.Init();
+ DocLinkToBCDoc."Shopify Document Type" := "Shpfy Shop Document Type"::"Shopify Shop Order";
+ DocLinkToBCDoc."Shopify Document Id" := SalesInvoiceHeader."Shpfy Order Id";
+ DocLinkToBCDoc."Document Type" := BCDocumentTypeConvert.Convert(SalesInvoiceHeader);
+ DocLinkToBCDoc."Document No." := SalesInvoiceHeader."No.";
+ DocLinkToBCDoc.Insert(true);
+ end;
+
+ local procedure GetNumberOfLines(var TempShpfyOrderLine: Record "Shpfy Order Line" temporary; var ShpfyOrderTaxLines: Dictionary of [Text, Decimal]): Integer
+ begin
+ exit(ShpfyOrderTaxLines.Count() + TempShpfyOrderLine.Count());
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyUpdateSalesInvoice.Codeunit.al b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyUpdateSalesInvoice.Codeunit.al
new file mode 100644
index 0000000000..cf03c116ea
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Codeunits/ShpfyUpdateSalesInvoice.Codeunit.al
@@ -0,0 +1,22 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Sales.History;
+
+codeunit 30364 "Shpfy Update Sales Invoice"
+{
+ Access = Internal;
+
+ [EventSubscriber(ObjectType::Page, Page::"Posted Sales Inv. - Update", 'OnAfterRecordChanged', '', false, false)]
+ local procedure CheckShopifyOrderIdOnAfterRecordChanged(var SalesInvoiceHeader: Record "Sales Invoice Header"; xSalesInvoiceHeader: Record "Sales Invoice Header"; var IsChanged: Boolean)
+ begin
+ if IsChanged then
+ exit;
+ IsChanged := SalesInvoiceHeader."Shpfy Order Id" <> xSalesInvoiceHeader."Shpfy Order Id";
+ end;
+
+ [EventSubscriber(ObjectType::Codeunit, Codeunit::"Sales Inv. Header - Edit", 'OnOnRunOnBeforeTestFieldNo', '', false, false)]
+ local procedure SetShopifyOrderIdOnBeforeSalesShptHeaderModify(var SalesInvoiceHeader: Record "Sales Invoice Header"; SalesInvoiceHeaderRec: Record "Sales Invoice Header")
+ begin
+ SalesInvoiceHeader."Shpfy Order Id" := SalesInvoiceHeaderRec."Shpfy Order Id";
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/PageExt/ShpfySalesInvoiceUpdate.PageExt.al b/Apps/W1/Shopify/app/src/Invoicing/PageExt/ShpfySalesInvoiceUpdate.PageExt.al
new file mode 100644
index 0000000000..d9b090faa4
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/PageExt/ShpfySalesInvoiceUpdate.PageExt.al
@@ -0,0 +1,44 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Sales.History;
+
+///
+/// PageExtension Shpfy Sales Invoice Update (ID 30125) extends Record Posted Sales Inv. - Update.
+///
+pageextension 30125 "Shpfy Sales Invoice Update" extends "Posted Sales Inv. - Update"
+{
+ layout
+ {
+ addlast(content)
+ {
+ group(Shopify)
+ {
+ Caption = 'Shopify';
+ Visible = ShopifyTabVisible;
+
+ field("Shpfy Order Id"; Rec."Shpfy Order Id")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Shopify Order Id';
+ Editable = (Rec."Shpfy Order Id" = 0) or (Rec."Shpfy Order Id" = -1) or (Rec."Shpfy Order Id" = -2);
+ ToolTip = 'Specifies the Shopify Order ID. Helps track the status of invoices within Shopify, with 0 indicating readiness to synchronize, -1 indicating an error, and -2 indicating that the shipment is skipped.';
+
+ trigger OnValidate()
+ begin
+ if not (Rec."Shpfy Order Id" in [0, -1, -2]) then
+ Error(ValueNotAllowedErr);
+ end;
+ }
+ }
+ }
+ }
+
+ trigger OnAfterGetRecord()
+ begin
+ ShopifyTabVisible := Rec."Shpfy Order Id" <> 0;
+ end;
+
+ var
+ ShopifyTabVisible: Boolean;
+ ValueNotAllowedErr: Label 'Allowed values are 0, -1 or -2. 0 indicates readiness to synchronize, -1 indicates an error, and -2 indicates that the invoice is skipped.';
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Reports/ShpfySyncInvoicesToShpfy.Report.al b/Apps/W1/Shopify/app/src/Invoicing/Reports/ShpfySyncInvoicesToShpfy.Report.al
new file mode 100644
index 0000000000..38e086d885
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Reports/ShpfySyncInvoicesToShpfy.Report.al
@@ -0,0 +1,105 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Sales.History;
+
+///
+/// Report Shpfy Sync Invoices to Shpfy (ID 30119).
+///
+report 30119 "Shpfy Sync Invoices to Shpfy"
+{
+ ApplicationArea = All;
+ Caption = 'Sync Invoices to Shopify';
+ ProcessingOnly = true;
+ UsageCategory = Tasks;
+
+ dataset
+ {
+ dataitem(SalesInvoiceHeader; "Sales Invoice Header")
+ {
+ RequestFilterFields = "No.", "Posting Date";
+ trigger OnPreDataItem()
+ var
+ ShopCodeNotSetErr: Label 'Shopify Shop Code is empty.';
+ PostedInvoiceSyncNotSetErr: Label 'Posted Invoice Sync is not enabled for this shop.';
+ begin
+ if ShopCode = '' then
+ Error(ShopCodeNotSetErr);
+
+ ShpfyShop.Get(ShopCode);
+
+ if not ShpfyShop."Posted Invoice Sync" then
+ Error(PostedInvoiceSyncNotSetErr);
+
+ ShpfyPostedInvoiceExport.SetShop(ShopCode);
+ SetRange("Shpfy Order Id", 0);
+
+ if GuiAllowed then begin
+ CurrSalesInvoiceHeaderNo := SalesInvoiceHeader."No.";
+ ProcessDialog.Open(ProcessMsg, CurrSalesInvoiceHeaderNo);
+ ProcessDialog.Update();
+ end;
+ end;
+
+ trigger OnAfterGetRecord()
+ begin
+ if GuiAllowed then begin
+ CurrSalesInvoiceHeaderNo := SalesInvoiceHeader."No.";
+ ProcessDialog.Update();
+ end;
+
+ ShpfyPostedInvoiceExport.Run(SalesInvoiceHeader);
+ end;
+
+ trigger OnPostDataItem()
+ var
+ ShpfyBackgroundSyncs: Codeunit "Shpfy Background Syncs";
+ begin
+ if GuiAllowed then
+ ProcessDialog.Close();
+
+ ShpfyBackgroundSyncs.InventorySync(ShopCode);
+ end;
+ }
+ }
+ requestpage
+ {
+ SaveValues = true;
+ layout
+ {
+ area(Content)
+ {
+ group(ShopFilter)
+ {
+ Caption = 'Options';
+ field(Shop; ShopCode)
+ {
+ ApplicationArea = All;
+ Caption = 'Shop Code';
+ Lookup = true;
+ LookupPageId = "Shpfy Shops";
+ TableRelation = "Shpfy Shop";
+ ToolTip = 'Specifies the Shopify Shop to which the invoice will be exported.';
+ ShowMandatory = true;
+ }
+ }
+ }
+ }
+ }
+
+ var
+ ShpfyShop: Record "Shpfy Shop";
+ ShpfyPostedInvoiceExport: Codeunit "Shpfy Posted Invoice Export";
+ ShopCode: Code[20];
+ CurrSalesInvoiceHeaderNo: Code[20];
+ ProcessDialog: Dialog;
+ ProcessMsg: Label 'Synchronizing Posted Sales Invoice #1####################', Comment = '#1 = Posted Sales Invoice No.';
+
+ ///
+ /// Sets a global shopify shop code to be used.
+ ///
+ /// Shopify shop code to be set.
+ internal procedure SetShop(NewShopCode: Code[20])
+ begin
+ ShopCode := NewShopCode;
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Invoicing/Tables/ShpfyInvoiceHeader.Table.al b/Apps/W1/Shopify/app/src/Invoicing/Tables/ShpfyInvoiceHeader.Table.al
new file mode 100644
index 0000000000..001b9f99e0
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Invoicing/Tables/ShpfyInvoiceHeader.Table.al
@@ -0,0 +1,27 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Table Shpfy Invoice Header (ID 30161).
+///
+table 30161 "Shpfy Invoice Header"
+{
+ Caption = 'Shopify Invoice Header';
+ DataClassification = CustomerContent;
+ Access = Internal;
+
+ fields
+ {
+ field(1; "Shopify Order Id"; BigInteger)
+ {
+ Caption = 'Shopify Order Id';
+ }
+ }
+
+ keys
+ {
+ key(PK; "Shopify Order Id")
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeBoolean.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeBoolean.Codeunit.al
new file mode 100644
index 0000000000..9b1b65d3a7
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeBoolean.Codeunit.al
@@ -0,0 +1,27 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30338 "Shpfy Mtfld Type Boolean" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ DummyBoolean: Boolean;
+ begin
+ exit(Evaluate(DummyBoolean, Value, 9));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('true');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeCollectRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeCollectRef.Codeunit.al
new file mode 100644
index 0000000000..25e78a3bdd
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeCollectRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30321 "Shpfy Mtfld Type Collect. Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/Collection\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/Collection/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeColor.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeColor.Codeunit.al
new file mode 100644
index 0000000000..0579755885
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeColor.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30354 "Shpfy Mtfld Type Color" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^#[0-9A-Fa-f]{6}$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('#fff123');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDate.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDate.Codeunit.al
new file mode 100644
index 0000000000..96b530e90c
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDate.Codeunit.al
@@ -0,0 +1,27 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30318 "Shpfy Mtfld Type Date" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ DummyDate: Date;
+ begin
+ exit(Evaluate(DummyDate, Value, 9));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('2022-02-02');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDateTime.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDateTime.Codeunit.al
new file mode 100644
index 0000000000..e83ed841ed
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDateTime.Codeunit.al
@@ -0,0 +1,27 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30315 "Shpfy Mtfld Type DateTime" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ DummyDateTime: DateTime;
+ begin
+ exit(Evaluate(DummyDateTime, Value, 9));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('2022-01-01T12:30:00');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDimension.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDimension.Codeunit.al
new file mode 100644
index 0000000000..107cac0063
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeDimension.Codeunit.al
@@ -0,0 +1,70 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30351 "Shpfy Mtfld Type Dimension" implements "Shpfy IMetafield Type"
+{
+ var
+ DimensionJsonTemplateTxt: Label '{"value": %1, "unit": "%2"}', Locked = true;
+
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Dimension: Decimal;
+ Unit: Enum "Shpfy Metafield Dimension Type";
+ begin
+ exit(TryExtractValues(Value, Dimension, Unit));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ var
+ MetafieldAssistEdit: Page "Shpfy Metafield Assist Edit";
+ Dimension: Decimal;
+ Unit: Enum "Shpfy Metafield Dimension Type";
+ begin
+ if Value <> '' then
+ if not TryExtractValues(Value, Dimension, Unit) then begin
+ Clear(Dimension);
+ Clear(Unit);
+ end;
+
+ if MetafieldAssistEdit.OpenForDimension(Dimension, Unit) then begin
+ MetafieldAssistEdit.GetDimensionValue(Dimension, Unit);
+ Value := StrSubstNo(DimensionJsonTemplateTxt, Format(Dimension, 0, 9), GetDimensionTypeName(Unit));
+ end else
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit(StrSubstNo(DimensionJsonTemplateTxt, '1.5', 'cm'));
+ end;
+
+ [TryFunction]
+ local procedure TryExtractValues(Value: Text; var Dimension: Decimal; var Unit: Enum "Shpfy Metafield Dimension Type")
+ var
+ JToken: JsonToken;
+ JObject: JsonObject;
+ begin
+ JObject.ReadFrom(Value);
+ JObject.SelectToken('value', JToken);
+ Dimension := JToken.AsValue().AsDecimal();
+ JObject.SelectToken('unit', JToken);
+ Unit := ConvertToDimensionType(JToken.AsValue().AsText());
+
+ if JObject.Keys.Count() <> 2 then
+ Error('');
+ end;
+
+ local procedure GetDimensionTypeName(DimensionType: Enum "Shpfy Metafield Dimension Type"): Text
+ begin
+ exit(DimensionType.Names().Get(DimensionType.Ordinals().IndexOf(DimensionType.AsInteger())));
+ end;
+
+ local procedure ConvertToDimensionType(Value: Text) Type: Enum "Shpfy Metafield Dimension Type"
+ begin
+ exit(Enum::"Shpfy Metafield Dimension Type".FromInteger(Type.Ordinals().Get(Type.Names().IndexOf(Value))));
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeFileRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeFileRef.Codeunit.al
new file mode 100644
index 0000000000..9e5c357782
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeFileRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30322 "Shpfy Mtfld Type File Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/(GenericFile|MediaImage|Video)\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/MediaImage/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeInteger.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeInteger.Codeunit.al
new file mode 100644
index 0000000000..28a7406b37
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeInteger.Codeunit.al
@@ -0,0 +1,37 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30339 "Shpfy Mtfld Type Integer" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ DummyInteger: BigInteger;
+ MinInt: BigInteger;
+ MaxInt: BigInteger;
+ begin
+ if not Evaluate(DummyInteger, Value, 9) then
+ exit(false);
+
+ Evaluate(MinInt, '-9007199254740991', 9);
+ Evaluate(MaxInt, '9007199254740991', 9);
+ if (DummyInteger < MinInt) or (DummyInteger > MaxInt) then
+ exit(false);
+
+ exit(true);
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('123');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeJson.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeJson.Codeunit.al
new file mode 100644
index 0000000000..95d5732b99
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeJson.Codeunit.al
@@ -0,0 +1,27 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30353 "Shpfy Mtfld Type Json" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ JsonObject: JsonObject;
+ begin
+ exit(JsonObject.ReadFrom(Value));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('{"ingredient": "flour", "amount": 0.3}');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMetaobjRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMetaobjRef.Codeunit.al
new file mode 100644
index 0000000000..9053ac668d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMetaobjRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30327 "Shpfy Mtfld Type Metaobj. Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/Metaobject\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/Metaobject/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMixedRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMixedRef.Codeunit.al
new file mode 100644
index 0000000000..ca7dca2ed4
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMixedRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30331 "Shpfy Mtfld Type Mixed Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/[A-Za-z]+\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/Product/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMoney.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMoney.Codeunit.al
new file mode 100644
index 0000000000..2acb6640d6
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMoney.Codeunit.al
@@ -0,0 +1,74 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Finance.Currency;
+
+codeunit 30317 "Shpfy Mtfld Type Money" implements "Shpfy IMetafield Type"
+{
+ var
+ MoneyJsonTemplateTxt: Label '{"amount": "%1", "currency_code": "%2"}', Locked = true;
+
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Amount: Decimal;
+ CurrencyCode: Code[10];
+ begin
+ exit(TryExtractValues(Value, Amount, CurrencyCode));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ var
+ MetafieldAssistEdit: Page "Shpfy Metafield Assist Edit";
+ Amount: Decimal;
+ CurrencyCode: Code[10];
+ begin
+ if Value <> '' then
+ if not TryExtractValues(Value, Amount, CurrencyCode) then begin
+ Clear(Amount);
+ Clear(CurrencyCode);
+ end;
+
+ if MetafieldAssistEdit.OpenForMoney(Amount, CurrencyCode) then begin
+ MetafieldAssistEdit.GetMoneyValue(Amount, CurrencyCode);
+ Value := StrSubstNo(MoneyJsonTemplateTxt, Format(Amount, 0, 9), CurrencyCode);
+ exit(true);
+ end else
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit(StrSubstNo(MoneyJsonTemplateTxt, '5.99', 'CAD'));
+ end;
+
+ ///
+ /// Tried to extract the amount and currency code from the JSON string.
+ ///
+ /// JSON string with the following format: {"amount": "5.99", "currency_code": "CAD"}
+ /// Return value: the amount extracted from the JSON string.
+ /// Return value: the currency code extracted from the JSON string.
+ /// True if no errors occurred during the extraction.
+ [TryFunction]
+ internal procedure TryExtractValues(Value: Text; var Amount: Decimal; var CurrencyCode: Code[10])
+ var
+ Currency: Record Currency;
+ JToken: JsonToken;
+ JObject: JsonObject;
+ begin
+ JObject.ReadFrom(Value);
+ JObject.SelectToken('amount', JToken);
+ Amount := JToken.AsValue().AsDecimal();
+ JObject.SelectToken('currency_code', JToken);
+#pragma warning disable AA0139
+ CurrencyCode := JToken.AsValue().AsText();
+#pragma warning restore AA0139
+ Currency.Get(CurrencyCode);
+
+ if JObject.Keys.Count() <> 2 then
+ Error('');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMultiText.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMultiText.Codeunit.al
new file mode 100644
index 0000000000..5ddb65e64f
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeMultiText.Codeunit.al
@@ -0,0 +1,30 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30352 "Shpfy Mtfld Type Multi Text" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ var
+ MetafieldAssistEdit: Page "Shpfy Metafield Assist Edit";
+ begin
+ if MetafieldAssistEdit.OpenForMultiLineText(Value) then begin
+ MetafieldAssistEdit.GetMultiLineText(Value);
+ exit(true);
+ end else
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('Ingredients\Flour\Water\Milk\Eggs');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumDecimal.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumDecimal.Codeunit.al
new file mode 100644
index 0000000000..7f6d6eed30
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumDecimal.Codeunit.al
@@ -0,0 +1,30 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30319 "Shpfy Mtfld Type Num Decimal" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ // +/-9999999999999.999999999
+ exit(Regex.IsMatch(Value, '^[-+]?\d{1,13}(?:\.\d{1,9})?$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('123.45');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumInteger.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumInteger.Codeunit.al
new file mode 100644
index 0000000000..dbce1c839b
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeNumInteger.Codeunit.al
@@ -0,0 +1,37 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30320 "Shpfy Mtfld Type Num Integer" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ DummyInteger: BigInteger;
+ MinInt: BigInteger;
+ MaxInt: BigInteger;
+ begin
+ if not Evaluate(DummyInteger, Value, 9) then
+ exit(false);
+
+ Evaluate(MinInt, '-9007199254740991', 9);
+ Evaluate(MaxInt, '9007199254740991', 9);
+ if (DummyInteger < MinInt) or (DummyInteger > MaxInt) then
+ exit(false);
+
+ exit(true);
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('123');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypePageRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypePageRef.Codeunit.al
new file mode 100644
index 0000000000..81401c2ee1
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypePageRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30328 "Shpfy Mtfld Type Page Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/OnlinePage\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/OnlinePage/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeProductRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeProductRef.Codeunit.al
new file mode 100644
index 0000000000..e9c765c5d6
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeProductRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30329 "Shpfy Mtfld Type Product Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/Product\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/Product/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeSingleText.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeSingleText.Codeunit.al
new file mode 100644
index 0000000000..5a207003f6
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeSingleText.Codeunit.al
@@ -0,0 +1,25 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30323 "Shpfy Mtfld Type Single Text" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('VIP shipping method');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeString.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeString.Codeunit.al
new file mode 100644
index 0000000000..ddb25456f8
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeString.Codeunit.al
@@ -0,0 +1,25 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30337 "Shpfy Mtfld Type String" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('Example');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeUrl.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeUrl.Codeunit.al
new file mode 100644
index 0000000000..bbb87ff1fc
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeUrl.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30324 "Shpfy Mtfld Type Url" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^(http|https|mailto|sms|tel)://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(/\S*)?$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('https://www.shopify.com');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVariantRef.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVariantRef.Codeunit.al
new file mode 100644
index 0000000000..e3fe97876d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVariantRef.Codeunit.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Utilities;
+
+codeunit 30330 "Shpfy Mtfld Type Variant Ref" implements "Shpfy IMetafield Type"
+{
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(false);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Regex: Codeunit Regex;
+ begin
+ exit(Regex.IsMatch(Value, '^gid:\/\/shopify\/Variant\/\d+$'));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ begin
+ Value := Value;
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit('gid://shopify/Variant/1234567890');
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVolume.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVolume.Codeunit.al
new file mode 100644
index 0000000000..18faa414ef
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeVolume.Codeunit.al
@@ -0,0 +1,72 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30325 "Shpfy Mtfld Type Volume" implements "Shpfy IMetafield Type"
+{
+ var
+ VolumeJsonTemplateTxt: Label '{"value": %1,"unit":"%2"}', Locked = true;
+
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Volume: Decimal;
+ Unit: Enum "Shpfy Metafield Volume Type";
+ begin
+ exit(TryExtractValues(Value, Volume, Unit));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ var
+ MetafieldAssistEdit: Page "Shpfy Metafield Assist Edit";
+ Volume: Decimal;
+ Unit: Enum "Shpfy Metafield Volume Type";
+ begin
+ if Value <> '' then
+ if not TryExtractValues(Value, Volume, Unit) then begin
+ Clear(Volume);
+ Clear(Unit);
+ end;
+
+ if MetafieldAssistEdit.OpenForVolume(Volume, Unit) then begin
+ MetafieldAssistEdit.GetVolumeValue(Volume, Unit);
+ Value := StrSubstNo(VolumeJsonTemplateTxt, Format(Volume, 0, 9), GetVolumeTypeName(Unit));
+ exit(true);
+ end else
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit(StrSubstNo(VolumeJsonTemplateTxt, '20.0', 'ml'));
+ end;
+
+
+ [TryFunction]
+ local procedure TryExtractValues(Value: Text; var Volume: Decimal; var Unit: Enum "Shpfy Metafield Volume Type")
+ var
+ JToken: JsonToken;
+ JObject: JsonObject;
+ begin
+ JObject.ReadFrom(Value);
+ JObject.SelectToken('value', JToken);
+ Volume := JToken.AsValue().AsDecimal();
+ JObject.SelectToken('unit', JToken);
+ Unit := ConvertToVolumeType(JToken.AsValue().AsText());
+
+ if JObject.Keys.Count() <> 2 then
+ Error('');
+ end;
+
+ local procedure GetVolumeTypeName(VolumeType: Enum "Shpfy Metafield Volume Type"): Text
+ begin
+ exit(VolumeType.Names().Get(VolumeType.Ordinals().IndexOf(VolumeType.AsInteger())));
+ end;
+
+ local procedure ConvertToVolumeType(Value: Text) Type: Enum "Shpfy Metafield Volume Type"
+ begin
+ exit(Enum::"Shpfy Metafield Volume Type".FromInteger(Type.Ordinals().Get(Type.Names().IndexOf(Value))));
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeWeight.Codeunit.al b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeWeight.Codeunit.al
new file mode 100644
index 0000000000..5bf2777206
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IMetafieldType/ShpfyMtfldTypeWeight.Codeunit.al
@@ -0,0 +1,72 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30326 "Shpfy Mtfld Type Weight" implements "Shpfy IMetafield Type"
+{
+ var
+ WeightJsonTemplateTxt: Label '{"value": %1,"unit":"%2"}', Locked = true;
+
+ procedure HasAssistEdit(): Boolean
+ begin
+ exit(true);
+ end;
+
+ procedure IsValidValue(Value: Text): Boolean
+ var
+ Weight: Decimal;
+ Unit: Enum "Shpfy Metafield Weight Type";
+ begin
+ exit(TryExtractValues(Value, Weight, Unit));
+ end;
+
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+ var
+ MetafieldAssistEdit: Page "Shpfy Metafield Assist Edit";
+ Weight: Decimal;
+ Unit: Enum "Shpfy Metafield Weight Type";
+ begin
+ if Value <> '' then
+ if not TryExtractValues(Value, Weight, Unit) then begin
+ Clear(Weight);
+ Clear(Unit);
+ end;
+
+ if MetafieldAssistEdit.OpenForWeight(Weight, Unit) then begin
+ MetafieldAssistEdit.GetWeightValue(Weight, Unit);
+ Value := StrSubstNo(WeightJsonTemplateTxt, Format(Weight, 0, 9), GetWeightTypeName(Unit));
+ exit(true);
+ end else
+ exit(false);
+ end;
+
+ procedure GetExampleValue(): Text
+ begin
+ exit(StrSubstNo(WeightJsonTemplateTxt, '2.5', 'kg'));
+ end;
+
+
+ [TryFunction]
+ local procedure TryExtractValues(Value: Text; var Weight: Decimal; var Unit: Enum "Shpfy Metafield Weight Type")
+ var
+ JToken: JsonToken;
+ JObject: JsonObject;
+ begin
+ JObject.ReadFrom(Value);
+ JObject.SelectToken('value', JToken);
+ Weight := JToken.AsValue().AsDecimal();
+ JObject.SelectToken('unit', JToken);
+ Unit := ConvertToWeightType(JToken.AsValue().AsText());
+
+ if JObject.Keys.Count() <> 2 then
+ Error('');
+ end;
+
+ local procedure GetWeightTypeName(WeightType: Enum "Shpfy Metafield Weight Type"): Text
+ begin
+ exit(WeightType.Names().Get(WeightType.Ordinals().IndexOf(WeightType.AsInteger())));
+ end;
+
+ local procedure ConvertToWeightType(Value: Text) Type: Enum "Shpfy Metafield Weight Type"
+ begin
+ exit(Enum::"Shpfy Metafield Weight Type".FromInteger(Type.Ordinals().Get(Type.Names().IndexOf(Value))));
+ end;
+}
\ No newline at end of file
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
new file mode 100644
index 0000000000..98646f1731
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerCustomer.Codeunit.al
@@ -0,0 +1,19 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30333 "Shpfy Metafield Owner Customer" implements "Shpfy IMetafield Owner Type"
+{
+ procedure GetTableId(): Integer
+ begin
+ exit(Database::"Shpfy Customer");
+ end;
+
+ procedure RetrieveMetafieldIdsFromShopify(OwnerId: BigInteger): Dictionary of [BigInteger, DateTime]
+ begin
+ Error('Not implemented');
+ end;
+
+ procedure GetShopCode(OwnerId: BigInteger): Code[20]
+ begin
+ exit('Not implemented');
+ 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
new file mode 100644
index 0000000000..3998b83f29
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerProduct.Codeunit.al
@@ -0,0 +1,42 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30334 "Shpfy Metafield Owner Product" implements "Shpfy IMetafield Owner Type"
+{
+ procedure GetTableId(): Integer
+ begin
+ exit(Database::"Shpfy Product");
+ 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('ProductId', Format(OwnerId));
+ GraphQLType := GraphQLType::ProductMetafieldIds;
+ JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+ if JsonHelper.GetJsonArray(JResponse, JMetafields, 'data.product.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
+ Product: Record "Shpfy Product";
+ begin
+ Product.Get(OwnerId);
+ exit(Product."Shop Code");
+ 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
new file mode 100644
index 0000000000..3a9ff1e4b0
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/IOwnerType/ShpfyMetafieldOwnerVariant.Codeunit.al
@@ -0,0 +1,42 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30335 "Shpfy Metafield Owner Variant" implements "Shpfy IMetafield Owner Type"
+{
+ procedure GetTableId(): Integer
+ begin
+ exit(Database::"Shpfy Variant");
+ 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('VariantId', Format(OwnerId));
+ GraphQLType := GraphQLType::VariantMetafieldIds;
+ JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+ if JsonHelper.GetJsonArray(JResponse, JMetafields, 'data.product.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
+ Variant: Record "Shpfy Variant";
+ begin
+ Variant.Get(OwnerId);
+ exit(Variant."Shop Code");
+ 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
new file mode 100644
index 0000000000..bd3ba3ad8d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Codeunits/ShpfyMetafieldAPI.Codeunit.al
@@ -0,0 +1,237 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30316 "Shpfy Metafield API"
+{
+ Access = Internal;
+
+ var
+ JsonHelper: Codeunit "Shpfy Json Helper";
+
+ #region To Shopify
+ ///
+ /// Creates or updates the metafields in Shopify.
+ ///
+ ///
+ /// Only metafields that have been updated in BC since last update in Shopify will be updated.
+ /// MetafieldSet mutation only accepts 25 metafields at a time, so the function will create multiple queries if needed.
+ ///
+ ///
+ ///
+ internal procedure CreateOrUpdateMetafieldsInShopify(ParentTableId: Integer; OwnerId: BigInteger)
+ var
+ TempMetafieldSet: Record "Shpfy Metafield" temporary;
+ MetafieldIds: Dictionary of [BigInteger, DateTime];
+ Continue: Boolean;
+ Count: Integer;
+ GraphQuery: TextBuilder;
+ begin
+ MetafieldIds := RetrieveMetafieldsFromShopify(ParentTableId, OwnerId);
+ CollectMetafieldsInBC(ParentTableId, OwnerId, TempMetafieldSet, MetafieldIds);
+
+ // MetafieldsSet mutation only accepts 25 metafields at a time
+ Continue := true;
+ if TempMetafieldSet.FindSet() then
+ while Continue do begin
+ Count := 0;
+ Continue := false;
+ GraphQuery.Clear();
+
+ repeat
+ if Count = GetMaxMetafieldsToUpdate() then begin
+ Continue := true;
+ Clear(Count);
+ break;
+ end;
+
+ CreateMetafieldQuery(TempMetafieldSet, GraphQuery);
+ Count += 1;
+ until TempMetafieldSet.Next() = 0;
+
+ UpdateMetafields(GraphQuery.ToText());
+ end;
+ end;
+
+ local procedure GetMaxMetafieldsToUpdate(): Integer
+ begin
+ exit(25);
+ end;
+
+ local procedure RetrieveMetafieldsFromShopify(ParentTableId: Integer; OwnerId: BigInteger): Dictionary of [BigInteger, DateTime]
+ var
+ Metafield: Record "Shpfy Metafield";
+ IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type";
+ begin
+ IMetafieldOwnerType := Metafield.GetOwnerType(ParentTableId);
+ exit(IMetafieldOwnerType.RetrieveMetafieldIdsFromShopify(OwnerId));
+ end;
+
+ local procedure CollectMetafieldsInBC(ParentTableId: Integer; OwnerId: BigInteger; var TempMetafieldSet: Record "Shpfy Metafield" temporary; MetafieldIds: Dictionary of [BigInteger, DateTime])
+ var
+ Metafield: Record "Shpfy Metafield";
+ UpdatedAt: DateTime;
+ begin
+ Metafield.SetRange("Parent Table No.", ParentTableId);
+ Metafield.SetRange("Owner Id", OwnerId);
+ if Metafield.FindSet() then
+ repeat
+ if MetafieldIds.Get(Metafield.Id, UpdatedAt) then begin
+ if Metafield."Last Updated by BC" > UpdatedAt then begin
+ TempMetafieldSet := Metafield;
+ TempMetafieldSet.Insert(false);
+ end;
+ end else begin
+ TempMetafieldSet := Metafield;
+ TempMetafieldSet.Insert(false);
+ end;
+ until Metafield.Next() = 0;
+ end;
+
+ ///
+ /// Updates the metafields in Shopify.
+ ///
+ /// GraphQL query for the metafields.
+ internal procedure UpdateMetafields(MetafieldsQuery: Text) JResponse: JsonToken
+ var
+ CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ Parameters: Dictionary of [Text, Text];
+ begin
+ Parameters.Add('Metafields', MetafieldsQuery);
+ JResponse := CommunicationMgt.ExecuteGraphQL(Enum::"Shpfy GraphQL Type"::MetafieldSet, Parameters);
+ end;
+
+ ///
+ /// Creates a GraphQL query for a metafield.
+ ///
+ /// Metafield record to create the query for.
+ /// Return value: TextBuilder to append the query to.
+ internal procedure CreateMetafieldQuery(MetafieldSet: Record "Shpfy Metafield"; GraphQuery: TextBuilder)
+ begin
+ GraphQuery.Append('{');
+ GraphQuery.Append('key: \"');
+ GraphQuery.Append(MetafieldSet.Name);
+ GraphQuery.Append('\",');
+ GraphQuery.Append('namespace: \"');
+ GraphQuery.Append(MetafieldSet."Namespace");
+ GraphQuery.Append('\",');
+ GraphQuery.Append('ownerId: \"gid://shopify/');
+ GraphQuery.Append(MetafieldSet.GetOwnerTypeName());
+ GraphQuery.Append('/');
+ GraphQuery.Append(Format(MetafieldSet."Owner Id"));
+ GraphQuery.Append('\",');
+ GraphQuery.Append('value: \"');
+ GraphQuery.Append(EscapeGrapQLData(MetafieldSet.Value));
+ GraphQuery.Append('\",');
+ GraphQuery.Append('type: \"');
+ GraphQuery.Append(GetTypeName(MetafieldSet.Type));
+ GraphQuery.Append('\"');
+ GraphQuery.Append('},');
+ end;
+
+ local procedure EscapeGrapQLData(Data: Text): Text
+ begin
+ exit(Data.Replace('\', '\\\\').Replace('"', '\\\"'));
+ end;
+
+ local procedure GetTypeName(Type: Enum "Shpfy Metafield Type"): Text
+ begin
+ exit(Enum::"Shpfy Metafield Type".Names().Get(Enum::"Shpfy Metafield Type".Ordinals().IndexOf(Type.AsInteger())));
+ end;
+ #endregion
+
+ #region From Shopify
+ ///
+ /// Updates the metafields in Business Central from Shopify.
+ ///
+ ///
+ /// Metafields with a value longer than 2048 characters will not be imported.
+ /// Some metafield types are unsupported in Business Central (i.e. Rating).
+ ///
+ /// JSON array of metafields from Shopify.
+ /// Table id of the parent resource.
+ /// Id of the parent resource.
+ internal procedure UpdateMetafieldsFromShopify(JMetafields: JsonArray; ParentTableNo: Integer; OwnerId: BigInteger)
+ var
+ JNode: JsonObject;
+ JItem: JsonToken;
+ MetafieldIds: List of [BigInteger];
+ MetafieldId: BigInteger;
+ begin
+ CollectMetafieldIds(OwnerId, MetafieldIds);
+
+ foreach JItem in JMetafields do begin
+ JsonHelper.GetJsonObject(JItem.AsObject(), JNode, 'node');
+ MetafieldId := UpdateMetadataField(ParentTableNo, OwnerId, JNode);
+ MetafieldIds.Remove(MetafieldId);
+ end;
+
+ DeleteUnusedMetafields(MetafieldIds);
+ end;
+
+ local procedure UpdateMetadataField(ParentTableNo: Integer; OwnerId: BigInteger; JNode: JsonObject): BigInteger
+ var
+ Metafield: Record "Shpfy Metafield";
+ ValueText: Text;
+ Type: Enum "Shpfy Metafield Type";
+ begin
+ // Shopify has no limit on the length of the value, but Business Central has a limit of 2048 characters.
+ // If the value is longer than 2048 characters, Metafield is not imported.
+ ValueText := JsonHelper.GetValueAsText(JNode, 'value');
+ if StrLen(ValueText) > MaxStrLen(Metafield.Value) then
+ exit(0);
+
+ // Some metafield types are unsupported in Business Central (i.e. Rating)
+ if not ConvertToMetafieldType(JsonHelper.GetValueAsText(JNode, 'type'), Type) then
+ exit(0);
+
+
+ Metafield.Validate("Parent Table No.", ParentTableNo);
+ Metafield."Owner Id" := OwnerId;
+ Metafield.Id := JsonHelper.GetValueAsBigInteger(JNode, 'legacyResourceId');
+ Metafield.Type := Type;
+#pragma warning disable AA0139
+ Metafield."Namespace" := JsonHelper.GetValueAsText(JNode, 'namespace');
+ Metafield.Name := JsonHelper.GetValueAsText(JNode, 'key');
+ Metafield.Value := ValueText;
+#pragma warning restore AA0139
+ if not Metafield.Modify(false) then
+ Metafield.Insert(false);
+
+ exit(Metafield.Id);
+ end;
+
+ local procedure ConvertToMetafieldType(Value: Text; var Type: Enum "Shpfy Metafield Type"): Boolean
+ var
+ EnumOrdinal: Integer;
+ begin
+ // Some metafield types are unsupported in Business Central (i.e. Rating)
+ if not Enum::"Shpfy Metafield Type".Ordinals().Get(Enum::"Shpfy Metafield Type".Names().IndexOf(Value), EnumOrdinal) then
+ exit(false);
+
+ Type := Enum::"Shpfy Metafield Type".FromInteger(EnumOrdinal);
+ exit(true);
+ end;
+
+ local procedure CollectMetafieldIds(ProductId: BigInteger; MetafieldIds: List of [BigInteger])
+ var
+ Metafield: Record "Shpfy Metafield";
+ begin
+ MetaField.SetRange("Parent Table No.", Database::"Shpfy Product");
+ Metafield.SetRange("Owner Id", ProductId);
+ if Metafield.FindSet() then
+ repeat
+ MetafieldIds.Add(Metafield.Id);
+ until Metafield.Next() = 0;
+ end;
+
+ local procedure DeleteUnusedMetafields(MetafieldIds: List of [BigInteger])
+ var
+ Metafield: Record "Shpfy Metafield";
+ MetafieldId: BigInteger;
+ begin
+ foreach MetafieldId in MetafieldIds do begin
+ Metafield.Get(MetafieldId);
+ Metafield.Delete(false);
+ end;
+ end;
+ #endregion
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldDimensionType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldDimensionType.Enum.al
new file mode 100644
index 0000000000..1c3c72cb46
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldDimensionType.Enum.al
@@ -0,0 +1,35 @@
+namespace Microsoft.Integration.Shopify;
+
+enum 30160 "Shpfy Metafield Dimension Type"
+{
+ Access = Internal;
+
+ value(0; in)
+ {
+ Caption = 'in';
+ }
+
+ value(1; ft)
+ {
+ Caption = 'ft';
+ }
+
+ value(2; yd)
+ {
+ Caption = 'yd';
+ }
+ value(3; mm)
+ {
+ Caption = 'mm';
+ }
+
+ value(4; cm)
+ {
+ Caption = 'cm';
+ }
+
+ value(5; m)
+ {
+ Caption = 'm';
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al
new file mode 100644
index 0000000000..71d42d21c1
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldOwnerType.Enum.al
@@ -0,0 +1,24 @@
+namespace Microsoft.Integration.Shopify;
+
+enum 30156 "Shpfy Metafield Owner Type" implements "Shpfy IMetafield Owner Type"
+{
+ Access = Internal;
+
+ value(0; Customer)
+ {
+ Caption = 'Customer';
+ Implementation = "Shpfy IMetafield Owner Type" = "Shpfy Metafield Owner Customer";
+ }
+
+ value(1; Product)
+ {
+ Caption = 'Product';
+ Implementation = "Shpfy IMetafield Owner Type" = "Shpfy Metafield Owner Product";
+ }
+
+ value(2; ProductVariant)
+ {
+ Caption = 'Variant';
+ Implementation = "Shpfy IMetafield Owner Type" = "Shpfy Metafield Owner Variant";
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldType.Enum.al
new file mode 100644
index 0000000000..b4c21fb5b4
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldType.Enum.al
@@ -0,0 +1,161 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Enum Shpfy Metafield Type (ID 30159).
+///
+enum 30159 "Shpfy Metafield Type" implements "Shpfy IMetafield Type"
+{
+ Access = Internal;
+ Caption = 'Shopify Metafield Type';
+
+ Extensible = false;
+
+ value(0; string)
+ {
+ Caption = 'String';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type String";
+ }
+
+ value(1; integer)
+ {
+ Caption = 'Integer';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Integer";
+ }
+
+ value(2; json)
+ {
+ Caption = 'JSON';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type JSON";
+ }
+
+ value(3; boolean)
+ {
+ Caption = 'True or false';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Boolean";
+ }
+
+ value(4; color)
+ {
+ Caption = 'Color';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Color";
+ }
+
+ value(5; date)
+ {
+ Caption = 'Date';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Date";
+ }
+
+ value(6; date_time)
+ {
+ Caption = 'Date and time';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type DateTime";
+ }
+
+ value(7; dimension)
+ {
+ Caption = 'Dimension';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Dimension";
+ }
+
+ value(8; money)
+ {
+ Caption = 'Money';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Money";
+ }
+
+ value(9; multi_line_text_field)
+ {
+ Caption = 'Multi-line text';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Multi Text";
+ }
+
+ value(10; number_decimal)
+ {
+ Caption = 'Decimal';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Num Decimal";
+ }
+
+ value(11; number_integer)
+ {
+ Caption = 'Integer';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Num Integer";
+ }
+
+ // Intentionally commented out as we are not supporting this type at the moment
+ // value(12; rating)
+ // {
+ // Caption = 'Rating';
+ // }
+
+ // Intentionally commented out as we are not supporting this type at the moment
+ // value(13; rich_text_field)
+ // {
+ // Caption = 'Rich text';
+ // }
+
+ value(14; single_line_text_field)
+ {
+ Caption = 'Single line text';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Single Text";
+ }
+
+ value(15; url)
+ {
+ Caption = 'URL';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type URL";
+ }
+
+ value(16; volume)
+ {
+ Caption = 'Volume';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Volume";
+ }
+
+ value(17; weight)
+ {
+ Caption = 'Weight';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Weight";
+ }
+ value(18; collection_reference)
+ {
+ Caption = 'Collection reference';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Collect. Ref";
+ }
+
+ value(19; file_reference)
+ {
+ Caption = 'File';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type File Ref";
+ }
+
+ value(20; metaobject_reference)
+ {
+ Caption = 'Metaobject';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Metaobj. Ref";
+ }
+
+ value(21; mixed_reference)
+ {
+ Caption = 'Mixed reference';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Mixed Ref";
+ }
+
+ value(22; page_reference)
+ {
+ Caption = 'Page';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Page Ref";
+ }
+
+ value(23; product_reference)
+ {
+ Caption = 'Product';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Product Ref";
+ }
+
+ value(24; variant_reference)
+ {
+ Caption = 'Variant';
+ Implementation = "Shpfy IMetafield Type" = "Shpfy Mtfld Type Variant Ref";
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Base/Enums/ShpfyMetafieldValueType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al
similarity index 75%
rename from Apps/W1/Shopify/app/src/Base/Enums/ShpfyMetafieldValueType.Enum.al
rename to Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al
index dc3bb8acd4..fcf76e44aa 100644
--- a/Apps/W1/Shopify/app/src/Base/Enums/ShpfyMetafieldValueType.Enum.al
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldValueType.Enum.al
@@ -8,6 +8,9 @@ enum 30102 "Shpfy Metafield Value Type"
Access = Internal;
Caption = 'Shopify Metafield Value Type';
Extensible = false;
+ ObsoleteState = Pending;
+ ObsoleteReason = 'Value Type is obsolete in Shopify API. Use Metafield Type instead.';
+ ObsoleteTag = '25.0';
value(0; String)
{
diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldVolumeType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldVolumeType.Enum.al
new file mode 100644
index 0000000000..c1b87eb4ea
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldVolumeType.Enum.al
@@ -0,0 +1,59 @@
+namespace Microsoft.Integration.Shopify;
+
+enum 30158 "Shpfy Metafield Volume Type"
+{
+ Access = Internal;
+
+ value(0; ml)
+ {
+ Caption = 'ml';
+ }
+
+ value(1; cl)
+ {
+ Caption = 'cl';
+ }
+
+ value(2; l)
+ {
+ Caption = 'L';
+ }
+ value(3; m3)
+ {
+ Caption = 'm3';
+ }
+
+ value(4; us_fl_oz)
+ {
+ Caption = 'fl oz';
+ }
+
+ value(5; us_pt)
+ {
+ Caption = 'pt';
+ }
+ value(6; us_qt)
+ {
+ Caption = 'qt';
+ }
+ value(7; us_gal)
+ {
+ Caption = 'gal';
+ }
+ value(8; imp_fl_oz)
+ {
+ Caption = 'imp fl oz';
+ }
+ value(9; imp_pt)
+ {
+ Caption = 'imp pt';
+ }
+ value(10; imp_qt)
+ {
+ Caption = 'imp qt';
+ }
+ value(11; imp_gal)
+ {
+ Caption = 'imp gal';
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldWeightType.Enum.al b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldWeightType.Enum.al
new file mode 100644
index 0000000000..2581e24b3a
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Enums/ShpfyMetafieldWeightType.Enum.al
@@ -0,0 +1,26 @@
+namespace Microsoft.Integration.Shopify;
+
+enum 30157 "Shpfy Metafield Weight Type"
+{
+ Access = Internal;
+
+ value(0; kg)
+ {
+ Caption = 'kg';
+ }
+
+ value(1; g)
+ {
+ Caption = 'g';
+ }
+
+ value(2; lb)
+ {
+ Caption = 'lb';
+ }
+
+ value(3; oz)
+ {
+ Caption = 'oz';
+ }
+}
\ 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
new file mode 100644
index 0000000000..f11f3540f7
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldOwnerType.Interface.al
@@ -0,0 +1,29 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Interface used to for metafield operations related to metafield owner resource.
+///
+interface "Shpfy IMetafield Owner Type"
+{
+ Access = Internal;
+
+ ///
+ /// Returns the table id where the owner record is stored in BC.
+ ///
+ /// Table id.
+ procedure GetTableId(): Integer
+
+ ///
+ /// Retrieves metafields belonging to the owner resource in a dictionary with the last updated at timestamp.
+ ///
+ /// Id of the owner resource.
+ /// Dictionary of metafield ids and last updated at timestamp.
+ procedure RetrieveMetafieldIdsFromShopify(OwnerId: BigInteger): Dictionary of [BigInteger, DateTime]
+
+ ///
+ /// Retrieves the shop code from the owner resource.
+ ///
+ /// Id of the owner resource.
+ /// Shop code.
+ procedure GetShopCode(OwnerId: BigInteger): Code[20]
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldType.Interface.al b/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldType.Interface.al
new file mode 100644
index 0000000000..47dc65200d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Interfaces/ShpfyIMetafieldType.Interface.al
@@ -0,0 +1,35 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Interface used for validating and editing values of a Shopify Metafield.
+///
+interface "Shpfy IMetafield Type"
+{
+ Access = Internal;
+
+ ///
+ /// Determines if Type defines an Assist Edit dialog.
+ ///
+ /// True if Type defines an Assist Edit dialog, otherwise false.
+ procedure HasAssistEdit(): Boolean
+
+ ///
+ /// Determines if provided value is valid for Type.
+ ///
+ /// Value to validate.
+ /// True if value is valid, otherwise False.
+ procedure IsValidValue(Value: Text): Boolean
+
+ ///
+ /// Opens a dialog to assist in editing the value.
+ ///
+ /// Value to edit. Value may be modified.
+ /// True if value was edited, otherwise False.
+ procedure AssistEdit(var Value: Text[2048]): Boolean
+
+ ///
+ /// Returns an example value for the Type.
+ ///
+ /// Example value.
+ procedure GetExampleValue(): Text
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafieldAssistEdit.Page.al b/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafieldAssistEdit.Page.al
new file mode 100644
index 0000000000..623fcd5ffc
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafieldAssistEdit.Page.al
@@ -0,0 +1,264 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Finance.Currency;
+
+page 30164 "Shpfy Metafield Assist Edit"
+{
+ Caption = 'Metafield Assist Edit';
+ PageType = StandardDialog;
+ ApplicationArea = All;
+ UsageCategory = Administration;
+
+ layout
+ {
+ area(Content)
+ {
+ group(MoneyGroup)
+ {
+ Visible = IsMoneyVisible;
+ ShowCaption = false;
+
+ field(MoneyValue; MoneyValue)
+ {
+ Caption = 'Value';
+ ToolTip = 'Enter the amount.';
+ }
+ field(MoneyCurrency; MoneyCurrency)
+ {
+ Caption = 'Currency';
+ ToolTip = 'Enter the currency code.';
+ TableRelation = Currency;
+ }
+ }
+ group(DimensionGroup)
+ {
+ Visible = IsDimensionVisible;
+ ShowCaption = false;
+
+ field(DimensionValue; DimensionValue)
+ {
+ Caption = 'Value';
+ ToolTip = 'Enter the value.';
+ }
+ field(DimensionUnit; DimensionUnit)
+ {
+ Caption = 'Unit';
+ ToolTip = 'Enter the unit of measure.';
+ }
+ }
+ group(VolumeGroup)
+ {
+ Visible = IsVolumeVisible;
+ ShowCaption = false;
+
+ field(VolumeValue; VolumeValue)
+ {
+ Caption = 'Value';
+ ToolTip = 'Enter the value.';
+ }
+ field(VolumeUnit; VolumeUnit)
+ {
+ Caption = 'Unit';
+ ToolTip = 'Enter the unit of measure.';
+ }
+ }
+ group(WeightGroup)
+ {
+ Visible = IsWeightVisible;
+ ShowCaption = false;
+
+ field(WeightValue; WeightValue)
+ {
+ Caption = 'Value';
+ ToolTip = 'Enter the value.';
+ }
+ field(WeightUnit; WeightUnit)
+ {
+ Caption = 'Unit';
+ ToolTip = 'Enter the unit of measure.';
+ }
+ }
+ group(MultiLineTextGroup)
+ {
+ Visible = IsMultiLineTextVisible;
+ ShowCaption = false;
+
+ field(MultiLineText; MultiLineText)
+ {
+ Caption = 'Text';
+ ToolTip = 'Enter the text.';
+ MultiLine = true;
+ ExtendedDatatype = RichContent;
+
+ trigger OnValidate()
+ var
+ TextTooLongErr: Label 'The text is too long. The maximum length is 2048 characters.';
+ begin
+ if StrLen(MultiLineText) > 2048 then
+ Error(ErrorInfo.Create(TextTooLongErr));
+ end;
+ }
+ }
+ }
+ }
+
+ #region Money
+ var
+ IsMoneyVisible: Boolean;
+ MoneyValue: Decimal;
+ MoneyCurrency: Code[10];
+
+ ///
+ /// Opens the page for assisting with input of money values.
+ ///
+ /// The amount to preset on the page.
+ /// The currency code to preset on the page.
+ /// True if the user clicks OK; otherwise, false.
+ internal procedure OpenForMoney(Amount: Decimal; CurrencyCode: Code[10]): Boolean
+ begin
+ IsMoneyVisible := true;
+ MoneyValue := Amount;
+ MoneyCurrency := CurrencyCode;
+
+ exit(CurrPage.RunModal() = Action::OK);
+ end;
+
+ ///
+ /// Gets the money value and currency code.
+ ///
+ /// Return value: The money value.
+ /// Return value: The currency code.
+ internal procedure GetMoneyValue(var Amount: Decimal; var Currency: Code[10])
+ begin
+ Amount := MoneyValue;
+ Currency := MoneyCurrency;
+ end;
+ #endregion
+
+ #region Dimension
+ var
+ IsDimensionVisible: Boolean;
+ DimensionValue: Decimal;
+ DimensionUnit: Enum "Shpfy Metafield Dimension Type";
+
+ ///
+ /// Opens the page for assisting with input of dimension values.
+ ///
+ /// The dimension to preset on the page.
+ /// The unit of measure to preset on the page.
+ /// True if the user clicks OK; otherwise, false.
+ internal procedure OpenForDimension(Dimension: Decimal; Unit: Enum "Shpfy Metafield Dimension Type"): Boolean
+ begin
+ IsDimensionVisible := true;
+ DimensionValue := Dimension;
+ DimensionUnit := Unit;
+
+ exit(CurrPage.RunModal() = Action::OK);
+ end;
+
+ ///
+ /// Gets the dimension value and unit of measure.
+ ///
+ /// Return value: The dimension value.
+ /// Return value: The unit of measure.
+ internal procedure GetDimensionValue(var Value: Decimal; var Unit: Enum "Shpfy Metafield Dimension Type")
+ begin
+ Value := DimensionValue;
+ Unit := DimensionUnit;
+ end;
+ #endregion
+
+ #region Volume
+ var
+ IsVolumeVisible: Boolean;
+ VolumeValue: Decimal;
+ VolumeUnit: Enum "Shpfy Metafield Volume Type";
+
+ ///
+ /// Opens the page for assisting with input of volume values.
+ ///
+ /// The volume to preset on the page.
+ /// The unit of measure to preset on the page.
+ /// True if the user clicks OK; otherwise, false.
+ internal procedure OpenForVolume(Volume: Decimal; Unit: Enum "Shpfy Metafield Volume Type"): Boolean
+ begin
+ IsVolumeVisible := true;
+ VolumeValue := Volume;
+ VolumeUnit := Unit;
+
+ exit(CurrPage.RunModal() = Action::OK);
+ end;
+
+ ///
+ /// Gets the volume value and unit of measure.
+ ///
+ /// Return value: The volume value.
+ /// Return value: The unit of measure.
+ internal procedure GetVolumeValue(var Volume: Decimal; var Unit: Enum "Shpfy Metafield Volume Type")
+ begin
+ Volume := VolumeValue;
+ Unit := VolumeUnit;
+ end;
+ #endregion
+
+ #region Weight
+ var
+ IsWeightVisible: Boolean;
+ WeightValue: Decimal;
+ WeightUnit: Enum "Shpfy Metafield Weight Type";
+
+ ///
+ /// Opens the page for assisting with input of weight values.
+ ///
+ /// The weight to preset on the page.
+ /// The unit of measure to preset on the page.
+ /// True if the user clicks OK; otherwise, false.
+ internal procedure OpenForWeight(Weight: Decimal; Unit: Enum "Shpfy Metafield Weight Type"): Boolean
+ begin
+ IsWeightVisible := true;
+ WeightValue := Weight;
+ WeightUnit := Unit;
+
+ exit(CurrPage.RunModal() = Action::OK);
+ end;
+
+ ///
+ /// Gets the weight value and unit of measure.
+ ///
+ /// Return value: The weight value.
+ /// Return value: The unit of measure.
+ internal procedure GetWeightValue(var Value: Decimal; var Unit: Enum "Shpfy Metafield Weight Type")
+ begin
+ Value := WeightValue;
+ Unit := WeightUnit;
+ end;
+ #endregion
+
+ #region MultiLineText
+ var
+ IsMultiLineTextVisible: Boolean;
+ MultiLineText: Text;
+
+ ///
+ /// Opens the page for assisting with input of multi-line text.
+ ///
+ /// The text to preset on the page.
+ /// True if the user clicks OK; otherwise, false.
+ internal procedure OpenForMultiLineText(Text: Text[2048]): Boolean
+ begin
+ IsMultiLineTextVisible := true;
+ MultiLineText := Text;
+
+ exit(CurrPage.RunModal() = Action::OK);
+ end;
+
+ ///
+ /// Gets the multi-line text.
+ ///
+ /// Return value: The multi-line text.
+ internal procedure GetMultiLineText(var Text: Text[2048])
+ begin
+ Text := CopyStr(MultiLineText, 1, MaxStrLen(Text));
+ end;
+ #endregion
+}
\ 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
new file mode 100644
index 0000000000..2029380a48
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Pages/ShpfyMetafields.Page.al
@@ -0,0 +1,130 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Page Shpfy Metafields (ID 30163).
+///
+page 30163 "Shpfy Metafields"
+{
+ Caption = 'Shopify Metafields';
+ Extensible = false;
+ PageType = List;
+ SourceTable = "Shpfy Metafield";
+ UsageCategory = None;
+ ApplicationArea = All;
+ DelayedInsert = true;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(Metafields)
+ {
+ Editable = IsPageEditable;
+
+ field(Namespace; Rec.Namespace)
+ {
+ ToolTip = 'Specifies the namespace of the metafield.';
+ }
+ field(Type; Rec.Type)
+ {
+ ToolTip = 'Specifies the type of value for the metafield.';
+ }
+ field(Name; Rec.Name)
+ {
+ ToolTip = 'Specifies the key of the metafield.';
+ }
+ field(Value; Rec.Value)
+ {
+ ToolTip = 'Specifies the value of the metafield.';
+ Editable = IsValueEditable;
+
+ trigger OnAssistEdit()
+ var
+ IMetafieldType: Interface "Shpfy IMetafield Type";
+ begin
+ IMetafieldType := Rec.Type;
+
+ if IMetafieldType.HasAssistEdit() then
+ if IMetafieldType.AssistEdit(Rec.Value) then
+ Rec.Validate(Value);
+ end;
+ }
+ }
+ }
+ }
+
+ trigger OnNewRecord(BelowxRec: Boolean)
+ begin
+ Evaluate(Rec."Parent Table No.", Rec.GetFilter("Parent Table No."));
+ Rec.Validate("Parent Table No.");
+ Evaluate(Rec."Owner Id", Rec.GetFilter("Owner Id"));
+ end;
+
+ trigger OnAfterGetCurrRecord()
+ var
+ IMetafieldType: Interface "Shpfy IMetafield Type";
+ begin
+ IMetafieldType := Rec.Type;
+ IsValueEditable := not IMetafieldType.HasAssistEdit();
+ end;
+
+ trigger OnInsertRecord(BelowxRec: Boolean): Boolean
+ begin
+ Rec.TestField(Namespace);
+ Rec.TestField(Name);
+ Rec.Validate(Value);
+
+ Rec.Id := SendMetafieldToShopify();
+ end;
+
+ var
+ Shop: Record "Shpfy Shop";
+ IsPageEditable: Boolean;
+ IsValueEditable: Boolean;
+
+ ///
+ /// Opens the page displaying metafields for the specified resource.
+ ///
+ /// Table id of the resource.
+ /// System Id of the resource.
+ internal procedure RunForResource(ParentTableId: Integer; OwnerId: BigInteger; ShopCode: Code[20])
+ var
+ Metafield: Record "Shpfy Metafield";
+ begin
+ Shop.Get(ShopCode);
+ IsPageEditable := (Shop."Sync Item" = Shop."Sync Item"::"To Shopify") and (Shop."Can Update Shopify Products");
+
+ Metafield.SetRange("Parent Table No.", ParentTableId);
+ Metafield.SetRange("Owner Id", OwnerId);
+
+ CurrPage.SetTableView(Metafield);
+ CurrPage.RunModal();
+ end;
+
+ local procedure SendMetafieldToShopify(): BigInteger
+ var
+ JsonHelper: Codeunit "Shpfy Json Helper";
+ MetafieldAPI: Codeunit "Shpfy Metafield API";
+ ShpfyCommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ UserErrorOnShopifyErr: Label 'Something went wrong while sending the metafield to Shopify. Check Shopify Log Entries for more details.';
+ GraphQuery: TextBuilder;
+ JResponse: JsonToken;
+ JMetafields: JsonArray;
+ JUserErrors: JsonArray;
+ JItem: JsonToken;
+ begin
+ ShpfyCommunicationMgt.SetShop(Shop);
+
+ MetafieldAPI.CreateMetafieldQuery(Rec, GraphQuery);
+ JResponse := MetafieldAPI.UpdateMetafields(GraphQuery.ToText());
+
+ JsonHelper.GetJsonArray(JResponse, JUserErrors, 'data.metafieldsSet.userErrors');
+
+ if JUserErrors.Count() = 0 then begin
+ JsonHelper.GetJsonArray(JResponse, JMetafields, 'data.metafieldsSet.metafields');
+ JMetafields.Get(0, JItem);
+ exit(JsonHelper.GetValueAsBigInteger(JItem, 'legacyResourceId'));
+ end else
+ Error(UserErrorOnShopifyErr);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al
new file mode 100644
index 0000000000..6e7955daa8
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Metafields/Tables/ShpfyMetafield.Table.al
@@ -0,0 +1,219 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Finance.GeneralLedger.Setup;
+
+///
+/// Table Shpfy Metafield (ID 30101).
+///
+table 30101 "Shpfy Metafield"
+{
+ Access = Internal;
+ Caption = 'Shopify Metafield';
+ DataClassification = CustomerContent;
+ DrillDownPageId = "Shpfy Metafields";
+ LookupPageId = "Shpfy Metafields";
+
+ fields
+ {
+ field(1; Id; BigInteger)
+ {
+ Caption = 'Id';
+ DataClassification = SystemMetadata;
+ Editable = false;
+ }
+
+#pragma warning disable AS0086 // false positive on extending the field length on internal table
+ field(2; Namespace; Text[255])
+ {
+ Caption = 'Namespace';
+ DataClassification = SystemMetadata;
+ }
+#pragma warning restore AS0086
+
+ field(3; "Owner Resource"; Text[50])
+ {
+ Caption = 'Owner Resource';
+ DataClassification = SystemMetadata;
+ ObsoleteState = Pending;
+ ObsoleteReason = 'Owner Resource is obsolete. Use Owner Type instead.';
+ ObsoleteTag = '25.0';
+
+ trigger OnValidate()
+ begin
+ case "Owner Resource" of
+ 'Customer':
+ Validate("Owner Type", "Owner Type"::Customer);
+ 'Product':
+ Validate("Owner Type", "Owner Type"::Product);
+ 'Variant':
+ Validate("Owner Type", "Owner Type"::ProductVariant);
+ end;
+ end;
+ }
+
+ field(4; "Owner Id"; BigInteger)
+ {
+ Caption = 'Owner Id';
+ DataClassification = SystemMetadata;
+ }
+
+#pragma warning disable AS0086 // false positive on extending the field length on internal table
+ field(5; Name; Text[64])
+ {
+ Caption = 'Key';
+ DataClassification = CustomerContent;
+ }
+#pragma warning restore AS0086
+
+ field(6; "Value Type"; Enum "Shpfy Metafield Value Type")
+ {
+ Caption = 'Value Type';
+ DataClassification = CustomerContent;
+ ObsoleteState = Pending;
+ ObsoleteReason = 'Value Type is obsolete in Shopify API. Use Type instead.';
+ ObsoleteTag = '25.0';
+ }
+
+#pragma warning disable AS0086 // false positive on extending the field length on internal table
+ field(7; Value; Text[2048])
+ {
+ Caption = 'Value';
+ DataClassification = CustomerContent;
+
+ trigger OnValidate()
+ var
+ ValueNotValidErr: Label 'The value is not valid for the type. Example value: ';
+ IMetafieldType: Interface "Shpfy IMetafield Type";
+ begin
+ IMetafieldType := Rec.Type;
+ if not IMetafieldType.IsValidValue(Value) then
+ Error(ErrorInfo.Create(ValueNotValidErr + IMetafieldType.GetExampleValue()));
+
+ if Rec.Type = Rec.Type::money then
+ CheckShopCurrency(Value);
+ end;
+ }
+#pragma warning restore AS0086
+ field(8; Type; Enum "Shpfy Metafield Type")
+ {
+ Caption = 'Type';
+ DataClassification = CustomerContent;
+ }
+ field(9; "Last Updated by BC"; DateTime)
+ {
+ Caption = 'Last Updated by BC';
+ DataClassification = SystemMetadata;
+ }
+ field(10; "Owner Type"; Enum "Shpfy Metafield Owner Type")
+ {
+ Caption = 'Owner Type';
+ DataClassification = SystemMetadata;
+
+ trigger OnValidate()
+ var
+ IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type";
+ begin
+ IMetafieldOwnerType := Rec."Owner Type";
+ "Parent Table No." := IMetafieldOwnerType.GetTableId();
+ end;
+ }
+
+ field(101; "Parent Table No."; Integer)
+ {
+ Caption = 'Parent Table No.';
+ DataClassification = SystemMetadata;
+ Editable = false;
+
+ trigger OnValidate()
+ begin
+ "Owner Type" := GetOwnerType("Parent Table No.");
+ end;
+ }
+ }
+
+ keys
+ {
+ key(PK; Id)
+ {
+ Clustered = true;
+ }
+ key(Idx1; "Parent Table No.", "Owner Id")
+ {
+ }
+ }
+
+ trigger OnInsert()
+ var
+ Metafield: Record "Shpfy Metafield";
+ begin
+ if Namespace = '' then
+ Namespace := 'Microsoft.Dynamics365.BusinessCentral';
+ if Id = 0 then
+ if Metafield.FindFirst() and (Metafield.Id < 0) then
+ Id := Metafield.Id - 1
+ else
+ Id := -1;
+ end;
+
+ trigger OnModify()
+ begin
+ "Last Updated by BC" := CurrentDateTime;
+ end;
+
+ ///
+ /// Get the owner type based on the resources's owner table number.
+ ///
+ /// The owning resource table number.
+ internal procedure GetOwnerType(ParentTableNo: Integer): Enum "Shpfy Metafield Owner Type"
+ begin
+ case ParentTableNo of
+ Database::"Shpfy Customer":
+ exit("Owner Type"::Customer);
+ Database::"Shpfy Product":
+ exit("Owner Type"::Product);
+ Database::"Shpfy Variant":
+ exit("Owner Type"::ProductVariant);
+ end;
+ end;
+
+ ///
+ /// Returns the name of the enum value for the owner type. Used when the full owner resource id needs to be built.
+ ///
+ /// The name of the owner type.
+ internal procedure GetOwnerTypeName(): Text
+ begin
+ exit("Owner Type".Names().Get("Owner Type".Ordinals().IndexOf("Owner Type".AsInteger())));
+ end;
+
+ local procedure CheckShopCurrency(MetafieldValue: Text[2048])
+ var
+ ShpfyMtfldTypeMoney: Codeunit "Shpfy Mtfld Type Money";
+ CurrencyCode: Code[10];
+ ShopCurrencyCode: Code[10];
+ Amount: Decimal;
+ CurrencyCodeMismatchErr: Label 'The currency code must match the shop currency code. Shop currency code: %1', Comment = '%1 - Shop currency code';
+ begin
+ ShopCurrencyCode := GetShopCurrencyCode();
+
+ ShpfyMtfldTypeMoney.TryExtractValues(MetafieldValue, Amount, CurrencyCode);
+ if CurrencyCode <> ShopCurrencyCode then
+ Error(ErrorInfo.Create(StrSubstNo(CurrencyCodeMismatchErr, ShopCurrencyCode)));
+ end;
+
+ local procedure GetShopCurrencyCode(): Code[10]
+ var
+ GeneralLedgerSetup: Record "General Ledger Setup";
+ Shop: Record "Shpfy Shop";
+ IMetafieldOwnerType: Interface "Shpfy IMetafield Owner Type";
+ begin
+ IMetafieldOwnerType := Rec."Owner Type";
+ Shop.Get(IMetafieldOwnerType.GetShopCode(Rec."Owner Id"));
+
+ if Shop."Currency Code" <> '' then
+ exit(Shop."Currency Code")
+ else begin
+ GeneralLedgerSetup.Get();
+ exit(GeneralLedgerSetup."LCY Code");
+ end;
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al b/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al
index b8790ab1bd..c4e67f7c00 100644
--- a/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyImportOrder.Codeunit.al
@@ -116,6 +116,22 @@ codeunit 30161 "Shpfy Import Order"
if CheckToCloseOrder(OrderHeader) then
CloseOrder(OrderHeader);
+
+ if ShopifyInvoiceExists(OrderHeader) then
+ MarkAsProcessed(OrderHeader);
+ end;
+
+ local procedure ShopifyInvoiceExists(OrderHeader: Record "Shpfy Order Header"): Boolean
+ var
+ ShpfyInvoiceHeader: Record "Shpfy Invoice Header";
+ begin
+ exit(ShpfyInvoiceHeader.Get(OrderHeader."Shopify Order Id"));
+ end;
+
+ local procedure MarkAsProcessed(OrderHeader: Record "Shpfy Order Header")
+ begin
+ OrderHeader.Validate(Processed, true);
+ OrderHeader.Modify()
end;
local procedure InsertOrderLinesAndRelatedRecords(var TempOrderLine: Record "Shpfy Order Line" temporary; var DataCaptureDict: Dictionary of [BigInteger, JsonToken]; var Redundancy: Integer)
@@ -495,13 +511,15 @@ codeunit 30161 "Shpfy Import Order"
JsonHelper.GetValueIntoField(JOrder, 'totalTipReceivedSet.presentmentMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Presentment Total Tip Received"));
JsonHelper.GetValueIntoField(JOrder, 'totalTaxSet.shopMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("VAT Amount"));
JsonHelper.GetValueIntoField(JOrder, 'totalTaxSet.presentmentMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Presentment VAT Amount"));
- JsonHelper.GetValueIntoField(JOrder, 'totalDiscountsSet.shopMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Discount Amount"));
- JsonHelper.GetValueIntoField(JOrder, 'totalDiscountsSet.presentmentMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Presentment Discount Amount"));
+ JsonHelper.GetValueIntoField(JOrder, 'currentTotalDiscountsSet.shopMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Discount Amount"));
+ JsonHelper.GetValueIntoField(JOrder, 'currentTotalDiscountsSet.presentmentMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Presentment Discount Amount"));
JsonHelper.GetValueIntoField(JOrder, 'totalShippingPriceSet.shopMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Shipping Charges Amount"));
JsonHelper.GetValueIntoField(JOrder, 'totalShippingPriceSet.presentmentMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Pres. Shipping Charges Amount"));
JsonHelper.GetValueIntoField(JOrder, 'currentTotalPriceSet.shopMoney.amount', OrderHeaderRecordRef, OrderHeader.FieldNo("Current Total Amount"));
JsonHelper.GetValueIntoField(JOrder, 'currentSubtotalLineItemsQuantity', OrderHeaderRecordRef, OrderHeader.FieldNo("Current Total Items Quantity"));
JsonHelper.GetValueIntoField(Jorder, 'poNumber', OrderHeaderRecordRef, OrderHeader.FieldNo("PO Number"));
+ JsonHelper.GetValueIntoField(JOrder, 'paymentTerms.paymentTermsType', OrderHeaderRecordRef, OrderHeader.FieldNo("Payment Terms Type"));
+ JsonHelper.GetValueIntoField(JOrder, 'paymentTerms.paymentTermsName', OrderHeaderRecordRef, OrderHeader.FieldNo("Payment Terms Name"));
OrderHeaderRecordRef.SetTable(OrderHeader);
if JsonHelper.GetJsonObject(JOrder, JObject, 'purchasingEntity') then
if JsonHelper.GetJsonObject(JOrder, JObject, 'purchasingEntity.company') then
@@ -620,7 +638,7 @@ codeunit 30161 "Shpfy Import Order"
OrderAttribute.Value := CopyStr(JsonHelper.GetValueAsText(JToken, 'value', MaxStrLen(OrderAttribute.Value)), 1, MaxStrLen(OrderAttribute.Value))
else
#endif
- OrderAttribute."Attribute Value" := CopyStr(JsonHelper.GetValueAsText(JToken, 'value', MaxStrLen(OrderAttribute."Attribute Value")), 1, MaxStrLen(OrderAttribute."Attribute Value"));
+ OrderAttribute."Attribute Value" := CopyStr(JsonHelper.GetValueAsText(JToken, 'value', MaxStrLen(OrderAttribute."Attribute Value")), 1, MaxStrLen(OrderAttribute."Attribute Value"));
OrderAttribute.Insert();
end;
end;
@@ -632,7 +650,7 @@ codeunit 30161 "Shpfy Import Order"
begin
OrderLineAttribute.SetRange("Order Id", ShopifyOrderId);
OrderLineAttribute.SetRange("Order Line Id", OrderLineId);
- if not OrderLineAttribute.IsEmpty then
+ if not OrderLineAttribute.IsEmpty() then
OrderLineAttribute.DeleteAll();
foreach JToken in JCustomAttributtes do begin
Clear(OrderLineAttribute);
@@ -723,7 +741,7 @@ codeunit 30161 "Shpfy Import Order"
JToken: JsonToken;
begin
OrderTaxLine.SetRange("Parent Id", ParentId);
- if not OrderTaxLine.IsEmpty then
+ if not OrderTaxLine.IsEmpty() then
OrderTaxLine.DeleteAll();
foreach JToken in JTaxLines do begin
RecordRef.Open(Database::"Shpfy Order Tax Line");
diff --git a/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyProcessOrder.Codeunit.al b/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyProcessOrder.Codeunit.al
index c2dadeb483..2d027d83bb 100644
--- a/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyProcessOrder.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Order handling/Codeunits/ShpfyProcessOrder.Codeunit.al
@@ -125,6 +125,8 @@ codeunit 30166 "Shpfy Process Order"
end;
if ShopifyOrderHeader."Payment Method Code" <> '' then
SalesHeader.Validate("Payment Method Code", ShopifyOrderHeader."Payment Method Code");
+ if ShopifyOrderHeader."Payment Terms Type" <> '' then
+ UpdatePaymentTerms(SalesHeader, ShopifyOrderHeader."Payment Terms Type", ShopifyOrderHeader."Payment Terms Name");
SalesHeader.Modify(true);
@@ -148,6 +150,17 @@ codeunit 30166 "Shpfy Process Order"
OrderEvents.OnAfterCreateSalesHeader(ShopifyOrderHeader, SalesHeader);
end;
+ local procedure UpdatePaymentTerms(var SalesHeader: Record "Sales Header"; PaymentTermsType: Code[20]; PaymentTermsName: Text[50])
+ var
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ begin
+ ShpfyPaymentTerms.SetRange(Type, PaymentTermsType);
+ ShpfyPaymentTerms.SetRange("Shop Code", ShopifyShop.Code);
+ ShpfyPaymentTerms.SetRange(Name, PaymentTermsName);
+ if ShpfyPaymentTerms.FindFirst() then
+ SalesHeader.Validate("Payment Terms Code", ShpfyPaymentTerms."Payment Terms Code");
+ end;
+
local procedure ApplyGlobalDiscounts(OrderHeader: Record "Shpfy Order Header"; var SalesHeader: Record "Sales Header")
var
OrderLine: Record "Shpfy Order Line";
diff --git a/Apps/W1/Shopify/app/src/Order handling/Tables/ShpfyOrderHeader.Table.al b/Apps/W1/Shopify/app/src/Order handling/Tables/ShpfyOrderHeader.Table.al
index e31860f410..3e54e0be84 100644
--- a/Apps/W1/Shopify/app/src/Order handling/Tables/ShpfyOrderHeader.Table.al
+++ b/Apps/W1/Shopify/app/src/Order handling/Tables/ShpfyOrderHeader.Table.al
@@ -751,6 +751,16 @@ table 30118 "Shpfy Order Header"
Caption = 'Shipping Agent Service Code';
TableRelation = "Shipping Agent Services".Code where("Shipping Agent Code" = field("Shipping Agent Code"));
}
+ field(1030; "Payment Terms Type"; Code[20])
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Payment Terms Type';
+ }
+ field(1040; "Payment Terms Name"; Text[50])
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Payment Terms Name';
+ }
}
keys
{
diff --git a/Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPaymentTermsAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPaymentTermsAPI.Codeunit.al
new file mode 100644
index 0000000000..143c9b5e5d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Payments/Codeunits/ShpfyPaymentTermsAPI.Codeunit.al
@@ -0,0 +1,85 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Codeunit Shpfy Payment Terms API (ID 30360).
+///
+codeunit 30360 "Shpfy Payment Terms API"
+{
+ Access = Internal;
+
+ var
+ CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ JsonHelper: Codeunit "Shpfy Json Helper";
+ ShopCode: Code[20];
+
+ ///
+ /// Synchronizes payment terms from shopify, ensuring that the payment terms are up-to-date with those defined in the shopify store.
+ ///
+ /// Shopify shop code to be used.
+ internal procedure PullPaymentTermsCodes()
+ var
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ JTemplates: JsonArray;
+ JTemplate: JsonToken;
+ JResponse: JsonToken;
+ begin
+ GraphQLType := GraphQLType::GetPaymentTerms;
+ JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType);
+
+ JsonHelper.GetJsonArray(JResponse, JTemplates, 'data.paymentTermsTemplates');
+ foreach JTemplate in JTemplates do
+ UpdatePaymentTerms(JTemplate);
+ end;
+
+ ///
+ /// Sets a global shopify shop to be used form payment terms api functionality.
+ ///
+ /// Shopify shop code to be set.
+ internal procedure SetShop(NewShopCode: Code[20])
+ begin
+ ShopCode := NewShopCode;
+ CommunicationMgt.SetShop(NewShopCode);
+ end;
+
+ local procedure UpdatePaymentTerms(JTemplate: JsonToken)
+ var
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ PaymentTermRecordRef: RecordRef;
+ Id: BigInteger;
+ IsNew: Boolean;
+ begin
+ Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JTemplate, 'id'));
+ IsNew := not ShpfyPaymentTerms.Get(ShopCode, Id);
+
+ if IsNew then begin
+ Clear(ShpfyPaymentTerms);
+ ShpfyPaymentTerms.Id := Id;
+ ShpfyPaymentTerms."Shop Code" := ShopCode;
+ end;
+
+ PaymentTermRecordRef.GetTable(ShpfyPaymentTerms);
+ JsonHelper.GetValueIntoField(JTemplate, 'name', PaymentTermRecordRef, ShpfyPaymentTerms.FieldNo(Name));
+ JsonHelper.GetValueIntoField(JTemplate, 'paymentTermsType', PaymentTermRecordRef, ShpfyPaymentTerms.FieldNo(Type));
+ JsonHelper.GetValueIntoField(JTemplate, 'dueInDays', PaymentTermRecordRef, ShpfyPaymentTerms.FieldNo("Due In Days"));
+ JsonHelper.GetValueIntoField(JTemplate, 'description', PaymentTermRecordRef, ShpfyPaymentTerms.FieldNo(Description));
+ PaymentTermRecordRef.SetTable(ShpfyPaymentTerms);
+
+ if ShpfyPaymentTerms.Type = 'FIXED' then
+ if ShouldBeMarkedAsPrimary() then
+ ShpfyPaymentTerms.Validate("Is Primary", true);
+
+ if IsNew then
+ ShpfyPaymentTerms.Insert(true)
+ else
+ ShpfyPaymentTerms.Modify(true);
+ end;
+
+ local procedure ShouldBeMarkedAsPrimary(): Boolean
+ var
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ begin
+ ShpfyPaymentTerms.SetRange("Shop Code", ShopCode);
+ ShpfyPaymentTerms.SetRange("Is Primary", true);
+ exit(ShpfyPaymentTerms.IsEmpty());
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Payments/Pages/ShpfyPaymentTermsMapping.Page.al b/Apps/W1/Shopify/app/src/Payments/Pages/ShpfyPaymentTermsMapping.Page.al
new file mode 100644
index 0000000000..bcf888a04d
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Payments/Pages/ShpfyPaymentTermsMapping.Page.al
@@ -0,0 +1,75 @@
+namespace Microsoft.Integration.Shopify;
+
+///
+/// Page Shpfy Payment Terms Mapping (ID 30162).
+///
+page 30162 "Shpfy Payment Terms Mapping"
+{
+ PageType = List;
+ ApplicationArea = All;
+ UsageCategory = Lists;
+ SourceTable = "Shpfy Payment Terms";
+ Caption = 'Shopify Payment Terms Mapping';
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(General)
+ {
+ field(Type; Rec.Type)
+ {
+ ToolTip = 'Specifies the value of the Type field.';
+ }
+ field(Name; Rec.Name)
+ {
+ ToolTip = 'Specifies the value of the Name field.';
+ }
+ field(Description; Rec.Description)
+ {
+ ToolTip = 'Specifies the value of the Description field.';
+ }
+ field("Payment Terms Code"; Rec."Payment Terms Code")
+ {
+ ToolTip = 'Specifies the value of the Payment Terms Code field.';
+ }
+ field("Is Primary"; Rec."Is Primary")
+ {
+ ToolTip = 'Specifies the value of the Is Primary field.';
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(Promoted)
+ {
+ group(Category_Process)
+ {
+ Caption = 'Process';
+ ShowAs = Standard;
+
+ actionref(PromotedRefresh; Refresh) { }
+ }
+ }
+ area(Processing)
+ {
+ action(Refresh)
+ {
+ ApplicationArea = All;
+ Caption = 'Refresh';
+ Image = Refresh;
+ ToolTip = 'Refreshes the list of Shopify Payment Terms.';
+
+ trigger OnAction()
+ var
+ ShpfyPaymentTermAPI: Codeunit "Shpfy Payment Terms API";
+ begin
+ ShpfyPaymentTermAPI.SetShop(CopyStr(Rec.GetFilter("Shop Code"), 1, 20));
+ ShpfyPaymentTermAPI.PullPaymentTermsCodes();
+ end;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Payments/Tables/ShpfyPaymentTerms.Table.al b/Apps/W1/Shopify/app/src/Payments/Tables/ShpfyPaymentTerms.Table.al
new file mode 100644
index 0000000000..fffe926394
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Payments/Tables/ShpfyPaymentTerms.Table.al
@@ -0,0 +1,78 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Foundation.PaymentTerms;
+
+///
+/// Table Shpfy Payment Terms (ID 30158).
+///
+table 30158 "Shpfy Payment Terms"
+{
+ Caption = 'Payment Terms';
+ DataClassification = CustomerContent;
+ Access = Internal;
+
+ fields
+ {
+ field(1; "Shop Code"; Code[20])
+ {
+ Caption = 'Shop Code';
+ TableRelation = "Shpfy Shop";
+ Editable = false;
+ }
+ field(2; Id; BigInteger)
+ {
+ Caption = 'ID';
+ Editable = false;
+ }
+ field(20; Name; Text[50])
+ {
+ Caption = 'Name';
+ Editable = false;
+ }
+ field(30; "Due In Days"; Integer)
+ {
+ Caption = 'Due In Days';
+ Editable = false;
+ }
+ field(40; Description; Text[50])
+ {
+ Caption = 'Description';
+ Editable = false;
+ }
+ field(50; Type; Code[20])
+ {
+ Caption = 'Type';
+ Editable = false;
+ }
+ field(60; "Is Primary"; Boolean)
+ {
+ Caption = 'Is Primary';
+
+ trigger OnValidate()
+ var
+ ShpfyPaymentTerms: Record "Shpfy Payment Terms";
+ PrimaryPaymentTermsExistsErr: Label 'Primary payment terms already exist for this shop.';
+ begin
+ ShpfyPaymentTerms.SetRange("Shop Code", Rec."Shop Code");
+ ShpfyPaymentTerms.SetRange("Is Primary", true);
+ ShpfyPaymentTerms.SetFilter(Id, '<>%1', Rec.Id);
+
+ if not ShpfyPaymentTerms.IsEmpty() then
+ Error(PrimaryPaymentTermsExistsErr);
+ end;
+ }
+ field(70; "Payment Terms Code"; Code[10])
+ {
+ TableRelation = "Payment Terms";
+ Caption = 'Payment Terms Code';
+ }
+ }
+
+ keys
+ {
+ key(PK; "Shop Code", Id)
+ {
+ Clustered = true;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyEdit.PermissionSet.al b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyEdit.PermissionSet.al
index 20ab6069b9..d8296da1f9 100644
--- a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyEdit.PermissionSet.al
+++ b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyEdit.PermissionSet.al
@@ -32,6 +32,8 @@ permissionset 30102 "Shpfy - Edit"
tabledata "Shpfy Gift Card" = IMD,
tabledata "Shpfy Initial Import Line" = imd,
tabledata "Shpfy Inventory Item" = IMD,
+ tabledata "Shpfy Invoice Header" = IMD,
+ tabledata "Shpfy Language" = IMD,
tabledata "Shpfy Log Entry" = IMD,
tabledata "Shpfy Metafield" = IMD,
tabledata "Shpfy Refund Header" = IMD,
@@ -51,6 +53,7 @@ permissionset 30102 "Shpfy - Edit"
tabledata "Shpfy Order Tax Line" = IMD,
tabledata "Shpfy Order Transaction" = IMD,
tabledata "Shpfy Payment Method Mapping" = IMD,
+ tabledata "Shpfy Payment Terms" = IMD,
tabledata "Shpfy Payment Transaction" = IMD,
tabledata "Shpfy Payout" = IMD,
tabledata "Shpfy Product" = IMD,
@@ -64,5 +67,6 @@ permissionset 30102 "Shpfy - Edit"
tabledata "Shpfy Tag" = IMD,
tabledata "Shpfy Tax Area" = IMD,
tabledata "Shpfy Transaction Gateway" = IMD,
+ tabledata "Shpfy Translation" = IMD,
tabledata "Shpfy Variant" = IMD;
}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyObjects.PermissionSet.al b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyObjects.PermissionSet.al
index 21ac49f7ad..470c20c8bc 100644
--- a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyObjects.PermissionSet.al
+++ b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyObjects.PermissionSet.al
@@ -61,6 +61,7 @@ permissionset 30104 "Shpfy - Objects"
table "Shpfy Synchronization Info" = X,
table "Shpfy Tag" = X,
table "Shpfy Tax Area" = X,
+ table "Shpfy Translation" = X,
table "Shpfy Transaction Gateway" = X,
table "Shpfy Variant" = X,
report "Shpfy Add Company to Shopify" = X,
@@ -114,6 +115,8 @@ permissionset 30104 "Shpfy - Objects"
codeunit "Shpfy Create Sales Doc. Refund" = X,
codeunit "Shpfy CreateProdStatusActive" = X,
codeunit "Shpfy CreateProdStatusDraft" = X,
+ codeunit "Shpfy Create Transl. Product" = X,
+ codeunit "Shpfy Create Transl. Variant" = X,
codeunit "Shpfy Cust. By Bill-to" = X,
codeunit "Shpfy Cust. By Default Cust." = X,
codeunit "Shpfy Cust. By Email/Phone" = X,
@@ -161,6 +164,7 @@ permissionset 30104 "Shpfy - Objects"
codeunit "Shpfy GQL LocationOrderLines" = X,
codeunit "Shpfy GQL Locations" = X,
codeunit "Shpfy GQL MarkOrderAsPaid" = X,
+ codeunit "Shpfy GQL MetafieldsSet" = X,
codeunit "Shpfy GQL Modify Inventory" = X,
codeunit "Shpfy GQL Next Locations" = X,
codeunit "Shpfy GQL NextAllCustomerIds" = X,
@@ -198,17 +202,22 @@ permissionset 30104 "Shpfy - Objects"
codeunit "Shpfy GQL ProductById" = X,
codeunit "Shpfy GQL ProductIds" = X,
codeunit "Shpfy GQL ProductImages" = X,
+ codeunit "Shpfy GQL ProductMetafieldIds" = X,
codeunit "Shpfy GQL RefundHeader" = X,
codeunit "Shpfy GQL RefundLines" = X,
codeunit "Shpfy GQL ReturnHeader" = X,
codeunit "Shpfy GQL ReturnLines" = X,
codeunit "Shpfy GQL ShipmentLines" = X,
+ codeunit "Shpfy GQL ShopLocales" = X,
+ codeunit "Shpfy GQL TranslationsRegister" = X,
+ codeunit "Shpfy GQL TranslResource" = X,
codeunit "Shpfy GQL UpdateCatalogPrices" = X,
codeunit "Shpfy GQL UpdateOrderAttr" = X,
codeunit "Shpfy GQL UpdateProductImage" = X,
codeunit "Shpfy GQL VariantById" = X,
codeunit "Shpfy GQL VariantIds" = X,
codeunit "Shpfy GQL VariantImages" = X,
+ codeunit "Shpfy GQL VariantMetafieldIds" = X,
codeunit "Shpfy GraphQL Queries" = X,
codeunit "Shpfy GraphQL Rate Limit" = X,
codeunit "Shpfy Guided Experience" = X,
@@ -227,6 +236,33 @@ permissionset 30104 "Shpfy - Objects"
codeunit "Shpfy Json Helper" = X,
codeunit "Shpfy Log Entries Delete" = X,
codeunit "Shpfy Math" = X,
+ codeunit "Shpfy Metafield API" = X,
+ codeunit "Shpfy Metafield Owner Customer" = X,
+ codeunit "Shpfy Metafield Owner Product" = X,
+ codeunit "Shpfy Metafield Owner Variant" = X,
+ codeunit "Shpfy Mtfld Type Boolean" = X,
+ codeunit "Shpfy Mtfld Type Collect. Ref" = X,
+ codeunit "Shpfy Mtfld Type Color" = X,
+ codeunit "Shpfy Mtfld Type Date" = X,
+ codeunit "Shpfy Mtfld Type DateTime" = X,
+ codeunit "Shpfy Mtfld Type Dimension" = X,
+ codeunit "Shpfy Mtfld Type File Ref" = X,
+ codeunit "Shpfy Mtfld Type Integer" = X,
+ codeunit "Shpfy Mtfld Type Json" = X,
+ codeunit "Shpfy Mtfld Type Metaobj. Ref" = X,
+ codeunit "Shpfy Mtfld Type Mixed Ref" = X,
+ codeunit "Shpfy Mtfld Type Money" = X,
+ codeunit "Shpfy Mtfld Type Multi Text" = X,
+ codeunit "Shpfy Mtfld Type Num Decimal" = X,
+ codeunit "Shpfy Mtfld Type Num Integer" = X,
+ codeunit "Shpfy Mtfld Type Page Ref" = X,
+ codeunit "Shpfy Mtfld Type Product Ref" = X,
+ codeunit "Shpfy Mtfld Type Single Text" = X,
+ codeunit "Shpfy Mtfld Type String" = X,
+ codeunit "Shpfy Mtfld Type Url" = X,
+ codeunit "Shpfy Mtfld Type Variant Ref" = X,
+ codeunit "Shpfy Mtfld Type Volume" = X,
+ codeunit "Shpfy Mtfld Type Weight" = X,
codeunit "Shpfy Name is CompanyName" = X,
codeunit "Shpfy Name is Empty" = X,
codeunit "Shpfy Name is First. LastName" = X,
@@ -285,6 +321,8 @@ permissionset 30104 "Shpfy - Objects"
codeunit "Shpfy Sync Shop Locations" = X,
codeunit "Shpfy ToArchivedProduct" = X,
codeunit "Shpfy ToDraftProduct" = X,
+ codeunit "Shpfy Translation API" = X,
+ codeunit "Shpfy Translation Mgt." = X,
codeunit "Shpfy Transactions" = X,
codeunit "Shpfy Update Customer" = X,
codeunit "Shpfy Update Item" = X,
@@ -321,6 +359,8 @@ permissionset 30104 "Shpfy - Objects"
page "Shpfy Log Entries" = X,
page "Shpfy Log Entry Card" = X,
page "Shpfy Main Contact Factbox" = X,
+ page "Shpfy Metafield Assist Edit" = X,
+ page "Shpfy Metafields" = X,
page "Shpfy Order" = X,
page "Shpfy Order Attributes" = X,
page "Shpfy Order Fulfillment" = X,
@@ -352,6 +392,7 @@ permissionset 30104 "Shpfy - Objects"
page "Shpfy Tag Factbox" = X,
page "Shpfy Tags" = X,
page "Shpfy Tax Areas" = X,
+ page "Shpfy Languages" = X,
page "Shpfy Transaction Gateways" = X,
page "Shpfy Transactions" = X,
page "Shpfy Variants" = X,
diff --git a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyRead.PermissionSet.al b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyRead.PermissionSet.al
index e93a734675..2fab53a2b8 100644
--- a/Apps/W1/Shopify/app/src/PermissionSets/ShpfyRead.PermissionSet.al
+++ b/Apps/W1/Shopify/app/src/PermissionSets/ShpfyRead.PermissionSet.al
@@ -32,6 +32,8 @@ permissionset 30100 "Shpfy - Read"
tabledata "Shpfy Gift Card" = R,
tabledata "Shpfy Initial Import Line" = r,
tabledata "Shpfy Inventory Item" = R,
+ tabledata "Shpfy Invoice Header" = R,
+ tabledata "Shpfy Language" = R,
tabledata "Shpfy Log Entry" = R,
tabledata "Shpfy Metafield" = R,
tabledata "Shpfy Order Attribute" = R,
@@ -47,6 +49,7 @@ permissionset 30100 "Shpfy - Read"
tabledata "Shpfy Order Tax Line" = R,
tabledata "Shpfy Order Transaction" = R,
tabledata "Shpfy Payment Method Mapping" = R,
+ tabledata "Shpfy Payment Terms" = R,
tabledata "Shpfy Payment Transaction" = R,
tabledata "Shpfy Payout" = R,
tabledata "Shpfy Product" = R,
@@ -64,6 +67,7 @@ permissionset 30100 "Shpfy - Read"
tabledata "Shpfy Tag" = R,
tabledata "Shpfy Tax Area" = R,
tabledata "Shpfy Transaction Gateway" = R,
+ tabledata "Shpfy Translation" = R,
tabledata "Shpfy Variant" = R;
}
#pragma warning restore AS0090, AS0049
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al
index 3878464a4b..2ae6043bf5 100644
--- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductAPI.Codeunit.al
@@ -564,7 +564,9 @@ codeunit 30176 "Shpfy Product API"
/// Return variable "Result" of type Boolean.
internal procedure UpdateShopifyProductFields(var ShopifyProduct: record "Shpfy Product"; JProduct: JsonObject) Result: Boolean
var
+ MetafieldAPI: Codeunit "Shpfy Metafield API";
UpdatedAt: DateTime;
+ JMetafields: JsonArray;
begin
UpdatedAt := JsonHelper.GetValueAsDateTime(JProduct, 'updatedAt');
if UpdatedAt < ShopifyProduct."Updated At" then
@@ -584,7 +586,7 @@ codeunit 30176 "Shpfy Product API"
ShopifyProduct."Product Type" := JsonHelper.GetValueAsText(JProduct, 'productType', MaxStrLen(ShopifyProduct."Product Type"));
#pragma warning restore AA0139
ShopifyProduct.UpdateTags(JsonHelper.GetArrayAsText(JProduct, 'tags'));
-#pragma warning disable AA0139
+#pragma warning disable AA0139
ShopifyProduct.Title := JsonHelper.GetValueAsText(JProduct, 'title', MaxStrLen(ShopifyProduct.Title));
ShopifyProduct.Vendor := JsonHelper.GetValueAsText(JProduct, 'vendor', MaxStrLen(ShopifyProduct.Vendor));
ShopifyProduct."SEO Description" := JsonHelper.GetValueAsText(JProduct, 'seo.description', MaxStrLen(ShopifyProduct."SEO Description"));
@@ -592,8 +594,10 @@ codeunit 30176 "Shpfy Product API"
#pragma warning restore AA0139
ShopifyProduct.Status := ConvertToProductStatus(JsonHelper.GetValueAsText(JProduct, 'status'));
ShopifyProduct.Modify(false);
- end;
+ if JsonHelper.GetJsonArray(JProduct, JMetafields, 'metafields.edges') then
+ MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Product", ShopifyProduct.Id);
+ end;
local procedure ConvertToProductStatus(Value: Text): Enum "Shpfy Product Status"
begin
diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductEvents.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductEvents.Codeunit.al
index 3c04a9b6d6..9af1e943cb 100644
--- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductEvents.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductEvents.Codeunit.al
@@ -61,8 +61,9 @@ codeunit 30177 "Shpfy Product Events"
/// Parameter of type Code[20].
/// Parameter of type Record "Shopify Shop".
/// Parameter of type Text.
+ /// Parameter of type Code[10].
[IntegrationEvent(false, false)]
- internal procedure OnAfterCreateProductBodyHtml(ItemNo: Code[20]; ShopifyShop: Record "Shpfy Shop"; var ProductBodyHtml: Text)
+ internal procedure OnAfterCreateProductBodyHtml(ItemNo: Code[20]; ShopifyShop: Record "Shpfy Shop"; var ProductBodyHtml: Text; LanguageCode: Code[10])
begin
end;
@@ -145,8 +146,9 @@ codeunit 30177 "Shpfy Product Events"
/// Parameter of type Record "Shopify Shop".
/// Parameter of type Text.
/// Parameter of type Boolean.
+ /// Parameter of type Code[10].
[IntegrationEvent(false, false)]
- internal procedure OnBeforeCreateProductBodyHtml(ItemNo: Code[20]; ShopifyShop: Record "Shpfy Shop"; var ProductBodyHtml: Text; var Handled: Boolean)
+ internal procedure OnBeforeCreateProductBodyHtml(ItemNo: Code[20]; ShopifyShop: Record "Shpfy Shop"; var ProductBodyHtml: Text; var Handled: Boolean; LanguageCode: Code[10])
begin
end;
diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al
index 213e7e7286..c1584e0aa7 100644
--- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductExport.Codeunit.al
@@ -66,126 +66,139 @@ codeunit 30178 "Shpfy Product Export"
GraphQueryList: List of [TextBuilder];
///
- /// Create Product Body.
+ /// Creates html body for a product from extended text, marketing text and attributes.
///
- /// Parameter of type Code[20].
- /// Return variable "ProductBodyHtml" of type Text.
- local procedure CreateProductBody(ItemNo: Code[20]) ProductBodyHtml: Text
+ /// Item number.
+ /// Language code to look for translations.
+ /// Product body html.
+ internal procedure CreateProductBody(ItemNo: Code[20]; LanguageCode: Code[10]) ProductBodyHtml: Text
var
Item: Record Item;
- ExtendedTextHeader: Record "Extended Text Header";
- ExtendedTextLine: Record "Extended Text Line";
- ItemAttrValueTranslation: Record "Item Attr. Value Translation";
- ItemAttribute: Record "Item Attribute";
- ItemAttributeTranslation: Record "Item Attribute Translation";
- ItemAttributeValue: Record "Item Attribute Value";
- ItemAttributeValueMapping: Record "Item Attribute Value Mapping";
- Translator: Report "Shpfy Translator";
EntityText: Codeunit "Entity Text";
EntityTextScenario: Enum "Entity Text Scenario";
IsHandled: Boolean;
MarketingText: Text;
Result: TextBuilder;
begin
- ProductEvents.OnBeforeCreateProductBodyHtml(ItemNo, Shop, ProductBodyHtml, IsHandled);
+ ProductEvents.OnBeforeCreateProductBodyHtml(ItemNo, Shop, ProductBodyHtml, IsHandled, LanguageCode);
if not IsHandled then begin
- if Shop."Sync Item Extended Text" then begin
- ExtendedTextHeader.SetRange("Table Name", ExtendedTextHeader."Table Name"::Item);
- ExtendedTextHeader.SetRange("No.", ItemNo);
- ExtendedTextHeader.SetFilter("Language Code", '%1|%2', '', Shop."Language Code");
- ExtendedTextHeader.SetRange("Starting Date", 0D, Today());
- ExtendedTextHeader.SetFilter("Ending Date", '%1|%2..', 0D, Today());
- if ExtendedTextHeader.FindSet() then begin
- result.Append('');
- repeat
- if (ExtendedTextHeader."Language Code" = Shop."Language Code") or ExtendedTextHeader."All Language Codes" then begin
- ExtendedTextLine.SetRange("Table Name", ExtendedTextHeader."Table Name");
- ExtendedTextLine.SetRange("No.", ExtendedTextHeader."No.");
- ExtendedTextLine.SetRange("Language Code", ExtendedTextHeader."Language Code");
- ExtendedTextLine.SetRange("Text No.", ExtendedTextHeader."Text No.");
- if ExtendedTextLine.FindSet() then begin
- Result.Append(' ');
- repeat
- Result.Append(ExtendedTextLine.Text);
- if strlen(ExtendedTextLine.Text) > 0 then
- case ExtendedTextLine.Text[StrLen(ExtendedTextLine.Text)] of
- '.', '?', '!', ':':
- begin
- Result.Append('
');
- Result.Append(' ');
- end;
- '/':
- ;
- else
- Result.Append(' ');
- end
- else begin
- Result.Append('
');
- Result.Append(' ');
- end;
- until ExtendedTextLine.Next() = 0;
- end;
- end;
- until ExtendedTextHeader.Next() = 0;
- result.Append('
');
- Result.Append('
');
- end;
- end;
+ if Shop."Sync Item Extended Text" then
+ AddExtendTextHtml(ItemNo, Result, LanguageCode);
if Shop."Sync Item Marketing Text" then
- if Item.Get(ItemNo) then begin
- MarketingText := EntityText.GetText(Database::Item, Item.SystemId, EntityTextScenario::"Marketing Text");
- if MarketingText <> '' then begin
- Result.Append('');
- Result.Append(MarketingText);
- Result.Append('
');
- Result.Append('
');
- end
+ if LanguageCode = Shop."Language Code" then
+ if Item.Get(ItemNo) then begin
+ MarketingText := EntityText.GetText(Database::Item, Item.SystemId, EntityTextScenario::"Marketing Text");
+ if MarketingText <> '' then begin
+ Result.Append('');
+ Result.Append(MarketingText);
+ Result.Append('
');
+ Result.Append('
');
+ end
+ end;
+
+ if Shop."Sync Item Attributes" then
+ AddAtributeHtml(ItemNo, Result, LanguageCode);
+
+ ProductBodyHtml := Result.ToText();
+ end;
+ ProductEvents.OnAfterCreateProductbodyHtml(ItemNo, Shop, ProductBodyHtml, LanguageCode);
+ end;
+
+ local procedure AddExtendTextHtml(ItemNo: Code[20]; Result: TextBuilder; LanguageCode: Code[10])
+ var
+ ExtendedTextHeader: Record "Extended Text Header";
+ ExtendedTextLine: Record "Extended Text Line";
+ begin
+ ExtendedTextHeader.SetRange("Table Name", ExtendedTextHeader."Table Name"::Item);
+ ExtendedTextHeader.SetRange("No.", ItemNo);
+ ExtendedTextHeader.SetFilter("Language Code", '%1|%2', '', LanguageCode);
+ ExtendedTextHeader.SetRange("Starting Date", 0D, Today());
+ ExtendedTextHeader.SetFilter("Ending Date", '%1|%2..', 0D, Today());
+ if ExtendedTextHeader.FindSet() then begin
+ result.Append('');
+ repeat
+ if (ExtendedTextHeader."Language Code" = LanguageCode) or ExtendedTextHeader."All Language Codes" then begin
+ ExtendedTextLine.SetRange("Table Name", ExtendedTextHeader."Table Name");
+ ExtendedTextLine.SetRange("No.", ExtendedTextHeader."No.");
+ ExtendedTextLine.SetRange("Language Code", ExtendedTextHeader."Language Code");
+ ExtendedTextLine.SetRange("Text No.", ExtendedTextHeader."Text No.");
+ if ExtendedTextLine.FindSet() then begin
+ Result.Append(' ');
+ repeat
+ Result.Append(ExtendedTextLine.Text);
+ if StrLen(ExtendedTextLine.Text) > 0 then
+ case ExtendedTextLine.Text[StrLen(ExtendedTextLine.Text)] of
+ '.', '?', '!', ':':
+ begin
+ Result.Append('
');
+ Result.Append(' ');
+ end;
+ '/':
+ ;
+ else
+ Result.Append(' ');
+ end
+ else begin
+ Result.Append('
');
+ Result.Append(' ');
+ end;
+ until ExtendedTextLine.Next() = 0;
+ end;
end;
+ until ExtendedTextHeader.Next() = 0;
+ result.Append('
');
+ Result.Append('
');
+ end;
+ end;
- if Shop."Sync Item Attributes" then begin
- ItemAttributeValueMapping.SetRange("Table ID", Database::Item);
- ItemAttributeValueMapping.SetRange("No.", ItemNo);
- if ItemAttributeValueMapping.FindSet() then begin
- Result.Append('');
- Result.Append('
');
- Result.Append(Translator.GetAttributeTitle(Shop."Language Code"));
- Result.Append('
');
- Result.Append('
');
- repeat
- if ItemAttribute.Get(ItemAttributeValueMapping."Item Attribute ID") and (not ItemAttribute.Blocked) then begin
- Result.Append(' ');
- Result.Append(' ');
- if ItemAttributeTranslation.Get(ItemAttributeValueMapping."Item Attribute ID", Shop."Language Code") then
- Result.Append(ItemAttributeTranslation.Name)
- else
- Result.Append(ItemAttribute.Name);
- Result.Append(' | ');
- Result.Append(' ');
- if ItemAttrValueTranslation.Get(ItemAttributeValueMapping."Item Attribute ID", ItemAttributeValueMapping."Item Attribute Value ID", Shop."Language Code") then
- Result.Append(ItemAttrValueTranslation.Name)
- else
- if ItemAttributeValue.Get(ItemAttributeValueMapping."Item Attribute ID", ItemAttributeValueMapping."Item Attribute Value ID") then begin
- Result.Append(ItemAttributeValue.Value);
- case ItemAttribute.Type of
- ItemAttribute.Type::Integer, ItemAttribute.Type::Decimal:
- begin
- Result.Append(' ');
- Result.Append(ItemAttribute."Unit of Measure");
- end;
+ local procedure AddAtributeHtml(ItemNo: Code[20]; Result: TextBuilder; LanguageCode: Code[10])
+ var
+ ItemAttrValueTranslation: Record "Item Attr. Value Translation";
+ ItemAttribute: Record "Item Attribute";
+ ItemAttributeTranslation: Record "Item Attribute Translation";
+ ItemAttributeValue: Record "Item Attribute Value";
+ ItemAttributeValueMapping: Record "Item Attribute Value Mapping";
+ Translator: Report "Shpfy Translator";
+ begin
+ ItemAttributeValueMapping.SetRange("Table ID", Database::Item);
+ ItemAttributeValueMapping.SetRange("No.", ItemNo);
+ if ItemAttributeValueMapping.FindSet() then begin
+ Result.Append('');
+ Result.Append(' ');
+ Result.Append(Translator.GetAttributeTitle(LanguageCode));
+ Result.Append(' ');
+ Result.Append(' ');
+ repeat
+ if ItemAttribute.Get(ItemAttributeValueMapping."Item Attribute ID") and (not ItemAttribute.Blocked) then begin
+ Result.Append(' ');
+ Result.Append(' ');
+ if ItemAttributeTranslation.Get(ItemAttributeValueMapping."Item Attribute ID", LanguageCode) then
+ Result.Append(ItemAttributeTranslation.Name)
+ else
+ Result.Append(ItemAttribute.Name);
+ Result.Append(' | ');
+ Result.Append(' ');
+ if ItemAttrValueTranslation.Get(ItemAttributeValueMapping."Item Attribute ID", ItemAttributeValueMapping."Item Attribute Value ID", LanguageCode) then
+ Result.Append(ItemAttrValueTranslation.Name)
+ else
+ if ItemAttributeValue.Get(ItemAttributeValueMapping."Item Attribute ID", ItemAttributeValueMapping."Item Attribute Value ID") then begin
+ Result.Append(ItemAttributeValue.Value);
+ case ItemAttribute.Type of
+ ItemAttribute.Type::Integer, ItemAttribute.Type::Decimal:
+ begin
+ Result.Append(' ');
+ Result.Append(ItemAttribute."Unit of Measure");
end;
- end;
- Result.Append(' | ');
- Result.Append(' ');
+ end;
end;
- until ItemAttributeValueMapping.Next() = 0;
- Result.Append(' ');
- Result.Append(' ');
+ Result.Append(' | ');
+ Result.Append('
');
end;
- end;
- ProductBodyHtml := Result.ToText();
+ until ItemAttributeValueMapping.Next() = 0;
+ Result.Append('
');
+ Result.Append('
');
end;
- ProductEvents.OnAfterCreateProductbodyHtml(ItemNo, Shop, ProductBodyHtml);
end;
///
@@ -269,7 +282,7 @@ codeunit 30178 "Shpfy Product Export"
end;
ShopifyProduct.Vendor := CopyStr(GetVendor(Item."Vendor No."), 1, MaxStrLen(ShopifyProduct.Vendor));
ShopifyProduct."Product Type" := CopyStr(GetProductType(Item."Item Category Code"), 1, MaxStrLen(ShopifyProduct."Product Type"));
- ShopifyProduct.SetDescriptionHtml(CreateProductBody(Item."No."));
+ ShopifyProduct.SetDescriptionHtml(CreateProductBody(Item."No.", Shop."Language Code"));
ShopifyProduct."Tags Hash" := ShopifyProduct.CalcTagsHash();
if Item.Blocked then
case Shop."Action for Removed Products" of
@@ -680,15 +693,33 @@ codeunit 30178 "Shpfy Product Export"
end;
until ItemUnitofMeasure.Next() = 0;
end;
+
+ UpdateMetafields(ShopifyProduct.Id);
+ UpdateProductTranslations(ShopifyProduct.Id, Item)
end;
end;
+ local procedure UpdateMetafields(ProductId: BigInteger)
+ var
+ ShpfyVariant: Record "Shpfy Variant";
+ MetafieldAPI: Codeunit "Shpfy Metafield API";
+ begin
+ MetafieldAPI.CreateOrUpdateMetafieldsInShopify(Database::"Shpfy Product", ProductId);
+
+ ShpfyVariant.SetRange("Product Id", ProductId);
+ ShpfyVariant.ReadIsolation := IsolationLevel::ReadCommitted;
+ if ShpfyVariant.FindSet() then
+ repeat
+ MetafieldAPI.CreateOrUpdateMetafieldsInShopify(Database::"Shpfy Variant", ShpfyVariant.Id);
+ until ShpfyVariant.Next() = 0;
+ end;
+
///
- /// Update Product Variant.
+ /// Updates a product variant in Shopify. Used when item variant does not exist in BC, but variants per UoM are maintained in Shopify.
///
- /// Parameter of type Record "Shopify Variant".
- /// Parameter of type Record Item.
- /// Parameter of type Record "Item Unit of Measure".
+ /// Shopify variant to update.
+ /// Item where information is taken from.
+ /// Item unit of measure where information is taken from.
local procedure UpdateProductVariant(ShopifyVariant: Record "Shpfy Variant"; Item: Record Item; ItemUnitofMeasure: Record "Item Unit of Measure")
var
TempShopifyVariant: Record "Shpfy Variant" temporary;
@@ -703,7 +734,7 @@ codeunit 30178 "Shpfy Product Export"
end;
///
- /// Update Product Variant.
+ /// Updates a Product Variant in Shopify. Used when variants per UoM are not maintained in Shopify.
///
/// Parameter of type Record "Shopify Variant".
/// Parameter of type Record Item.
@@ -717,12 +748,14 @@ codeunit 30178 "Shpfy Product Export"
FillInProductVariantData(ShopifyVariant, Item, ItemVariant);
if OnlyUpdatePrice then
VariantApi.UpdateProductPrice(ShopifyVariant, TempShopifyVariant, BulkOperationInput, GraphQueryList, RecordCount)
- else
+ else begin
VariantApi.UpdateProductVariant(ShopifyVariant, TempShopifyVariant);
+ UpdateVariantTranslations(ShopifyVariant.Id, ItemVariant);
+ end;
end;
///
- /// Update Product Variant.
+ /// Update a Product Variant in Shopify. Used when item variant exists in BC and variants per UoM are maintained in Shopify.
///
/// Parameter of type Record "Shopify Variant".
/// Parameter of type Record Item.
@@ -737,7 +770,57 @@ codeunit 30178 "Shpfy Product Export"
FillInProductVariantData(ShopifyVariant, Item, ItemVariant, ItemUnitofMeasure);
if OnlyUpdatePrice then
VariantApi.UpdateProductPrice(ShopifyVariant, TempShopifyVariant, BulkOperationInput, GraphQueryList, RecordCount)
- else
+ else begin
VariantApi.UpdateProductVariant(ShopifyVariant, TempShopifyVariant);
+ UpdateVariantTranslations(ShopifyVariant.Id, ItemVariant);
+ end;
+ end;
+
+ #region Translations
+ local procedure UpdateProductTranslations(ProductId: BigInteger; Item: Record Item)
+ var
+ TempTranslation: Record "Shpfy Translation" temporary;
+ TranslationAPI: Codeunit "Shpfy Translation API";
+ begin
+ if OnlyUpdatePrice then
+ exit;
+
+ TempTranslation."Resource Type" := TempTranslation."Resource Type"::Product;
+ TempTranslation."Resource ID" := ProductId;
+
+ CollectTranslations(Item, TempTranslation, TempTranslation."Resource Type");
+ TranslationAPI.CreateOrUpdateTranslations(TempTranslation);
+ end;
+
+ local procedure UpdateVariantTranslations(VariantId: BigInteger; ItemVariant: Record "Item Variant")
+ var
+ TempTranslation: Record "Shpfy Translation" temporary;
+ TranslationAPI: Codeunit "Shpfy Translation API";
+ begin
+ if OnlyUpdatePrice then
+ exit;
+
+ TempTranslation."Resource Type" := TempTranslation."Resource Type"::ProductVariant;
+ TempTranslation."Resource ID" := VariantId;
+
+ CollectTranslations(ItemVariant, TempTranslation, TempTranslation."Resource Type");
+ TranslationAPI.CreateOrUpdateTranslations(TempTranslation);
+ end;
+
+ local procedure CollectTranslations(RecVariant: Variant; var TempTranslation: Record "Shpfy Translation" temporary; ICreateTranslation: Interface "Shpfy ICreate Translation")
+ var
+ ShpfyLanguage: Record "Shpfy Language";
+ TranslationAPI: Codeunit "Shpfy Translation API";
+ Digests: Dictionary of [Text, Text];
+ begin
+ Digests := TranslationAPI.RetrieveTranslatableContentDigests(TempTranslation."Resource Type", TempTranslation."Resource ID");
+
+ ShpfyLanguage.SetRange("Shop Code", Shop.Code);
+ ShpfyLanguage.SetRange("Sync Translations", true);
+ if ShpfyLanguage.FindSet() then
+ repeat
+ ICreateTranslation.CreateTranslation(RecVariant, ShpfyLanguage, TempTranslation, Digests);
+ until ShpfyLanguage.Next() = 0;
end;
+ #endregion
}
diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductPriceCalc.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductPriceCalc.Codeunit.al
index f6d11bea70..31f62f11cf 100644
--- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductPriceCalc.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyProductPriceCalc.Codeunit.al
@@ -31,6 +31,7 @@ codeunit 30182 "Shpfy Product Price Calc."
TaxLiable: Boolean;
VATCountryRegionCode: Code[10];
CustomerPriceGroup: Code[10];
+ CustomerNo: Code[20];
CustomerDiscGroup: Code[20];
CustomerPostingGroup: Code[20];
PricesIncludingVAT: Boolean;
@@ -91,22 +92,35 @@ codeunit 30182 "Shpfy Product Price Calc."
/// Create Temp Sales Header.
///
local procedure CreateTempSalesHeader()
+ var
+ Customer: Record Customer;
begin
Clear(TempSalesHeader);
TempSalesHeader."Document Type" := TempSalesHeader."Document Type"::Quote;
TempSalesHeader."No." := Shop.Code;
- TempSalesHeader."Sell-to Customer No." := Shop.Code;
- TempSalesHeader."Bill-to Customer No." := Shop.Code;
+ if CustomerNo <> '' then begin
+ Customer.Get(CustomerNo);
+ TempSalesHeader."Sell-to Customer No." := CustomerNo;
+ TempSalesHeader."Bill-to Customer No." := CustomerNo;
+ TempSalesHeader."Customer Price Group" := Customer."Customer Price Group";
+ TempSalesHeader."Customer Disc. Group" := Customer."Customer Disc. Group";
+ TempSalesHeader."Allow Line Disc." := Customer."Allow Line Disc.";
+ end
+ else begin
+ TempSalesHeader."Sell-to Customer No." := Shop.Code;
+ TempSalesHeader."Bill-to Customer No." := Shop.Code;
+ TempSalesHeader."Customer Price Group" := CustomerPriceGroup;
+ TempSalesHeader."Customer Disc. Group" := CustomerDiscGroup;
+ TempSalesHeader."Allow Line Disc." := AllowLineDisc;
+ end;
+
TempSalesHeader."Gen. Bus. Posting Group" := GenBusPostingGroup;
TempSalesHeader."VAT Bus. Posting Group" := VATBusPostingGroup;
TempSalesHeader."Tax Area Code" := TaxAreaCode;
TempSalesHeader."Tax Liable" := TaxLiable;
TempSalesHeader."VAT Country/Region Code" := VATCountryRegionCode;
- TempSalesHeader."Customer Price Group" := CustomerPriceGroup;
- TempSalesHeader."Customer Disc. Group" := CustomerDiscGroup;
TempSalesHeader."Customer Posting Group" := CustomerPostingGroup;
TempSalesHeader."Prices Including VAT" := PricesIncludingVAT;
- TempSalesHeader."Allow Line Disc." := AllowLineDisc;
TempSalesHeader.Validate("Document Date", WorkDate());
TempSalesHeader.Validate("Order Date", WorkDate());
TempSalesHeader.Validate("Currency Code", Shop."Currency Code");
@@ -206,6 +220,7 @@ codeunit 30182 "Shpfy Product Price Calc."
CustomerPostingGroup := ShopifyCatalog."Customer Posting Group";
PricesIncludingVAT := ShopifyCatalog."Prices Including VAT";
AllowLineDisc := ShopifyCatalog."Allow Line Disc.";
+ CustomerNo := ShopifyCatalog."Customer No.";
end;
end;
end;
diff --git a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyVariantAPI.Codeunit.al b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyVariantAPI.Codeunit.al
index 340ed848a6..9d3c452983 100644
--- a/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyVariantAPI.Codeunit.al
+++ b/Apps/W1/Shopify/app/src/Products/Codeunits/ShpfyVariantAPI.Codeunit.al
@@ -500,6 +500,7 @@ codeunit 30189 "Shpfy Variant API"
/// Return variable "Result" of type Boolean.
internal procedure UpdateShopifyVariantFields(ShopifyProduct: Record "Shpfy Product"; var ShopifyVariant: Record "Shpfy Variant"; var ShopifyInventoryItem: Record "Shpfy Inventory Item"; JVariant: JsonObject) Result: Boolean
var
+ MetafieldAPI: Codeunit "Shpfy Metafield API";
RecordRef: RecordRef;
UpdatedAt: DateTime;
JMetafields: JsonArray;
@@ -595,7 +596,19 @@ codeunit 30189 "Shpfy Variant API"
end;
if JsonHelper.GetJsonObject(JVariant, JNode, 'metafields') then
if JsonHelper.GetJsonArray(JNode, JMetafields, 'edges') then
- foreach JItem in JMetafields do;
+ MetafieldAPI.UpdateMetafieldsFromShopify(JMetafields, Database::"Shpfy Variant", ShopifyVariant.Id);
+ end;
+
+ ///
+ /// Deletes a product variant from Shopify.
+ ///
+ /// Id of the Shopify variant to delete.
+ internal procedure DeleteProductVariant(ShopifyVariantId: BigInteger)
+ var
+ Parameters: Dictionary of [Text, Text];
+ begin
+ Parameters.Add('VariantId', Format(ShopifyVariantId));
+ CommunicationMgt.ExecuteGraphQL(Enum::"Shpfy GraphQL Type"::ProductVariantDelete, Parameters);
end;
///
diff --git a/Apps/W1/Shopify/app/src/Products/Pages/ShpfyProducts.Page.al b/Apps/W1/Shopify/app/src/Products/Pages/ShpfyProducts.Page.al
index 29ce2f2628..3635ac301a 100644
--- a/Apps/W1/Shopify/app/src/Products/Pages/ShpfyProducts.Page.al
+++ b/Apps/W1/Shopify/app/src/Products/Pages/ShpfyProducts.Page.al
@@ -315,6 +315,24 @@ page 30126 "Shpfy Products"
Tags.RunModal();
end;
}
+ action(Metafields)
+ {
+ ApplicationArea = All;
+ Caption = 'Metafields';
+ Image = PriceAdjustment;
+ Promoted = true;
+ PromotedCategory = Process;
+ PromotedIsBig = true;
+ PromotedOnly = true;
+ ToolTip = 'Add metafields to a product. This can be used for adding custom data fields to products in Shopify.';
+
+ trigger OnAction()
+ var
+ Metafields: Page "Shpfy Metafields";
+ begin
+ Metafields.RunForResource(Database::"Shpfy Product", Rec.Id, Rec."Shop Code");
+ end;
+ }
group(Sync)
{
action(SyncProducts)
diff --git a/Apps/W1/Shopify/app/src/Products/Pages/ShpfyVariants.Page.al b/Apps/W1/Shopify/app/src/Products/Pages/ShpfyVariants.Page.al
index 3291b4e345..39b3107e07 100644
--- a/Apps/W1/Shopify/app/src/Products/Pages/ShpfyVariants.Page.al
+++ b/Apps/W1/Shopify/app/src/Products/Pages/ShpfyVariants.Page.al
@@ -213,6 +213,20 @@ page 30127 "Shpfy Variants"
end;
end;
}
+ action(Metafields)
+ {
+ ApplicationArea = All;
+ Caption = 'Metafields';
+ Image = PriceAdjustment;
+ ToolTip = 'Add metafields to a variant. This can be used for adding custom data fields to variants in Shopify.';
+
+ trigger OnAction()
+ var
+ Metafields: Page "Shpfy Metafields";
+ begin
+ Metafields.RunForResource(Database::"Shpfy Variant", Rec.Id, Rec."Shop Code");
+ end;
+ }
action(AddItemsAsVariants)
{
ApplicationArea = All;
diff --git a/Apps/W1/Shopify/app/src/Products/Tables/ShpfyProduct.Table.al b/Apps/W1/Shopify/app/src/Products/Tables/ShpfyProduct.Table.al
index 751923005f..aa699d3747 100644
--- a/Apps/W1/Shopify/app/src/Products/Tables/ShpfyProduct.Table.al
+++ b/Apps/W1/Shopify/app/src/Products/Tables/ShpfyProduct.Table.al
@@ -152,6 +152,7 @@ table 30127 "Shpfy Product"
var
Shop: Record "Shpfy Shop";
ShopifyVariant: Record "Shpfy Variant";
+ Metafield: Record "Shpfy Metafield";
IRemoveProduct: Interface "Shpfy IRemoveProductAction";
begin
if Shop.Get(Rec."Shop Code") then begin
@@ -161,6 +162,11 @@ table 30127 "Shpfy Product"
ShopifyVariant.SetRange("Product Id", Id);
if not ShopifyVariant.IsEmpty then
ShopifyVariant.DeleteAll(true);
+
+ Metafield.SetRange("Parent Table No.", Database::"Shpfy Product");
+ Metafield.SetRange("Owner Id", Id);
+ if not Metafield.IsEmpty then
+ Metafield.DeleteAll();
end;
///
diff --git a/Apps/W1/Shopify/app/src/Products/Tables/ShpfyVariant.Table.al b/Apps/W1/Shopify/app/src/Products/Tables/ShpfyVariant.Table.al
index 85032e64c5..500f760cbd 100644
--- a/Apps/W1/Shopify/app/src/Products/Tables/ShpfyVariant.Table.al
+++ b/Apps/W1/Shopify/app/src/Products/Tables/ShpfyVariant.Table.al
@@ -192,9 +192,15 @@ table 30129 "Shpfy Variant"
trigger OnDelete()
var
InventoryItem: Record "Shpfy Inventory Item";
+ Metafield: Record "Shpfy Metafield";
begin
InventoryItem.SetRange("Variant Id", Id);
if not InventoryItem.IsEmpty then
InventoryItem.DeleteAll();
+
+ Metafield.SetRange("Parent Table No.", Database::"Shpfy Variant");
+ Metafield.SetRange("Owner Id", Id);
+ if not Metafield.IsEmpty then
+ Metafield.DeleteAll();
end;
}
diff --git a/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslProduct.Codeunit.al b/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslProduct.Codeunit.al
new file mode 100644
index 0000000000..f5cf9aaa35
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslProduct.Codeunit.al
@@ -0,0 +1,30 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Inventory.Item;
+
+codeunit 30342 "Shpfy Create Transl. Product" implements "Shpfy ICreate Translation"
+{
+ Access = Internal;
+
+ procedure CreateTranslation(RecVariant: Variant; ShpfyLanguage: Record "Shpfy Language"; var TempTranslation: Record "Shpfy Translation" temporary; Digests: Dictionary of [Text, Text])
+ var
+ Item: Record Item;
+ TranslationMgt: Codeunit "Shpfy Translation Mgt.";
+ ProductExport: Codeunit "Shpfy Product Export";
+ TranslationText: Text;
+ TranslationKey: Text[100];
+ Digest: Text;
+ begin
+ Item := RecVariant;
+
+ TranslationText := TranslationMgt.GetItemTranslation(Item."No.", '', ShpfyLanguage."Language Code");
+ TranslationKey := 'title';
+ if Digests.Get(TranslationKey, Digest) and (TranslationText <> '') then
+ TempTranslation.AddTranslation(ShpfyLanguage.Locale, TranslationKey, Digests.Get(TranslationKey), TranslationText);
+
+ TranslationKey := 'body_html';
+ TranslationText := ProductExport.CreateProductBody(Item."No.", ShpfyLanguage."Language Code");
+ if Digests.Get(TranslationKey, Digest) and (TranslationText <> '') then
+ TempTranslation.AddTranslation(ShpfyLanguage.Locale, TranslationKey, Digest, TranslationText);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslVariant.Codeunit.al b/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslVariant.Codeunit.al
new file mode 100644
index 0000000000..994fe76ff5
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Codeunits/ICreateTranslation/ShpfyCreateTranslVariant.Codeunit.al
@@ -0,0 +1,24 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Inventory.Item;
+
+codeunit 30313 "Shpfy Create Transl. Variant" implements "Shpfy ICreate Translation"
+{
+ Access = Internal;
+
+ procedure CreateTranslation(RecVariant: Variant; ShpfyLanguage: Record "Shpfy Language"; var TempTranslation: Record "Shpfy Translation" temporary; Digests: Dictionary of [Text, Text])
+ var
+ ItemVariant: Record "Item Variant";
+ TranslationMgt: Codeunit "Shpfy Translation Mgt.";
+ TranslationText: Text;
+ TranslationKey: Text;
+ Digest: Text;
+ begin
+ ItemVariant := RecVariant;
+
+ TranslationText := TranslationMgt.GetItemTranslation(ItemVariant."Item No.", ItemVariant.Code, ShpfyLanguage."Language Code");
+ TranslationKey := 'option1';
+ if Digests.Get(TranslationKey, Digest) and (TranslationText <> '') then
+ TempTranslation.AddTranslation(ShpfyLanguage.Locale, TranslationKey, Digests.Get(TranslationKey), TranslationText);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationApi.Codeunit.al b/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationApi.Codeunit.al
new file mode 100644
index 0000000000..2ef8c0b21c
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationApi.Codeunit.al
@@ -0,0 +1,157 @@
+namespace Microsoft.Integration.Shopify;
+
+codeunit 30213 "Shpfy Translation API"
+{
+
+ #region Shop Locales
+ ///
+ /// Retrieves the languages for a shop from Shopify and updates the table with the new languages.
+ ///
+ ///
+ /// Primary language is skipped as it is handled by the Shop."Language Code" field.
+ ///
+ internal procedure PullLanguages(ShopCode: Code[20])
+ var
+ ShpfyLanguage: Record "Shpfy Language";
+ Shop: Record "Shpfy Shop";
+ CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ JsonHelper: Codeunit "Shpfy Json Helper";
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ JResponse: JsonToken;
+ JLocales: JsonArray;
+ JLocale: JsonToken;
+ LocaleText: Text[2];
+ IsPrimary: Boolean;
+ CurrentLocales: List of [Text[2]];
+ begin
+ Shop.Get(ShopCode);
+ CommunicationMgt.SetShop(Shop.Code);
+
+ GraphQLType := GraphQLType::ShopLocales;
+ JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType);
+
+ CurrentLocales := CollectLocales(Shop.Code);
+
+ JsonHelper.GetJsonArray(JResponse, JLocales, 'data.shopLocales');
+ foreach JLocale in JLocales do begin
+ CurrentLocales.Remove(LocaleText);
+ LocaleText := CopyStr(JsonHelper.GetValueAsText(JLocale, 'locale'), 1, MaxStrLen(LocaleText));
+ IsPrimary := JsonHelper.GetValueAsBoolean(JLocale, 'primary');
+
+ if not IsPrimary then // Primary language is handled by Shop."Language Code"
+ if not ShpfyLanguage.Get(Shop.Code, LocaleText) then
+ ShpfyLanguage.AddLanguage(Shop, LocaleText);
+ end;
+
+ foreach LocaleText in CurrentLocales do begin
+ ShpfyLanguage.Get(Shop.Code, LocaleText);
+ ShpfyLanguage.Delete(true);
+ end;
+ end;
+
+ local procedure CollectLocales(ShopCode: Code[20]) Locales: List of [Text[2]]
+ var
+ ShpfyLanguage: Record "Shpfy Language";
+ begin
+ ShpfyLanguage.SetRange("Shop Code", ShopCode);
+ if ShpfyLanguage.FindSet() then
+ repeat
+ Locales.Add(ShpfyLanguage.Locale);
+ until ShpfyLanguage.Next() = 0;
+ end;
+ #endregion
+
+ #region Translations
+ ///
+ /// Creates or updates a translation for a product in Shopify.
+ ///
+ /// Product Id in Shopify
+ /// Temporary record set with product translations
+ internal procedure CreateOrUpdateTranslations(var TempTranslation: Record "Shpfy Translation" temporary): JsonToken
+ var
+ GraphQuery: TextBuilder;
+ begin
+ if TempTranslation.FindSet() then begin
+ repeat
+ CreateTranslationGraphQuery(TempTranslation, GraphQuery);
+ until TempTranslation.Next() = 0;
+
+ exit(UpdateTranslations(GetResourceTypeName(TempTranslation."Resource Type"), TempTranslation."Resource ID", GraphQuery.ToText()));
+ end;
+ end;
+
+ local procedure CreateTranslationGraphQuery(var TempTranslation: Record "Shpfy Translation" temporary; GraphQuery: TextBuilder): Text
+ begin
+ GraphQuery.Append('{');
+ GraphQuery.Append('key: \"');
+ GraphQuery.Append(TempTranslation.Name);
+ GraphQuery.Append('\",');
+ GraphQuery.Append('locale: \"');
+ GraphQuery.Append(TempTranslation.Locale);
+ GraphQuery.Append('\",');
+ GraphQuery.Append('value: \"');
+ GraphQuery.Append(EscapeGrapQLData(TempTranslation.GetValue()));
+ GraphQuery.Append('\",');
+ GraphQuery.Append('translatableContentDigest: \"');
+ GraphQuery.Append(TempTranslation."Transl. Content Digest");
+ GraphQuery.Append('\",');
+ GraphQuery.Append('},');
+ end;
+
+ local procedure EscapeGrapQLData(Data: Text): Text
+ begin
+ exit(Data.Replace('\', '\\\\').Replace('"', '\\\"'));
+ end;
+
+ local procedure GetResourceTypeName(Type: Enum "Shpfy Resource Type"): Text
+ begin
+ exit(Type.Names().Get(Type.Ordinals().IndexOf(Type.AsInteger())));
+ end;
+
+ local procedure UpdateTranslations(ResourceType: Text; ResourceId: BigInteger; TranslationsQuery: Text): JsonToken
+ var
+ CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ Parameters: Dictionary of [Text, Text];
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ begin
+ Parameters.Add('ResourceType', ResourceType);
+ Parameters.Add('ResourceId', Format(ResourceId));
+ Parameters.Add('Translations', TranslationsQuery);
+
+ GraphQLType := GraphQLType::TranslationsRegister;
+ exit(CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters));
+ end;
+ #endregion
+
+ #region Translatable Resources
+
+ ///
+ /// Retrieves the translatable content digests for a resource in Shopify.
+ ///
+ /// Type of the resource to retrieve the digests
+ /// Id of the resource to retrieve the digests
+ /// Dictionary with the translatable content digests
+ internal procedure RetrieveTranslatableContentDigests(ResourceType: Enum "Shpfy Resource Type"; ResourceId: BigInteger) Digests: Dictionary of [Text, Text]
+ var
+ CommunicationMgt: Codeunit "Shpfy Communication Mgt.";
+ JsonHelper: Codeunit "Shpfy Json Helper";
+ Parameters: Dictionary of [Text, Text];
+ GraphQLType: Enum "Shpfy GraphQL Type";
+ JResponse: JsonToken;
+ JTranslatablesContents: JsonArray;
+ JTranslatableContent: JsonToken;
+ begin
+ Parameters.Add('ResourceType', GetResourceTypeName(ResourceType));
+ Parameters.Add('ResourceId', Format(ResourceId));
+
+ GraphQLType := GraphQLType::GetTranslResource;
+ JResponse := CommunicationMgt.ExecuteGraphQL(GraphQLType, Parameters);
+
+ if not JsonHelper.IsNull(JResponse, 'data.translatableResource') then begin
+ JsonHelper.GetJsonArray(JResponse, JTranslatablesContents, 'data.translatableResource.translatableContent');
+ foreach JTranslatableContent in JTranslatablesContents do
+ Digests.Add(JsonHelper.GetValueAsText(JTranslatableContent, 'key'), JsonHelper.GetValueAsText(JTranslatableContent, 'digest'));
+ end;
+ end;
+ #endregion
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationMgt.Codeunit.al b/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationMgt.Codeunit.al
new file mode 100644
index 0000000000..63075c9d84
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Codeunits/ShpfyTranslationMgt.Codeunit.al
@@ -0,0 +1,28 @@
+namespace Microsoft.Integration.Shopify;
+
+using Microsoft.Inventory.Item;
+
+codeunit 30314 "Shpfy Translation Mgt."
+{
+
+ ///
+ /// Returns the translation of an item or item variant.
+ ///
+ /// Item No to find translation for.
+ /// Variant Code to find translation for.
+ /// Language Code to find translation for.
+ /// Translation of the item or item variant, or blank if not found.
+ internal procedure GetItemTranslation(ItemNo: Code[20]; VariantCode: Code[10]; LanguageCode: Code[10]): Text[100]
+ var
+ ItemTranslation: Record "Item Translation";
+ begin
+ ItemTranslation.SetRange("Item No.", ItemNo);
+ ItemTranslation.SetRange("Language Code", LanguageCode);
+ ItemTranslation.SetRange("Variant Code", VariantCode);
+ if ItemTranslation.FindFirst() then
+ exit(ItemTranslation.Description);
+ end;
+
+
+
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Enums/ShpfyResourceType.Enum.al b/Apps/W1/Shopify/app/src/Translations/Enums/ShpfyResourceType.Enum.al
new file mode 100644
index 0000000000..86fc9b17bf
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Enums/ShpfyResourceType.Enum.al
@@ -0,0 +1,20 @@
+namespace Microsoft.Integration.Shopify;
+
+enum 30161 "Shpfy Resource Type" implements "Shpfy ICreate Translation"
+{
+ Access = Internal;
+ Caption = 'Shopify Resource Type';
+ Extensible = false;
+
+ value(0; Product)
+ {
+ Caption = 'Product';
+ Implementation = "Shpfy ICreate Translation" = "Shpfy Create Transl. Product";
+ }
+
+ value(1; ProductVariant)
+ {
+ Caption = 'Product Variant';
+ Implementation = "Shpfy ICreate Translation" = "Shpfy Create Transl. Variant";
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Interfaces/ShpfyICreateTranslation.Interface.al b/Apps/W1/Shopify/app/src/Translations/Interfaces/ShpfyICreateTranslation.Interface.al
new file mode 100644
index 0000000000..29bdee6641
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Interfaces/ShpfyICreateTranslation.Interface.al
@@ -0,0 +1,17 @@
+namespace Microsoft.Integration.Shopify;
+
+interface "Shpfy ICreate Translation"
+{
+ Access = Internal;
+
+ ///
+ /// Create a translation record for the given resource and language.
+ /// Translation record is only created if the translation is not already present in the Shopify.
+ /// These records are used to create the query for updating the translation in Shopify.
+ ///
+ /// Variant record of the resource for which the translation is to be created.
+ /// Language record for which the translation is to be created.
+ /// Temporary translation record set where the translation will be stored.
+ /// Dictionary of translatable content digests for the resource.
+ procedure CreateTranslation(RecVariant: Variant; ShpfyLanguage: Record "Shpfy Language"; var TempTranslation: Record "Shpfy Translation" temporary; Digests: Dictionary of [Text, Text])
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Pages/ShpfyLanguages.Page.al b/Apps/W1/Shopify/app/src/Translations/Pages/ShpfyLanguages.Page.al
new file mode 100644
index 0000000000..c8ae32de07
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Pages/ShpfyLanguages.Page.al
@@ -0,0 +1,58 @@
+namespace Microsoft.Integration.Shopify;
+
+page 30138 "Shpfy Languages"
+{
+ ApplicationArea = All;
+ Caption = 'Shpfy Languages';
+ PageType = List;
+ SourceTable = "Shpfy Language";
+ UsageCategory = None;
+ InsertAllowed = false;
+ DeleteAllowed = false;
+
+ layout
+ {
+ area(content)
+ {
+ repeater(General)
+ {
+ field(Locale; Rec.Locale)
+ {
+ ToolTip = 'Specifies the shop locale to sync translations.';
+ }
+ field("Language Code"; Rec."Language Code")
+ {
+ ToolTip = 'Specifies the language code for the locale.';
+ }
+ field("Sync Translations"; Rec."Sync Translations")
+ {
+ ToolTip = 'Specifies if the translations should be synced for this locale.';
+ }
+ }
+ }
+ }
+
+ actions
+ {
+ area(processing)
+ {
+ action(Refresh)
+ {
+ ApplicationArea = All;
+ Caption = 'Refresh';
+ Promoted = true;
+ PromotedOnly = true;
+ PromotedCategory = Process;
+ Image = Refresh;
+ ToolTip = 'Refreshes the list of Shopify languages.';
+
+ trigger OnAction()
+ var
+ ShpfyTranslationAPI: Codeunit "Shpfy Translation API";
+ begin
+ ShpfyTranslationAPI.PullLanguages(CopyStr(Rec.GetFilter("Shop Code"), 1, 20));
+ end;
+ }
+ }
+ }
+}
diff --git a/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyLanguage.Table.al b/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyLanguage.Table.al
new file mode 100644
index 0000000000..7d45913f5f
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyLanguage.Table.al
@@ -0,0 +1,72 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Globalization;
+
+table 30156 "Shpfy Language"
+{
+ Caption = 'Shopify Language';
+ DataClassification = CustomerContent;
+
+ fields
+ {
+ field(1; "Shop Code"; Code[20])
+ {
+ Caption = 'Shop Code';
+ DataClassification = CustomerContent;
+ Editable = false;
+ TableRelation = "Shpfy Shop";
+ }
+
+ field(2; Locale; text[2])
+ {
+ Caption = 'Locale';
+ DataClassification = SystemMetadata;
+ Editable = false;
+ }
+
+ field(3; "Sync Translations"; Boolean)
+ {
+ Caption = 'Sync translations';
+ DataClassification = CustomerContent;
+
+ trigger OnValidate()
+ begin
+ Rec.TestField("Language Code");
+ end;
+ }
+ field(4; "Language Code"; Code[10])
+ {
+ Caption = 'Language Code';
+ TableRelation = Language;
+ DataClassification = CustomerContent;
+
+ trigger OnValidate()
+ begin
+ Rec.TestField("Sync Translations", false);
+ end;
+ }
+ }
+
+ keys
+ {
+ key(PK; "Shop Code", Locale)
+ {
+ Clustered = true;
+ }
+ }
+
+ ///
+ /// Adds a language to the table.
+ ///
+ /// Shop the language belongs to.
+ /// Locale of the language.
+ internal procedure AddLanguage(Shop: Record "Shpfy Shop"; NewLocale: Text[2])
+ var
+ ShpfyLanguage: Record "Shpfy Language";
+ begin
+ ShpfyLanguage.Init();
+ ShpfyLanguage."Shop Code" := Shop.Code;
+ ShpfyLanguage.Locale := NewLocale;
+ ShpfyLanguage.Insert(true);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyTranslation.Table.al b/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyTranslation.Table.al
new file mode 100644
index 0000000000..8bc8e3b1c8
--- /dev/null
+++ b/Apps/W1/Shopify/app/src/Translations/Tables/ShpfyTranslation.Table.al
@@ -0,0 +1,128 @@
+namespace Microsoft.Integration.Shopify;
+
+using System.Reflection;
+
+table 30157 "Shpfy Translation"
+{
+ Access = Internal;
+ Caption = 'Shopify Translation';
+ DataClassification = CustomerContent;
+
+ fields
+ {
+ field(1; "Resource Type"; Enum "Shpfy Resource Type")
+ {
+ Caption = 'Resource Type';
+ DataClassification = SystemMetadata;
+ }
+
+ field(2; "Resource ID"; BigInteger)
+ {
+ Caption = 'Resource ID';
+ DataClassification = SystemMetadata;
+ }
+ field(3; "Locale"; text[2])
+ {
+ Caption = 'Locale';
+ DataClassification = SystemMetadata;
+ }
+
+ field(4; Name; Text[100])
+ {
+ Caption = 'Key';
+ DataClassification = SystemMetadata;
+ }
+ field(5; Value; Blob)
+ {
+ Caption = 'Value';
+ DataClassification = CustomerContent;
+ }
+ field(6; "Transl. Content Digest"; Text[100])
+ {
+ Caption = 'Transl. Content Digest';
+ DataClassification = SystemMetadata;
+ }
+ }
+
+ keys
+ {
+ key(PK; "Resource Type", "Resource ID", Locale, Name)
+ {
+ Clustered = true;
+ }
+ }
+
+ ///
+ /// Gets the value of the translation as text
+ ///
+ /// Translation value as text
+ internal procedure GetValue(): Text
+ var
+ TypeHelper: Codeunit "Type Helper";
+ InStream: InStream;
+ begin
+ CalcFields(Value);
+ Value.CreateInStream(InStream, TextEncoding::UTF8);
+ exit(TypeHelper.ReadAsTextWithSeparator(InStream, TypeHelper.LFSeparator()));
+ end;
+
+ ///
+ /// Sets the value of the translation
+ ///
+ /// New value of the translation
+ internal procedure SetValue(NewTranslation: Text)
+ var
+ OutStream: OutStream;
+ begin
+ Clear(Value);
+ Value.CreateOutStream(OutStream, TextEncoding::UTF8);
+ OutStream.WriteText(NewTranslation);
+ if Modify(true) then;
+ end;
+
+ ///
+ /// Adds a new translation to the record if it's different than the one in the database.
+ /// Resource Type and Id should be already set wher calling this function.
+ ///
+ /// Locale of the translation
+ /// Key used to destingiush translations in Shopify
+ /// Digest of the original translatable content
+ /// Text of the translation
+ internal procedure AddTranslation(NewLocale: Text[2]; TranslationKey: Text; Digest: Text; TranslationText: Text)
+ begin
+ Rec.Locale := NewLocale;
+ Rec.Name := CopyStr(TranslationKey, 1, MaxStrLen(Rec.Name));
+ Rec."Transl. Content Digest" := CopyStr(Digest, 1, MaxStrLen(Rec."Transl. Content Digest"));
+ Rec.Insert(false);
+
+ Rec.SetValue(TranslationText);
+ if not HasTranslationChanged(Rec) then
+ Rec.Delete(false);
+ end;
+
+ ///
+ /// Determines if the translation of an item or item variant has changed since last sync.
+ ///
+ /// Temporary record containing the new translations.
+ /// True if the translation has changed, false otherwise.
+ local procedure HasTranslationChanged(var TempTranslation: Record "Shpfy Translation" temporary): Boolean
+ var
+ ShpfyTranslation: Record "Shpfy Translation";
+ TranslationValue: Text;
+ begin
+ TranslationValue := TempTranslation.GetValue();
+ if TranslationValue = '' then
+ exit(false);
+
+ ShpfyTranslation := TempTranslation;
+ if ShpfyTranslation.Find() then begin
+ if ShpfyTranslation.GetValue() <> TranslationValue then begin
+ ShpfyTranslation.SetValue(TranslationValue);
+ exit(true);
+ end;
+ end else begin
+ ShpfyTranslation.Insert(true);
+ exit(true);
+ end;
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Shopify/test/Catalogs/ShpfyCatalogPricesTest.Codeunit.al b/Apps/W1/Shopify/test/Catalogs/ShpfyCatalogPricesTest.Codeunit.al
index 88b4e98318..a9fd48055e 100644
--- a/Apps/W1/Shopify/test/Catalogs/ShpfyCatalogPricesTest.Codeunit.al
+++ b/Apps/W1/Shopify/test/Catalogs/ShpfyCatalogPricesTest.Codeunit.al
@@ -59,6 +59,7 @@ codeunit 139646 "Shpfy Catalog Prices Test"
// [GIVEN] Update the Catalog."Customer Discount Group" field and set the catalog to the calculation codeunit.
Catalog."Customer Discount Group" := CustomerDiscountGroup.Code;
+ Catalog."Allow Line Disc." := true;
Catalog.Modify();
ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
@@ -72,4 +73,251 @@ codeunit 139646 "Shpfy Catalog Prices Test"
// [THEN] InitPrice - InitDiscountPerc = Price
LibraryAssert.AreNearlyEqual(InitPrice * (1 - InitDiscountPerc / 100), Price, 0.01, 'Discount Price');
end;
+
+ [Test]
+ procedure UnitTestCalcCatalogPriceAllCustomers()
+ var
+ Shop: Record "Shpfy Shop";
+ Catalog: Record "Shpfy Catalog";
+ ShopifyCompany: Record "Shpfy Company";
+ Item: Record Item;
+ Customer: Record Customer;
+ LibrarySales: Codeunit "Library - Sales";
+ InitializeTest: Codeunit "Shpfy Initialize Test";
+ ProductInitTest: Codeunit "Shpfy Product Init Test";
+ CatalogInitialize: Codeunit "Shpfy Catalog Initialize";
+ CompanyInitialize: Codeunit "Shpfy Company Initialize";
+ ProductPriceCalculation: Codeunit "Shpfy Product Price Calc.";
+ InitUnitCost: Decimal;
+ InitPrice: Decimal;
+#if CLEAN23
+ InitDiscountPerc: Decimal;
+#endif
+ UnitCost: Decimal;
+ Price: Decimal;
+ ComparePrice: Decimal;
+ begin
+ // [GIVEN] Initializing test environment and creating necessary test records.
+ Shop := InitializeTest.CreateShop();
+ CompanyInitialize.CreateShopifyCompany(ShopifyCompany);
+ Catalog := CatalogInitialize.CreateCatalog(ShopifyCompany);
+ CatalogInitialize.CopyParametersFromShop(Catalog, Shop);
+ InitUnitCost := Any.DecimalInRange(10, 100, 1);
+ InitPrice := Any.DecimalInRange(2 * InitUnitCost, 4 * InitUnitCost, 1);
+ Item := ProductInitTest.CreateItem(Shop."Item Templ. Code", InitUnitCost, InitPrice);
+
+ // Creating a customer entry, though it is generic as discounts apply to all customers.
+ LibrarySales.CreateCustomer(Customer);
+
+ // [WHEN] Calculating initial prices without any discounts applied.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Confirm initial price calculations match expectations.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Initial unit cost should match expected.');
+ LibraryAssert.AreEqual(InitPrice, Price, 'Initial price should match expected before discount application.');
+
+ // [GIVEN] Updating the catalog to apply a universal discount to all customers.
+#if CLEAN23
+ InitDiscountPerc := Any.DecimalInRange(5, 20, 1);
+ ProductInitTest.CreateAllCustomerPriceList(Shop.Code, Item."No.", InitPrice, InitDiscountPerc);
+ Catalog."Customer No." := Customer."No.";
+ Catalog.Modify();
+
+ // [WHEN] Recalculating prices after applying the discount.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Validate the results reflect the universal discount.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Unit cost should remain consistent after discount application.');
+ LibraryAssert.AreEqual(InitPrice, ComparePrice, 'Compare price should reflect the original price prior to any discounts.');
+ LibraryAssert.AreNearlyEqual(InitPrice * (1 - InitDiscountPerc / 100), Price, 0.01, 'The final price should accurately reflect the applied discount for all customers.');
+#endif
+ end;
+
+ [Test]
+ procedure UnitTestCalcCustomerCatalogPrice()
+ var
+ Shop: Record "Shpfy Shop";
+ Catalog: Record "Shpfy Catalog";
+ ShopifyCompany: Record "Shpfy Company";
+ Item: Record Item;
+#if CLEAN23
+ Customer: Record Customer;
+ LibrarySales: Codeunit "Library - Sales";
+#endif
+ InitializeTest: Codeunit "Shpfy Initialize Test";
+ ProductInitTest: Codeunit "Shpfy Product Init Test";
+ CatalogInitialize: Codeunit "Shpfy Catalog Initialize";
+ CompanyInitialize: Codeunit "Shpfy Company Initialize";
+ ProductPriceCalculation: Codeunit "Shpfy Product Price Calc.";
+ InitUnitCost: Decimal;
+ InitPrice: Decimal;
+ UnitCost: Decimal;
+ Price: Decimal;
+ ComparePrice: Decimal;
+#if CLEAN23
+ CustDiscPerc: Decimal;
+#endif
+ begin
+ // [GIVEN] Setting up the test environment: Shop, Catalog, Item, and Customer with specific pricing and discount.
+ Shop := InitializeTest.CreateShop();
+ CompanyInitialize.CreateShopifyCompany(ShopifyCompany);
+ Catalog := CatalogInitialize.CreateCatalog(ShopifyCompany);
+ CatalogInitialize.CopyParametersFromShop(Catalog, Shop);
+ InitUnitCost := Any.DecimalInRange(10, 100, 1);
+ InitPrice := Any.DecimalInRange(2 * InitUnitCost, 4 * InitUnitCost, 1);
+ Item := ProductInitTest.CreateItem(Shop."Item Templ. Code", InitUnitCost, InitPrice);
+
+ // [WHEN] Calculating prices without and then with customer-specific discounts.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Verify initial unit cost matches setup.');
+ LibraryAssert.AreEqual(InitPrice, Price, 'Verify initial price matches setup before discount.');
+#if CLEAN23
+ // Creating a customer entry, though it is generic as discounts apply to all customers.
+ LibrarySales.CreateCustomer(Customer);
+ // [GIVEN] Applying customer-specific discounts.
+ CustDiscPerc := Any.DecimalInRange(5, 20, 1);
+ ProductInitTest.CreateCustomerPriceList(Shop.Code, Item."No.", InitPrice, CustDiscPerc, Customer);
+ Catalog."Customer No." := Customer."No.";
+ Catalog.Modify();
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+
+ // [WHEN] Recalculating prices with customer-specific discounts.
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Confirming pricing accuracy with applied discounts.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Unit cost should remain unchanged after applying discounts.');
+ LibraryAssert.AreEqual(InitPrice, ComparePrice, 'Compare price should reflect the original price pre-discount.');
+ LibraryAssert.AreNearlyEqual(InitPrice * (1 - CustDiscPerc / 100), Price, 0.01, 'Discounted price should be accurately calculated.');
+#endif
+ end;
+
+ [Test]
+ procedure UnitTestCalcCustomerCatalogPriceAllCustomers()
+ var
+ Shop: Record "Shpfy Shop";
+ Catalog: Record "Shpfy Catalog";
+ ShopifyCompany: Record "Shpfy Company";
+ Item: Record Item;
+#if CLEAN23
+ Customer: Record Customer;
+ LibrarySales: Codeunit "Library - Sales";
+#endif
+ InitializeTest: Codeunit "Shpfy Initialize Test";
+ ProductInitTest: Codeunit "Shpfy Product Init Test";
+ CatalogInitialize: Codeunit "Shpfy Catalog Initialize";
+ CompanyInitialize: Codeunit "Shpfy Company Initialize";
+ ProductPriceCalculation: Codeunit "Shpfy Product Price Calc.";
+ InitUnitCost: Decimal;
+ InitPrice: Decimal;
+#if CLEAN23
+ InitPerc: Decimal;
+ CustDiscPerc: Decimal;
+#endif
+ UnitCost: Decimal;
+ Price: Decimal;
+ ComparePrice: Decimal;
+ begin
+ // [GIVEN] Setting up shop, catalog, item, and customer-specific pricing.
+ Shop := InitializeTest.CreateShop();
+ CompanyInitialize.CreateShopifyCompany(ShopifyCompany);
+ Catalog := CatalogInitialize.CreateCatalog(ShopifyCompany);
+ CatalogInitialize.CopyParametersFromShop(Catalog, Shop);
+ InitUnitCost := Any.DecimalInRange(10, 100, 1);
+ InitPrice := Any.DecimalInRange(2 * InitUnitCost, 4 * InitUnitCost, 1);
+ Item := ProductInitTest.CreateItem(Shop."Item Templ. Code", InitUnitCost, InitPrice);
+
+ // [WHEN] Calculating prices without discounts applied.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Verifying initial prices match expectations.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Initial unit cost should match.');
+ LibraryAssert.AreEqual(InitPrice, Price, 'Initial price should match before discount.');
+
+#if CLEAN23
+ // Creating a customer entry, though it is generic as discounts apply to all customers.
+ LibrarySales.CreateCustomer(Customer);
+ // [GIVEN] Applying a universal discount for all customers.
+ CustDiscPerc := Any.DecimalInRange(5, 20, 1);
+ ProductInitTest.CreateCustomerPriceList(Shop.Code, Item."No.", InitPrice, CustDiscPerc, Customer);
+ ProductInitTest.CreateAllCustomerPriceList(Shop.Code, Item."No.", InitPrice, InitPerc);
+ Catalog."Customer No." := Customer."No.";
+ Catalog.Modify();
+
+ // [WHEN] Recalculating prices with discounts.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Confirming discounts are accurately reflected in the final prices.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Unit cost should remain consistent.');
+ LibraryAssert.AreEqual(InitPrice, ComparePrice, 'Compare price should reflect initial pricing.');
+ LibraryAssert.AreNearlyEqual(InitPrice * (1 - CustDiscPerc / 100), Price, 0.01, 'Discounted price should be accurately calculated.');
+#endif
+ end;
+
+ [Test]
+ procedure UnitTestCalcCustomerDiscountCatalogPrice()
+ var
+ Shop: Record "Shpfy Shop";
+ Catalog: Record "Shpfy Catalog";
+ ShopifyCompany: Record "Shpfy Company";
+ Item: Record Item;
+#if CLEAN23
+ Customer: Record Customer;
+ CustomerDiscountGroup: Record "Customer Discount Group";
+ LibrarySales: Codeunit "Library - Sales";
+#endif
+ InitializeTest: Codeunit "Shpfy Initialize Test";
+ ProductInitTest: Codeunit "Shpfy Product Init Test";
+ CatalogInitialize: Codeunit "Shpfy Catalog Initialize";
+ CompanyInitialize: Codeunit "Shpfy Company Initialize";
+ ProductPriceCalculation: Codeunit "Shpfy Product Price Calc.";
+ InitUnitCost: Decimal;
+ InitPrice: Decimal;
+#if CLEAN23
+ InitDiscountPerc: Decimal;
+#endif
+ UnitCost: Decimal;
+ Price: Decimal;
+ ComparePrice: Decimal;
+ begin
+ // [GIVEN] Creating shop, catalog, item, and setting customer discount details.
+ Shop := InitializeTest.CreateShop();
+ CompanyInitialize.CreateShopifyCompany(ShopifyCompany);
+ Catalog := CatalogInitialize.CreateCatalog(ShopifyCompany);
+ CatalogInitialize.CopyParametersFromShop(Catalog, Shop);
+ InitUnitCost := Any.DecimalInRange(10, 100, 1);
+ InitPrice := Any.DecimalInRange(2 * InitUnitCost, 4 * InitUnitCost, 1);
+ Item := ProductInitTest.CreateItem(Shop."Item Templ. Code", InitUnitCost, InitPrice);
+
+ // [WHEN] Calculating initial prices without any discounts applied.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Verifying initial price settings.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Initial unit cost should match setup.');
+ LibraryAssert.AreEqual(InitPrice, Price, 'Initial price should match setup without discounts.');
+#if CLEAN23
+ LibrarySales.CreateCustomer(Customer);
+ InitDiscountPerc := Any.DecimalInRange(5, 20, 1);
+ CustomerDiscountGroup := ProductInitTest.CreatePriceList(Shop.Code, Item."No.", InitPrice, InitDiscountPerc);
+ // [GIVEN] Updating catalog with customer-specific discount group details.
+ Catalog."Customer No." := Customer."No.";
+ Customer."Customer Disc. Group" := CustomerDiscountGroup.Code;
+ Customer.Modify();
+ Catalog.Modify();
+
+ // [WHEN] Recalculating prices post-update.
+ ProductPriceCalculation.SetShopAndCatalog(Shop, Catalog);
+ ProductPriceCalculation.CalcPrice(Item, '', '', UnitCost, Price, ComparePrice);
+
+ // [THEN] Confirming accurate reflection of discount updates in final prices.
+ LibraryAssert.AreEqual(InitUnitCost, UnitCost, 'Unit cost should remain unchanged post-update.');
+ LibraryAssert.AreEqual(InitPrice, ComparePrice, 'Compare Price should match initial settings.');
+ LibraryAssert.AreNearlyEqual(InitPrice * (1 - InitDiscountPerc / 100), Price, 0.01, 'Accurate calculation of discounted price should be verified.');
+#endif
+ end;
}
diff --git a/Apps/W1/Shopify/test/Products/ShpfyProductInitTest.Codeunit.al b/Apps/W1/Shopify/test/Products/ShpfyProductInitTest.Codeunit.al
index ac230b10e7..11caf9c052 100644
--- a/Apps/W1/Shopify/test/Products/ShpfyProductInitTest.Codeunit.al
+++ b/Apps/W1/Shopify/test/Products/ShpfyProductInitTest.Codeunit.al
@@ -183,10 +183,12 @@ codeunit 139603 "Shpfy Product Init Test"
CustomerPriceGroup: Record "Customer Price Group";
SalesPrice: Record "Sales Price";
begin
- CustomerPriceGroup.Init();
- CustomerPriceGroup.Code := Code;
- CustomerPriceGroup."Allow Line Disc." := true;
- CustomerPriceGroup.Insert();
+ if not CustomerPriceGroup.Get(Code) then begin
+ CustomerPriceGroup.Init();
+ CustomerPriceGroup.Code := Code;
+ CustomerPriceGroup."Allow Line Disc." := true;
+ CustomerPriceGroup.Insert();
+ end;
SalesPrice.Init();
SalesPrice."Sales Type" := Enum::"Sales Price Type"::"All Customers";
@@ -199,9 +201,11 @@ codeunit 139603 "Shpfy Product Init Test"
var
SalesLineDiscount: Record "Sales Line Discount";
begin
- CustDiscGrp.Init();
- CustDiscGrp.Code := Code;
- CustDiscGrp.Insert();
+ if not CustDiscGrp.get(Code) then begin
+ CustDiscGrp.Init();
+ CustDiscGrp.Code := Code;
+ CustDiscGrp.Insert();
+ end;
SalesLineDiscount.Init();
SalesLineDiscount.Type := Enum::"Sales Line Discount Type"::Item;
@@ -214,25 +218,65 @@ codeunit 139603 "Shpfy Product Init Test"
#else
internal procedure CreatePriceList(Code: Code[10]; ItemNo: Code[20]; Price: Decimal; DiscountPerc: Decimal) CustDiscGrp: Record "Customer Discount Group"
var
- CustomerPriceGroup: Record "Customer Price Group";
PriceListLine: Record "Price List Line";
+ CustomerPriceGroup: Record "Customer Price Group";
begin
- CustomerPriceGroup.Init();
- CustomerPriceGroup.Code := Code;
- CustomerPriceGroup."Allow Line Disc." := true;
- CustomerPriceGroup.Insert();
+ if not CustomerPriceGroup.Get(Code) then begin
+ CustomerPriceGroup.Init();
+ CustomerPriceGroup.Code := Code;
+ CustomerPriceGroup."Allow Line Disc." := true;
+ CustomerPriceGroup.Insert();
+ end;
- CustDiscGrp.Init();
- CustDiscGrp.Code := Code;
- CustDiscGrp.Insert();
+ if not CustDiscGrp.Get(Code) then begin
+ CustDiscGrp.Init();
+ CustDiscGrp.Code := Code;
+ CustDiscGrp.Insert();
+ end;
PriceListLine.Init();
PriceListLine."Asset Type" := PriceListLine."Asset Type"::Item;
+ PriceListLine."Asset No." := ItemNo;
PriceListLine."Product No." := ItemNo;
- PriceListLine."Unit Price" := Price;
+ PriceListLine."Price Type" := PriceListLine."Price Type"::Sale;
+ PriceListLine."Amount Type" := PriceListLine."Amount Type"::Discount;
PriceListLine."Source Type" := PriceListLine."Source Type"::"Customer Disc. Group";
PriceListLine."Source No." := CustDiscGrp.Code;
PriceListLine.Validate("Line Discount %", DiscountPerc);
+ PriceListLine.Status := PriceListLine.Status::Active;
+ PriceListLine.Insert();
+ end;
+
+ internal procedure CreateAllCustomerPriceList(Code: Code[10]; ItemNo: Code[20]; Price: Decimal; DiscountPerc: Decimal)
+ var
+ PriceListLine: Record "Price List Line";
+ begin
+ PriceListLine.Init();
+ PriceListLine."Asset Type" := PriceListLine."Asset Type"::Item;
+ PriceListLine."Asset No." := ItemNo;
+ PriceListLine."Product No." := ItemNo;
+ PriceListLine."Price Type" := PriceListLine."Price Type"::Sale;
+ PriceListLine."Amount Type" := PriceListLine."Amount Type"::Discount;
+ PriceListLine."Source Type" := PriceListLine."Source Type"::"All Customers";
+ PriceListLine.Validate("Line Discount %", DiscountPerc);
+ PriceListLine.Status := PriceListLine.Status::Active;
+ PriceListLine.Insert();
+ end;
+
+ internal procedure CreateCustomerPriceList(Code: Code[10]; ItemNo: Code[20]; Price: Decimal; DiscountPerc: Decimal; Cust: Record "Customer")
+ var
+ PriceListLine: Record "Price List Line";
+ begin
+ PriceListLine.Init();
+ PriceListLine."Asset Type" := PriceListLine."Asset Type"::Item;
+ PriceListLine."Asset No." := ItemNo;
+ PriceListLine."Product No." := ItemNo;
+ PriceListLine."Price Type" := PriceListLine."Price Type"::Sale;
+ PriceListLine."Amount Type" := PriceListLine."Amount Type"::Discount;
+ PriceListLine."Source Type" := PriceListLine."Source Type"::Customer;
+ PriceListLine."Source No." := Cust."No.";
+ PriceListLine.Validate("Line Discount %", DiscountPerc);
+ PriceListLine.Status := PriceListLine.Status::Active;
PriceListLine.Insert();
end;
#endif
diff --git a/Apps/W1/StatisticalAccounts/app/src/StatisticalAccountsJournal.Page.al b/Apps/W1/StatisticalAccounts/app/src/StatisticalAccountsJournal.Page.al
index ad7af82030..59bea6d28b 100644
--- a/Apps/W1/StatisticalAccounts/app/src/StatisticalAccountsJournal.Page.al
+++ b/Apps/W1/StatisticalAccounts/app/src/StatisticalAccountsJournal.Page.al
@@ -32,6 +32,7 @@ page 2633 "Statistical Accounts Journal"
begin
CurrPage.SaveRecord();
Rec.LookupBatchName(CurrentJnlBatchName, Rec);
+ CurrPage.Update(false)
end;
trigger OnValidate()
diff --git a/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al b/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al
index 3901fc9b9c..7a06b8eb9e 100644
--- a/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al
+++ b/Apps/W1/StatisticalAccounts/test/src/StatisticalAccountTest.Codeunit.al
@@ -11,6 +11,7 @@ codeunit 139683 "Statistical Account Test"
LibraryERM: Codeunit "Library - ERM";
LibraryDimension: Codeunit "Library - Dimension";
LibraryRandom: Codeunit "Library - Random";
+ LibraryUtility: Codeunit "Library - Utility";
Initialized: Boolean;
EMPLOYEESLbl: Label 'EMPLOYEES';
OFFICESPACELbl: Label 'OFFICESPACE';
@@ -458,6 +459,46 @@ codeunit 139683 "Statistical Account Test"
StatisticalAccountsJournal.Close();
end;
+ [Test]
+ [HandlerFunctions('StatAccJnlBatcheModalPageHandler')]
+ procedure SwitchBatchNameOnStatAccJnl()
+ var
+ StatisticalAccount: Record "Statistical Account";
+ StatAccJnlBatch: array[2] of Record "Statistical Acc. Journal Batch";
+ StatAccJnlLine: Record "Statistical Acc. Journal Line";
+ StatAccJnlPage: TestPage "Statistical Accounts Journal";
+ i: Integer;
+ begin
+ // [SCENARIO 544841] Switching the batch name on the Statistical Account Journal works correctly
+
+ Initialize();
+ CreateStatisticalAccount(StatisticalAccount);
+ // [GIVEN] Two statistical Account Journal Batches - "X" and "Y"
+ for i := 1 to ArrayLen(StatAccJnlBatch) do begin
+ StatAccJnlBatch[i].Validate(Name, LibraryUtility.GenerateGUID());
+ StatAccJnlBatch[i].Insert(true);
+ end;
+ // [GIVEN] Statistical Account Journal Line for batch "X"
+ StatAccJnlLine.Validate("Journal Batch Name", StatAccJnlBatch[1].Name);
+ StatAccJnlLine.Validate("Statistical Account No.", StatisticalAccount."No.");
+ StatAccJnlLine.Insert(true);
+
+ // [GIVEN] Statistical account opened for the batch "X"
+ StatAccJnlPage.OpenEdit();
+ StatAccJnlPage.CurrentJnlBatchName.SetValue(StatAccJnlBatch[1].Name);
+
+ LibraryVariableStorage.Enqueue(StatAccJnlBatch[2].Name); // for StatAccJnlBatcheModalPageHandler
+ // [WHEN] Stan switches the batch to "Y" via lookup
+ StatAccJnlPage.CurrentJnlBatchName.Lookup();
+
+ // [THEN] No statistical account journal lines shown for this batch
+ StatAccJnlPage.StatisticalAccountNo.AssertEquals('');
+ LibraryVariableStorage.AssertEmpty();
+
+ // Tear down
+ StatAccJnlPage.Close();
+ end;
+
local procedure SetupFinancialReport()
var
AccScheduleLine: Record "Acc. Schedule Line";
@@ -758,4 +799,11 @@ codeunit 139683 "Statistical Account Test"
until StatisticalAccountJournalLine.Next() = 0;
end;
+ [ModalPageHandler]
+ procedure StatAccJnlBatcheModalPageHandler(var StatBatch: TestPage "Statistical Acc. Journal Batch")
+ begin
+ StatBatch.Filter.SetFilter(Name, LibraryVariableStorage.DequeueText());
+ StatBatch.OK().Invoke();
+ end;
+
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al b/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al
index 36e5528835..9ff0359fd1 100644
--- a/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Certificate/SustItemCard.PageExt.al
@@ -6,11 +6,10 @@ pageextension 6222 "Sust. Item Card" extends "Item Card"
{
layout
{
- addbefore("Posting Details")
+ addafter(Warehouse)
{
group("Sustainability")
{
- Visible = Rec.Type = Rec.Type::Inventory;
Caption = 'Sustainability';
field("GHG Credit"; Rec."GHG Credit")
{
diff --git a/Apps/W1/Sustainability/app/src/Certificate/SustVendorCard.PageExt.al b/Apps/W1/Sustainability/app/src/Certificate/SustVendorCard.PageExt.al
index 43ac52e3f3..641de43088 100644
--- a/Apps/W1/Sustainability/app/src/Certificate/SustVendorCard.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Certificate/SustVendorCard.PageExt.al
@@ -11,11 +11,13 @@ pageextension 6221 "Sust. Vendor Card" extends "Vendor Card"
field("Sust. Cert. No."; Rec."Sust. Cert. No.")
{
ApplicationArea = Basic, Suite;
+ Importance = Additional;
ToolTip = 'Specifies the Sust. Cert. No. of Vendor';
}
field("Sust. Cert. Name"; Rec."Sust. Cert. Name")
{
ApplicationArea = Basic, Suite;
+ Importance = Additional;
ToolTip = 'Specifies the Sust. Cert. Name of Vendor';
}
}
diff --git a/Apps/W1/Sustainability/app/src/Certificate/SustainabilityCertificate.Table.al b/Apps/W1/Sustainability/app/src/Certificate/SustainabilityCertificate.Table.al
index aedf465e8b..1fb0253721 100644
--- a/Apps/W1/Sustainability/app/src/Certificate/SustainabilityCertificate.Table.al
+++ b/Apps/W1/Sustainability/app/src/Certificate/SustainabilityCertificate.Table.al
@@ -13,6 +13,7 @@ table 6222 "Sustainability Certificate"
{
DataClassification = CustomerContent;
Caption = 'No.';
+ NotBlank = true;
}
field(2; "Name"; Text[100])
{
diff --git a/Apps/W1/Sustainability/app/src/Emission/BatchUpdateCarbonEmission.Report.al b/Apps/W1/Sustainability/app/src/Emission/BatchUpdateCarbonEmission.Report.al
new file mode 100644
index 0000000000..ff55412f24
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/Emission/BatchUpdateCarbonEmission.Report.al
@@ -0,0 +1,104 @@
+namespace Microsoft.Sustainability.Emission;
+
+using Microsoft.Sustainability.Ledger;
+using Microsoft.Sustainability.Posting;
+
+report 6213 "Batch Update Carbon Emission"
+{
+ Caption = 'Batch Update Carbon Emission';
+ UsageCategory = Tasks;
+ ApplicationArea = Basic, Suite;
+ ProcessingOnly = true;
+ Permissions = tabledata "Sustainability Ledger Entry" = ri;
+
+ dataset
+ {
+ dataitem("Sustainability Ledger Entry"; "Sustainability Ledger Entry")
+ {
+ RequestFilterFields = "Posting Date", "Account No.", "Account Category";
+
+ trigger OnAfterGetRecord()
+ var
+ SustLedgEntry: Record "Sustainability Ledger Entry";
+ Counter: Integer;
+ CommitCounter: Integer;
+ begin
+ SustLedgEntry.CopyFilters("Sustainability Ledger Entry");
+ SustLedgEntry.SetFilter("Carbon Fee", '%1', 0);
+ if SustLedgEntry.FindSet() then begin
+ OpenDialog(SustLedgEntry, RecordCount);
+
+ repeat
+ UpdateCarbonEmission(SustLedgEntry);
+ UpdateDialog(CommitCounter, Counter);
+
+ TryCommitRecord(CommitCounter);
+ until SustLedgEntry.Next() = 0;
+
+ CloseDialog();
+ end;
+
+ ShowCompletionMsg(RecordCount, Counter);
+ CurrReport.Break();
+ end;
+ }
+ }
+
+ local procedure TryCommitRecord(var CommitCounter: Integer)
+ begin
+ if CommitCounter = 1000 then begin
+ Commit();
+ CommitCounter := 0;
+ end;
+ end;
+
+ local procedure UpdateDialog(var CommitCounter: Integer; var Counter: Integer)
+ begin
+ CommitCounter += 1;
+
+ if not GuiAllowed then
+ exit;
+
+ Counter += 1;
+ Window.Update(1, Round(Counter / RecordCount * 10000, 1));
+ end;
+
+ local procedure OpenDialog(var SustLedgEntry: Record "Sustainability Ledger Entry"; var RecordCount: Integer)
+ begin
+ if not GuiAllowed() then
+ exit;
+
+ RecordCount := SustLedgEntry.Count();
+ Window.Open(ProcessBarMsg);
+ end;
+
+ local procedure CloseDialog()
+ begin
+ if not GuiAllowed() then
+ exit;
+
+ Window.Close();
+ end;
+
+ local procedure ShowCompletionMsg(RecordCount: Integer; Counter: Integer)
+ begin
+ if not GuiAllowed() then
+ exit;
+
+ Message(StrSubstNo(UpdateCompleteMsg, Counter, RecordCount));
+ end;
+
+ local procedure UpdateCarbonEmission(var NewSustLedgEntry: Record "Sustainability Ledger Entry")
+ var
+ SustainabilityPostMgmt: Codeunit "Sustainability Post Mgt";
+ begin
+ SustainabilityPostMgmt.UpdateCarbonFeeEmission(NewSustLedgEntry);
+ NewSustLedgEntry.Modify(true);
+ end;
+
+ var
+ Window: Dialog;
+ ProcessBarMsg: Label 'Processing: @1@@@@@@@', Comment = '1 - overall progress';
+ UpdateCompleteMsg: Label 'Carbon Fee Emission updated on %1 out of %2 entries.', Comment = '%1 - Records Updated, %2 - Total Record Count';
+ RecordCount: Integer;
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Emission/EmissionFee.Table.al b/Apps/W1/Sustainability/app/src/Emission/EmissionFee.Table.al
new file mode 100644
index 0000000000..81ddcfaf30
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/Emission/EmissionFee.Table.al
@@ -0,0 +1,100 @@
+namespace Microsoft.Sustainability.Emission;
+
+using Microsoft.Foundation.Address;
+using Microsoft.Inventory.Location;
+using Microsoft.Sustainability.Account;
+
+table 6226 "Emission Fee"
+{
+ Caption = 'Emission Fee';
+ DataClassification = CustomerContent;
+ LookupPageId = "Emission Fees";
+ DrillDownPageId = "Emission Fees";
+
+ fields
+ {
+ field(1; "Emission Type"; Enum "Emission Type")
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Emission Type';
+
+ trigger OnValidate()
+ begin
+ if Rec."Emission Type" = Rec."Emission Type"::CO2 then
+ Rec.Validate("Carbon Equivalent Factor", 1);
+
+ if Rec."Emission Type" <> Rec."Emission Type"::CO2 then
+ Rec.TestField("Carbon Fee", 0);
+ end;
+ }
+ field(3; "Carbon Fee"; Decimal)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Carbon Fee';
+ DecimalPlaces = 2 : 5;
+
+ trigger OnValidate()
+ begin
+ if (Rec."Carbon Fee" <> 0) then
+ Rec.TestField("Emission Type", Rec."Emission Type"::CO2);
+ end;
+ }
+ field(4; "Carbon Equivalent Factor"; Decimal)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Carbon Equivalent Factor';
+ DecimalPlaces = 2 : 5;
+ }
+ field(5; "Starting Date"; Date)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Starting Date';
+
+ trigger OnValidate()
+ begin
+ if (Rec."Starting Date" > Rec."Ending Date") and (Rec."Ending Date" <> 0D) then
+ Error(InvalidStartDateErr, Rec.FieldCaption("Starting Date"), Rec.FieldCaption("Ending Date"));
+ end;
+ }
+ field(6; "Ending Date"; Date)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Ending Date';
+
+ trigger OnValidate()
+ begin
+ if CurrFieldNo = 0 then
+ exit;
+
+ Rec.Validate("Starting Date");
+ end;
+ }
+ field(7; "Scope Type"; Enum "Emission Scope")
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Scope Type';
+ }
+ field(8; "Responsibility Center"; Code[10])
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Responsibility Center';
+ TableRelation = "Responsibility Center".Code;
+ }
+ field(35; "Country/Region Code"; Code[10])
+ {
+ Caption = 'Country/Region Code';
+ TableRelation = "Country/Region";
+ }
+ }
+
+ keys
+ {
+ key(Key1; "Emission Type", "Scope Type", "Starting Date", "Ending Date", "Country/Region Code", "Responsibility Center")
+ {
+ Clustered = true;
+ }
+ }
+
+ var
+ InvalidStartDateErr: Label '%1 cannot be after %2', Comment = '%1 - Starting Date,%2 - Ending Date';
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Emission/EmissionFees.Page.al b/Apps/W1/Sustainability/app/src/Emission/EmissionFees.Page.al
new file mode 100644
index 0000000000..6699ad3d3a
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/Emission/EmissionFees.Page.al
@@ -0,0 +1,62 @@
+namespace Microsoft.Sustainability.Emission;
+
+page 6245 "Emission Fees"
+{
+ PageType = List;
+ ApplicationArea = All;
+ UsageCategory = Lists;
+ SourceTable = "Emission Fee";
+ Caption = 'Emission Fees';
+ DelayedInsert = true;
+
+ layout
+ {
+ area(Content)
+ {
+ repeater(GroupName)
+ {
+ field("Emission Type"; Rec."Emission Type")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies gas emission type.';
+ }
+ field("Scope Type"; Rec."Scope Type")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the value of the Scope Type field.';
+ }
+ field("Starting Date"; Rec."Starting Date")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the value of the Starting Date field.';
+ }
+ field("Ending Date"; Rec."Ending Date")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the value of the Ending Date field.';
+ }
+ field("Country/Region Code"; Rec."Country/Region Code")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the value of the Country/Region Code field.';
+ }
+ field("Responsibility Center"; Rec."Responsibility Center")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the value of the Responsibility Center field.';
+ }
+ field("Carbon Fee"; Rec."Carbon Fee")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies internal carbon fee that a company charges itself for each unit of CO2 equivalent that it emits.';
+ }
+ field("Carbon Equivalent Factor"; Rec."Carbon Equivalent Factor")
+ {
+ Editable = not (Rec."Emission Type" = Rec."Emission Type"::CO2);
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies the coefficient that converts the impact of various greenhouse gases into the equivalent amount of carbon dioxide based on their global warming potential.';
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Emission/EmissionType.Enum.al b/Apps/W1/Sustainability/app/src/Emission/EmissionType.Enum.al
new file mode 100644
index 0000000000..30eccec95a
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/Emission/EmissionType.Enum.al
@@ -0,0 +1,19 @@
+namespace Microsoft.Sustainability.Emission;
+
+enum 6216 "Emission Type"
+{
+ Extensible = true;
+
+ value(0; " ")
+ {
+ }
+ value(1; CO2)
+ {
+ }
+ value(2; CH4)
+ {
+ }
+ value(3; N2O)
+ {
+ }
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Journal/SustainabilityJnlLine.Table.al b/Apps/W1/Sustainability/app/src/Journal/SustainabilityJnlLine.Table.al
index 13eef2a6a5..9d307dfc2f 100644
--- a/Apps/W1/Sustainability/app/src/Journal/SustainabilityJnlLine.Table.al
+++ b/Apps/W1/Sustainability/app/src/Journal/SustainabilityJnlLine.Table.al
@@ -101,7 +101,6 @@ table 6214 "Sustainability Jnl. Line"
field(10; "Account Subcategory"; Code[20])
{
Caption = 'Account Subcategory';
- Editable = false;
TableRelation = "Sustain. Account Subcategory".Code where("Category Code" = field("Account Category"));
}
field(11; Description; Text[100])
diff --git a/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntries.Page.al b/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntries.Page.al
index 015bf04389..baff0b918c 100644
--- a/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntries.Page.al
+++ b/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntries.Page.al
@@ -123,9 +123,15 @@ page 6220 "Sustainability Ledger Entries"
{
ToolTip = 'Specifies the emission N2O of the entry.';
}
- field("Emission Fee"; Rec."Emission Fee")
+ field("CO2e Emission"; Rec."CO2e Emission")
{
- ToolTip = 'Specifies the emission Fee of the entry.';
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies total carbon dioxide and other equivalents emission expressing different greenhouse gases impact in terms of the amount of CO2 that would create the same effect.';
+ }
+ field("Carbon Fee"; Rec."Carbon Fee")
+ {
+ ApplicationArea = Basic, Suite;
+ ToolTip = 'Specifies internal carbon fee that a company charges itself for each unit of CO2 equivalent that it emits.';
}
field("Country/Region Code"; Rec."Country/Region Code")
{
diff --git a/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntry.Table.al b/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntry.Table.al
index 2c13b3fecf..d0a344399a 100644
--- a/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntry.Table.al
+++ b/Apps/W1/Sustainability/app/src/Ledger/SustainabilityLedgerEntry.Table.al
@@ -150,6 +150,7 @@ table 6216 "Sustainability Ledger Entry"
{
Caption = 'Dimension Set ID';
TableRelation = "Dimension Set Entry";
+
trigger OnLookup()
begin
ShowDimensions();
@@ -181,6 +182,18 @@ table 6216 "Sustainability Ledger Entry"
Caption = 'Reason Code';
TableRelation = "Reason Code";
}
+ field(32; "CO2e Emission"; Decimal)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'CO2e Emission';
+ DecimalPlaces = 2 : 5;
+ }
+ field(33; "Carbon Fee"; Decimal)
+ {
+ DataClassification = CustomerContent;
+ Caption = 'Carbon Fee';
+ DecimalPlaces = 2 : 5;
+ }
field(5146; "Emission Scope"; Enum "Emission Scope")
{
Caption = 'Emission Scope';
diff --git a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityAdmin.permissionset.al b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityAdmin.permissionset.al
index 1186e2b316..f4c652da80 100644
--- a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityAdmin.permissionset.al
+++ b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityAdmin.permissionset.al
@@ -1,7 +1,12 @@
namespace Microsoft.Sustainability;
-using Microsoft.Sustainability.Setup;
using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.Certificate;
+using Microsoft.Sustainability.Emission;
+using Microsoft.Sustainability.FinancialReporting;
+using Microsoft.Sustainability.RoleCenters;
+using Microsoft.Sustainability.Scorecard;
+using Microsoft.Sustainability.Setup;
permissionset 6212 "Sustainability Admin"
{
@@ -14,5 +19,14 @@ permissionset 6212 "Sustainability Admin"
tabledata "Sustainability Setup" = M,
tabledata "Sustainability Account" = IMD,
tabledata "Sustain. Account Category" = IMD,
- tabledata "Sustain. Account Subcategory" = IMD;
+ tabledata "Sustain. Account Subcategory" = IMD,
+ tabledata "Emission Fee" = IMD,
+ tabledata "Sust. Account (Analysis View)" = IMD,
+ tabledata "Sust. Certificate Area" = IMD,
+ tabledata "Sust. Certificate Standard" = IMD,
+ tabledata "Sustainability Certificate" = IMD,
+ tabledata "Sustainability Cue" = IMD,
+ tabledata "Sustainability Goal" = IMD,
+ tabledata "Sustainability Goal Cue" = IMD,
+ tabledata "Sustainability Scorecard" = IMD;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityObjects.permissionset.al b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityObjects.permissionset.al
index 8d10e5f0c7..7feef74dda 100644
--- a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityObjects.permissionset.al
+++ b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityObjects.permissionset.al
@@ -1,13 +1,18 @@
namespace Microsoft.Sustainability;
+using Microsoft.API.V1;
+using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.Calculation;
+using Microsoft.Sustainability.Certificate;
+using Microsoft.Sustainability.Emission;
+using Microsoft.Sustainability.FinancialReporting;
using Microsoft.Sustainability.Journal;
using Microsoft.Sustainability.Ledger;
-using Microsoft.Sustainability.Calculation;
using Microsoft.Sustainability.Posting;
-using Microsoft.Sustainability.Account;
using Microsoft.Sustainability.Reports;
+using Microsoft.Sustainability.RoleCenters;
+using Microsoft.Sustainability.Scorecard;
using Microsoft.Sustainability.Setup;
-using Microsoft.API.V1;
permissionset 6210 "Sustainability - Objects"
{
@@ -24,6 +29,15 @@ permissionset 6210 "Sustainability - Objects"
table "Sustainability Jnl. Line" = X,
table "Sustainability Ledger Entry" = X,
table "Sustainability Setup" = X,
+ table "Emission Fee" = X,
+ table "Sust. Account (Analysis View)" = X,
+ table "Sust. Certificate Area" = X,
+ table "Sust. Certificate Standard" = X,
+ table "Sustainability Certificate" = X,
+ table "Sustainability Cue" = X,
+ table "Sustainability Goal" = X,
+ table "Sustainability Goal Cue" = X,
+ table "Sustainability Scorecard" = X,
page "Chart of Sustain. Accounts" = X,
page "Collect Amount from G/L Entry" = X,
page "G/L Accounts Subform" = X,
@@ -46,6 +60,20 @@ permissionset 6210 "Sustainability - Objects"
page "Sust. Acc. Subcategory" = X,
page "Sustainability Journal Line" = X,
page "Sustainability Ledg. Entries" = X,
+ page "Emission Fees" = X,
+ page "Emission Scope Ratio Chart" = X,
+ page "Headline Sustainability RC" = X,
+ page "Sust. Accs. (Analysis View)" = X,
+ page "Sust. Certificate Areas" = X,
+ page "Sust. Certificate Card" = X,
+ page "Sust. Certificate Standards" = X,
+ page "Sustainability Activities" = X,
+ page "Sustainability Certificates" = X,
+ page "Sustainability Goal Cue" = X,
+ page "Sustainability Goals" = X,
+ page "Sustainability Manager RC" = X,
+ page "Sustainability Scorecard" = X,
+ page "Sustainability Scorecards" = X,
codeunit "Sustainability Account Mgt." = X,
codeunit "Sustainability Journal Mgt." = X,
codeunit "Sustainability Jnl.-Post" = X,
@@ -56,7 +84,21 @@ permissionset 6210 "Sustainability - Objects"
codeunit "Sustainability Calc. Mgt." = X,
codeunit "Sustain. Jnl. Errors Mgt." = X,
codeunit "Check Sust. Jnl. Line. Backgr." = X,
+ codeunit "Acc. Sch. Line Mgmt. Helper" = X,
+ codeunit "Acc. Schedule Line Subscribers" = X,
+ codeunit "Analysis View Entry Subscriber" = X,
+ codeunit AnalysisViewEntryToSustEntries = X,
+ codeunit "Compute Sust. Goal Cue" = X,
+ codeunit "Install Sustainability Setup" = X,
+ codeunit "RC Headline Page Sust." = X,
+ codeunit "Sust. Acc. Analysis View Mgt." = X,
+ codeunit "Sust. Certificate Subscribers" = X,
+ codeunit "Sust. Preview Post Instance" = X,
+ codeunit "Sust. Preview Post. Subscriber" = X,
+ codeunit "Sust. Preview Posting Handler" = X,
+ codeunit "Sustainability Chart Mgmt." = X,
report "Emission By Category" = X,
report "Emission Per Facility" = X,
- report "Total Emissions" = X;
+ report "Total Emissions" = X,
+ report "Batch Update Carbon Emission" = X;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityRead.permissionset.al b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityRead.permissionset.al
index a11d6069e9..9992d056d2 100644
--- a/Apps/W1/Sustainability/app/src/Permissions/SustainabilityRead.permissionset.al
+++ b/Apps/W1/Sustainability/app/src/Permissions/SustainabilityRead.permissionset.al
@@ -1,8 +1,13 @@
namespace Microsoft.Sustainability;
+using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.Certificate;
+using Microsoft.Sustainability.Emission;
+using Microsoft.Sustainability.FinancialReporting;
using Microsoft.Sustainability.Journal;
using Microsoft.Sustainability.Ledger;
-using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.RoleCenters;
+using Microsoft.Sustainability.Scorecard;
using Microsoft.Sustainability.Setup;
permissionset 6211 "Sustainability Read"
@@ -21,5 +26,14 @@ permissionset 6211 "Sustainability Read"
tabledata "Sustainability Jnl. Batch" = R,
tabledata "Sustainability Jnl. Line" = R,
tabledata "Sustainability Ledger Entry" = R,
- tabledata "Sustainability Setup" = R;
+ tabledata "Sustainability Setup" = R,
+ tabledata "Emission Fee" = R,
+ tabledata "Sust. Account (Analysis View)" = R,
+ tabledata "Sust. Certificate Area" = R,
+ tabledata "Sust. Certificate Standard" = R,
+ tabledata "Sustainability Certificate" = R,
+ tabledata "Sustainability Cue" = R,
+ tabledata "Sustainability Goal" = R,
+ tabledata "Sustainability Goal Cue" = R,
+ tabledata "Sustainability Scorecard" = R;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al b/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al
index bdc4b9a973..201a3b0d22 100644
--- a/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al
+++ b/Apps/W1/Sustainability/app/src/Posting/SustainabilityPostMgt.Codeunit.al
@@ -1,8 +1,9 @@
namespace Microsoft.Sustainability.Posting;
+using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.Emission;
using Microsoft.Sustainability.Journal;
using Microsoft.Sustainability.Ledger;
-using Microsoft.Sustainability.Account;
codeunit 6212 "Sustainability Post Mgt"
{
@@ -25,6 +26,7 @@ codeunit 6212 "Sustainability Post Mgt"
CopyDateFromAccountSubCategory(SustainabilityLedgerEntry, SustainabilityJnlLine."Account Category", SustainabilityJnlLine."Account Subcategory");
SustainabilityLedgerEntry.Validate("User ID", CopyStr(UserId(), 1, 50));
+ UpdateCarbonFeeEmission(SustainabilityLedgerEntry);
SustainabilityLedgerEntry.Insert(true);
end;
@@ -37,6 +39,66 @@ codeunit 6212 "Sustainability Post Mgt"
SustainabilityJnlLine.FilterGroup(0);
end;
+ procedure UpdateCarbonFeeEmission(var SustainabilityLedgerEntry: Record "Sustainability Ledger Entry")
+ var
+ AccountCategory: Record "Sustain. Account Category";
+ ScopeType: Enum "Emission Scope";
+ begin
+ if AccountCategory.Get(SustainabilityLedgerEntry."Account Category") then
+ ScopeType := AccountCategory."Emission Scope";
+
+ UpdateCarbonFeeEmissionValues(SustainabilityLedgerEntry, ScopeType);
+ end;
+
+ local procedure UpdateCarbonFeeEmissionValues(
+ var SustainabilityLedgerEntry: Record "Sustainability Ledger Entry";
+ ScopeType: Enum "Emission Scope"): Decimal
+ var
+ EmissionFee: Record "Emission Fee";
+ CO2eEmission: Decimal;
+ CarbonFee: Decimal;
+ CO2Factor: Decimal;
+ N2OFactor: Decimal;
+ CH4Factor: Decimal;
+ EmissionCarbonFee: Decimal;
+ begin
+ EmissionFee.SetFilter("Scope Type", '%1|%2', ScopeType, ScopeType::" ");
+ EmissionFee.SetFilter("Starting Date", '<=%1|%2', SustainabilityLedgerEntry."Posting Date", 0D);
+ EmissionFee.SetFilter("Ending Date", '>=%1|%2', SustainabilityLedgerEntry."Posting Date", 0D);
+ EmissionFee.SetFilter("Country/Region Code", '%1|%2', SustainabilityLedgerEntry."Country/Region Code", '');
+
+ if SustainabilityLedgerEntry."Emission CO2" <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::CO2) then begin
+ CO2Factor := EmissionFee."Carbon Equivalent Factor";
+ EmissionCarbonFee := EmissionFee."Carbon Fee";
+ end;
+
+ if SustainabilityLedgerEntry."Emission N2O" <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::N2O) then begin
+ N2OFactor := EmissionFee."Carbon Equivalent Factor";
+ EmissionCarbonFee += EmissionFee."Carbon Fee";
+ end;
+
+ if SustainabilityLedgerEntry."Emission CH4" <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::CH4) then begin
+ CH4Factor := EmissionFee."Carbon Equivalent Factor";
+ EmissionCarbonFee += EmissionFee."Carbon Fee";
+ end;
+
+ CO2eEmission := (SustainabilityLedgerEntry."Emission CO2" * CO2Factor) + (SustainabilityLedgerEntry."Emission N2O" * N2OFactor) + (SustainabilityLedgerEntry."Emission CH4" * CH4Factor);
+ CarbonFee := CO2eEmission * EmissionCarbonFee;
+
+ SustainabilityLedgerEntry."CO2e Emission" := CO2eEmission;
+ SustainabilityLedgerEntry."Carbon Fee" := CarbonFee;
+ end;
+
+ local procedure FindEmissionFeeForEmissionType(var EmissionFee: Record "Emission Fee"; EmissionType: Enum "Emission Type"): Boolean
+ begin
+ EmissionFee.SetRange("Emission Type", EmissionType);
+ if EmissionFee.FindLast() then
+ exit(true);
+ end;
+
internal procedure GetStartPostingProgressMessage(): Text
begin
exit(PostingSustainabilityJournalLbl);
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPstdCrMemoSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPstdCrMemoSubform.PageExt.al
index bcf1fa47b5..3e5a4bfeb4 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPstdCrMemoSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPstdCrMemoSubform.PageExt.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Purchase;
using Microsoft.Purchases.History;
+using Microsoft.Sustainability.Setup;
pageextension 6213 "Sust. Pstd Cr. Memo. Subform" extends "Posted Purch. Cr. Memo Subform"
{
@@ -10,6 +11,7 @@ pageextension 6213 "Sust. Pstd Cr. Memo. Subform" extends "Posted Purch. Cr. Mem
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6213 "Sust. Pstd Cr. Memo. Subform" extends "Posted Purch. Cr. Mem
{
field("Emission CO2"; Rec."Emission CO2")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 field.';
}
field("Emission CH4"; Rec."Emission CH4")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 field.';
}
field("Emission N2O"; Rec."Emission N2O")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPstdPurchInvSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPstdPurchInvSubform.PageExt.al
index 10a3459746..5c24698af1 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPstdPurchInvSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPstdPurchInvSubform.PageExt.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Purchase;
using Microsoft.Purchases.History;
+using Microsoft.Sustainability.Setup;
pageextension 6212 "Sust. Pstd Purch. Inv. Subform" extends "Posted Purch. Invoice Subform"
{
@@ -10,6 +11,7 @@ pageextension 6212 "Sust. Pstd Purch. Inv. Subform" extends "Posted Purch. Invoi
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6212 "Sust. Pstd Purch. Inv. Subform" extends "Posted Purch. Invoi
{
field("Emission CO2"; Rec."Emission CO2")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 field.';
}
field("Emission CH4"; Rec."Emission CH4")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 field.';
}
field("Emission N2O"; Rec."Emission N2O")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPurchCrMemoSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPurchCrMemoSubform.PageExt.al
index 7d96150ee1..af2f7f838c 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPurchCrMemoSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPurchCrMemoSubform.PageExt.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Purchase;
using Microsoft.Purchases.Document;
+using Microsoft.Sustainability.Setup;
pageextension 6215 "Sust. Purch. Cr. Memo Subform" extends "Purch. Cr. Memo Subform"
{
@@ -10,6 +11,7 @@ pageextension 6215 "Sust. Purch. Cr. Memo Subform" extends "Purch. Cr. Memo Subf
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6215 "Sust. Purch. Cr. Memo Subform" extends "Purch. Cr. Memo Subf
{
field("Emission CO2 Per Unit"; Rec."Emission CO2 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 Per Unit field.';
}
field("Emission CH4 Per Unit"; Rec."Emission CH4 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 Per Unit field.';
}
field("Emission N2O Per Unit"; Rec."Emission N2O Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O Per Unit field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPurchInvSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPurchInvSubform.PageExt.al
index 0e2390cb97..f941162c05 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPurchInvSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPurchInvSubform.PageExt.al
@@ -1,5 +1,6 @@
namespace Microsoft.Sustainability.Purchase;
+using Microsoft.Sustainability.Setup;
using Microsoft.Purchases.Document;
pageextension 6214 "Sust. Purch. Inv. Subform" extends "Purch. Invoice Subform"
@@ -10,6 +11,7 @@ pageextension 6214 "Sust. Purch. Inv. Subform" extends "Purch. Invoice Subform"
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6214 "Sust. Purch. Inv. Subform" extends "Purch. Invoice Subform"
{
field("Emission CO2 Per Unit"; Rec."Emission CO2 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 Per Unit field.';
}
field("Emission CH4 Per Unit"; Rec."Emission CH4 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 Per Unit field.';
}
field("Emission N2O Per Unit"; Rec."Emission N2O Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O Per Unit field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPurchOrderSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPurchOrderSubform.PageExt.al
index f85dd0f420..abbcd3ee44 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPurchOrderSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPurchOrderSubform.PageExt.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Purchase;
using Microsoft.Purchases.Document;
+using Microsoft.Sustainability.Setup;
pageextension 6211 "Sust. Purch. Order Subform" extends "Purchase Order Subform"
{
@@ -10,6 +11,7 @@ pageextension 6211 "Sust. Purch. Order Subform" extends "Purchase Order Subform"
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6211 "Sust. Purch. Order Subform" extends "Purchase Order Subform"
{
field("Emission CO2 Per Unit"; Rec."Emission CO2 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 Per Unit field.';
}
field("Emission CH4 Per Unit"; Rec."Emission CH4 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 Per Unit field.';
}
field("Emission N2O Per Unit"; Rec."Emission N2O Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O Per Unit field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPurchRetOrdSubform.PageExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustPurchRetOrdSubform.PageExt.al
index 3e50a5ad03..e7710cc7d9 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPurchRetOrdSubform.PageExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPurchRetOrdSubform.PageExt.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Purchase;
using Microsoft.Purchases.Document;
+using Microsoft.Sustainability.Setup;
pageextension 6216 "Sust. Purch. Ret. Ord. Subform" extends "Purchase Return Order Subform"
{
@@ -10,6 +11,7 @@ pageextension 6216 "Sust. Purch. Ret. Ord. Subform" extends "Purchase Return Ord
{
field("Sust. Account No."; Rec."Sust. Account No.")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Sustainability Account No. field.';
}
@@ -18,19 +20,39 @@ pageextension 6216 "Sust. Purch. Ret. Ord. Subform" extends "Purchase Return Ord
{
field("Emission CO2 Per Unit"; Rec."Emission CO2 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CO2 Per Unit field.';
}
field("Emission CH4 Per Unit"; Rec."Emission CH4 Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission CH4 Per Unit field.';
}
field("Emission N2O Per Unit"; Rec."Emission N2O Per Unit")
{
+ Visible = SustainabilityVisible;
ApplicationArea = Basic, Suite;
ToolTip = 'Specifies the value of the Emission N2O Per Unit field.';
}
}
}
+
+ trigger OnOpenPage()
+ begin
+ VisibleSustainabilityControls();
+ end;
+
+ local procedure VisibleSustainabilityControls()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+
+ SustainabilityVisible := SustainabilitySetup."Use Emissions In Purch. Doc.";
+ end;
+
+ var
+ SustainabilityVisible: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustPurchaseSubscriber.Codeunit.al b/Apps/W1/Sustainability/app/src/Purchase/SustPurchaseSubscriber.Codeunit.al
index a13d447dc9..0c1e869c51 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustPurchaseSubscriber.Codeunit.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustPurchaseSubscriber.Codeunit.al
@@ -78,32 +78,45 @@ codeunit 6225 "Sust. Purchase Subscriber"
var
SustainabilityJnlLine: Record "Sustainability Jnl. Line";
SustainabilityPostMgt: Codeunit "Sustainability Post Mgt";
+ GHGCredit: Boolean;
+ Sign: Integer;
CO2ToPost: Decimal;
CH4ToPost: Decimal;
N2OToPost: Decimal;
begin
+ GHGCredit := IfGHGCreditLine(PurchaseLine);
+
+ if GHGCredit then begin
+ PurchaseLine.TestField("Emission CH4 Per Unit", 0);
+ PurchaseLine.TestField("Emission N2O Per Unit", 0);
+ end;
+
+ Sign := GetPostingSign(PurchaseHeader, GHGCredit);
+
CO2ToPost := PurchaseLine."Emission CO2" - PurchaseLine."Posted Emission CO2";
CH4ToPost := PurchaseLine."Emission CH4" - PurchaseLine."Posted Emission CH4";
N2OToPost := PurchaseLine."Emission N2O" - PurchaseLine."Posted Emission N2O";
+ CO2ToPost := CO2ToPost * Sign;
+ CH4ToPost := CH4ToPost * Sign;
+ N2OToPost := N2OToPost * Sign;
+
if not CanPostSustainabilityJnlLine(PurchaseHeader, PurchaseLine, CO2ToPost, CH4ToPost, N2OToPost) then
exit;
- if PurchaseHeader."Document Type" in [PurchaseHeader."Document Type"::"Credit Memo", PurchaseHeader."Document Type"::"Return Order"] then begin
- CO2ToPost := -CO2ToPost;
- CH4ToPost := -CH4ToPost;
- N2OToPost := -N2OToPost;
- end;
-
SustainabilityJnlLine.Init();
SustainabilityJnlLine."Journal Template Name" := PurchaseHeader."Journal Templ. Name";
SustainabilityJnlLine."Journal Batch Name" := '';
SustainabilityJnlLine."Source Code" := SrcCode;
SustainabilityJnlLine.Validate("Posting Date", PurchaseHeader."Posting Date");
- if PurchaseHeader."Document Type" in [PurchaseHeader."Document Type"::"Credit Memo", PurchaseHeader."Document Type"::"Return Order"] then
- SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"Credit Memo")
+
+ if GHGCredit then
+ SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"GHG Credit")
else
- SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::Invoice);
+ if PurchaseHeader."Document Type" in [PurchaseHeader."Document Type"::"Credit Memo", PurchaseHeader."Document Type"::"Return Order"] then
+ SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"Credit Memo")
+ else
+ SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::Invoice);
SustainabilityJnlLine.Validate("Document No.", GenJnlLineDocNo);
SustainabilityJnlLine.Validate("Account No.", PurchaseLine."Sust. Account No.");
@@ -118,49 +131,41 @@ codeunit 6225 "Sust. Purchase Subscriber"
SustainabilityJnlLine.Validate("Emission CO2", CO2ToPost);
SustainabilityJnlLine.Validate("Emission CH4", CH4ToPost);
SustainabilityJnlLine.Validate("Emission N2O", N2OToPost);
+ SustainabilityJnlLine.Validate("Country/Region Code", PurchaseHeader."Buy-from Country/Region Code");
SustainabilityPostMgt.InsertLedgerEntry(SustainabilityJnlLine);
+ end;
- PostCarbonCreditSustainabilityLine(PurchaseLine, SustainabilityJnlLine);
+ local procedure GetPostingSign(PurchaseHeader: Record "Purchase Header"; GHGCredit: Boolean): Integer
+ var
+ Sign: Integer;
+ begin
+ Sign := 1;
+
+ case PurchaseHeader."Document Type" of
+ PurchaseHeader."Document Type"::"Credit Memo", PurchaseHeader."Document Type"::"Return Order":
+ if not GHGCredit then
+ Sign := -1;
+ else
+ if GHGCredit then
+ Sign := -1;
+ end;
+
+ exit(Sign);
end;
- local procedure PostCarbonCreditSustainabilityLine(PurchaseLine: Record "Purchase Line"; FromSustainabilityJnlLine: Record "Sustainability Jnl. Line")
+ local procedure IfGHGCreditLine(PurchaseLine: Record "Purchase Line"): Boolean
var
- PurchaseLine1: Record "Purchase Line";
- SustainabilityJnlLine: Record "Sustainability Jnl. Line";
Item: Record Item;
- SustainabilityPostMgt: Codeunit "Sustainability Post Mgt";
- CO2Emission: Decimal;
- EmissionFee: Decimal;
begin
if PurchaseLine.Type <> PurchaseLine.Type::Item then
- exit;
-
- if not Item.Get(PurchaseLine."No.") then
- exit;
-
- if not Item."GHG Credit" then
- exit;
-
- // To ensure that Carbon Credit is posted with full Amount and Quantity.
- if not PurchaseLine1.Get(PurchaseLine."Document Type", PurchaseLine."Document No.", PurchaseLine."Line No.") then
- exit;
+ exit(false);
- EmissionFee := PurchaseLine1."Line Amount";
- CO2Emission := PurchaseLine1.Quantity * Item."Carbon Credit Per UOM";
+ if PurchaseLine."No." = '' then
+ exit(false);
- if PurchaseLine."Document Type" in [PurchaseLine."Document Type"::Order, PurchaseLine."Document Type"::Invoice] then begin
- CO2Emission := -CO2Emission;
- EmissionFee := -EmissionFee;
- end;
+ Item.Get(PurchaseLine."No.");
- SustainabilityJnlLine.Init();
- SustainabilityJnlLine := FromSustainabilityJnlLine;
- SustainabilityJnlLine.Validate("Document Type", SustainabilityJnlLine."Document Type"::"GHG Credit");
- SustainabilityJnlLine.Validate("Emission CO2", CO2Emission);
- SustainabilityJnlLine.Validate("Emission CH4", 0);
- SustainabilityJnlLine.Validate("Emission N2O", 0);
- SustainabilityJnlLine.Validate("Emission Fee", EmissionFee);
- SustainabilityPostMgt.InsertLedgerEntry(SustainabilityJnlLine);
+ exit(Item."GHG Credit");
end;
local procedure CanPostSustainabilityJnlLine(PurchaseHeader: Record "Purchase Header"; PurchaseLine: Record "Purchase Line"; CO2ToPost: Decimal; CH4ToPost: Decimal; N2OToPost: Decimal): Boolean
diff --git a/Apps/W1/Sustainability/app/src/Purchase/SustainabilityPurchLine.TableExt.al b/Apps/W1/Sustainability/app/src/Purchase/SustainabilityPurchLine.TableExt.al
index 06754b6ad8..b7be6ea2a9 100644
--- a/Apps/W1/Sustainability/app/src/Purchase/SustainabilityPurchLine.TableExt.al
+++ b/Apps/W1/Sustainability/app/src/Purchase/SustainabilityPurchLine.TableExt.al
@@ -3,6 +3,7 @@ namespace Microsoft.Sustainability.Purchase;
using Microsoft.Sustainability.Account;
using Microsoft.Sustainability.Setup;
using Microsoft.Purchases.Document;
+using Microsoft.Inventory.Item;
tableextension 6211 "Sustainability Purch. Line" extends "Purchase Line"
{
@@ -37,6 +38,9 @@ tableextension 6211 "Sustainability Purch. Line" extends "Purchase Line"
end;
CreateDimFromDefaultDim(FieldNo(Rec."Sust. Account No."));
+
+ if Rec.Type = Rec.Type::Item then
+ UpdateCarbonCreditInformation();
end;
}
field(6211; "Sust. Account Name"; Text[100])
@@ -221,11 +225,22 @@ tableextension 6211 "Sustainability Purch. Line" extends "Purchase Line"
end;
local procedure ValidateEmissionPrerequisite(PurchaseLine: Record "Purchase Line"; CurrentFieldNo: Integer)
+ var
+ Item: Record Item;
begin
case CurrentFieldNo of
- PurchaseLine.FieldNo("Emission CO2 Per Unit"),
- PurchaseLine.FieldNo("Emission CH4 Per Unit"),
- PurchaseLine.FieldNo("Emission N2O Per Unit"):
+ PurchaseLine.FieldNo("Emission N2O Per Unit"),
+ PurchaseLine.FieldNo("Emission CH4 Per Unit"):
+ begin
+ PurchaseLine.TestField("Sust. Account No.");
+
+ if (PurchaseLine.Type = PurchaseLine.Type::Item) and (PurchaseLine."No." <> '') then begin
+ Item.Get(PurchaseLine."No.");
+ if Item."GHG Credit" then
+ Item.TestField("GHG Credit", false);
+ end;
+ end;
+ PurchaseLine.FieldNo("Emission CO2 Per Unit"):
PurchaseLine.TestField("Sust. Account No.");
PurchaseLine.FieldNo("Sust. Account No."),
PurchaseLine.FieldNo("Sust. Account Category"),
@@ -251,6 +266,19 @@ tableextension 6211 "Sustainability Purch. Line" extends "Purchase Line"
Error(EmissionShouldNotBeLessThanPostedErr, PurchLine."Emission N2O", PurchLine."Posted Emission N2O", PurchLine."Document Type", PurchLine."Document No.", PurchLine."Line No.");
end;
+ local procedure UpdateCarbonCreditInformation()
+ var
+ Item: Record Item;
+ begin
+ if not Item.Get(Rec."No.") then
+ exit;
+
+ if not Item."GHG Credit" then
+ exit;
+
+ Rec.Validate("Emission CO2 Per Unit", Item."Carbon Credit Per UOM");
+ end;
+
var
SustainabilitySetup: Record "Sustainability Setup";
InvalidTypeForSustErr: Label 'Sustainability is only applicable for Type: %1 or %2.', Comment = '%1 - Purchase Line Type Item, %2 - Purchase Line Type G/L Account';
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/CH4EmissionRatioChart.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/CH4EmissionRatioChart.Page.al
new file mode 100644
index 0000000000..3ca294d278
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/CH4EmissionRatioChart.Page.al
@@ -0,0 +1,41 @@
+namespace Microsoft.Sustainability.RoleCenters;
+
+using System.Visualization;
+using System.Integration;
+
+page 6246 "CH4 Emission Ratio Chart"
+{
+ PageType = CardPart;
+ SourceTable = "Business Chart Buffer";
+ Caption = 'CH4 Emission Ratio Chart';
+
+ layout
+ {
+ area(Content)
+ {
+ usercontrol(BusinessChart; BusinessChart)
+ {
+ ApplicationArea = Basic, Suite;
+
+ trigger AddInReady()
+ begin
+ UpdateChartData();
+ end;
+
+ trigger Refresh()
+ begin
+ UpdateChartData();
+ end;
+ }
+ }
+ }
+
+ var
+ SustainabilityChartMgmt: Codeunit "Sustainability Chart Mgmt.";
+
+ local procedure UpdateChartData()
+ begin
+ SustainabilityChartMgmt.GenerateChartByEmissionGas(Rec, 'CH4');
+ Rec.UpdateChart(CurrPage.BusinessChart);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/ComputeSustGoalCue.Codeunit.al b/Apps/W1/Sustainability/app/src/RoleCenters/ComputeSustGoalCue.Codeunit.al
index 34144df0a1..985b0e9796 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/ComputeSustGoalCue.Codeunit.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/ComputeSustGoalCue.Codeunit.al
@@ -3,11 +3,19 @@ using Microsoft.Sustainability.Scorecard;
codeunit 6230 "Compute Sust. Goal Cue"
{
+ var
+ CalledFromManualRefresh: Boolean;
+
procedure GetLatestSustainabilityGoalCue(var SustGoalCue: Record "Sustainability Goal Cue")
begin
ComputeValues(SustGoalCue);
end;
+ procedure SetCalledFromManualRefresh(NewCalledFromManualRefresh: Boolean)
+ begin
+ CalledFromManualRefresh := NewCalledFromManualRefresh;
+ end;
+
local procedure ComputeValues(var SustGoalCue: Record "Sustainability Goal Cue")
var
SustainabilityGoal: Record "Sustainability Goal";
@@ -34,11 +42,10 @@ codeunit 6230 "Compute Sust. Goal Cue"
SustGoalCue."Last Refreshed Datetime" := CurrentDateTime();
SustGoalCue.Modify();
- if SustGoalCue.GetFilter("Date Filter") <> '' then
- SustainabilityGoal.CopyFilter("Baseline Period", SustGoalCue."Date Filter");
if SustainabilityGoal.FindSet() then
repeat
SustainabilityGoal.UpdateCurrentDateFilter(SustainabilityGoal."Start Date", SustainabilityGoal."End Date");
+ SustainabilityGoal.UpdateBaselineDateFilter(SustainabilityGoal."Baseline Start Date", SustainabilityGoal."Baseline End Date");
SustainabilityGoal.CalcFields(
"Current Value for CO2",
"Current Value for CH4",
@@ -108,6 +115,9 @@ codeunit 6230 "Compute Sust. Goal Cue"
if IsHandled then
exit(CanRefresh);
+ if CalledFromManualRefresh then
+ exit(true);
+
if LastUpdatedDateTime = 0DT then
exit(true);
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/EmissionScopeRatioChart.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/EmissionScopeRatioChart.Page.al
index 2792a6f5dc..a4ff87a6d2 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/EmissionScopeRatioChart.Page.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/EmissionScopeRatioChart.Page.al
@@ -7,6 +7,7 @@ page 6237 "Emission Scope Ratio Chart"
{
PageType = CardPart;
SourceTable = "Business Chart Buffer";
+ Caption = 'CO2 Emission Ratio Chart';
layout
{
@@ -34,7 +35,7 @@ page 6237 "Emission Scope Ratio Chart"
local procedure UpdateChartData()
begin
- SustainabilityChartMgmt.GenerateDate(Rec);
+ SustainabilityChartMgmt.GenerateChartByEmissionGas(Rec, 'CO2');
Rec.UpdateChart(CurrPage.BusinessChart);
end;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/HeadlineSustainabilityRC.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/HeadlineSustainabilityRC.Page.al
index 4211d5f30c..cf2de23335 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/HeadlineSustainabilityRC.Page.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/HeadlineSustainabilityRC.Page.al
@@ -28,6 +28,7 @@ page 6238 "Headline Sustainability RC"
group(Footprint)
{
ShowCaption = false;
+ Visible = CanShowCarbonFootprintHeadline;
field(FootprintText; RCHeadlinePageSust.GetFootPrintText())
{
ApplicationArea = Basic, Suite;
@@ -55,6 +56,27 @@ page 6238 "Headline Sustainability RC"
}
}
+ actions
+ {
+ area(Processing)
+ {
+ action("Refresh Now")
+ {
+ ApplicationArea = All;
+ Caption = 'Refresh Now';
+ Image = Refresh;
+ ToolTip = 'Refresh Headlines for Sustainability Emission and Carbon Foorprint';
+
+ trigger OnAction()
+ begin
+ RCHeadlinePageSust.GetFootPrintText();
+ FormatLine();
+ CurrPage.Update();
+ end;
+ }
+ }
+ }
+
trigger OnOpenPage()
begin
RCHeadlinesPageCommon.HeadlineOnOpenPage(Page::"Headline RC Order Processor");
@@ -62,9 +84,25 @@ page 6238 "Headline Sustainability RC"
UserGreetingVisible := RCHeadlinesPageCommon.IsUserGreetingVisible();
end;
+ trigger OnAfterGetCurrRecord()
+ begin
+ FormatLine();
+ end;
+
+ trigger OnAfterGetRecord()
+ begin
+ FormatLine();
+ end;
+
+ local procedure FormatLine()
+ begin
+ CanShowCarbonFootprintHeadline := RCHeadlinePageSust.CanShowFootPrint();
+ end;
+
var
RCHeadlinesPageCommon: Codeunit "RC Headlines Page Common";
RCHeadlinePageSust: Codeunit "RC Headline Page Sust.";
DefaultFieldsVisible: Boolean;
UserGreetingVisible: Boolean;
+ CanShowCarbonFootprintHeadline: Boolean;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/N2OEmissionRatioChart.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/N2OEmissionRatioChart.Page.al
new file mode 100644
index 0000000000..3fa5580a26
--- /dev/null
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/N2OEmissionRatioChart.Page.al
@@ -0,0 +1,41 @@
+namespace Microsoft.Sustainability.RoleCenters;
+
+using System.Visualization;
+using System.Integration;
+
+page 6247 "N2O Emission Ratio Chart"
+{
+ PageType = CardPart;
+ SourceTable = "Business Chart Buffer";
+ Caption = 'N2O Emission Ratio Chart';
+
+ layout
+ {
+ area(Content)
+ {
+ usercontrol(BusinessChart; BusinessChart)
+ {
+ ApplicationArea = Basic, Suite;
+
+ trigger AddInReady()
+ begin
+ UpdateChartData();
+ end;
+
+ trigger Refresh()
+ begin
+ UpdateChartData();
+ end;
+ }
+ }
+ }
+
+ var
+ SustainabilityChartMgmt: Codeunit "Sustainability Chart Mgmt.";
+
+ local procedure UpdateChartData()
+ begin
+ SustainabilityChartMgmt.GenerateChartByEmissionGas(Rec, 'N2O');
+ Rec.UpdateChart(CurrPage.BusinessChart);
+ end;
+}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/RCHeadlinePageSust.Codeunit.al b/Apps/W1/Sustainability/app/src/RoleCenters/RCHeadlinePageSust.Codeunit.al
index db3d57105f..ae357d4c3c 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/RCHeadlinePageSust.Codeunit.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/RCHeadlinePageSust.Codeunit.al
@@ -35,6 +35,7 @@ codeunit 6222 "RC Headline Page Sust."
SustainabilityCue.SetFilter("Date Filter", '%1', WorkDate());
SustainabilityCue.CalcFields("Emission CO2", "Emission CH4", "Emission N2O");
TodaysTotalEmission := SustainabilityCue."Emission CO2" + SustainabilityCue."Emission CH4" + SustainabilityCue."Emission N2O";
+ TodaysTotalEmission := Round(TodaysTotalEmission, 0.001, '=');
ShowFootPrintText := (TodaysTotalEmission <> 0) or (YesterdaysTotalEmission <> 0);
if ShowFootPrintText then
@@ -56,6 +57,6 @@ codeunit 6222 "RC Headline Page Sust."
ShowFootPrintText: Boolean;
MoreTxt: Label 'more';
LessTxt: Label 'less';
- CarbonFootprintLbl: Label 'Your carbon footprint is %1 and this is %2 % %3 than yesterday.', Comment = '%1 - Todays Emission, %2 - yesterdays comparison, %3 - More or less';
+ CarbonFootprintLbl: Label 'Your today''s carbon footprint is %1 and this is %2 % %3 than yesterday.', Comment = '%1 - Todays Emission, %2 - yesterdays comparison, %3 - More or less';
CarbonFootprintTxt: Text;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityChartMgmt.Codeunit.al b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityChartMgmt.Codeunit.al
index 735e533f9b..0953d1f77d 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityChartMgmt.Codeunit.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityChartMgmt.Codeunit.al
@@ -2,6 +2,7 @@ namespace Microsoft.Sustainability.RoleCenters;
using Microsoft.Sustainability.Setup;
using System.Visualization;
+using Microsoft.Sustainability.Account;
codeunit 6219 "Sustainability Chart Mgmt."
{
@@ -25,8 +26,8 @@ codeunit 6219 "Sustainability Chart Mgmt."
BussChartBuffer.AddMeasure('Ratio', 0, BussChartBuffer."Data Type"::Decimal, BussChartBuffer."Chart Type"::Doughnut);
- BussChartBuffer.SetXAxis('EmissionType', BussChartBuffer."Data Type"::String);
- BussChartBuffer.AddColumn('CO2');
+ BussChartBuffer.SetXAxis('Scope', BussChartBuffer."Data Type"::String);
+ BussChartBuffer.AddColumn('Scope 1');
BussChartBuffer.SetValueByIndex(0, Index, GetRatio(SustainabilityCue."Emission CO2", TotalEmission));
Index += 1;
@@ -39,6 +40,73 @@ codeunit 6219 "Sustainability Chart Mgmt."
Index += 1;
end;
+ procedure GenerateChartByEmissionGas(var BussChartBuffer: Record "Business Chart Buffer"; EmissionGas: Text)
+ var
+ SustainabilityCue: Record "Sustainability Cue";
+ TotalEmission: Decimal;
+ Scope1Emission: Decimal;
+ Scope2Emission: Decimal;
+ Scope3Emission: Decimal;
+ Index: Integer;
+ begin
+ if not SustainabilityCue.Get() then
+ SustainabilityCue.Insert();
+
+ if (BussChartBuffer."Period Filter Start Date" <> 0D) or (BussChartBuffer."Period Filter End Date" <> 0D) then
+ SustainabilityCue.SetRange("Date Filter", BussChartBuffer."Period Filter Start Date", BussChartBuffer."Period Filter End Date");
+
+ TotalEmission := GetEmissionValue(SustainabilityCue, EmissionGas);
+
+ Scope1Emission := GetEmissionByScope(SustainabilityCue, Enum::"Emission Scope"::"Scope 1", EmissionGas);
+ Scope2Emission := GetEmissionByScope(SustainabilityCue, Enum::"Emission Scope"::"Scope 2", EmissionGas);
+ Scope3Emission := GetEmissionByScope(SustainabilityCue, Enum::"Emission Scope"::"Scope 3", EmissionGas);
+
+ BussChartBuffer.Initialize();
+ Index := 0;
+
+ BussChartBuffer.AddMeasure(EmissionGas, 0, BussChartBuffer."Data Type"::Decimal, BussChartBuffer."Chart Type"::Doughnut);
+
+ BussChartBuffer.SetXAxis('Emission', BussChartBuffer."Data Type"::String);
+ BussChartBuffer.AddColumn('Scope 1');
+ BussChartBuffer.SetValueByIndex(0, Index, GetRatio(Scope1Emission, TotalEmission));
+ Index += 1;
+
+ BussChartBuffer.AddColumn('Scope 2');
+ BussChartBuffer.SetValueByIndex(0, Index, GetRatio(Scope2Emission, TotalEmission));
+ Index += 1;
+
+ BussChartBuffer.AddColumn('Scope 3');
+ BussChartBuffer.SetValueByIndex(0, Index, GetRatio(Scope3Emission, TotalEmission));
+ Index += 1;
+ end;
+
+ local procedure GetEmissionByScope(var SustainabilityCue: Record "Sustainability Cue"; Scope: Enum "Emission Scope"; EmissionGas: Text): Decimal
+ begin
+ SustainabilityCue.SetFilter("Scope Filter", '%1', Scope);
+ exit(GetEmissionValue(SustainabilityCue, EmissionGas));
+ end;
+
+ local procedure GetEmissionValue(var SustainabilityCue: Record "Sustainability Cue"; EmissionGas: Text) Value: Decimal
+ begin
+ case EmissionGas of
+ 'CO2':
+ begin
+ SustainabilityCue.CalcFields("Emission CO2");
+ Value := SustainabilityCue."Emission CO2";
+ end;
+ 'CH4':
+ begin
+ SustainabilityCue.CalcFields("Emission CH4");
+ Value := SustainabilityCue."Emission CH4";
+ end;
+ 'N2O':
+ begin
+ SustainabilityCue.CalcFields("Emission N2O");
+ Value := SustainabilityCue."Emission N2O";
+ end;
+ end;
+ end;
+
local procedure GetRatio(EmissionValue: Decimal; TotalEmission: Decimal): Decimal
var
SustainabilitySetup: Record "Sustainability Setup";
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityCue.Table.al b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityCue.Table.al
index c2ff4c075a..c3e53b893f 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityCue.Table.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityCue.Table.al
@@ -3,6 +3,7 @@ namespace Microsoft.Sustainability.RoleCenters;
using Microsoft.Sustainability.Ledger;
using Microsoft.Sustainability.Setup;
using Microsoft.EServices.EDocument;
+using Microsoft.Sustainability.Account;
using Microsoft.Purchases.Payables;
using Microsoft.Purchases.Document;
@@ -23,7 +24,7 @@ table 6220 "Sustainability Cue"
AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places"));
Caption = 'Emission CO2';
FieldClass = FlowField;
- CalcFormula = sum("Sustainability Ledger Entry"."Emission CO2" where("Posting Date" = field("Date Filter")));
+ CalcFormula = sum("Sustainability Ledger Entry"."Emission CO2" where("Posting Date" = field("Date Filter"), "Emission Scope" = field("Scope Filter")));
}
field(3; "Emission CH4"; Decimal)
{
@@ -31,7 +32,7 @@ table 6220 "Sustainability Cue"
AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places"));
Caption = 'Emission CH4';
FieldClass = FlowField;
- CalcFormula = sum("Sustainability Ledger Entry"."Emission CH4" where("Posting Date" = field("Date Filter")));
+ CalcFormula = sum("Sustainability Ledger Entry"."Emission CH4" where("Posting Date" = field("Date Filter"), "Emission Scope" = field("Scope Filter")));
}
field(4; "Emission N2O"; Decimal)
{
@@ -39,7 +40,7 @@ table 6220 "Sustainability Cue"
AutoFormatExpression = SustainabilitySetup.GetFormat(SustainabilitySetup.FieldNo("Emission Decimal Places"));
Caption = 'Emission N2O';
FieldClass = FlowField;
- CalcFormula = sum("Sustainability Ledger Entry"."Emission N2O" where("Posting Date" = field("Date Filter")));
+ CalcFormula = sum("Sustainability Ledger Entry"."Emission N2O" where("Posting Date" = field("Date Filter"), "Emission Scope" = field("Scope Filter")));
}
field(6; "Ongoing Purchase Orders"; Integer)
{
@@ -86,6 +87,11 @@ table 6220 "Sustainability Cue"
Caption = 'Due Next Week Filter';
FieldClass = FlowFilter;
}
+ field(22; "Scope Filter"; Enum "Emission Scope")
+ {
+ Caption = 'Scope Filter';
+ FieldClass = FlowFilter;
+ }
}
keys
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityGoalCue.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityGoalCue.Page.al
index 93c1ad378b..46c5a4aa6f 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityGoalCue.Page.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityGoalCue.Page.al
@@ -55,6 +55,29 @@ page 6240 "Sustainability Goal Cue"
}
}
+ actions
+ {
+ area(Processing)
+ {
+ action("Refresh Now")
+ {
+ ApplicationArea = All;
+ Caption = 'Refresh Now';
+ ToolTip = 'Refresh the cues for Sustainability Goals';
+ Image = Refresh;
+
+ trigger OnAction()
+ begin
+ ComputeSustGoalCue.SetCalledFromManualRefresh(true);
+ ComputeSustGoalCue.GetLatestSustainabilityGoalCue(Rec);
+ ComputeSustGoalCue.SetCalledFromManualRefresh(false);
+
+ CurrPage.Update(true);
+ end;
+ }
+ }
+ }
+
trigger OnOpenPage()
begin
Rec.Reset();
diff --git a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityManagerRC.Page.al b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityManagerRC.Page.al
index 51d3a04d8f..84a2500a88 100644
--- a/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityManagerRC.Page.al
+++ b/Apps/W1/Sustainability/app/src/RoleCenters/SustainabilityManagerRC.Page.al
@@ -43,9 +43,24 @@ page 6235 "Sustainability Manager RC"
{
ApplicationArea = Basic, Suite;
}
- part(Scope; "Emission Scope Ratio Chart")
+ group("Emission By Scope")
{
- ApplicationArea = Basic, Suite;
+ Caption = 'CO2 Emission By Scope';
+ part(CO2RatioChart; "Emission Scope Ratio Chart")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'CO2';
+ }
+ part(CH4RatioChart; "CH4 Emission Ratio Chart")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'CH4';
+ }
+ part(N2ORatioChart; "N2O Emission Ratio Chart")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'N2O';
+ }
}
}
}
@@ -54,46 +69,54 @@ page 6235 "Sustainability Manager RC"
{
area(Creation)
{
- action(SustainabilityJournal)
+ group("Journals")
{
- ApplicationArea = Basic, Suite;
- RunObject = Page "Sustainability Journal";
- Caption = 'Sustainability Journal';
- ToolTip = 'Executes the Sustainability Journal action.';
- }
- action(RecurringSustainabilityJnl)
- {
- ApplicationArea = Basic, Suite;
- RunObject = Page "Recurring Sustainability Jnl.";
- Caption = 'Recurring Sustainability Journals';
- ToolTip = 'Executes the Recurring Sustainability Journals action.';
+ Caption = 'Journals';
+ action(SustainabilityJournal)
+ {
+ ApplicationArea = Basic, Suite;
+ RunObject = Page "Sustainability Journal";
+ Caption = 'Sustainability Journal';
+ ToolTip = 'Executes the Sustainability Journal action.';
+ }
+ action(RecurringSustainabilityJnl)
+ {
+ ApplicationArea = Basic, Suite;
+ RunObject = Page "Recurring Sustainability Jnl.";
+ Caption = 'Recurring Sustainability Journals';
+ ToolTip = 'Executes the Recurring Sustainability Journals action.';
+ }
}
}
area(Reporting)
{
- action(TotalEmissions)
- {
- Caption = 'Total Emissions';
- RunObject = report "Total Emissions";
- Image = Report;
- ToolTip = 'View total emissions details.';
- ApplicationArea = Basic, Suite;
- }
- action(EmissionByCategory)
+ group("Reports")
{
- Caption = 'Emission By Category';
- RunObject = report "Emission By Category";
- Image = Report;
- ToolTip = 'View emissions details by category.';
- ApplicationArea = Basic, Suite;
- }
- action(EmissionPerFacility)
- {
- Caption = 'Emission Per Facility';
- RunObject = report "Emission Per Facility";
- Image = Report;
- ToolTip = 'View emissions details by responsibility center.';
- ApplicationArea = Basic, Suite;
+ Caption = 'Reports';
+ action(TotalEmissions)
+ {
+ Caption = 'Total Emissions';
+ RunObject = report "Total Emissions";
+ Image = Report;
+ ToolTip = 'View total emissions details.';
+ ApplicationArea = Basic, Suite;
+ }
+ action(EmissionByCategory)
+ {
+ Caption = 'Emission By Category';
+ RunObject = report "Emission By Category";
+ Image = Report;
+ ToolTip = 'View emissions details by category.';
+ ApplicationArea = Basic, Suite;
+ }
+ action(EmissionPerFacility)
+ {
+ Caption = 'Emission Per Facility';
+ RunObject = report "Emission Per Facility";
+ Image = Report;
+ ToolTip = 'View emissions details by responsibility center.';
+ ApplicationArea = Basic, Suite;
+ }
}
}
area(Sections)
diff --git a/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoal.Table.al b/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoal.Table.al
index ed01bd9964..0b44223e0d 100644
--- a/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoal.Table.al
+++ b/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoal.Table.al
@@ -19,11 +19,20 @@ table 6219 "Sustainability Goal"
field(1; "No."; Code[20])
{
Caption = 'No.';
+
+ trigger OnValidate()
+ begin
+ if Rec."No." <> xRec."No." then begin
+ Rec.TestField("Scorecard No.");
+ UpdateScorecardInformation(Rec."Scorecard No.");
+ end;
+ end;
}
field(2; "Scorecard No."; Code[20])
{
Caption = 'Scorecard No.';
TableRelation = "Sustainability Scorecard"."No.";
+ NotBlank = true;
trigger OnValidate()
begin
@@ -166,6 +175,25 @@ table 6219 "Sustainability Goal"
ValidateIfMainGoalIsAlreadyMarked();
end;
}
+ field(23; "Baseline Start Date"; Date)
+ {
+ Caption = 'Baseline Start Date';
+
+ trigger OnValidate()
+ begin
+ if (Rec."Baseline Start Date" > Rec."Baseline End Date") and (Rec."Baseline End Date" <> 0D) then
+ Error(InvalidStartAndEndDateErr, Rec.FieldCaption("Baseline Start Date"), Rec.FieldCaption("Baseline End Date"));
+ end;
+ }
+ field(24; "Baseline End Date"; Date)
+ {
+ Caption = 'Baseline End Date';
+
+ trigger OnValidate()
+ begin
+ Rec.Validate("Baseline Start Date");
+ end;
+ }
}
keys
@@ -176,6 +204,14 @@ table 6219 "Sustainability Goal"
}
}
+ trigger OnInsert()
+ var
+ SustainabilitySetup: Record "Sustainability Setup";
+ begin
+ SustainabilitySetup.Get();
+ Rec.Validate("Unit of Measure", SustainabilitySetup."Emission Unit of Measure Code");
+ end;
+
local procedure ValidateIfMainGoalIsAlreadyMarked()
var
SustainabilityGoal: Record "Sustainability Goal";
@@ -204,6 +240,11 @@ table 6219 "Sustainability Goal"
Rec.SetFilter("Current Period Filter", '%1..%2', StartDate, EndDate);
end;
+ procedure UpdateBaselineDateFilter(StartDate: Date; EndDate: Date)
+ begin
+ Rec.SetFilter("Baseline Period", '%1..%2', StartDate, EndDate);
+ end;
+
procedure ApplyOwnerFilter(var SustainabilityGoal: Record "Sustainability Goal")
begin
SustainabilityGoal.SetRange(Owner, UserId());
@@ -244,15 +285,25 @@ table 6219 "Sustainability Goal"
SustainabilityGoals."Current Value for CH4" := 0;
SustainabilityGoals."Current Value for N2O" := 0;
+ SustainabilityGoals."Baseline for CO2" := 0;
+ SustainabilityGoals."Baseline for CH4" := 0;
+ SustainabilityGoals."Baseline for N2O" := 0;
+
if not SustainabilityGoals2.Get(SustainabilityGoals."Scorecard No.", SustainabilityGoals."No.", SustainabilityGoals."Line No.") then
exit;
SustainabilityGoals2.UpdateCurrentDateFilter(SustainabilityGoals."Start Date", SustainabilityGoals."End Date");
+ SustainabilityGoals2.UpdateBaselineDateFilter(SustainabilityGoals."Baseline Start Date", SustainabilityGoals."Baseline End Date");
SustainabilityGoals2.CalcFields("Current Value for CO2", "Current Value for CH4", "Current Value for N2O");
+ SustainabilityGoals2.CalcFields("Baseline for CO2", "Baseline for CH4", "Baseline for N2O");
SustainabilityGoals."Current Value for CO2" := SustainabilityGoals2."Current Value for CO2";
SustainabilityGoals."Current Value for CH4" := SustainabilityGoals2."Current Value for CH4";
SustainabilityGoals."Current Value for N2O" := SustainabilityGoals2."Current Value for N2O";
+
+ SustainabilityGoals."Baseline for CO2" := SustainabilityGoals2."Baseline for CO2";
+ SustainabilityGoals."Baseline for CH4" := SustainabilityGoals2."Baseline for CH4";
+ SustainabilityGoals."Baseline for N2O" := SustainabilityGoals2."Baseline for N2O";
end;
var
diff --git a/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoals.Page.al b/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoals.Page.al
index 4009e10cf2..f2d49efdb1 100644
--- a/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoals.Page.al
+++ b/Apps/W1/Sustainability/app/src/Scorecard/SustainabilityGoals.Page.al
@@ -48,6 +48,18 @@ page 6234 "Sustainability Goals"
ShowMandatory = true;
ToolTip = 'Specifies the value of the Owner field.';
}
+ field("Country/Region Code"; Rec."Country/Region Code")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Country/Region Code';
+ ToolTip = 'Specifies the value of the Country/Region Code field.';
+ }
+ field("Responsibility Center"; Rec."Responsibility Center")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Responsibility Center';
+ ToolTip = 'Specifies the value of the Responsibility Center field.';
+ }
field("Start Date"; Rec."Start Date")
{
ApplicationArea = Basic, Suite;
@@ -72,6 +84,30 @@ page 6234 "Sustainability Goals"
CurrPage.Update(true);
end;
}
+ field("Baseline Start Date"; Rec."Baseline Start Date")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Baseline Start Date';
+ ToolTip = 'Specifies the value of the Baseline Start Date field.';
+
+ trigger OnValidate()
+ begin
+ FormatLine();
+ CurrPage.Update(true);
+ end;
+ }
+ field("Baseline End Date"; Rec."Baseline End Date")
+ {
+ ApplicationArea = Basic, Suite;
+ Caption = 'Baseline End Date';
+ ToolTip = 'Specifies the value of the Baseline End Date field.';
+
+ trigger OnValidate()
+ begin
+ FormatLine();
+ CurrPage.Update(true);
+ end;
+ }
field("Baseline for CO2"; Rec."Baseline for CO2")
{
ApplicationArea = Basic, Suite;
@@ -176,7 +212,7 @@ page 6234 "Sustainability Goals"
trigger OnAction()
begin
- ApplyOwnerFilter(Rec);
+ Rec.ApplyOwnerFilter(Rec);
CurrPage.Update(false);
end;
}
@@ -192,7 +228,7 @@ page 6234 "Sustainability Goals"
trigger OnAction()
begin
- RemoveOwnerFilter(Rec);
+ Rec.RemoveOwnerFilter(Rec);
CurrPage.Update(false);
end;
}
@@ -223,16 +259,25 @@ page 6234 "Sustainability Goals"
local procedure FormatLine()
var
- DateNotification: Notification;
+ CurrentPeriodDateNotification: Notification;
+ BaselinePeriodDateNotification: Notification;
begin
CanEditScorecard := not CalledFromScorecard;
if Rec.GetFilter("Current Period Filter") <> '' then begin
Rec.SetFilter("Current Period Filter", '');
- DateNotification.Id := CreateGuid();
- DateNotification.Message := StrSubstNo(CannotApplyCurrentPeriodFilterFromPageMsg, Rec.FieldCaption("Start Date"), Rec.FieldCaption("End Date"));
- DateNotification.Scope := NotificationScope::LocalScope;
- DateNotification.Send();
+ CurrentPeriodDateNotification.Id := CreateGuid();
+ CurrentPeriodDateNotification.Message := StrSubstNo(CannotApplyCurrentPeriodFilterFromPageMsg, Rec.FieldCaption("Start Date"), Rec.FieldCaption("End Date"));
+ CurrentPeriodDateNotification.Scope := NotificationScope::LocalScope;
+ CurrentPeriodDateNotification.Send();
+ end;
+
+ if Rec.GetFilter("Baseline Period") <> '' then begin
+ Rec.SetFilter("Baseline Period", '');
+ BaselinePeriodDateNotification.Id := CreateGuid();
+ BaselinePeriodDateNotification.Message := StrSubstNo(CannotApplyCurrentPeriodFilterFromPageMsg, Rec.FieldCaption("Baseline Start Date"), Rec.FieldCaption("Baseline End Date"));
+ BaselinePeriodDateNotification.Scope := NotificationScope::LocalScope;
+ BaselinePeriodDateNotification.Send();
end;
Rec.UpdateCurrentEmissionValues(Rec);
diff --git a/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Page.al b/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Page.al
index 4033384c54..aef136edeb 100644
--- a/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Page.al
+++ b/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Page.al
@@ -1,6 +1,7 @@
namespace Microsoft.Sustainability.Setup;
using Microsoft.Sustainability.Account;
+using Microsoft.Sustainability.Emission;
using Microsoft.Sustainability.Journal;
page 6221 "Sustainability Setup"
@@ -46,6 +47,14 @@ page 6221 "Sustainability Setup"
ToolTip = 'Specifies if the background error check of sustainability journal lines is enabled.';
}
}
+ group(Procurement)
+ {
+ Caption = 'Procurement';
+ field("Use Emissions In Purch. Doc."; Rec."Use Emissions In Purch. Doc.")
+ {
+ ToolTip = 'Specifies the value of the Use Emissions In Purchase Documents field.';
+ }
+ }
group(Calculations)
{
Caption = 'Calculations';
@@ -122,11 +131,19 @@ page 6221 "Sustainability Setup"
RunObject = Page "Sustainability Jnl. Templates";
ToolTip = 'Set up templates for the journals that you use for sustainability reporting tasks. Templates allow you to work in a journal window that is designed for a specific purpose.';
}
+ action(EmissionFees)
+ {
+ Caption = 'Emission Fees';
+ Image = CostBudget;
+ RunObject = Page "Emission Fees";
+ ToolTip = 'View or add Emission Fees.';
+ }
}
area(Promoted)
{
actionref(SustainAccountCategory_Promoted; SustainAccountCategory) { }
actionref(SustainabilityJournalTemplate_Promoted; SustainabilityJournalTemplate) { }
+ actionref(EmissionFees_Promoted; EmissionFees) { }
}
}
trigger OnOpenPage()
diff --git a/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Table.al b/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Table.al
index 5bf898b68a..6db152ee29 100644
--- a/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Table.al
+++ b/Apps/W1/Sustainability/app/src/Setup/SustainabilitySetup.Table.al
@@ -101,6 +101,10 @@ table 6217 "Sustainability Setup"
Caption = 'Enable Background Error Check';
InitValue = true;
}
+ field(16; "Use Emissions In Purch. Doc."; Boolean)
+ {
+ Caption = 'Use Emissions In Purchase Documents';
+ }
}
keys
diff --git a/Apps/W1/Sustainability/test/src/LibrarySustainability.Codeunit.al b/Apps/W1/Sustainability/test/src/LibrarySustainability.Codeunit.al
index c5ac2c90fb..544ae70521 100644
--- a/Apps/W1/Sustainability/test/src/LibrarySustainability.Codeunit.al
+++ b/Apps/W1/Sustainability/test/src/LibrarySustainability.Codeunit.al
@@ -90,8 +90,8 @@ codeunit 148182 "Library - Sustainability"
procedure InsertSustainabilityGoal(var SustainabilityGoal: Record "Sustainability Goal"; GoalCode: Code[20]; ScorecardCode: Code[20]; LineNo: Integer; Name: Text[100])
begin
SustainabilityGoal.Init();
- SustainabilityGoal.Validate("No.", GoalCode);
SustainabilityGoal.Validate("Scorecard No.", ScorecardCode);
+ SustainabilityGoal.Validate("No.", GoalCode);
SustainabilityGoal.Validate("Line No.", LineNo);
SustainabilityGoal.Validate(Name, Name);
SustainabilityGoal.Insert(true);
@@ -124,6 +124,19 @@ codeunit 148182 "Library - Sustainability"
SustainabilityCertificate.Insert(true);
end;
+ procedure InsertEmissionFee(var EmissionFee: Record "Emission Fee"; EmissionType: Enum "Emission Type"; ScopeType: Enum "Emission Scope"; StartingDate: Date; EndingDate: Date; CountryRegionCode: Code[10]; CarbonEquivalentFactor: Decimal)
+ begin
+ EmissionFee.Init();
+ EmissionFee.Validate("Emission Type", EmissionType);
+ EmissionFee.Validate("Scope Type", ScopeType);
+ EmissionFee.Validate("Starting Date", StartingDate);
+ EmissionFee.Validate("Ending Date", EndingDate);
+ EmissionFee.Validate("Country/Region Code", CountryRegionCode);
+ if EmissionType <> EmissionType::CO2 then
+ EmissionFee.Validate("Carbon Equivalent Factor", CarbonEquivalentFactor);
+ EmissionFee.Insert();
+ end;
+
procedure CleanUpBeforeTesting()
var
SustainabilityJnlTemplate: Record "Sustainability Jnl. Template";
@@ -135,6 +148,7 @@ codeunit 148182 "Library - Sustainability"
SustainabilityAccountSubcategory: Record "Sustain. Account Subcategory";
SustainabilityGoal: Record "Sustainability Goal";
SustainabilityScorecard: Record "Sustainability Scorecard";
+ EmissionFee: Record "Emission Fee";
begin
SustainabilityJnlTemplate.DeleteAll();
SustainabilityJnlBatch.DeleteAll();
@@ -145,5 +159,6 @@ codeunit 148182 "Library - Sustainability"
SustainabilityAccountSubcategory.DeleteAll();
SustainabilityGoal.DeleteAll();
SustainabilityScorecard.DeleteAll();
+ EmissionFee.DeleteAll();
end;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al
index 7a7a6bd6f4..1bb08a4502 100644
--- a/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al
+++ b/Apps/W1/Sustainability/test/src/SustCertificateTest.Codeunit.al
@@ -15,8 +15,6 @@ codeunit 148187 "Sust. Certificate Test"
CategoryCodeLbl: Label 'CategoryCode%1', Locked = true, Comment = '%1 = Number';
SubcategoryCodeLbl: Label 'SubcategoryCode%1', Locked = true, Comment = '%1 = Number';
ValueMustBeEqualErr: Label '%1 must be equal to %2 in the %3.', Comment = '%1 = Field Caption , %2 = Expected Value, %3 = Table Caption';
- FieldShouldBeVisibleErr: Label '%1 should be visible in Page %2', Comment = '%1 = Field Caption , %2 = Page Caption';
- FieldShouldNotBeVisibleErr: Label '%1 should not be visible in Page %2', Comment = '%1 = Field Caption , %2 = Page Caption';
FieldShouldNotBeEnabledErr: Label '%1 should not be enabled in Page %2', Comment = '%1 = Field Caption , %2 = Page Caption';
FieldShouldBeEnabledErr: Label '%1 should be enabled in Page %2', Comment = '%1 = Field Caption , %2 = Page Caption';
@@ -271,172 +269,6 @@ codeunit 148187 "Sust. Certificate Test"
StrSubstNo(ValueMustBeEqualErr, Vendor.FieldCaption("Sust. Cert. Name"), SustainabilityCertificate.Name, Vendor.TableCaption()));
end;
- [Test]
- procedure VerifySustainabilityFieldsShouldBeVisibleInItemCard()
- var
- Item: Record Item;
- SustCertificateArea: Record "Sust. Certificate Area";
- SustCertificateStandard: Record "Sust. Certificate Standard";
- SustainabilityCertificate: Record "Sustainability Certificate";
- ItemCard: TestPage "Item Card";
- begin
- // [SCENARIO 496566] Verify Sustainability Fields should be visible in Item Card.
- LibrarySustainability.CleanUpBeforeTesting();
-
- // [GIVEN] Create Sustainability Certificate Area.
- LibrarySustainability.InsertSustainabilityCertificateArea(SustCertificateArea);
-
- // [GIVEN] Create Sustainability Certificate Standard.
- LibrarySustainability.InsertSustainabilityCertificateStandard(SustCertificateStandard);
-
- // [GIVEN] Create Sustainability Certificate.
- LibrarySustainability.InsertSustainabilityCertificate(
- SustainabilityCertificate,
- SustCertificateArea."No.",
- SustCertificateStandard."No.",
- SustainabilityCertificate.Type::Item);
-
- // [GIVEN] Create an Item.
- LibraryInventory.CreateItem(Item);
-
- // [GIVEN] Update "Type" and "Sust. Cert. No." in an Item.
- Item.Validate(Type, Item.Type::Inventory);
- Item.Validate("Sust. Cert. No.", SustainabilityCertificate."No.");
- Item.Modify();
-
- // [WHEN] Open Item Card.
- ItemCard.OpenView();
- ItemCard.GoToRecord(Item);
-
- // [VERIFY] Verify Sustainability fields should be visible in an Item Card.
- Assert.AreEqual(
- true,
- ItemCard."Sust. Cert. No.".Visible(),
- StrSubstNo(FieldShouldBeVisibleErr, Item.FieldCaption("Sust. Cert. No."), Item.TableCaption()));
- Assert.AreEqual(
- true,
- ItemCard."Sust. Cert. Name".Visible(),
- StrSubstNo(FieldShouldBeVisibleErr, Item.FieldCaption("Sust. Cert. Name"), Item.TableCaption()));
- Assert.AreEqual(
- true,
- ItemCard."GHG Credit".Visible(),
- StrSubstNo(FieldShouldBeVisibleErr, Item.FieldCaption("GHG Credit"), Item.TableCaption()));
- Assert.AreEqual(
- true,
- ItemCard."Carbon Credit Per UOM".Visible(),
- StrSubstNo(FieldShouldBeVisibleErr, Item.FieldCaption("Carbon Credit Per UOM"), Item.TableCaption()));
- end;
-
- [Test]
- procedure VerifySustainabilityFieldsShouldNotBeVisibleforTypeNonInventoryInItemCard()
- var
- Item: Record Item;
- SustCertificateArea: Record "Sust. Certificate Area";
- SustCertificateStandard: Record "Sust. Certificate Standard";
- SustainabilityCertificate: Record "Sustainability Certificate";
- ItemCard: TestPage "Item Card";
- begin
- // [SCENARIO 496566] Verify Sustainability Fields should not be visible for Type "Non-Inventory" in Item Card.
- LibrarySustainability.CleanUpBeforeTesting();
-
- // [GIVEN] Create Sustainability Certificate Area.
- LibrarySustainability.InsertSustainabilityCertificateArea(SustCertificateArea);
-
- // [GIVEN] Create Sustainability Certificate Standard.
- LibrarySustainability.InsertSustainabilityCertificateStandard(SustCertificateStandard);
-
- // [GIVEN] Create Sustainability Certificate.
- LibrarySustainability.InsertSustainabilityCertificate(
- SustainabilityCertificate,
- SustCertificateArea."No.",
- SustCertificateStandard."No.",
- SustainabilityCertificate.Type::Item);
-
- // [GIVEN] Create an Item.
- LibraryInventory.CreateItem(Item);
-
- // [GIVEN] Update "Type" and "Sust. Cert. No." in an Item.
- Item.Validate(Type, Item.Type::"Non-Inventory");
- Item.Modify();
-
- // [WHEN] Open Item Card.
- ItemCard.OpenView();
- ItemCard.GoToRecord(Item);
-
- // [VERIFY] Verify Sustainability fields should be not visible for Type "Non-Inventory" in an Item Card.
- Assert.AreEqual(
- false,
- ItemCard."Sust. Cert. No.".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Sust. Cert. No."), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."Sust. Cert. Name".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Sust. Cert. Name"), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."GHG Credit".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("GHG Credit"), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."Carbon Credit Per UOM".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Carbon Credit Per UOM"), Item.TableCaption()));
- end;
-
- [Test]
- procedure VerifySustainabilityFieldsShouldNotBeVisibleforTypeServiceInItemCard()
- var
- Item: Record Item;
- SustCertificateArea: Record "Sust. Certificate Area";
- SustCertificateStandard: Record "Sust. Certificate Standard";
- SustainabilityCertificate: Record "Sustainability Certificate";
- ItemCard: TestPage "Item Card";
- begin
- // [SCENARIO 496566] Verify Sustainability Fields should not be visible for Type "Service" in Item Card.
- LibrarySustainability.CleanUpBeforeTesting();
-
- // [GIVEN] Create Sustainability Certificate Area.
- LibrarySustainability.InsertSustainabilityCertificateArea(SustCertificateArea);
-
- // [GIVEN] Create Sustainability Certificate Standard.
- LibrarySustainability.InsertSustainabilityCertificateStandard(SustCertificateStandard);
-
- // [GIVEN] Create Sustainability Certificate.
- LibrarySustainability.InsertSustainabilityCertificate(
- SustainabilityCertificate,
- SustCertificateArea."No.",
- SustCertificateStandard."No.",
- SustainabilityCertificate.Type::Item);
-
- // [GIVEN] Create an Item.
- LibraryInventory.CreateItem(Item);
-
- // [GIVEN] Update "Type" and "Sust. Cert. No." in an Item.
- Item.Validate(Type, Item.Type::Service);
- Item.Modify();
-
- // [WHEN] Open Item Card.
- ItemCard.OpenView();
- ItemCard.GoToRecord(Item);
-
- // [VERIFY] Verify Sustainability fields should be not visible for Type "Service" in an Item Card.
- Assert.AreEqual(
- false,
- ItemCard."Sust. Cert. No.".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Sust. Cert. No."), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."Sust. Cert. Name".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Sust. Cert. Name"), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."GHG Credit".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("GHG Credit"), Item.TableCaption()));
- Assert.AreEqual(
- false,
- ItemCard."Carbon Credit Per UOM".Visible(),
- StrSubstNo(FieldShouldNotBeVisibleErr, Item.FieldCaption("Carbon Credit Per UOM"), Item.TableCaption()));
- end;
-
[Test]
procedure VerifySustCertNoShouldThrowErrorIfCertificateTypeIsVendor()
var
@@ -714,9 +546,6 @@ codeunit 148187 "Sust. Certificate Test"
CategoryCode: Code[20];
SubcategoryCode: Code[20];
AccountCode: Code[20];
- EmissionCO2PerUnit: Decimal;
- EmissionCH4PerUnit: Decimal;
- EmissionN2OPerUnit: Decimal;
PostedInvNo: Code[20];
begin
// [SCENARIO 496566] Verify Sustainability Ledger Entry should be created When "GHG Credit" is enabled in Item.
@@ -746,11 +575,6 @@ codeunit 148187 "Sust. Certificate Test"
CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10));
SustainabilityAccount.Get(AccountCode);
- // [GIVEN] Generate Emission per Unit.
- EmissionCO2PerUnit := LibraryRandom.RandInt(5);
- EmissionCH4PerUnit := LibraryRandom.RandInt(5);
- EmissionN2OPerUnit := LibraryRandom.RandInt(5);
-
// [GIVEN] Create a Purchase Header.
LibraryPurchase.CreatePurchHeader(PurchaseHeader, "Purchase Document Type"::Order, LibraryPurchase.CreateVendorNo());
PurchaseHeader.SetHideValidationDialog(true);
@@ -767,9 +591,6 @@ codeunit 148187 "Sust. Certificate Test"
// [GIVEN] Update Sustainability Account No.,Emission CO2 Per Unit,Emission CH4 Per Unit,Emission N2O Per Unit.
PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandIntInRange(10, 200));
PurchaseLine.Validate("Sust. Account No.", AccountCode);
- PurchaseLine.Validate("Emission CO2 Per Unit", EmissionCO2PerUnit);
- PurchaseLine.Validate("Emission CH4 Per Unit", EmissionCH4PerUnit);
- PurchaseLine.Validate("Emission N2O Per Unit", EmissionN2OPerUnit);
PurchaseLine.Modify();
// [WHEN] Post a Purchase Document.
@@ -777,7 +598,7 @@ codeunit 148187 "Sust. Certificate Test"
// [VERIFY] Verify Sustainability Ledger Entry should be created When "GHG Credit" is enabled in Item.
SustainabilityLedgerEntry.SetRange("Document No.", PostedInvNo);
- Assert.RecordCount(SustainabilityLedgerEntry, 2);
+ Assert.RecordCount(SustainabilityLedgerEntry, 1);
SustainabilityLedgerEntry.SetRange("Document Type", SustainabilityLedgerEntry."Document Type"::"GHG Credit");
SustainabilityLedgerEntry.FindFirst();
@@ -798,38 +619,13 @@ codeunit 148187 "Sust. Certificate Test"
0,
SustainabilityLedgerEntry.TableCaption()));
Assert.AreEqual(
- -(PurchaseLine.Quantity * Item."Carbon Credit Per UOM"),
+ -(PurchaseLine."Qty. per Unit of Measure" * Item."Carbon Credit Per UOM"),
SustainabilityLedgerEntry."Emission CO2",
StrSubstNo(
ValueMustBeEqualErr,
SustainabilityLedgerEntry.FieldCaption("Emission CO2"),
- -(PurchaseLine.Quantity * Item."Carbon Credit Per UOM"),
+ -(PurchaseLine."Qty. per Unit of Measure" * Item."Carbon Credit Per UOM"),
SustainabilityLedgerEntry.TableCaption()));
- Assert.AreEqual(
- -PurchaseLine."Line Amount",
- SustainabilityLedgerEntry."Emission Fee",
- StrSubstNo(
- ValueMustBeEqualErr,
- SustainabilityLedgerEntry.FieldCaption("Emission Fee"),
- -PurchaseLine."Line Amount",
- SustainabilityLedgerEntry.TableCaption()));
-
- SustainabilityLedgerEntry.Reset();
- SustainabilityLedgerEntry.SetRange("Document No.", PostedInvNo);
- SustainabilityLedgerEntry.SetRange("Document Type", SustainabilityLedgerEntry."Document Type"::Invoice);
- SustainabilityLedgerEntry.FindFirst();
- Assert.AreEqual(
- EmissionCO2PerUnit,
- SustainabilityLedgerEntry."Emission CO2",
- StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Emission CO2"), EmissionCO2PerUnit, SustainabilityLedgerEntry.TableCaption()));
- Assert.AreEqual(
- EmissionCH4PerUnit,
- SustainabilityLedgerEntry."Emission CH4",
- StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Emission CH4"), EmissionCO2PerUnit, SustainabilityLedgerEntry.TableCaption()));
- Assert.AreEqual(
- EmissionN2OPerUnit,
- SustainabilityLedgerEntry."Emission N2O",
- StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Emission N2O"), EmissionN2OPerUnit, SustainabilityLedgerEntry.TableCaption()));
end;
[Test]
@@ -849,8 +645,6 @@ codeunit 148187 "Sust. Certificate Test"
SubcategoryCode: Code[20];
AccountCode: Code[20];
EmissionCO2PerUnit: Decimal;
- EmissionCH4PerUnit: Decimal;
- EmissionN2OPerUnit: Decimal;
begin
// [SCENARIO 496566] Verify Sustainability Ledger entry should be Kocked Off when the Cancel Credit Memo is posted If "GHG Credit" is enabled in Item.
LibrarySustainability.CleanUpBeforeTesting();
@@ -887,8 +681,6 @@ codeunit 148187 "Sust. Certificate Test"
// [GIVEN] Generate Emission per Unit.
EmissionCO2PerUnit := LibraryRandom.RandInt(5);
- EmissionCH4PerUnit := LibraryRandom.RandInt(5);
- EmissionN2OPerUnit := LibraryRandom.RandInt(5);
// [GIVEN] Create a Purchase Header.
LibraryPurchase.CreatePurchHeader(PurchaseHeader, "Purchase Document Type"::Order, LibraryPurchase.CreateVendorNo());
@@ -905,8 +697,8 @@ codeunit 148187 "Sust. Certificate Test"
PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandIntInRange(10, 200));
PurchaseLine.Validate("Sust. Account No.", AccountCode);
PurchaseLine.Validate("Emission CO2 Per Unit", EmissionCO2PerUnit);
- PurchaseLine.Validate("Emission CH4 Per Unit", EmissionCH4PerUnit);
- PurchaseLine.Validate("Emission N2O Per Unit", EmissionN2OPerUnit);
+ PurchaseLine.Validate("Emission CH4 Per Unit", 0);
+ PurchaseLine.Validate("Emission N2O Per Unit", 0);
PurchaseLine.Modify();
// [GIVEN] Update Reason Code in Purchase Header.
@@ -917,12 +709,9 @@ codeunit 148187 "Sust. Certificate Test"
// [VERIFY] Verify Sustainability Ledger entry should be Kocked Off when the Cancel Credit Memo is posted If "GHG Credit" is enabled in Item.
SustainabilityLedgerEntry.SetRange("Account No.", AccountCode);
- SustainabilityLedgerEntry.CalcSums("Emission CO2", "Emission CH4", "Emission N2O", "Emission Fee");
- Assert.RecordCount(SustainabilityLedgerEntry, 4);
- Assert.AreEqual(
- 0,
- SustainabilityLedgerEntry."Emission Fee",
- StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Emission Fee"), 0, SustainabilityLedgerEntry.TableCaption()));
+ SustainabilityLedgerEntry.CalcSums("Emission CO2", "Emission CH4", "Emission N2O");
+ Assert.RecordCount(SustainabilityLedgerEntry, 2);
+
Assert.AreEqual(
0,
SustainabilityLedgerEntry."Emission CO2",
@@ -962,37 +751,6 @@ codeunit 148187 "Sust. Certificate Test"
true, true, true, '', false);
end;
- local procedure CreateAndPostPurchaseOrderWithSustAccount(AccountCode: Code[20]; PostingDate: Date; ItemNo: Code[20]; EmissionCO2PerUnit: Decimal; EmissionCH4PerUnit: Decimal; EmissionN2OPerUnit: Decimal): Code[2]
- var
- PurchaseHeader: Record "Purchase Header";
- PurchaseLine: Record "Purchase Line";
- begin
- // Create a Purchase Header.
- LibraryPurchase.CreatePurchHeader(PurchaseHeader, "Purchase Document Type"::Order, LibraryPurchase.CreateVendorNo());
- PurchaseHeader.SetHideValidationDialog(true);
- PurchaseHeader.Validate("Posting Date", PostingDate);
- PurchaseHeader.Modify();
-
- // Create a Purchase Line.
- LibraryPurchase.CreatePurchaseLine(
- PurchaseLine,
- PurchaseHeader,
- "Purchase Line Type"::Item,
- ItemNo,
- LibraryRandom.RandInt(10));
-
- // Update Sustainability Account No.,Emission CO2 Per Unit,Emission CH4 Per Unit,Emission N2O Per Unit.
- PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandIntInRange(10, 200));
- PurchaseLine.Validate("Sust. Account No.", AccountCode);
- PurchaseLine.Validate("Emission CO2 Per Unit", EmissionCO2PerUnit);
- PurchaseLine.Validate("Emission CH4 Per Unit", EmissionCH4PerUnit);
- PurchaseLine.Validate("Emission N2O Per Unit", EmissionN2OPerUnit);
- PurchaseLine.Modify();
-
- // Post a Purchase Document.
- exit(LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true));
- end;
-
local procedure UpdateReasonCodeinPurchaseHeader(var PurchaseHeader: Record "Purchase Header")
var
ReasonCode: Record "Reason Code";
diff --git a/Apps/W1/Sustainability/test/src/SustainabilityCheckTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustainabilityCheckTest.Codeunit.al
index c635bc871f..4afc7cf672 100644
--- a/Apps/W1/Sustainability/test/src/SustainabilityCheckTest.Codeunit.al
+++ b/Apps/W1/Sustainability/test/src/SustainabilityCheckTest.Codeunit.al
@@ -5,7 +5,10 @@ codeunit 148183 "Sustainability Check Test"
var
Assert: Codeunit "Assert";
+ LibraryRandom: Codeunit "Library - Random";
LibrarySustainability: Codeunit "Library - Sustainability";
+ FieldShouldNotBeEditableErr: Label '%1 should not be editable for Emission Type %2 in Page %3', Comment = '%1 = Field Caption , %2 = Emission Type, %3 = Page Caption';
+ AmountMustBeEqualErr: Label '%1 must be equal to %2 in Page %3', Comment = '%1 = Field Caption ,%2 = Total Amount, %3 = Page Caption';
[Test]
procedure TestCommonConditionCheck()
@@ -131,4 +134,88 @@ codeunit 148183 "Sustainability Check Test"
TempErrorMessage.SetRange("Context Record ID", JnlLine2);
Assert.IsTrue(TempErrorMessage.Count() > 0, 'Expected at least one error for the second line');
end;
+
+ [Test]
+ procedure VerifyCarbonEquivalentFactorShouldNotBeEditableForEmissionTypeCO2()
+ var
+ EmissionFees: TestPage "Emission Fees";
+ begin
+ // [SCENARIO 538580] Verify "Carbon Equivalent Factor" field should not be editable for "Emission Type" = CO2 in Page Emission Fees.
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a new Emission Fees.
+ EmissionFees.OpenNew();
+
+ // [WHEN] Update "Emission Type" = CO2 in Emission Fees.
+ EmissionFees."Emission Type".SetValue("Emission Type"::CO2);
+
+ // [VERIFY] Verify "Carbon Equivalent Factor" field should not be editable for "Emission Type" = CO2 in Page Emission Fees.
+ Assert.AreEqual(
+ false,
+ EmissionFees."Carbon Equivalent Factor".Editable(),
+ StrSubstNo(FieldShouldNotBeEditableErr, EmissionFees."Carbon Equivalent Factor".Caption(), "Emission Type"::CO2, EmissionFees.Caption()));
+
+ EmissionFees.Close();
+ end;
+
+ [Test]
+ procedure VerifyCarbonEquivalentFactorShouldBeEditableForOtherThanEmissionTypeCO2()
+ var
+ EmissionFees: TestPage "Emission Fees";
+ begin
+ // [SCENARIO 538580] Verify "Carbon Equivalent Factor" field should be editable for other than "Emission Type" = CO2 in Page Emission Fees.
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a new Emission Fees.
+ EmissionFees.OpenNew();
+
+ // [WHEN] Update "Emission Type" = CH4 in Emission Fees.
+ EmissionFees."Emission Type".SetValue("Emission Type"::CH4);
+
+ // [VERIFY] Verify "Carbon Equivalent Factor" field should be editable for "Emission Type" = CH4 in Page Emission Fees.
+ Assert.AreEqual(
+ true,
+ EmissionFees."Carbon Equivalent Factor".Editable(),
+ StrSubstNo(FieldShouldNotBeEditableErr, EmissionFees."Carbon Equivalent Factor".Caption(), "Emission Type"::CH4, EmissionFees.Caption()));
+
+ // [GIVEN] Close Emission Fees.
+ EmissionFees.Close();
+
+ // [GIVEN] Create a new Emission Fees.
+ EmissionFees.OpenNew();
+
+ // [WHEN] Update "Emission Type" = N2O in Emission Fees.
+ EmissionFees."Emission Type".SetValue("Emission Type"::N2O);
+
+ // [VERIFY] Verify "Carbon Equivalent Factor" field should be editable for "Emission Type" = N2O in Page Emission Fees.
+ Assert.AreEqual(
+ true,
+ EmissionFees."Carbon Equivalent Factor".Editable(),
+ StrSubstNo(FieldShouldNotBeEditableErr, EmissionFees."Carbon Equivalent Factor".Caption(), "Emission Type"::N2O, EmissionFees.Caption()));
+
+ EmissionFees.Close();
+ end;
+
+ [Test]
+ procedure VerifyCarbonEquivalentFactorShouldBeEqualtoOneForEmissionTypeCO2()
+ var
+ EmissionFees: TestPage "Emission Fees";
+ begin
+ // [SCENARIO 538580] Verify "Carbon Equivalent Factor" field should be equal to one for "Emission Type" = CO2 in Page Emission Fees.
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a new Emission Fees.
+ EmissionFees.OpenNew();
+
+ // [WHEN] Update "Emission Type" = CO2 in Emission Fees.
+ EmissionFees."Emission Type".SetValue("Emission Type"::CO2);
+
+ // [VERIFY] Verify "Carbon Equivalent Factor" field should be equal to one for "Emission Type" = CO2 in Page Emission Fees.
+ Assert.AreEqual(
+ LibraryRandom.RandIntInRange(1, 1),
+ EmissionFees."Carbon Equivalent Factor".AsDecimal(),
+ StrSubstNo(AmountMustBeEqualErr, EmissionFees."Carbon Equivalent Factor".Caption(), LibraryRandom.RandIntInRange(1, 1), EmissionFees.Caption()));
+
+ EmissionFees.Close();
+ end;
}
\ No newline at end of file
diff --git a/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al b/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al
index 00de122ae5..3468665009 100644
--- a/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al
+++ b/Apps/W1/Sustainability/test/src/SustainabilityPostingTest.Codeunit.al
@@ -1390,6 +1390,8 @@ codeunit 148184 "Sustainability Posting Test"
// [GIVEN] Update Owner in the Sustainability Goal.
SustainabilityGoal[1].Validate(Owner, UserSetup."User ID");
+ SustainabilityGoal[1].Validate("Baseline Start Date", Today());
+ SustainabilityGoal[1].Validate("Baseline End Date", Today());
SustainabilityGoal[1].Modify();
// [GIVEN] Create another Sustainability Goal.
@@ -1402,6 +1404,8 @@ codeunit 148184 "Sustainability Posting Test"
// [GIVEN] Update Owner in the Sustainability Goal.
SustainabilityGoal[2].Validate(Owner, UserSetup."User ID");
+ SustainabilityGoal[2].Validate("Baseline Start Date", Today() + 1);
+ SustainabilityGoal[2].Validate("Baseline End Date", Today() + 1);
SustainabilityGoal[2].Modify();
// [GIVEN] Create a Sustainability Account.
@@ -1474,7 +1478,6 @@ codeunit 148184 "Sustainability Posting Test"
// [WHEN] Open and Filter Sustainability Goals page.
SustainabilityGoals.OpenView();
- SustainabilityGoals.Filter.SetFilter("Baseline Period", Format(Today));
SustainabilityGoals.GoToRecord(SustainabilityGoal[1]);
// [VERIFY] Verify Sustainability BaseLine Fields should be filtered based on "Baseline Period" in Sustainability Goals Page.
@@ -1484,7 +1487,6 @@ codeunit 148184 "Sustainability Posting Test"
// [WHEN] Open and Filter Sustainability Goals page.
SustainabilityGoals.GoToRecord(SustainabilityGoal[2]);
- SustainabilityGoals.Filter.SetFilter("Baseline Period", Format(Today + 1));
// [VERIFY] Verify Sustainability BaseLine Fields should be filtered based on "Baseline Period" in Sustainability Goals Page.
SustainabilityGoals."Baseline for CH4".AssertEquals(EmissionCH4PerUnit + 1);
@@ -1730,6 +1732,363 @@ codeunit 148184 "Sustainability Posting Test"
LibraryVariableStorage.Clear();
end;
+ [Test]
+ procedure VerifyCO2eEmissionAndCarbonFeeInSustainabilityLedgerEntryWhenPurchDocumentIsPosted()
+ var
+ PurchaseLine: Record "Purchase Line";
+ CountryRegion: Record "Country/Region";
+ PurchaseHeader: Record "Purchase Header";
+ EmissionFee: array[3] of Record "Emission Fee";
+ SustainabilityAccount: Record "Sustainability Account";
+ SustainabilityLedgerEntry: Record "Sustainability Ledger Entry";
+ AccountCode: Code[20];
+ CategoryCode: Code[20];
+ SubcategoryCode: Code[20];
+ PostedInvoiceNo: Code[20];
+ ExpectedCO2eEmission: Decimal;
+ EmissionCO2PerUnit: Decimal;
+ EmissionCH4PerUnit: Decimal;
+ EmissionN2OPerUnit: Decimal;
+ ExpectedCarbonFee: Decimal;
+ begin
+ // [SCENARIO 538580] Verify CO2e Emission and Carbon Fee in Sustainability Ledger Entry When Purchase Document is posted.
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a Sustainability Account.
+ CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10));
+ SustainabilityAccount.Get(AccountCode);
+ SustainabilityAccount.CalcFields("Emission Scope");
+
+ // [GIVEN] Create Country/Region.
+ LibraryERM.CreateCountryRegion(CountryRegion);
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CH4.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[1],
+ "Emission Type"::CH4,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CO2.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[2],
+ "Emission Type"::CO2,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+ EmissionFee[2].Validate("Carbon Fee", LibraryRandom.RandDecInDecimalRange(0.5, 2, 1));
+ EmissionFee[2].Modify();
+
+ // [GIVEN] Create Emission Fee for "Emission Type" N2O.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[3],
+ "Emission Type"::N2O,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Generate Emission per Unit.
+ EmissionCO2PerUnit := LibraryRandom.RandInt(5);
+ EmissionCH4PerUnit := LibraryRandom.RandInt(5);
+ EmissionN2OPerUnit := LibraryRandom.RandInt(5);
+
+ // [GIVEN] Save Expected CO2e Emission and Carbon Fee.
+ ExpectedCO2eEmission := EmissionCH4PerUnit * EmissionFee[1]."Carbon Equivalent Factor" + EmissionCO2PerUnit * EmissionFee[2]."Carbon Equivalent Factor" + EmissionN2OPerUnit * EmissionFee[3]."Carbon Equivalent Factor";
+ ExpectedCarbonFee := ExpectedCO2eEmission * (EmissionFee[1]."Carbon Fee" + EmissionFee[2]."Carbon Fee" + EmissionFee[3]."Carbon Fee");
+
+ // [GIVEN] Create a Purchase Header.
+ LibraryPurchase.CreatePurchHeader(PurchaseHeader, "Purchase Document Type"::Order, LibraryPurchase.CreateVendorNo());
+
+ // [GIVEN] Update "Buy-from Country/Region Code" in Purchase Header.
+ PurchaseHeader."Buy-from Country/Region Code" := CountryRegion.Code;
+ PurchaseHeader.Modify();
+
+ // [GIVEN] Create a Purchase Line.
+ LibraryPurchase.CreatePurchaseLine(
+ PurchaseLine,
+ PurchaseHeader,
+ "Purchase Line Type"::Item,
+ LibraryInventory.CreateItemNo(),
+ LibraryRandom.RandInt(10));
+
+ // [GIVEN] Update Sustainability Account No.,Emission CO2 Per Unit,Emission CH4 Per Unit,Emission N2O Per Unit.
+ PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandIntInRange(10, 200));
+ PurchaseLine.Validate("Sust. Account No.", AccountCode);
+ PurchaseLine.Validate("Emission CO2 Per Unit", EmissionCO2PerUnit);
+ PurchaseLine.Validate("Emission CH4 Per Unit", EmissionCH4PerUnit);
+ PurchaseLine.Validate("Emission N2O Per Unit", EmissionN2OPerUnit);
+ PurchaseLine.Modify();
+
+ // [WHEN] Post a Purchase Document.
+ PostedInvoiceNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);
+
+ // [VERIFY] Verify CO2e Emission and Carbon Fee in Sustainability Ledger Entry When Purchase Document is posted.
+ SustainabilityLedgerEntry.SetRange("Document No.", PostedInvoiceNo);
+ SustainabilityLedgerEntry.FindFirst();
+ Assert.AreEqual(
+ ExpectedCO2eEmission,
+ SustainabilityLedgerEntry."CO2e Emission",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), ExpectedCO2eEmission, SustainabilityLedgerEntry.TableCaption()));
+ Assert.AreEqual(
+ ExpectedCarbonFee,
+ SustainabilityLedgerEntry."Carbon Fee",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Carbon Fee"), ExpectedCarbonFee, SustainabilityLedgerEntry.TableCaption()));
+ end;
+
+ [Test]
+ [HandlerFunctions('ConfirmHandler,MessageHandler')]
+ procedure VerifyCO2eEmissionAndCarbonFeeInSustainabilityLedgerEntryWhenSustJnlLineIsPosted()
+ var
+ UnitOfMeasure: Record "Unit of Measure";
+ CountryRegion: Record "Country/Region";
+ EmissionFee: array[3] of Record "Emission Fee";
+ SustainabilityAccount: Record "Sustainability Account";
+ SustainabilityJnlBatch: Record "Sustainability Jnl. Batch";
+ SustainabilityJournalLine: Record "Sustainability Jnl. Line";
+ SustainabilityLedgerEntry: Record "Sustainability Ledger Entry";
+ SustainAccountSubcategory: Record "Sustain. Account Subcategory";
+ SustainabilityJournalMgt: Codeunit "Sustainability Journal Mgt.";
+ AccountCode: Code[20];
+ CategoryCode: Code[20];
+ SubcategoryCode: Code[20];
+ ExpectedCO2eEmission: Decimal;
+ EmissionCO2PerUnit: Decimal;
+ EmissionCH4PerUnit: Decimal;
+ EmissionN2OPerUnit: Decimal;
+ ExpectedCarbonFee: Decimal;
+ begin
+ // [SCENARIO 538580] Verify CO2e Emission and Carbon Fee in Sustainability Ledger Entry When Sustainability Journal Line is posted.
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a Sustainability Account.
+ CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10));
+ SustainabilityAccount.Get(AccountCode);
+ SustainabilityAccount.CalcFields("Emission Scope");
+ SustainAccountSubcategory.Get(CategoryCode, SubcategoryCode);
+
+ // [GIVEN] Create Country/Region.
+ LibraryERM.CreateCountryRegion(CountryRegion);
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CH4.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[1],
+ "Emission Type"::CH4,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CO2.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[2],
+ "Emission Type"::CO2,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+ EmissionFee[2].Validate("Carbon Fee", LibraryRandom.RandDecInDecimalRange(0.5, 2, 1));
+ EmissionFee[2].Modify();
+
+ // [GIVEN] Create Emission Fee for "Emission Type" N2O.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[3],
+ "Emission Type"::N2O,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Generate Emission per Unit.
+ EmissionCO2PerUnit := LibraryRandom.RandIntInRange(1, 1) * SustainAccountSubcategory."Emission Factor CO2";
+ EmissionCH4PerUnit := LibraryRandom.RandIntInRange(1, 1) * SustainAccountSubcategory."Emission Factor CH4";
+ EmissionN2OPerUnit := LibraryRandom.RandIntInRange(1, 1) * SustainAccountSubcategory."Emission Factor N2O";
+
+ // [GIVEN] Save Expected CO2e Emission and Carbon Fee.
+ ExpectedCO2eEmission := EmissionCH4PerUnit * EmissionFee[1]."Carbon Equivalent Factor" + EmissionCO2PerUnit * EmissionFee[2]."Carbon Equivalent Factor" + EmissionN2OPerUnit * EmissionFee[3]."Carbon Equivalent Factor";
+ ExpectedCarbonFee := ExpectedCO2eEmission * (EmissionFee[1]."Carbon Fee" + EmissionFee[2]."Carbon Fee" + EmissionFee[3]."Carbon Fee");
+
+ // [GIVEN] Get Sustainability Journal Batch
+ SustainabilityJnlBatch := SustainabilityJournalMgt.GetASustainabilityJournalBatch(false);
+
+ // [GIVEN] Create a Sustainability Journal Line.
+ SustainabilityJournalLine := LibrarySustainability.InsertSustainabilityJournalLine(SustainabilityJnlBatch, SustainabilityAccount, 1000);
+
+ // [GIVEN] Create Unit of Measure Code.
+ LibraryInventory.CreateUnitOfMeasureCode(UnitOfMeasure);
+
+ // [GIVEN] Update "Buy-from Country/Region Code" in Sustainability Journal Line.
+ SustainabilityJournalLine.Validate("Document No.", SustainabilityJournalMgt.GetDocumentNo(false, SustainabilityJnlBatch, '', SustainabilityJournalLine."Posting Date"));
+ SustainabilityJournalLine.Validate(Description, LibraryRandom.RandText(10));
+ SustainabilityJournalLine.Validate("Unit of Measure", UnitOfMeasure.Code);
+ SustainabilityJournalLine.Validate("Fuel/Electricity", LibraryRandom.RandIntInRange(1, 1));
+ SustainabilityJournalLine.Validate("Country/Region Code", CountryRegion.Code);
+ SustainabilityJournalLine.Modify();
+
+ // [WHEN] Post a Sustainability Journal Line.
+ SustainabilityJournalLine.SetRange("Journal Template Name", SustainabilityJournalLine."Journal Template Name");
+ SustainabilityJournalLine.SetRange("Journal Batch Name", SustainabilityJournalLine."Journal Batch Name");
+ Codeunit.Run(Codeunit::"Sustainability Jnl.-Post", SustainabilityJournalLine);
+
+ // [VERIFY] Verify "CO2e Emission" and "Carbon Fee" in Sustainability Ledger Entry When Sustainability Journal Line is posted.
+ SustainabilityLedgerEntry.SetRange("Journal Template Name", SustainabilityJournalLine."Journal Template Name");
+ SustainabilityLedgerEntry.SetRange("Journal Batch Name", SustainabilityJournalLine."Journal Batch Name");
+ SustainabilityLedgerEntry.SetRange("Posting Date", SustainabilityJournalLine."Posting Date");
+ SustainabilityLedgerEntry.FindFirst();
+ Assert.AreEqual(
+ ExpectedCO2eEmission,
+ SustainabilityLedgerEntry."CO2e Emission",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), ExpectedCO2eEmission, SustainabilityLedgerEntry.TableCaption()));
+ Assert.AreEqual(
+ ExpectedCarbonFee,
+ SustainabilityLedgerEntry."Carbon Fee",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Carbon Fee"), ExpectedCarbonFee, SustainabilityLedgerEntry.TableCaption()));
+ end;
+
+ [Test]
+ [HandlerFunctions('MessageHandler')]
+ procedure VerifyCO2eEmissionAndCarbonFeeValuesInSustainabilityLedgerEntrythrougReportBatchUpdateCarbonEmission()
+ var
+ PurchaseLine: Record "Purchase Line";
+ CountryRegion: Record "Country/Region";
+ PurchaseHeader: Record "Purchase Header";
+ EmissionFee: array[3] of Record "Emission Fee";
+ SustainabilityAccount: Record "Sustainability Account";
+ SustainabilityLedgerEntry: Record "Sustainability Ledger Entry";
+ BatchUpdateCarbonEmission: Report "Batch Update Carbon Emission";
+ AccountCode: Code[20];
+ CategoryCode: Code[20];
+ SubcategoryCode: Code[20];
+ PostedInvoiceNo: Code[20];
+ ExpectedCO2eEmission: Decimal;
+ EmissionCO2PerUnit: Decimal;
+ EmissionCH4PerUnit: Decimal;
+ EmissionN2OPerUnit: Decimal;
+ ExpectedCarbonFee: Decimal;
+ begin
+ // [SCENARIO 538580] Verify CO2e Emission and Carbon Fee in Sustainability Ledger Entry throug Report "Batch Update Carbon Emission".
+ LibrarySustainability.CleanUpBeforeTesting();
+
+ // [GIVEN] Create a Sustainability Account.
+ CreateSustainabilityAccount(AccountCode, CategoryCode, SubcategoryCode, LibraryRandom.RandInt(10));
+ SustainabilityAccount.Get(AccountCode);
+ SustainabilityAccount.CalcFields("Emission Scope");
+
+ // [GIVEN] Create Country/Region.
+ LibraryERM.CreateCountryRegion(CountryRegion);
+
+ // [GIVEN] Generate Emission per Unit.
+ EmissionCO2PerUnit := LibraryRandom.RandInt(5);
+ EmissionCH4PerUnit := LibraryRandom.RandInt(5);
+ EmissionN2OPerUnit := LibraryRandom.RandInt(5);
+
+ // [GIVEN] Create a Purchase Header.
+ LibraryPurchase.CreatePurchHeader(PurchaseHeader, "Purchase Document Type"::Order, LibraryPurchase.CreateVendorNo());
+
+ // [GIVEN] Update "Buy-from Country/Region Code" in Purchase Header.
+ PurchaseHeader."Buy-from Country/Region Code" := CountryRegion.Code;
+ PurchaseHeader.Modify();
+
+ // [GIVEN] Create a Purchase Line.
+ LibraryPurchase.CreatePurchaseLine(
+ PurchaseLine,
+ PurchaseHeader,
+ "Purchase Line Type"::Item,
+ LibraryInventory.CreateItemNo(),
+ LibraryRandom.RandInt(10));
+
+ // [GIVEN] Update Sustainability Account No.,Emission CO2 Per Unit,Emission CH4 Per Unit,Emission N2O Per Unit.
+ PurchaseLine.Validate("Direct Unit Cost", LibraryRandom.RandIntInRange(10, 200));
+ PurchaseLine.Validate("Sust. Account No.", AccountCode);
+ PurchaseLine.Validate("Emission CO2 Per Unit", EmissionCO2PerUnit);
+ PurchaseLine.Validate("Emission CH4 Per Unit", EmissionCH4PerUnit);
+ PurchaseLine.Validate("Emission N2O Per Unit", EmissionN2OPerUnit);
+ PurchaseLine.Modify();
+
+ // [WHEN] Post a Purchase Document.
+ PostedInvoiceNo := LibraryPurchase.PostPurchaseDocument(PurchaseHeader, true, true);
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CH4.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[1],
+ "Emission Type"::CH4,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Create Emission Fee for "Emission Type" CO2.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[2],
+ "Emission Type"::CO2,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+ EmissionFee[2].Validate("Carbon Fee", LibraryRandom.RandDecInDecimalRange(0.5, 2, 1));
+ EmissionFee[2].Modify();
+
+ // [GIVEN] Create Emission Fee for "Emission Type" N2O.
+ LibrarySustainability.InsertEmissionFee(
+ EmissionFee[3],
+ "Emission Type"::N2O,
+ SustainabilityAccount."Emission Scope",
+ CalcDate('<-CM>', WorkDate()),
+ CalcDate('', WorkDate()),
+ CountryRegion.Code,
+ LibraryRandom.RandDecInDecimalRange(0.5, 1, 1));
+
+ // [GIVEN] Save Expected CO2e Emission and Carbon Fee.
+ GetCarbonFeeEmissionValues(
+ WorkDate(),
+ CountryRegion.Code,
+ EmissionCO2PerUnit,
+ EmissionN2OPerUnit,
+ EmissionCH4PerUnit,
+ SustainabilityAccount."Emission Scope",
+ ExpectedCO2eEmission,
+ ExpectedCarbonFee);
+
+ // [GIVEN] Verify CO2e Emission and Carbon Fee field value should be zero in Sustainability Ledger Entry.
+ SustainabilityLedgerEntry.SetRange("Document No.", PostedInvoiceNo);
+ SustainabilityLedgerEntry.FindFirst();
+ Assert.AreEqual(
+ 0,
+ SustainabilityLedgerEntry."CO2e Emission",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), 0, SustainabilityLedgerEntry.TableCaption()));
+ Assert.AreEqual(
+ 0,
+ SustainabilityLedgerEntry."Carbon Fee",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Carbon Fee"), 0, SustainabilityLedgerEntry.TableCaption()));
+
+ // [WHEN] Run Report "Batch Update Carbon Emission".
+ BatchUpdateCarbonEmission.UseRequestPage(false);
+ BatchUpdateCarbonEmission.Run();
+
+ // [VERIFY] Verify CO2e Emission and Carbon Fee in Sustainability Ledger Entry throug Report "Batch Update Carbon Emission".
+ SustainabilityLedgerEntry.SetRange("Document No.", PostedInvoiceNo);
+ SustainabilityLedgerEntry.FindFirst();
+ Assert.AreEqual(
+ ExpectedCO2eEmission,
+ SustainabilityLedgerEntry."CO2e Emission",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("CO2e Emission"), ExpectedCO2eEmission, SustainabilityLedgerEntry.TableCaption()));
+ Assert.AreEqual(
+ ExpectedCarbonFee,
+ SustainabilityLedgerEntry."Carbon Fee",
+ StrSubstNo(ValueMustBeEqualErr, SustainabilityLedgerEntry.FieldCaption("Carbon Fee"), ExpectedCarbonFee, SustainabilityLedgerEntry.TableCaption()));
+ end;
+
local procedure CreateUserSetup(var UserSetup: Record "User Setup"; UserID: Code[50])
begin
UserSetup.Init();
@@ -1884,6 +2243,56 @@ codeunit 148184 "Sustainability Posting Test"
exit(PurchaseHeader."No.");
end;
+ local procedure GetCarbonFeeEmissionValues(
+ PostingDate: Date;
+ CountryRegionCode: Code[20];
+ EmissionCO2: Decimal;
+ EmissionN2O: Decimal;
+ EmissionCH4: Decimal;
+ ScopeType: Enum "Emission Scope";
+ var CO2eEmission: Decimal;
+ var CarbonFee: Decimal): Decimal
+ var
+ EmissionFee: Record "Emission Fee";
+ CO2Factor: Decimal;
+ N2OFactor: Decimal;
+ CH4Factor: Decimal;
+ CarbonFeeEmission: Decimal;
+ begin
+ EmissionFee.SetFilter("Scope Type", '%1|%2', ScopeType, ScopeType::" ");
+ EmissionFee.SetFilter("Starting Date", '<=%1|%2', PostingDate, 0D);
+ EmissionFee.SetFilter("Ending Date", '>=%1|%2', PostingDate, 0D);
+ EmissionFee.SetFilter("Country/Region Code", '%1|%2', CountryRegionCode, '');
+
+ if EmissionCO2 <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::CO2) then begin
+ CO2Factor := EmissionFee."Carbon Equivalent Factor";
+ CarbonFeeEmission := EmissionFee."Carbon Fee";
+ end;
+
+ if EmissionN2O <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::N2O) then begin
+ N2OFactor := EmissionFee."Carbon Equivalent Factor";
+ CarbonFeeEmission += EmissionFee."Carbon Fee";
+ end;
+
+ if EmissionCH4 <> 0 then
+ if FindEmissionFeeForEmissionType(EmissionFee, Enum::"Emission Type"::CH4) then begin
+ CH4Factor := EmissionFee."Carbon Equivalent Factor";
+ CarbonFeeEmission += EmissionFee."Carbon Fee";
+ end;
+
+ CO2eEmission := (EmissionCO2 * CO2Factor) + (EmissionN2O * N2OFactor) + (EmissionCH4 * CH4Factor);
+ CarbonFee := CO2eEmission * CarbonFeeEmission;
+ end;
+
+ local procedure FindEmissionFeeForEmissionType(var EmissionFee: Record "Emission Fee"; EmissionType: Enum "Emission Type"): Boolean
+ begin
+ EmissionFee.SetRange("Emission Type", EmissionType);
+ if EmissionFee.FindLast() then
+ exit(true);
+ end;
+
[ModalPageHandler]
[Scope('OnPrem')]
procedure PurchaseOrderStatisticsPageHandler(var PurchaseOrderStatisticsPage: TestPage "Purchase Order Statistics")
@@ -1971,4 +2380,15 @@ codeunit 148184 "Sustainability Posting Test"
SustainabilityLedgerEntries.Filter.GetFilter("Posting Date"),
StrSubstNo(FilterMustBeEqualErr, ExpectedFilter, SustainabilityLedgerEntries.Caption()));
end;
+
+ [ConfirmHandler]
+ procedure ConfirmHandler(Question: Text[1024]; var Reply: Boolean)
+ begin
+ Reply := true;
+ end;
+
+ [MessageHandler]
+ procedure MessageHandler(Msg: Text[1024])
+ begin
+ end;
}
\ No newline at end of file
diff --git a/Apps/W1/VATGroupManagement/app/src/Codeunits/VATGroupCommunication.Codeunit.al b/Apps/W1/VATGroupManagement/app/src/Codeunits/VATGroupCommunication.Codeunit.al
index acfeb777a9..27d903389d 100644
--- a/Apps/W1/VATGroupManagement/app/src/Codeunits/VATGroupCommunication.Codeunit.al
+++ b/Apps/W1/VATGroupManagement/app/src/Codeunits/VATGroupCommunication.Codeunit.al
@@ -8,6 +8,7 @@ using Microsoft.Finance.VAT.Reporting;
using System.Azure.KeyVault;
using System.Environment;
using System.Security.Authentication;
+using System.Telemetry;
#if not CLEAN25
using System.Text;
#endif
@@ -151,11 +152,11 @@ codeunit 4700 "VAT Group Communication"
end;
if (FirstPartyAppId <> '') and (not FirstPartyAppCertificate.IsEmpty()) then begin
- Session.LogMessage('0000MXQ', AttemptingAuthCodeTokenWithCertTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', VATGroupTok);
+ Session.LogMessage('0000MXQ', AttemptingAuthCodeTokenWithCertTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', VATGroupTok, 'AppId', FirstPartyAppId);
OAuth2.AcquireTokenByAuthorizationCodeWithCertificate(FirstPartyAppId, FirstPartyAppCertificate, AuthorityURL, RedirectURL, ResourceURL, PromptInteraction::Login, BearerToken, AuthError)
end else begin
CreateScopesFromResourceURL(ResourceURL, Scopes);
- Session.LogMessage('0000MXR', AttemptingAuthCodeTokenWithClientSecretTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', VATGroupTok);
+ Session.LogMessage('0000MXR', AttemptingAuthCodeTokenWithClientSecretTxt, Verbosity::Normal, DataClassification::SystemMetadata, TelemetryScope::ExtensionPublisher, 'Category', VATGroupTok, 'AppId', ClientId);
OAuth2.AcquireTokenByAuthorizationCode(ClientId, ClientSecret, AuthorityURL, RedirectURL, Scopes, PromptInteraction::Login, BearerToken, AuthError);
end;
@@ -285,6 +286,7 @@ codeunit 4700 "VAT Group Communication"
[NonDebuggable]
local procedure PrepareHeaders(HttpRequestMessage: HttpRequestMessage; IsBatch: Boolean)
var
+ FeatureTelemetry: Codeunit "Feature Telemetry";
#if not CLEAN25
Base64Convert: Codeunit "Base64 Convert";
#endif
@@ -293,6 +295,8 @@ codeunit 4700 "VAT Group Communication"
Base64AuthHeader: SecretText;
#endif
begin
+ FeatureTelemetry.LogUptake('0000NG8', FeatureName(), Enum::"Feature Uptake Status"::Used);
+ FeatureTelemetry.LogUsage('0000NG9', FeatureName(), 'Submitting VAT return to group representative.');
HttpRequestMessage.GetHeaders(HttpRequestHeaders);
HttpRequestHeaders.Add('Accept', 'application/json');
@@ -394,4 +398,9 @@ codeunit 4700 "VAT Group Communication"
Scopes.Add(ResourceURL + BCReadWriteScopeTok);
Scopes.Add(ResourceURL + BCUserImpersonationScopeTok);
end;
+
+ internal procedure FeatureName(): Text
+ begin
+ exit('VAT Group Management');
+ end;
}
\ No newline at end of file
diff --git a/Apps/W1/VATGroupManagement/app/src/Pages/VATGroupSetupGuide.Page.al b/Apps/W1/VATGroupManagement/app/src/Pages/VATGroupSetupGuide.Page.al
index d97ec39eff..be906180de 100644
--- a/Apps/W1/VATGroupManagement/app/src/Pages/VATGroupSetupGuide.Page.al
+++ b/Apps/W1/VATGroupManagement/app/src/Pages/VATGroupSetupGuide.Page.al
@@ -12,6 +12,7 @@ using System.Environment;
using System.Security.Authentication;
using System.Threading;
using System.Utilities;
+using System.Telemetry;
page 4705 "VAT Group Setup Guide"
{
@@ -506,9 +507,15 @@ page 4705 "VAT Group Setup Guide"
InFooterBar = true;
trigger OnAction()
+ var
+ FeatureTelemetry: Codeunit "Feature Telemetry";
+ VATGroupCommunication: Codeunit "VAT Group Communication";
begin
ValidateAndFinishSetup();
CurrPage.Close();
+ FeatureTelemetry.LogUptake('0000NGA', VATGroupCommunication.FeatureName(), Enum::"Feature Uptake Status"::"Set up");
+ FeatureTelemetry.LogUptake('0000NGB', VATGroupCommunication.FeatureName(), Enum::"Feature Uptake Status"::Used);
+ FeatureTelemetry.LogUsage('0000NGD', VATGroupCommunication.FeatureName(), 'Successfully set up');
end;
}
}
@@ -544,6 +551,8 @@ page 4705 "VAT Group Setup Guide"
var
EnvironmentInformation: Codeunit "Environment Information";
VATGroupHelperFunctions: Codeunit "VAT Group Helper Functions";
+ FeatureTelemetry: Codeunit "Feature Telemetry";
+ VATGroupCommunication: Codeunit "VAT Group Communication";
begin
if not VATReportSetup.Get() then
Error(NoVATReportSetupErr);
@@ -559,6 +568,7 @@ page 4705 "VAT Group Setup Guide"
VATGroupAuthenticationTypeSaas := VATGroupAuthenticationTypeSaas::OAuth2;
if IsSaaS then
GroupRepresentativeOnSaaS := true;
+ FeatureTelemetry.LogUptake('0000NGC', VATGroupCommunication.FeatureName(), Enum::"Feature Uptake Status"::Discovered);
end;
trigger OnQueryClosePage(CloseAction: Action): Boolean
diff --git a/Build/DisabledTests/APIV2.json b/Build/DisabledTests/APIV2.json
index aca041cb71..031498bd24 100644
--- a/Build/DisabledTests/APIV2.json
+++ b/Build/DisabledTests/APIV2.json
@@ -289,6 +289,16 @@
"CodeunitName": "APIV2 - G/L Setup E2E",
"Method": "*"
},
+ {
+ "codeunitId": 139861 ,
+ "CodeunitName": "APIV2JobQueueLogEntriesE2E",
+ "Method": "*"
+ },
+ {
+ "codeunitId": 139862 ,
+ "CodeunitName": "APIV2JobQueueEntriesE2E",
+ "Method": "*"
+ },
{
"codeunitId": 139865,
"CodeunitName": "APIV2 - Purch. Cr. Memos E2E",
@@ -334,4 +344,4 @@
"CodeunitName": "APIV2 - Fixed Assets E2E",
"Method": "*"
}
-]
\ No newline at end of file
+]
diff --git a/Build/DisabledTests/OrderTakerAgent.json b/Build/DisabledTests/OrderTakerAgent.json
new file mode 100644
index 0000000000..c9a1f1ccaf
--- /dev/null
+++ b/Build/DisabledTests/OrderTakerAgent.json
@@ -0,0 +1,7 @@
+[
+ {
+ "codeunitId": 133503,
+ "CodeunitName": "SOA Harms Test",
+ "Method": "*"
+ }
+]
\ No newline at end of file
diff --git a/Build/DisabledTests/SalesLinesSuggestionsTests.json b/Build/DisabledTests/SalesLinesSuggestionsTests.json
index d6d49dc2b9..4fa1f6d6cd 100644
--- a/Build/DisabledTests/SalesLinesSuggestionsTests.json
+++ b/Build/DisabledTests/SalesLinesSuggestionsTests.json
@@ -68,5 +68,20 @@
"codeunitId": 149826,
"CodeunitName": "Extract Info. Accuracy",
"Method": "*"
+ },
+ {
+ "codeunitId": 139782,
+ "CodeunitName": "Item Entity Search",
+ "Method": "*"
+ },
+ {
+ "codeunitId": 149823,
+ "CodeunitName": "Load Suggestions from csv",
+ "Method": "*"
+ },
+ {
+ "codeunitId": 149828,
+ "CodeunitName": "Search Items With Filters Test",
+ "Method": "*"
}
]
\ No newline at end of file
diff --git a/Build/projects/1st Party Apps (W1)/.AL-Go/cloudDevEnv.ps1 b/Build/projects/1st Party Apps (W1)/.AL-Go/cloudDevEnv.ps1
index 38799b4107..1b8ca3f1a9 100644
--- a/Build/projects/1st Party Apps (W1)/.AL-Go/cloudDevEnv.ps1
+++ b/Build/projects/1st Party Apps (W1)/.AL-Go/cloudDevEnv.ps1
@@ -6,11 +6,27 @@
Param(
[string] $environmentName = "",
[bool] $reuseExistingEnvironment,
- [switch] $fromVSCode
+ [switch] $fromVSCode,
+ [switch] $clean
)
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
+function DownloadHelperFile {
+ param(
+ [string] $url,
+ [string] $folder
+ )
+
+ $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
+ $name = [System.IO.Path]::GetFileName($url)
+ Write-Host "Downloading $name from $url"
+ $path = Join-Path $folder $name
+ Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
+ $ProgressPreference = $prevProgressPreference
+ return $path
+}
+
try {
Clear-Host
Write-Host
@@ -24,17 +40,11 @@ Write-Host -ForegroundColor Yellow @'
'@
-$webClient = New-Object System.Net.WebClient
-$webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
-$webClient.Encoding = [System.Text.Encoding]::UTF8
-$GitHubHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/Github-Helper.psm1'
-Write-Host "Downloading GitHub Helper module from $GitHubHelperUrl"
-$GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
-$webClient.DownloadFile($GitHubHelperUrl, $GitHubHelperPath)
-$ALGoHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/AL-Go-Helper.ps1'
-Write-Host "Downloading AL-Go Helper script from $ALGoHelperUrl"
-$ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
-$webClient.DownloadFile($ALGoHelperUrl, $ALGoHelperPath)
+$tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
+New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
+$GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Github-Helper.psm1' -folder $tmpFolder
+$ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/AL-Go-Helper.ps1' -folder $tmpFolder
+DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Packages.json' -folder $tmpFolder | Out-Null
Import-Module $GitHubHelperPath
. $ALGoHelperPath -local
@@ -78,7 +88,8 @@ CreateDevEnv `
-environmentName $environmentName `
-reuseExistingEnvironment:$reuseExistingEnvironment `
-baseFolder $baseFolder `
- -project $project
+ -project $project `
+ -clean:$clean
}
catch {
Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
diff --git a/Build/projects/1st Party Apps (W1)/.AL-Go/localDevEnv.ps1 b/Build/projects/1st Party Apps (W1)/.AL-Go/localDevEnv.ps1
index a561a18d8f..c98482bd3d 100644
--- a/Build/projects/1st Party Apps (W1)/.AL-Go/localDevEnv.ps1
+++ b/Build/projects/1st Party Apps (W1)/.AL-Go/localDevEnv.ps1
@@ -5,15 +5,32 @@
#
Param(
[string] $containerName = "",
+ [ValidateSet("UserPassword", "Windows")]
[string] $auth = "",
[pscredential] $credential = $null,
[string] $licenseFileUrl = "",
[switch] $fromVSCode,
- [switch] $accept_insiderEula
+ [switch] $accept_insiderEula,
+ [switch] $clean
)
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
+function DownloadHelperFile {
+ param(
+ [string] $url,
+ [string] $folder
+ )
+
+ $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
+ $name = [System.IO.Path]::GetFileName($url)
+ Write-Host "Downloading $name from $url"
+ $path = Join-Path $folder $name
+ Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
+ $ProgressPreference = $prevProgressPreference
+ return $path
+}
+
try {
Clear-Host
Write-Host
@@ -27,17 +44,11 @@ Write-Host -ForegroundColor Yellow @'
'@
-$webClient = New-Object System.Net.WebClient
-$webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
-$webClient.Encoding = [System.Text.Encoding]::UTF8
-$GitHubHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/Github-Helper.psm1'
-Write-Host "Downloading GitHub Helper module from $GitHubHelperUrl"
-$GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
-$webClient.DownloadFile($GitHubHelperUrl, $GitHubHelperPath)
-$ALGoHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/AL-Go-Helper.ps1'
-Write-Host "Downloading AL-Go Helper script from $ALGoHelperUrl"
-$ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
-$webClient.DownloadFile($ALGoHelperUrl, $ALGoHelperPath)
+$tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
+New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
+$GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Github-Helper.psm1' -folder $tmpFolder
+$ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/AL-Go-Helper.ps1' -folder $tmpFolder
+DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Packages.json' -folder $tmpFolder | Out-Null
Import-Module $GitHubHelperPath
. $ALGoHelperPath -local
@@ -134,7 +145,8 @@ CreateDevEnv `
-auth $auth `
-credential $credential `
-licenseFileUrl $licenseFileUrl `
- -accept_insiderEula:$accept_insiderEula
+ -accept_insiderEula:$accept_insiderEula `
+ -clean:$clean
}
catch {
Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
diff --git a/Build/projects/1st Party Apps Tests (W1)/.AL-Go/cloudDevEnv.ps1 b/Build/projects/1st Party Apps Tests (W1)/.AL-Go/cloudDevEnv.ps1
index 38799b4107..1b8ca3f1a9 100644
--- a/Build/projects/1st Party Apps Tests (W1)/.AL-Go/cloudDevEnv.ps1
+++ b/Build/projects/1st Party Apps Tests (W1)/.AL-Go/cloudDevEnv.ps1
@@ -6,11 +6,27 @@
Param(
[string] $environmentName = "",
[bool] $reuseExistingEnvironment,
- [switch] $fromVSCode
+ [switch] $fromVSCode,
+ [switch] $clean
)
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
+function DownloadHelperFile {
+ param(
+ [string] $url,
+ [string] $folder
+ )
+
+ $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
+ $name = [System.IO.Path]::GetFileName($url)
+ Write-Host "Downloading $name from $url"
+ $path = Join-Path $folder $name
+ Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
+ $ProgressPreference = $prevProgressPreference
+ return $path
+}
+
try {
Clear-Host
Write-Host
@@ -24,17 +40,11 @@ Write-Host -ForegroundColor Yellow @'
'@
-$webClient = New-Object System.Net.WebClient
-$webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
-$webClient.Encoding = [System.Text.Encoding]::UTF8
-$GitHubHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/Github-Helper.psm1'
-Write-Host "Downloading GitHub Helper module from $GitHubHelperUrl"
-$GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
-$webClient.DownloadFile($GitHubHelperUrl, $GitHubHelperPath)
-$ALGoHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/AL-Go-Helper.ps1'
-Write-Host "Downloading AL-Go Helper script from $ALGoHelperUrl"
-$ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
-$webClient.DownloadFile($ALGoHelperUrl, $ALGoHelperPath)
+$tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
+New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
+$GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Github-Helper.psm1' -folder $tmpFolder
+$ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/AL-Go-Helper.ps1' -folder $tmpFolder
+DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Packages.json' -folder $tmpFolder | Out-Null
Import-Module $GitHubHelperPath
. $ALGoHelperPath -local
@@ -78,7 +88,8 @@ CreateDevEnv `
-environmentName $environmentName `
-reuseExistingEnvironment:$reuseExistingEnvironment `
-baseFolder $baseFolder `
- -project $project
+ -project $project `
+ -clean:$clean
}
catch {
Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
diff --git a/Build/projects/1st Party Apps Tests (W1)/.AL-Go/localDevEnv.ps1 b/Build/projects/1st Party Apps Tests (W1)/.AL-Go/localDevEnv.ps1
index a561a18d8f..c98482bd3d 100644
--- a/Build/projects/1st Party Apps Tests (W1)/.AL-Go/localDevEnv.ps1
+++ b/Build/projects/1st Party Apps Tests (W1)/.AL-Go/localDevEnv.ps1
@@ -5,15 +5,32 @@
#
Param(
[string] $containerName = "",
+ [ValidateSet("UserPassword", "Windows")]
[string] $auth = "",
[pscredential] $credential = $null,
[string] $licenseFileUrl = "",
[switch] $fromVSCode,
- [switch] $accept_insiderEula
+ [switch] $accept_insiderEula,
+ [switch] $clean
)
$errorActionPreference = "Stop"; $ProgressPreference = "SilentlyContinue"; Set-StrictMode -Version 2.0
+function DownloadHelperFile {
+ param(
+ [string] $url,
+ [string] $folder
+ )
+
+ $prevProgressPreference = $ProgressPreference; $ProgressPreference = 'SilentlyContinue'
+ $name = [System.IO.Path]::GetFileName($url)
+ Write-Host "Downloading $name from $url"
+ $path = Join-Path $folder $name
+ Invoke-WebRequest -UseBasicParsing -uri $url -OutFile $path
+ $ProgressPreference = $prevProgressPreference
+ return $path
+}
+
try {
Clear-Host
Write-Host
@@ -27,17 +44,11 @@ Write-Host -ForegroundColor Yellow @'
'@
-$webClient = New-Object System.Net.WebClient
-$webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
-$webClient.Encoding = [System.Text.Encoding]::UTF8
-$GitHubHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/Github-Helper.psm1'
-Write-Host "Downloading GitHub Helper module from $GitHubHelperUrl"
-$GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
-$webClient.DownloadFile($GitHubHelperUrl, $GitHubHelperPath)
-$ALGoHelperUrl = 'https://raw.githubusercontent.com/microsoft/AL-Go/4c5bfbca1adebbf997f63882df4b9074a19aac1d/Actions/AL-Go-Helper.ps1'
-Write-Host "Downloading AL-Go Helper script from $ALGoHelperUrl"
-$ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
-$webClient.DownloadFile($ALGoHelperUrl, $ALGoHelperPath)
+$tmpFolder = Join-Path ([System.IO.Path]::GetTempPath()) "$([Guid]::NewGuid().ToString())"
+New-Item -Path $tmpFolder -ItemType Directory -Force | Out-Null
+$GitHubHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Github-Helper.psm1' -folder $tmpFolder
+$ALGoHelperPath = DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/AL-Go-Helper.ps1' -folder $tmpFolder
+DownloadHelperFile -url 'https://raw.githubusercontent.com/microsoft/AL-Go/564c339085ae0ffa74c5bde71c15fea7fe54bbf1/Actions/Packages.json' -folder $tmpFolder | Out-Null
Import-Module $GitHubHelperPath
. $ALGoHelperPath -local
@@ -134,7 +145,8 @@ CreateDevEnv `
-auth $auth `
-credential $credential `
-licenseFileUrl $licenseFileUrl `
- -accept_insiderEula:$accept_insiderEula
+ -accept_insiderEula:$accept_insiderEula `
+ -clean:$clean
}
catch {
Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"