import moment from 'moment' import { schema } from 'nexus' import OrderController from '../../controllers/order' import GeneralUtils from '../../utils/general' import { logger } from '../../utils/logger' import { send as sendNotification } from '../../utils/notification' import * as Slack from '../../utils/slack' import WalterService from '../../utils/walterService' import Fragments from '../Fragments' import { OrderWhereInput, OrderOrderByInput } from 'nexus-plugin-prisma/client' export const Order = schema.objectType({ name: 'Order', definition(t) { t.model.id() t.model.createdAt() t.model.updatedAt() t.model.status() t.model.number() t.model.paidAt() t.model.subtotalPrice() t.model.totalShipping() t.model.totalTax() t.model.totalPrice() t.model.totalDiscounts() t.model.isCharged() t.model.note() t.model.ratingComment() t.model.rating() t.model.isAReorder() t.model.receivedRating() t.model.showedRatingPopup() t.model.sentToPartnerAt() t.model.lastSendToPartnerDate() t.model.reminderSentToCustomer() t.model.completedThankYouSentToCustomer() t.model.employeeName() t.model.employeeEmail() t.model.booking() t.model.deliveryAddress() t.model.employeeMobilePhone() t.model.serviceProject() t.model.lineItems() t.model.additionalServices() // t.model.//() subscriptionOrders Order[] // t.model.//() Order_B_OrderSubscriptionRootOf // t.model.//() Order_A_OrderSubscriptionsOfOrde // t.model.//() Order_B_OrderSubscriptionsOfOrder Order[] @relation("OrderSubscriptionsOfOrder") //unsure TO REVIEW t.model.subscription() t.model.review() t.model.customer() t.model.promoCodes() }, }) export const OrderMutation = schema.extendType({ type: 'Mutation', definition: (t) => { t.crud.deleteOneOrder({ alias: 'deleteOrder' }) t.crud.updateOneOrder({ alias: 'updateOrder' }) t.crud.createOneOrder({ alias: 'createOrder' }) // Needed that because we are sending subtotal as a string for older version and it was crashing everything t.field('createOrderV2', { type: 'Order', args: { data: schema.arg({ type: 'OrderCreateInput', description: 'description', nullable: false, }), }, resolve: async (parent, { data }, ctx: NexusContext) => { const createdOrder = await OrderController.createOrder({ data }) return ctx.prisma.order.findOne({ where: { id: createdOrder.id } }) }, }) t.field('createOrder', { type: 'Order', args: { data: schema.arg({ type: 'OrderCreateInput' }), }, resolve: async (parent, { data }, ctx: NexusContext) => { const createdOrder = await OrderController.createOrder({ data }) return ctx.prisma.order.findOne({ where: { id: createdOrder.id } }) }, }) t.field('updateOrder', { type: 'Order', args: { data: schema.arg({ type: 'OrderUpdateInput' }), where: schema.arg({ type: 'OrderWhereUniqueInput' }), }, resolve: async (parent, { data, where }, ctx: NexusContext) => { const orderBeforeUpdate = await ctx.prisma.order.findOne({ where: { ...where }, include: Fragments.ORDER_FIELDS, }) if (!orderBeforeUpdate) { throw new Error("Can't find order") } const updatedOrder = await ctx.prisma.order.update({ data, where: { ...where }, include: Fragments.ORDER_FIELDS, }) // Send notif that order is now RESCHEDULED if (data.booking && data.booking.update) { sendNotification({ forceSend: true, // For now, we want to force notification for order users: [updatedOrder.customer], title: 'Order rescheduled', // prettier-ignore message: `Your ${WalterService.getDataForServiceType(updatedOrder.serviceProject.service.type,'TITLE')} order has been rescheduled to ${moment(updatedOrder.booking.date).format('LLLL')}`, data: updatedOrder, type: 'ORDER_STATUS_CHANGED', forceSMS: true, serviceProjectId: updatedOrder.serviceProject.id, }) Slack.send({ // prettier-ignore subject: `Order #${updatedOrder.number} rescheduled for ${moment(updatedOrder.booking.date).format('LLLL')} instead of ${moment( orderBeforeUpdate.booking.date, ).format('LLLL')}`, channel: 'order', }).catch(logger.error) } // Status changed if (data.status) { // Charge customer if (data.status === 'COMPLETED' && !updatedOrder.isCharged) { try { await OrderController.charge(updatedOrder.id) // Send thank you message // Sendbird.sendMessageV2({ // message: `Hi ${updatedOrder.customer.firstName || // ''}, we hope everything went well with your ${WalterService.getDataForServiceType( // updatedOrder.serviceProject.service.type, // 'TITLE' // )} order that was on ${moment(updatedOrder.booking.date).format('LLLL')}.`, // serviceProjectId: updatedOrder.serviceProject.id, // fromSendbirdUserId: 'walter', // otherUserId: updatedOrder.customer.id, // isSupport: true // }) // // Needed because we don't put in queue yet. In case of a crash, just log // .catch(logger.error) } catch (error) { await ctx.prisma.order .update({ where: { id: updatedOrder.id }, data: { status: orderBeforeUpdate.status, hasPaymentError: true }, }) .catch(logger.error) // Send error to FE throw error } } // CONFIRMED else if (data.status === 'CONFIRMED') { const employeeInfo = data.employeeName ? `Contact info: ${GeneralUtils.getStringMessageBasedOnArray([ updatedOrder.employeeName, updatedOrder.employeeMobilePhone ? updatedOrder.employeeMobilePhone.number : null, updatedOrder.employeeEmail, ])} ` : null sendNotification({ forceSend: true, // For now, we want to force notification for order users: [updatedOrder.customer], title: 'Order status changed', // prettier-ignore message: `Your ${WalterService.getDataForServiceType(updatedOrder.serviceProject.service.type,'TITLE')} order is now ${updatedOrder.status.toLowerCase()} ${updatedOrder.booking ? `for ${moment(updatedOrder.booking.date).format('LLLL')}${employeeInfo ? `\n\n${employeeInfo}` : ''}` : ''}`, data: updatedOrder, type: 'ORDER_STATUS_CHANGED', forceSMS: true, serviceProjectId: updatedOrder.serviceProject.id, }) } // PENDING else if (data.status === 'PENDING') { sendNotification({ forceSend: true, // For now, we want to force notification for order users: [updatedOrder.customer], title: 'Order status changed', // prettier-ignore message: `Your ${WalterService.getDataForServiceType(updatedOrder.serviceProject.service.type,'TITLE',)} order is now ${updatedOrder.status.toLowerCase()} ${updatedOrder.booking? `for ${moment(updatedOrder.booking.date).format('LLLL')}`: ''}`, data: updatedOrder, type: 'ORDER_STATUS_CHANGED', forceSMS: true, serviceProjectId: updatedOrder.serviceProject.id, }) } else if (data.status === 'CANCELED') { // Send notif that order is now CANCELED sendNotification({ forceSend: true, // For now, we want to force notification for order users: [updatedOrder.customer], serviceProjectId: updatedOrder.serviceProject.id, data: updatedOrder, title: 'Order status changed', message: `${GeneralUtils.firstLetterToUpperCase( WalterService.getDataForServiceType( updatedOrder.serviceProject.service.type, 'TITLE' ) )} order for ${moment(updatedOrder.booking.date).format('LLLL')} has been canceled`, type: 'ORDER_STATUS_CHANGED', }) } // FOR US if (data.status !== orderBeforeUpdate.status) { Slack.send({ channel: 'order', // prettier-ignore subject: `Order #${updatedOrder.number} on ${moment(updatedOrder.booking.date).format('LLLL')} is now ${data.status}`, }).catch(logger.error) } } return ctx.prisma.order.findOne({ where }) }, }) t.field('sendOrder', { type: 'Boolean', args: { phoneNumber: schema.stringArg(), email: schema.stringArg(), orderId: schema.stringArg(), }, resolve: async (parent, { phoneNumber, email, orderId }) => { if (!phoneNumber && !email) { throw new Error('Phone number or Email is required') } if (!orderId) { throw new Error("Didn't received the orderId for sending the order") } await OrderController.forwardOrderToSomeone({ orderId, email, phoneNumber }) return true }, }) }, }) export const OrderQuery = schema.extendType({ type: 'Query', definition(t) { t.crud.order() t.crud.orders({ filtering: true, ordering: true, pagination: true }) t.field('orders', { type: 'Order', list: true, args: { where: schema.arg({ type: 'OrderWhereInput' }), orderBy: schema.arg({ type: 'OrderOrderByInput', list: true }), first: schema.arg({ type: 'Int' }), skip: schema.arg({ type: 'Int' }), }, resolve: async ( parent, { where, orderBy, first, skip, }: { where?: OrderWhereInput orderBy?: OrderOrderByInput[] first?: number skip?: number }, ctx: NexusContext ) => { return ctx.prisma.order.findMany({ where: where, ...(orderBy?.length > 0 && { orderBy: orderBy[0] }), take: first, skip: skip, }) }, }) }, })