Skip to content

Commit

Permalink
[Shopify] Shopify return location on refunds (#26728)
Browse files Browse the repository at this point in the history
This pull request does not have a related issue as it's part of delivery
for development agreed directly with @AndreiPanko

Added a setting to the shopify shop to use original return location
(From shopify) or a default return location set in the Shopify shop when
creating Credit Memos for Refunds.

Modified the return and refund creation to pull original location from
shopify refund or return orders.
When an item on a single return order in shopify is restocked to
multiple locations, we cannot determine a single location to put on a
return line.

If there is any reason that Original location couldn't be determined
then the Default Return Location will be used during the creation of a
Credit Memo.

Fixes #26819

Fixes
[AB#540965](https://dynamicssmb2.visualstudio.com/1fcb79e7-ab07-432a-a3c6-6cf5a88ba4a5/_workitems/edit/540965)

---------

Co-authored-by: Tine Staric <[email protected]>
  • Loading branch information
tinestaric and Tine Staric authored Jul 16, 2024
1 parent 0ba11c1 commit 096df31
Show file tree
Hide file tree
Showing 15 changed files with 311 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Microsoft.Integration.Shopify;

enum 30162 "Shpfy Return Location Priority"
{
Access = Public;
Extensible = false;

value(0; "Default Return Location")
{
Caption = 'Default Return Location';
}
value(1; "Original -> Default Location")
{
Caption = 'Original -> Default Location';
}
}
6 changes: 6 additions & 0 deletions Apps/W1/Shopify/app/src/Base/Pages/ShpfyShopCard.Page.al
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,12 @@ page 30101 "Shpfy Shop Card"
ShowCaption = false;
Visible = IsReturnRefundsVisible;

field("Return Location Priority"; Rec."Return Location Priority")
{
ApplicationArea = All;
Caption = 'Return Location Priority';
ToolTip = 'Specifies the priority of the return location.';
}
field("Location Code of Returns"; Rec."Return Location")
{
ApplicationArea = All;
Expand Down
11 changes: 8 additions & 3 deletions Apps/W1/Shopify/app/src/Base/Tables/ShpfyShop.Table.al
Original file line number Diff line number Diff line change
Expand Up @@ -233,8 +233,8 @@ table 30102 "Shpfy Shop"
ObsoleteState = Pending;
ObsoleteTag = '24.0';
#else
ObsoleteState = Removed;
ObsoleteTag = '27.0';
ObsoleteState = Removed;
ObsoleteTag = '27.0';
#endif
}
field(30; "Shopify Can Update Customer"; Boolean)
Expand Down Expand Up @@ -511,7 +511,7 @@ table 30102 "Shpfy Shop"
}
field(73; "Return Location"; Code[10])
{
Caption = 'Return Location';
Caption = 'Default Return Location';
DataClassification = CustomerContent;
TableRelation = Location where("Use As In-Transit" = const(false));
}
Expand Down Expand Up @@ -778,6 +778,11 @@ table 30102 "Shpfy Shop"
end;
#endif
}
field(128; "Return Location Priority"; Enum "Shpfy Return Location Priority")
{
Caption = 'Return Location Priority';
DataClassification = CustomerContent;
}
field(200; "Shop Id"; Integer)
{
DataClassification = SystemMetadata;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ codeunit 30232 "Shpfy GQL NextRefundLines" implements "Shpfy IGraphQL"

internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ refund(id: \"gid://shopify/Refund/{{RefundId}}\") { refundLineItems(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { lineItem { id } quantity restockType restocked priceSet { presentmentMoney { amount } shopMoney { amount }} subtotalSet { presentmentMoney { amount } shopMoney { amount }} totalTaxSet { presentmentMoney { amount } shopMoney { amount }}}}}}"}');
exit('{"query":"{ refund(id: \"gid://shopify/Refund/{{RefundId}}\") { refundLineItems(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { lineItem { id } quantity restockType location { legacyResourceId } restocked priceSet { presentmentMoney { amount } shopMoney { amount }} subtotalSet { presentmentMoney { amount } shopMoney { amount }} totalTaxSet { presentmentMoney { amount } shopMoney { amount }}}}}}"}');
end;

internal procedure GetExpectedCost(): Integer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Microsoft.Integration.Shopify;

/// <summary>
/// Codeunit Shpfy GQL NextRevFulfillOrdLns (ID 30349) implements Interface Shpfy IGraphQL.
/// </summary>
codeunit 30349 "Shpfy GQL NextRevFulfillOrdLns" implements "Shpfy IGraphQL"
{
Access = Internal;

/// <summary>
/// GetGraphQL.
/// </summary>
/// <returns>Return value of type Text.</returns>
internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ reverseFulfillmentOrder(id: \"{{FulfillOrderId}}\") { lineItems(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { id fulfillmentLineItem { id lineItem { id name } } dispositions { id quantity type location { id legacyResourceId } } } } } }"}');
end;

/// <summary>
/// GetExpectedCost.
/// </summary>
/// <returns>Return value of type Integer.</returns>
internal procedure GetExpectedCost(): Integer
begin
exit(15);
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Microsoft.Integration.Shopify;

/// <summary>
/// Codeunit Shpfy GQL NextRevFulfillOrders (ID 30347) implements Interface Shpfy IGraphQL.
/// </summary>
codeunit 30347 "Shpfy GQL NextRevFulfillOrders" implements "Shpfy IGraphQL"
{
Access = Internal;

/// <summary>
/// GetGraphQL.
/// </summary>
/// <returns>Return value of type Text.</returns>
internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ return(id: \"gid://shopify/Return/{{ReturnId}}\") { reverseFulfillmentOrders(first: 10, after:\"{{After}}\") { pageInfo { endCursor hasNextPage } nodes { id } } } }"}');

end;

/// <summary>
/// GetExpectedCost.
/// </summary>
/// <returns>Return value of type Integer.</returns>
internal procedure GetExpectedCost(): Integer
begin
exit(7);
end;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ codeunit 30230 "Shpfy GQL RefundLines" implements "Shpfy IGraphQL"

internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ refund(id: \"gid://shopify/Refund/{{RefundId}}\") { refundLineItems(first: 10) { pageInfo { endCursor hasNextPage } nodes { lineItem { id } quantity restockType restocked priceSet { presentmentMoney { amount } shopMoney { amount }} subtotalSet { presentmentMoney { amount } shopMoney { amount }} totalTaxSet { presentmentMoney { amount } shopMoney { amount }}}}}}"}');
exit('{"query":"{ refund(id: \"gid://shopify/Refund/{{RefundId}}\") { refundLineItems(first: 10) { pageInfo { endCursor hasNextPage } nodes { lineItem { id } quantity restockType location { legacyResourceId } restocked priceSet { presentmentMoney { amount } shopMoney { amount }} subtotalSet { presentmentMoney { amount } shopMoney { amount }} totalTaxSet { presentmentMoney { amount } shopMoney { amount }}}}}}"}');
end;

internal procedure GetExpectedCost(): Integer
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Microsoft.Integration.Shopify;

/// <summary>
/// Codeunit Shpfy GQL RevFulfillOrderLines (ID 30348) implements Interface Shpfy IGraphQL.
/// </summary>
codeunit 30348 "Shpfy GQL RevFulfillOrderLines" implements "Shpfy IGraphQL"
{
Access = Internal;

/// <summary>
/// GetGraphQL.
/// </summary>
/// <returns>Return value of type Text.</returns>
internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ reverseFulfillmentOrder(id: \"{{FulfillOrderId}}\") { id lineItems(first: 10) { nodes { id fulfillmentLineItem { id lineItem { id name } } dispositions { id quantity type location { id legacyResourceId } } } } } }"}');
end;

/// <summary>
/// GetExpectedCost.
/// </summary>
/// <returns>Return value of type Integer.</returns>
internal procedure GetExpectedCost(): Integer
begin
exit(15);
end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Microsoft.Integration.Shopify;

/// <summary>
/// Codeunit Shpfy GQL RevFulfillOrders (ID 30346) implements Interface Shpfy IGraphQL.
/// </summary>
codeunit 30346 "Shpfy GQL RevFulfillOrders" implements "Shpfy IGraphQL"
{
Access = Internal;

/// <summary>
/// GetGraphQL.
/// </summary>
/// <returns>Return value of type Text.</returns>
internal procedure GetGraphQL(): Text
begin
exit('{"query":"{ return(id: \"gid://shopify/Return/{{ReturnId}}\") { reverseFulfillmentOrders(first: 10) { pageInfo { endCursor hasNextPage } nodes { id } } } }"}');

end;

/// <summary>
/// GetExpectedCost.
/// </summary>
/// <returns>Return value of type Integer.</returns>
internal procedure GetExpectedCost(): Integer
begin
exit(7);
end;
}
20 changes: 20 additions & 0 deletions Apps/W1/Shopify/app/src/GraphQL/Enums/ShpfyGraphQLType.Enum.al
Original file line number Diff line number Diff line change
Expand Up @@ -405,4 +405,24 @@ enum 30111 "Shpfy GraphQL Type" implements "Shpfy IGraphQL"
Caption = 'Get Order Transactions';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL OrderTransactions";
}
value(87; GetReverseFulfillmentOrders)
{
Caption = 'Get Reverse Fulfillment Orders';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL RevFulfillOrders";
}
value(88; GetNextReverseFulfillmentOrders)
{
Caption = 'Get Next Reverse Fulfillment Orders';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL NextRevFulfillOrders";
}
value(89; GetReverseFulfillmentOrderLines)
{
Caption = 'Get Reverse Fulfillment Order Lines';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL RevFulfillOrderLines";
}
value(90; GetNextReverseFulfillmentOrderLines)
{
Caption = 'Get Next Reverse Fulfillment Order Lines';
Implementation = "Shpfy IGraphQL" = "Shpfy GQL NextRevFulfillOrdLns";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ codeunit 30228 "Shpfy Refunds API"
RefundHeader: Record "Shpfy Refund Header";
GraphQLType: Enum "Shpfy GraphQL Type";
Parameters: Dictionary of [text, Text];
ReturnLocations: Dictionary of [BigInteger, BigInteger];
JResponse: JsonToken;
JLines: JsonArray;
JLine: JsonToken;
begin
GetRefundHeader(RefundId, UpdatedAt, RefundHeader);
ReturnLocations := CollectReturnLocations(RefundHeader."Return Id");

Parameters.Add('RefundId', Format(RefundId));
GraphQLType := "Shpfy GraphQL Type"::GetRefundLines;
repeat
Expand All @@ -46,8 +49,9 @@ codeunit 30228 "Shpfy Refunds API"
Parameters.Set('After', JsonHelper.GetValueAsText(JResponse, 'data.refund.refundLineItems.pageInfo.endCursor'))
else
Parameters.Add('After', JsonHelper.GetValueAsText(JResponse, 'data.refund.refundLineItems.pageInfo.endCursor'));

foreach JLine in JLines do
FillInRefundLine(RefundId, JLine.AsObject(), IsNonZeroOrReturnRefund(RefundHeader));
FillInRefundLine(RefundId, JLine.AsObject(), IsNonZeroOrReturnRefund(RefundHeader), ReturnLocations);
until not JsonHelper.GetValueAsBoolean(JResponse, 'data.refund.refundLineItems.pageInfo.hasNextPage');
end;

Expand Down Expand Up @@ -88,21 +92,34 @@ codeunit 30228 "Shpfy Refunds API"
DataCapture.Add(Database::"Shpfy Refund Header", RefundHeader.SystemId, JResponse);
end;

local procedure FillInRefundLine(RefundId: BigInteger; JLine: JsonObject; NonZeroOrReturnRefund: Boolean)

local procedure CollectReturnLocations(ReturnId: BigInteger): Dictionary of [BigInteger, BigInteger]
var
ReturnsAPI: Codeunit "Shpfy Returns API";
begin
if ReturnId <> 0 then
exit(ReturnsAPI.GetReturnLocations(ReturnId));
end;

local procedure FillInRefundLine(RefundId: BigInteger; JLine: JsonObject; NonZeroOrReturnRefund: Boolean; ReturnLocations: Dictionary of [BigInteger, BigInteger])
var
DataCapture: Record "Shpfy Data Capture";
RefundLine: Record "Shpfy Refund Line";
RefundLineRecordRef: RecordRef;
Id: BigInteger;
ReturnLocation: BigInteger;
begin
Id := CommunicationMgt.GetIdOfGId(JsonHelper.GetValueAsText(JLine, 'lineItem.id'));

if not RefundLine.Get(RefundId, Id) then begin
RefundLine."Refund Line Id" := Id;
RefundLine."Refund Id" := RefundId;
RefundLine."Order Line Id" := Id;
RefundLine.Insert();
end;

RefundLine."Restock Type" := RefundEnumConvertor.ConvertToReStockType(JsonHelper.GetValueAsText(JLine, 'restockType'));

RefundLineRecordRef.GetTable(RefundLine);
JsonHelper.GetValueIntoField(JLine, 'quantity', RefundLineRecordRef, RefundLine.FieldNo(Quantity));
JsonHelper.GetValueIntoField(JLine, 'restocked', RefundLineRecordRef, RefundLine.FieldNo(Restocked));
Expand All @@ -113,8 +130,17 @@ codeunit 30228 "Shpfy Refunds API"
JsonHelper.GetValueIntoField(JLine, 'totalTaxSet.shopMoney.amount', RefundLineRecordRef, RefundLine.FieldNo("Total Tax Amount"));
JsonHelper.GetValueIntoField(JLine, 'totalTaxSet.presentmentMoney.amount', RefundLineRecordRef, RefundLine.FieldNo("Presentment Total Tax Amount"));
RefundLineRecordRef.SetTable(RefundLine);

RefundLine."Can Create Credit Memo" := NonZeroOrReturnRefund;
RefundLine."Location Id" := JsonHelper.GetValueAsBigInteger(JLine, 'location.legacyResourceId');

// If refund was created from a return, the location needs to come from the return
// If Item was restocked to multiple locations, the return location is not known
if (RefundLine."Location Id" = 0) and (ReturnLocations.Get(RefundLine."Order Line Id", ReturnLocation)) then
RefundLine."Location Id" := ReturnLocation;

RefundLine.Modify();

RefundLineRecordRef.Close();
DataCapture.Add(Database::"Shpfy Refund Line", RefundLine.SystemId, JLine);
end;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ table 30145 "Shpfy Refund Line"
CalcFormula = lookup("Shpfy Order Line"."Gift Card" where("Line Id" = field("Order Line Id")));
Editable = false;
}
field(105; "Location Id"; BigInteger)
{
Caption = 'Location Id';
DataClassification = SystemMetadata;
Editable = false;
}
}
keys
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,6 @@ codeunit 30246 "Shpfy Create Sales Doc. Refund"
SalesHeader.Validate("Document Date", DT2Date(RefundHeader."Created At"));
if OrderMgt.FindTaxArea(OrderHeader, ShopifyTaxArea) and (ShopifyTaxArea."Tax Area Code" <> '') then
SalesHeader.Validate("Tax Area Code", ShopifyTaxArea."Tax Area Code");
SalesHeader.Validate("Location Code", Shop."Return Location");
end;
SalesHeader."Shpfy Refund Id" := RefundHeader."Refund Id";
SalesHeader.Modify(true);
Expand Down Expand Up @@ -143,6 +142,7 @@ codeunit 30246 "Shpfy Create Sales Doc. Refund"
RefundLine: Record "Shpfy Refund Line";
ReturnLine: Record "Shpfy Return Line";
GiftCard: Record "Shpfy Gift Card";
ShopLocation: Record "Shpfy Shop Location";
LineNo: Integer;
OpenAmount: Decimal;
IsHandled: Boolean;
Expand Down Expand Up @@ -181,7 +181,13 @@ codeunit 30246 "Shpfy Create Sales Doc. Refund"
SalesLine.Validate("No.", RefundLine."Item No.");
if RefundLine."Variant Code" <> '' then
SalesLine.Validate("Variant Code", RefundLine."Variant Code");
SalesLine.Validate("Location Code", Shop."Return Location");

if ShopLocation.Get(Shop.Code, RefundLine."Location Id") then
SalesLine.Validate("Location Code", ShopLocation."Default Location Code");

If (Shop."Return Location Priority" = "Shpfy Return Location Priority"::"Default Return Location") or (SalesLine."Location Code" = '') then
SalesLine.Validate("Location Code", Shop."Return Location");

end;
SalesLine.Validate(Quantity, RefundLine.Quantity);
SalesLine.Validate("Unit Price", RefundLine.Amount);
Expand Down Expand Up @@ -233,7 +239,13 @@ codeunit 30246 "Shpfy Create Sales Doc. Refund"
SalesLine.Validate("No.", ReturnLine."Item No.");
if ReturnLine."Variant Code" <> '' then
SalesLine.Validate("Variant Code", ReturnLine."Variant Code");
SalesLine.Validate("Location Code", Shop."Return Location");

if ShopLocation.Get(Shop.Code, ReturnLine."Location Id") then
SalesLine.Validate("Location Code", ShopLocation."Default Location Code");

If (Shop."Return Location Priority" = "Shpfy Return Location Priority"::"Default Return Location") or (SalesLine."Location Code" = '') then
SalesLine.Validate("Location Code", Shop."Return Location");

SalesLine.Validate(Quantity, ReturnLine.Quantity);
SalesLine.Validate("Unit Price", ReturnLine."Discounted Total Amount" / ReturnLine.Quantity);
end;
Expand Down
Loading

0 comments on commit 096df31

Please sign in to comment.