Tesco Claims Handle
Version | Date | Created / Updated | Notes |
---|---|---|---|
v1.0 | Hristiyan Georgiev | Initial version | |
v1.1 | 02/02/2025 | Hristiyan Georgiev | Added response and mapping for accepting the return |
The seller initiated cancellations/returns will be handled via the claims functionality in Hemi. The purpose of the page is to give a good understanding on how that will work.
Seller or Operator initiated refunds will be handled via Claims in Hemi. This means when the customer or marketplace operator puts a request for refund/cancellation we will need to store a claim and then act on it (Reject or Accept).
Here is a diagram to get a better understanding on how it should work :
Per account we can set by default to Accept or Reject refund; In Account Tesco
table we need to add additional options which will be Cancel Action Default and Return Action Default which will work as a default action if we would like to automatically accept or reject all claims for the specific account. If is “Accept“ , by default we accept the refund, if is “Reject“ , by default we reject the refund.
All the validations, triggers etc. are as per our claims abstraction - Claims general management.
Please note that since the acceptance/rejection of claims is done on a line level, we want to store each line as a separate claim in Hemi.
Get Refund Requests
GraphQL Query :
query GetRefundRequestsUpdatedSince
($pageSize: Int $endCursor: String $updatedSince: ISO8601DateTime!)
{
updatedRefundRequests
(
updatedSince: $updatedSince
first: $pageSize
after: $endCursor
)
{
pageInfo{
hasNextPage
endCursor
}
edges{
node{
invoice{
id
}
id
status
createdAt
initiatedBy
invoice{
id
}
lineItems{
lineItem{
id
}
id
dispatched
reason
}
}
}
}
}
Query Variables :
{
"pageSize": 50,
"endCursor": "",
"updatedSince": "2025-01-30T08:36:56+00:00"
}
Variables Mapping :
Variable | Value | Note |
---|---|---|
pageSize |
50 | This will be our page size for this query |
endCursor |
Should be null when doing the first query, and then if in the response we have hasNextPage = true, we need to use the string returned in the endCursor field. | |
updatedSince |
We want to overlap with 30 minutes. So it will be last_date_run - 30 minutes. The format is 2025-01-30T08:36:56+00:00 |
Example Response :
{
"data": {
"updatedRefundRequests": {
"pageInfo": {
"hasNextPage": false,
"endCursor": "Mg"
},
"edges": [
{
"node": {
"invoice": {
"id": "SW52b2ljZS0xMzkzOA=="
},
"id": "UmVmdW5kUmVxdWVzdC02OTk=",
"status": "AWAITING",
"createdAt": "2024-11-07T10:07:06+11:00",
"initiatedBy": "ADMIN",
"lineItems": [
{
"lineItem": {
"id": "TGluZUl0ZW0tNDM0Mw=="
},
"id": "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTk1Ng==",
"dispatched": true,
"reason": "Change of mind"
}
]
}
}
]
}
}
}
Response Mapping :
Integration Field | Hemi Mapping | Notes | ||||||
---|---|---|---|---|---|---|---|---|
data |
||||||||
updatedRefundRequests |
||||||||
pageInfo |
||||||||
hasNextPage |
N/A | |||||||
endCursor |
N/A | |||||||
edges |
||||||||
node |
||||||||
invoice |
||||||||
id |
We map this with Orders > Marketplace Order ID so we know for which order to create the claim. |
|||||||
id |
N/A | |||||||
status |
||||||||
createdAt |
Order Claim > Marketplace Date |
It will be in a format 2024-11-07T10:04:39+11:00 we need to convert it to unix timestamp |
||||||
initiatedBy |
Order Claim > Initiated By |
|||||||
lineItems |
||||||||
lineItem |
We might receive lineItem as null sometimes, and when we have it we cannot associate the claim with any product. In this case we want to skip and not save this claim. |
|||||||
id |
We need to map this with Product in Order > Item Order Line ID so we can understand which item is this for |
|||||||
id |
Order Claim > Marketplace ID |
This is the id that we need to send when accepting/rejecting the return/cancellation |
||||||
dispatched |
We need to create a logic here. |
If dispatched
= false this means it is cancellation and we need to save the claim with Type
= Cancel
If dispatched
= true then it is a return and we need to save the claim with Type
= Return.
Exchange process is not supported by API so we will not cover it. |
| | | | | | reason
| | Order Claim
> Marketplace Reason
| |
| | | | | | status
| | Order Claim
> Marketplace Status
AND
Order Claim
> Claim Status
| Mapping for Claim Status
as follows :
-PENDING_APPROVAL we map with “Created”
-AWAITING_RETURN we map with “Accepted”
-REFUND_ACCEPTED we map with “Accepted & Refunded”
-REFUNDED we map with “Accepted & Refunded”
-REFUND_DENIED we map with “Rejected” |
After the claim has been received and successfully stored we can either approve or reject it. If we approve it, there is a 2 step approval : we first approve the return, and then, once the product is returned into the warehouse, we accept the refund.
For cancellations, there is only one step approval - it is the acceptance of the refund.
Accept Return
The validations are as per the claims abstraction. Claims general management
GrpahQL Query :
mutation RefundRequestLineItemReturn($input: RefundRequestLineItemReturnMutationInput!) {
refundRequestLineItemReturn(
input: $input
) {
refundRequestLineItem {
status
}
errors {
field
messages
}
}
}
Query variables :
{
"input": {
"refundRequestLineItemId": "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTEwNDE=",
"notes": [
{
"note": "This item does not need to be returned."
}
]
}
}
Variables mapping :
Integration Field | Hemi Mapping | Notes | ||
---|---|---|---|---|
input |
||||
refundRequestLineItemId |
Order Claim > Marketplace ID |
|||
notes |
||||
note |
Order Claim > Action Reason |
We need to also have a logic and if the Action Reason is empty, we want to have a default reason of “Refund Accepted” |
<v1.1> Example success response :
{
"data": {
"refundRequestLineItemReturn": {
"refundRequestLineItem": {
"status": "AWAITING_RETURN"
},
"errors": null
}
}
}
Example error response :
{
"data": {
"refundRequestLineItemReturn": {
"refundRequestLineItem": null,
"errors": [
{
"field": "status",
"messages": [
"The refund request line item status should be pending_approval."
]
}
]
}
}
}
Response Mapping :
Integration Field | Hemi Mapping | Notes | |||
---|---|---|---|---|---|
data |
|||||
refundRequestLineItemReturn |
|||||
refundRequestLineItem |
|||||
status |
Order Claim > Marketplace Status |
AND
Order Claim > Claim Status |
With same mapping for the Claim Status as in the above table. |
errors |
field |
Order Error > Message with Type = Claim Accept |
We need to concatenate field and messages and store them together |
messages |
Order Error > Message with Type = Claim Accept |
We need to concatenate field and messages and store them together |
||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
</v1.1>
After receiving the package we need to approve(push) or reject the refund to the customer. In order to approve it, we need to obviously push a refund and this is done by making similar call as above, just with a different mutation.
When rejecting the refund, there is a separate call for that explained below.
Accept Refund
This call serves either as the first (and only) step for accepting Cancellation or the second step in accepting a Return. So we need to have a logic and if we are accepting a cancellation, we need to skip the first step (Accept Return) and go straight to this step.
Triggers, validations etc are as per Refunds send general logic
The refund record needs to be associated with the claim and with Refund Type
= Return. Once we have created a refund associated with the claim, we need to make sure we are not making a standard refund call, but rather the below call.
GraphQL Query :
mutation RefundRequestLineItemAccept(
$input: RefundRequestLineItemAcceptMutationInput!
) {
refundRequestLineItemAccept(input: $input) {
refundRequestLineItem {
status
}
errors {
field
messages
}
}
}
Query Variables :
{
"input": {
"refundRequestLineItemId": "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTk1NQ==",
"notes": [
{
"note": "This item does not need to be returned."
}
]
}
}
Variables Mapping :
Integration Field | Hemi Mapping | Notes | ||
---|---|---|---|---|
input |
||||
refundRequestLineItemId |
Order Claim > Marketplace ID |
|||
notes |
||||
note |
Order Claim > Action Reason |
We need to also have a logic and if the Action Reason is empty, we want to have a default reason of “Refund Accepted” |
Example success response :
{
"data": {
"refundRequestLineItemAccept": {
"refundRequestLineItem": {
"status": "REFUND_ACCEPTED"
},
"errors": null
}
}
}
Example error response :
{
"data": {
"refundRequestLineItemAccept": {
"refundRequestLineItem": null,
"errors": [
{
"field": "status",
"messages": [
"The refund request line item status should be pending_approval or awaiting_return."
]
}
]
}
}
}
Response Mapping :
Integration Field | Hemi Mapping | Notes | |||
---|---|---|---|---|---|
data |
|||||
refundRequestLineItemAccept |
|||||
refundRequestLineItem |
|||||
status |
Order Claim > Marketplace Status |
AND
Order Claim
> Claim Status
| With same mapping for the Claim Status
as in the first table. |
| | | errors
| | | |
| | | | field
| Order Error
> Message
with Type
= Claim Accept | We need to concatenate field
and messages
and store them together |
| | | | messages
| Order Error
> Message
with Type
= Claim Accept | We need to concatenate field
and messages
and store them together |
Reject Return/Refund
Please note that we can perform a reject action either on the first step (reject the return) or the second step (reject the refund after the return has been approved and item(s) received in the warehouse) or reject a cancellation request. All three are using the same call.
The validations are as per the claims abstraction. Claims general management
GraphQL Query :
mutation RefundRequestLineItemDeny(
$input: RefundRequestLineItemDenyMutationInput!
) {
refundRequestLineItemDeny(input: $input) {
refundRequestLineItem {
status
}
errors {
field
messages
}
}
}
Query Variables :
{
"input": {
"refundRequestLineItemId": "UmVmdW5kUmVxdWVzdExpbmVJdGVtLTk1Ng==",
"denyRefundReason": "Return time exceeded",
"notes": [
{
"note": "Refund request not accepted"
}
]
}
}
Variables Mapping :
Integration Field | Hemi Mapping | Notes | ||
---|---|---|---|---|
input |
||||
refundRequestLineItemId |
Order Claim > Marketplace ID |
|||
denyRefundReason |
Order Claim > Action Reason |
We need to also have a logic and if the Action Reason is empty, we want to have a default reason of “Refund request not accepted” |
||
notes |
||||
note |
Hardcoded as “Request not accepted” |
Example Success Response :
{
"data": {
"refundRequestLineItemDeny": {
"refundRequestLineItem": {
"status": "REFUND_DENIED"
},
"errors": null
}
}
}
Example Error Response :
{
"data": {
"refundRequestLineItemDeny": {
"refundRequestLineItem": null,
"errors": [
{
"field": "status",
"messages": [
"The refund request line item status should be pending_approval or awaiting_return."
]
}
]
}
}
}
Response Mapping :
Integration Field | Hemi Mapping | Notes | |||
---|---|---|---|---|---|
data |
|||||
refundRequestLineItemDeny |
|||||
refundRequestLineItem |
|||||
status |
Order Claim > Marketplace Status |
||||
errors |
|||||
field |
Order Error > Message with Type = Claim Reject |
We need to concatenate field and messages and store them together |
|||
messages |
Order Error > Message with Type = Claim Reject |
We need to concatenate field and messages and store them together |