Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: easypost integration #175

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions graphql/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,5 @@ type Mutation {
comment: ReturnRequestCommentInput
refundData: RefundDataInput
): ReturnRequestResponse @withUserProfile
sendReturnLabel(requestId: String!, labelUrl: String!): Boolean
}
3 changes: 2 additions & 1 deletion graphql/types/ReturnRequest.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ input PickupReturnDataInput {
country: String!
zipCode: String!
addressType: AddressType!
labelUrl: String!
}

enum AddressType {
Expand Down Expand Up @@ -110,7 +111,7 @@ type PickupReturnData {
country: String!
zipCode: String!
addressType: AddressType!
returnLabel: String
labelUrl: String
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This key coming from MD and mapped on schema.json under masterdata builder is called returnLabel. In order to be consistent we should either change how we are saving it on masterdata or how we are declaring it on schema. I think the first one is the preferred one since, since changing schema on md can have side effects in the indexation process.

}

type RefundPaymentData {
Expand Down
3 changes: 2 additions & 1 deletion manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"vtex.css-handles": "0.x",
"vtex.easypost": "0.x",
"vtex.tenant-graphql": "0.x",
"vtex.catalog-graphql": "1.x"
"vtex.catalog-graphql": "1.x",
"vtex.apps-graphql": "3.x"
},
"builders": {
"admin": "0.x",
Expand Down
8 changes: 8 additions & 0 deletions messages/context.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@
"return-app.return-request-details.order-id.link": "Order {orderId}",
"return-app.return-request-details.current-status.request-id": "<b>Request id:</b> {id}",
"return-app.return-request-details.current-status.status": "<b>Status:</b>",
"return-app.return-request-details.current-status.see-return-label": "See return label",
"admin/return-app.return-request-list.table-data.requestId.tooltip": "Tooltip with an explanation of how the customer can access the data",
"admin/return-app.settings.modal-warning.title": "Title of a modal to warn about max days settings",
"admin/return-app.settings.modal-warning.first-paragraph": "First parahraph describing the warning",
Expand All @@ -320,6 +321,13 @@
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.placehoder": "Select a drop off point",
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.error": "There was an error loading the drop off points",
"admin/return-app.return-request-details.localized-product-name.tooltip": "Tooltip for localized product names",
"admin/return-app.return-request-details.return-label.modal-title": "Are you sure you want to create a return shipping label?",
"admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.",
"admin/return-app.return-request-details.return-label.create-return-label": "Create return label",
"admin/return-app.return-request-details.return-label.see-return-label": "See return label",
"admin/return-app.return-request-details.return-label-info.tooltip": "Create a return shipping label and send it to the customer via email. The request status has to be processing. For this integration to work properly you must install and configure vtex.easypost.",
"admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully",
"admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.",
"admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests",
"admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.description": "Automatically create the return invoice in the OMS for the requests",
"return-app.return-request-details.payent-method.refund-option.refund-process": "Refund process: {automaticallyRefundPaymentMethod, select, true{Automatically} false{Manually}}",
Expand Down
16 changes: 12 additions & 4 deletions messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -308,18 +308,26 @@
"return-app.return-request-details.order-id.link": "Order {orderId}",
"return-app.return-request-details.current-status.request-id": "<b>Request ID:</b> {id}",
"return-app.return-request-details.current-status.status": "<b>Status:</b>",
"return-app.return-request-details.current-status.see-return-label": "See return label",
"admin/return-app.return-request-list.table-data.requestId.tooltip": "Customers can see their request ID inside the request details",
"admin/return-app.settings.modal-warning.title": "Please review your settings",
"admin/return-app.settings.modal-warning.first-paragraph": "It looks like you are trying to save custom return options, none of which reach <b>{maxDays}</b>. This is the maximum days set for a return.",
"admin/return-app.settings.modal-warning.second-paragraph": "A store user could potentially initiate the flow to create a return, but be unable to continue if their order date falls outside any of your custom options.",
"admin/return-app.settings.modal-warning.third-paragraph": "If you click Edit, the maximum days for a return will be reduced from <b>{maxDays}</b> to <b>{customMaxDays}</b>. This is the Max Days shown in your custom return options.",
"admin/return-app.settings.modal-warning.confirm": "Edit",
"admin/return-app.settings.modal-warning.cancel": "Cancel",
"store/return-app.return-order-details.pickup-address.drop-off-points": "Select a drop-off point",
"store/return-app.return-order-details.pickup-address.drop-off-points.tooltip": "You can return your items in one of our selected drop-off points",
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.placehoder": "Select a drop-off point",
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.error": "There was an error loading the drop-off points",
"store/return-app.return-order-details.pickup-address.drop-off-points": "Select a drop off point",
"store/return-app.return-order-details.pickup-address.drop-off-points.tooltip": "You can return your items in one of our selected drop off points",
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.placehoder": "Select a drop off point",
"store/return-app.return-order-details.pickup-address.drop-off-points.dropdown.error": "There was an error loading the drop off points",
"admin/return-app.return-request-details.localized-product-name.tooltip": "Original item name from the order",
"admin/return-app.return-request-details.return-label.modal-title": "Are you sure you want to create a return shipping label?",
"admin/return-app.return-request-details.return-label.modal-message": "This action is irreversible. The created label will be sent via email to the customer.",
"admin/return-app.return-request-details.return-label.create-return-label": "Create return label",
"admin/return-app.return-request-details.return-label.see-return-label": "See return label",
"admin/return-app.return-request-details.return-label-info.tooltip": "Create a return shipping label and send it to the customer via email. The request status has to be processing. For this integration to work properly you must install and configure vtex.easypost.",
"admin/return-app.return-request-details.return-label.alert.success": "The return label was created successfully",
"admin/return-app.return-request-details.return-label.alert.error": "There was an error creating the return label, please try again.",
"admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.label": "Automatically refund requests",
"admin/return-app.settings.section.payment-options.refund-method-strategy.checkbox.description": "Automatically create a return invoice in OMS for the requests",
"return-app.return-request-details.payent-method.refund-option.refund-process": "Refund process: {automaticallyRefundPaymentMethod, select, true{Automatic} false{Manual}}",
Expand Down
5 changes: 3 additions & 2 deletions node/clients/mail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
StatusUpdateMailData,
ConfirmationMailData,
Template,
ReturnLabelMailData,
} from '../typings/mailClient'

const MAIL_SERVICE_PATH = '/api/mail-service/pvt/sendmail'
Expand All @@ -21,7 +22,7 @@ export class MailClient extends JanusClient {
}

public sendMail(
mailData: StatusUpdateMailData | ConfirmationMailData
mailData: StatusUpdateMailData | ConfirmationMailData | ReturnLabelMailData
): Promise<string> {
return this.http.post(MAIL_SERVICE_PATH, mailData, {
metric: 'mail-post-send',
Expand All @@ -34,7 +35,7 @@ export class MailClient extends JanusClient {
})
}

public publishTemplate(template: Template): Promise<any> {
public publishTemplate(template: Template): Promise<unknown> {
return this.http.post(TEMPLATE_RENDER_PATH, template, {
headers: {
...this.options?.headers,
Expand Down
5 changes: 3 additions & 2 deletions node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
"@vtex/tsconfig": "^0.6.0",
"tslint": "^6.1.3",
"tslint-config-vtex": "^2.1.0",
"vtex.apps-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.apps-graphql",
"vtex.catalog-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.catalog-graphql",
"vtex.css-handles": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.css-handles",
"vtex.easypost": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.easypost",
"vtex.format-currency": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.format-currency",
"vtex.my-account": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.my-account",
"vtex.my-account-commons": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.my-account-commons",
"vtex.render-runtime": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.render-runtime",
"vtex.return-app": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.return-app",
"vtex.return-app": "https://rraep--powerplanet.myvtex.com/_v/private/typings/linked/v1/[email protected]+build1662483831/public/@types/vtex.return-app",
"vtex.store-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.store-graphql",
"vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].1/public/@types/vtex.styleguide",
"vtex.styleguide": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected].2/public/@types/vtex.styleguide",
"vtex.tenant-graphql": "http://vtex.vtexassets.com/_v/public/typings/v1/[email protected]/public/@types/vtex.tenant-graphql"
},
"scripts": {
Expand Down
2 changes: 2 additions & 0 deletions node/resolvers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ import { returnRequestList } from './returnRequestList'
import { ReturnRequestResponse } from './ReturnRequestResponse'
import { updateReturnRequestStatus } from './updateReturnRequestStatus'
import { nearestPickupPoints } from './nearestPickupPoints'
import { sendReturnLabel } from './sendReturnLabel'

export const mutations = {
createReturnRequest,
updateReturnRequestStatus,
sendReturnLabel,
...settingsMutation,
}

Expand Down
16 changes: 16 additions & 0 deletions node/resolvers/sendReturnLabel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { sendReturnLabelService } from '../services/sendReturnLabelService'

interface QuerySendReturnLabelArgs {
requestId: string
labelUrl: string
}

export const sendReturnLabel = async (
_: unknown,
args: QuerySendReturnLabelArgs,
ctx: Context
) => {
const { requestId, labelUrl } = args

return sendReturnLabelService(ctx, requestId, labelUrl)
}
10 changes: 4 additions & 6 deletions node/services/createReturnRequestService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import type { ReturnRequestCreated, ReturnRequestInput } from 'vtex.return-app'
import { UserInputError, ResolverError } from '@vtex/api'
import type { DocumentResponse } from '@vtex/clients'

import {
SETTINGS_PATH,
OMS_RETURN_REQUEST_CONFIRMATION,
} from '../utils/constants'
import { SETTINGS_PATH } from '../utils/constants'
import { isUserAllowed } from '../utils/isUserAllowed'
import { canOrderBeReturned } from '../utils/canOrderBeReturned'
import { canReturnAllItems } from '../utils/canReturnAllItems'
Expand All @@ -17,6 +14,7 @@ import { createRefundableTotals } from '../utils/createRefundableTotals'
import { OMS_RETURN_REQUEST_CONFIRMATION_TEMPLATE } from '../utils/templates'
import type { ConfirmationMailData } from '../typings/mailClient'
import { getCustomerEmail } from '../utils/getCostumerEmail'
import { templateName } from '../utils/emailTemplates'
import { validateItemCondition } from '../utils/validateItemCondition'

export const createReturnRequestService = async (
Expand Down Expand Up @@ -285,7 +283,7 @@ export const createReturnRequestService = async (
// We add a try/catch here so we avoid sending an error to the browser only if the email fails.
try {
const templateExists = await mail.getTemplate(
OMS_RETURN_REQUEST_CONFIRMATION(locale)
templateName('confirmation', locale)
)

if (!templateExists) {
Expand All @@ -305,7 +303,7 @@ export const createReturnRequestService = async (
} = shippingData

const mailData: ConfirmationMailData = {
templateName: OMS_RETURN_REQUEST_CONFIRMATION(locale),
templateName: templateName('confirmation', locale),
jsonData: {
data: {
status: 'new',
Expand Down
96 changes: 96 additions & 0 deletions node/services/sendReturnLabelService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { ResolverError } from '@vtex/api'
import type { ReturnRequest } from 'vtex.return-app'

import type { ReturnLabelMailData } from '../typings/mailClient'
import { templateName } from '../utils/emailTemplates'
import { OMS_RETURN_REQUEST_LABEL_TEMPLATE } from '../utils/templates'
import { formatRequestToPartialUpdate } from './updateRequestStatusService'

export const sendReturnLabelService = async (
ctx: Context,
requestId: string,
labelUrl: string
) => {
const {
clients: { mail, returnRequest: returnRequestClient },
vtex: { logger },
} = ctx

if (!requestId || !labelUrl) {
throw new ResolverError(
'The requestId or the labelUrl was not provided',
400
)
}

const returnRequest = (await returnRequestClient.get(requestId, [
'_all',
])) as ReturnRequest

const {
pickupReturnData,
cultureInfoData: { locale },
} = returnRequest

const updatedPickupReturnData = {
...pickupReturnData,
labelUrl,
}

const updatedRequest = {
...formatRequestToPartialUpdate(returnRequest),
pickupReturnData: updatedPickupReturnData,
}

try {
await returnRequestClient.update(requestId, updatedRequest)
} catch (error) {
const mdValidationErrors = error?.response?.data?.errors[0]?.errors

const errorMessageString = mdValidationErrors
? JSON.stringify(
{
message: 'Schema Validation error',
errors: mdValidationErrors,
},
null,
2
)
: error.message

throw new ResolverError(errorMessageString, error.response?.status || 500)
}

try {
const templateExists = await mail.getTemplate(templateName('label', locale))

if (!templateExists) {
await mail.publishTemplate(OMS_RETURN_REQUEST_LABEL_TEMPLATE(locale))
}

const { customerProfileData } = updatedRequest

const mailData: ReturnLabelMailData = {
templateName: templateName('label', locale),
jsonData: {
data: {
name: customerProfileData.name,
DocumentId: requestId,
email: customerProfileData.email,
labelUrl,
},
},
}

await mail.sendMail(mailData)
} catch (error) {
logger.warn({
message: `Failed to send email for return request ${requestId}`,
error,
})

return false
}

return true
}
21 changes: 14 additions & 7 deletions node/services/updateRequestStatusService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,14 @@ import { validateStatusUpdate } from '../utils/validateStatusUpdate'
import { createOrUpdateStatusPayload } from '../utils/createOrUpdateStatusPayload'
import { createRefundData } from '../utils/createRefundData'
import { handleRefund } from '../utils/handleRefund'
import { OMS_RETURN_REQUEST_STATUS_UPDATE } from '../utils/constants'
import { OMS_RETURN_REQUEST_STATUS_UPDATE_TEMPLATE } from '../utils/templates'
import type { StatusUpdateMailData } from '../typings/mailClient'
import { templateName } from '../utils/emailTemplates'

// A partial update on MD requires all required field to be sent. https://vtex.slack.com/archives/C8EE14F1C/p1644422359807929
// And the request to update fails when we pass the auto generated ones.
// If any new field is added to the ReturnRequest as required, it has to be added here too.
const formatRequestToPartialUpdate = (
export const formatRequestToPartialUpdate = (
request: ReturnRequest
): ReturnRequest => {
const {
Expand Down Expand Up @@ -232,7 +232,7 @@ export const updateRequestStatusService = async (
// We add a try/catch here so we avoid sending an error to the browser only if the email fails.
try {
const templateExists = await mail.getTemplate(
OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale)
templateName('status-update', cultureInfoData.locale)
)

if (!templateExists) {
Expand All @@ -251,7 +251,7 @@ export const updateRequestStatusService = async (
} = updatedRequest

const mailData: StatusUpdateMailData = {
templateName: OMS_RETURN_REQUEST_STATUS_UPDATE(cultureInfoData?.locale),
templateName: templateName('status-update', cultureInfoData.locale),
jsonData: {
data: {
status: updatedStatus,
Expand All @@ -260,15 +260,22 @@ export const updateRequestStatusService = async (
email: customerProfileData?.email ?? '',
paymentMethod: refundPaymentData?.refundPaymentMethod ?? '',
iban: refundPaymentData?.iban ?? '',
refundedAmount:
Number(updatedRefundData?.refundedItemsValue) +
Number(updatedRefundData?.refundedShippingValue),
},
products: items,
refundStatusData: updatedRefundStatusData,
},
}

if (updatedRefundData?.refundedItemsValue) {
let refundedAmount = Number(updatedRefundData.refundedItemsValue)

if (updatedRefundData.refundedShippingValue) {
refundedAmount += Number(updatedRefundData.refundedShippingValue)
}

mailData.jsonData.data.refundedAmount = refundedAmount
}

await mail.sendMail(mailData)
} catch (error) {
logger.warn({
Expand Down
Loading