✨ feat(order): add customer email and optional fields, update order creation flow
- Added `customerEmail` field to Order model. - Made `customerNote` optional in the Order model. - Updated order creation logic to send a tracking email. - Updated order validation to handle optional fields. - Refactored configurations import path from `errors/configs` to `configs`. - Minor modifications across account and order services for consistency.
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Order" ADD COLUMN "customerEmail" TEXT,
|
||||||
|
ALTER COLUMN "customerNote" DROP NOT NULL;
|
||||||
@@ -20,8 +20,9 @@ model Order {
|
|||||||
status STATUS
|
status STATUS
|
||||||
customerName String
|
customerName String
|
||||||
customerPhone String
|
customerPhone String
|
||||||
|
customerEmail String?
|
||||||
customerAddress String
|
customerAddress String
|
||||||
customerNote String
|
customerNote String?
|
||||||
paymentType PAYMENT_TYPE
|
paymentType PAYMENT_TYPE
|
||||||
|
|
||||||
createdAt DateTime @default(now())
|
createdAt DateTime @default(now())
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { NextFunction, Request, Response } from "express";
|
import { NextFunction, Request, Response } from "express";
|
||||||
import { configs } from "../errors/configs";
|
import { configs } from "../configs";
|
||||||
import { AppError } from "../utils/app_error";
|
import { AppError } from "../utils/app_error";
|
||||||
import { jwtHelpers, JwtPayloadType } from "../utils/JWT";
|
import { jwtHelpers, JwtPayloadType } from "../utils/JWT";
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ErrorRequestHandler } from "express";
|
import { ErrorRequestHandler } from "express";
|
||||||
import { ZodError } from "zod";
|
import { ZodError } from "zod";
|
||||||
import { configs } from "../errors/configs";
|
import { configs } from "../configs";
|
||||||
import handleZodError from "../errors/zodError";
|
import handleZodError from "../errors/zodError";
|
||||||
import { TErrorSources } from "../types/error";
|
import { TErrorSources } from "../types/error";
|
||||||
import { AppError } from "../utils/app_error";
|
import { AppError } from "../utils/app_error";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { configs } from "../../errors/configs";
|
import { configs } from "../../configs";
|
||||||
import catchAsync from "../../utils/catch_async";
|
import catchAsync from "../../utils/catch_async";
|
||||||
import manageResponse from "../../utils/manage_response";
|
import manageResponse from "../../utils/manage_response";
|
||||||
import { account_services } from "./account.service";
|
import { account_services } from "./account.service";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import bcrypt from "bcrypt";
|
import bcrypt from "bcrypt";
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
import { configs } from "../../errors/configs";
|
import { configs } from "../../configs";
|
||||||
import { prisma } from "../../lib/prisma";
|
import { prisma } from "../../lib/prisma";
|
||||||
import { emailQueue } from "../../queues/email/email.queue";
|
import { emailQueue } from "../../queues/email/email.queue";
|
||||||
import { AppError } from "../../utils/app_error";
|
import { AppError } from "../../utils/app_error";
|
||||||
@@ -72,7 +72,7 @@ const create_account_into_db = async (req: Request) => {
|
|||||||
email: payload.email,
|
email: payload.email,
|
||||||
textBody: "You can use otp or verification link for verifying your account"
|
textBody: "You can use otp or verification link for verifying your account"
|
||||||
})
|
})
|
||||||
return result;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
const verify_account_using_otp_into_db = async (req: Request) => {
|
const verify_account_using_otp_into_db = async (req: Request) => {
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
|
|
||||||
import { Request } from "express";
|
import { Request } from "express";
|
||||||
|
import { configs } from "../../configs";
|
||||||
import { prisma } from "../../lib/prisma";
|
import { prisma } from "../../lib/prisma";
|
||||||
import { AppError } from "../../utils/app_error";
|
import { orderEmailQueue } from "../../queues/email/order/order.email.queue";
|
||||||
|
|
||||||
const get_all_order_from_db = async (req: Request) => {
|
const get_all_order_from_db = async (req: Request) => {
|
||||||
// define your own login here
|
// define your own login here
|
||||||
@@ -11,68 +12,42 @@ const get_all_order_from_db = async (req: Request) => {
|
|||||||
|
|
||||||
const get_single_order_from_db = async (req: Request) => {
|
const get_single_order_from_db = async (req: Request) => {
|
||||||
// define your own login here
|
// define your own login here
|
||||||
const { id } = req.params;
|
const { id } = req.params as { id: string };
|
||||||
const result = await prisma.order.findUnique({ where: { id } });
|
const result = await prisma.order.findUnique({ where: { id } });
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const create_order_into_db = async (req: Request) => {
|
const create_order_into_db = async (req: Request) => {
|
||||||
// define your own login here
|
const payload = req?.body;
|
||||||
|
payload.status = "INITIATED";
|
||||||
|
payload.paymentType = "COD"
|
||||||
|
|
||||||
const user = req.user
|
// nwo init order
|
||||||
console.log(user)
|
const result = await prisma.order.create({ data: payload });
|
||||||
const {
|
|
||||||
shopAccountId,
|
// if email exist sent tracking link
|
||||||
productPrice,
|
if (payload.customerEmail) {
|
||||||
productQuantity,
|
const trackingLink = `${configs.jwt.front_end_url}/track-order/${result.id}`;
|
||||||
productName,
|
await orderEmailQueue.add("order-email-queue", {
|
||||||
customerName,
|
email: payload.customerEmail,
|
||||||
customerPhone,
|
subject: "Order Tracking",
|
||||||
customerAddress,
|
textBody: `Your order has been created. Track your order here: ${trackingLink}`,
|
||||||
customerNote,
|
htmlBody: `<p>Your order has been created. Track your order here: <a href="${trackingLink}">Track Order</a></p>`
|
||||||
paymentType,
|
})
|
||||||
status,
|
|
||||||
} = req.body
|
|
||||||
const isUserExists = await prisma.account.findFirst({
|
|
||||||
where: {
|
|
||||||
id: user?.accountId
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if (!isUserExists) {
|
|
||||||
throw new AppError("Account not found", 404);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await prisma.order.create({
|
|
||||||
data: {
|
|
||||||
productPrice,
|
|
||||||
productQuantity,
|
|
||||||
productName,
|
|
||||||
customerName,
|
|
||||||
customerPhone,
|
|
||||||
customerAddress,
|
|
||||||
customerNote,
|
|
||||||
paymentType,
|
|
||||||
status,
|
|
||||||
account: {
|
|
||||||
connect: {
|
|
||||||
id: user?.accountId,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const update_order_into_db = async (req: Request) => {
|
const update_order_into_db = async (req: Request) => {
|
||||||
// define your own login here
|
// define your own login here
|
||||||
const { id } = req.params;
|
const { id } = req.params as { id: string };
|
||||||
const result = await prisma.order.update({ where: { id }, data: req.body });
|
const result = await prisma.order.update({ where: { id }, data: req.body });
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
const delete_order_from_db = async (req: Request) => {
|
const delete_order_from_db = async (req: Request) => {
|
||||||
// define your own login here
|
// define your own login here
|
||||||
const { id } = req.params;
|
const { id } = req.params as { id: string };
|
||||||
const result = await prisma.order.delete({ where: { id } });
|
const result = await prisma.order.delete({ where: { id } });
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,18 +10,16 @@ export const orderSwaggerDocs = {
|
|||||||
content: {
|
content: {
|
||||||
"application/json": {
|
"application/json": {
|
||||||
example: JSON.stringify({
|
example: JSON.stringify({
|
||||||
|
"shopAccountId": "",
|
||||||
"productPrice": 1500,
|
"productPrice": 1500,
|
||||||
"productQuantity": 2,
|
"productQuantity": 2,
|
||||||
"productName": "Wireless Mouse",
|
"productName": "Wireless Mouse",
|
||||||
"customerName": "Rahim Uddin",
|
"customerName": "Rahim Uddin",
|
||||||
"customerPhone": "+8801712345678",
|
"customerPhone": "+8801712345678",
|
||||||
|
"customerEmail": "softvence.abumahid@gmail.com",
|
||||||
"customerAddress": "Rangpur, Bangladesh",
|
"customerAddress": "Rangpur, Bangladesh",
|
||||||
"customerNote": "Please deliver between 3-5 PM",
|
"customerNote": "Please deliver between 3-5 PM"
|
||||||
"paymentType": "Cash on Delivery",
|
})
|
||||||
"status": "Pending"
|
|
||||||
|
|
||||||
}), // put your request body
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,29 +1,28 @@
|
|||||||
|
|
||||||
import { uuid, z } from "zod";
|
import { z } from "zod";
|
||||||
|
|
||||||
const create_order = z.object({
|
const create_order = z.object({
|
||||||
shopAccountId:z.string(),
|
shopAccountId: z.string(),
|
||||||
productPrice:z.number(),
|
productPrice: z.number(),
|
||||||
productQuantity:z.number(),
|
productQuantity: z.number(),
|
||||||
productName:z.string(),
|
productName: z.string(),
|
||||||
customerName:z.string(),
|
customerName: z.string(),
|
||||||
customerPhone:z.string(),
|
customerPhone: z.string(),
|
||||||
customerAddress:z.string(),
|
customerEmail: z.string().optional(),
|
||||||
customerNote:z.string(),
|
customerAddress: z.string(),
|
||||||
paymentType:z.string(),
|
customerNote: z.string().optional()
|
||||||
status:z.string()
|
|
||||||
});
|
});
|
||||||
const update_order = z.object({
|
const update_order = z.object({
|
||||||
shopAccountId:z.string().optional(),
|
shopAccountId: z.string().optional(),
|
||||||
productPrice:z.number().optional(),
|
productPrice: z.number().optional(),
|
||||||
productQuantity:z.number().optional(),
|
productQuantity: z.number().optional(),
|
||||||
productName:z.string().optional(),
|
productName: z.string().optional(),
|
||||||
customerName:z.string().optional(),
|
customerName: z.string().optional(),
|
||||||
customerPhone:z.string().optional(),
|
customerPhone: z.string().optional(),
|
||||||
customerAddress:z.string().optional(),
|
customerEmail: z.string().optional(),
|
||||||
customerNote:z.string().optional(),
|
customerAddress: z.string().optional(),
|
||||||
paymentType:z.string().optional(),
|
customerNote: z.string().optional(),
|
||||||
status:z.string().optional()
|
status: z.string().optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const order_validations = {
|
export const order_validations = {
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
import sendMail from "../../../utils/mail_sender";
|
||||||
|
import { TOrderEmailQueue } from "./order.email.queue";
|
||||||
|
|
||||||
|
// email.processor.ts
|
||||||
|
export const orderEmailProcessor = async (job: any) => {
|
||||||
|
const payload: TOrderEmailQueue = job.data;
|
||||||
|
await sendMail({
|
||||||
|
to: payload.email as string,
|
||||||
|
subject: payload.subject,
|
||||||
|
htmlBody: payload.htmlBody as string,
|
||||||
|
textBody: payload.textBody as string,
|
||||||
|
});
|
||||||
|
console.log("Sending email job complete:", job.id);
|
||||||
|
};
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
import { Queue } from "bullmq";
|
||||||
|
import { redisConnection } from "../../connection";
|
||||||
|
|
||||||
|
export type TOrderEmailQueue = {
|
||||||
|
email: string;
|
||||||
|
subject: string;
|
||||||
|
textBody?: string;
|
||||||
|
htmlBody?: string;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const orderEmailQueue = new Queue<TOrderEmailQueue>("order-email-queue", {
|
||||||
|
connection: redisConnection,
|
||||||
|
});
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// email.worker.ts
|
||||||
|
import { Worker } from "bullmq";
|
||||||
|
import { redisConnection } from "../../connection";
|
||||||
|
import { orderEmailProcessor } from "./order.email.processor";
|
||||||
|
import { TOrderEmailQueue } from "./order.email.queue";
|
||||||
|
|
||||||
|
|
||||||
|
export const emailWorker = new Worker<TOrderEmailQueue>(
|
||||||
|
"order-email-queue",
|
||||||
|
async (job) => orderEmailProcessor(job),
|
||||||
|
{
|
||||||
|
connection: redisConnection,
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
import "./email/email.worker";
|
import "./email/email.worker";
|
||||||
|
import "./email/order/order.email.worker";
|
||||||
|
|
||||||
console.log("Workers running...");
|
console.log("Workers running...");
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { v2 as cloudinary } from 'cloudinary';
|
import { v2 as cloudinary } from 'cloudinary';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { configs } from '../errors/configs';
|
import { configs } from '../configs';
|
||||||
|
|
||||||
type ICloudinaryResponse = {
|
type ICloudinaryResponse = {
|
||||||
asset_id: string;
|
asset_id: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import nodemailer from 'nodemailer';
|
import nodemailer from 'nodemailer';
|
||||||
import { configs } from '../errors/configs';
|
import { configs } from '../configs';
|
||||||
type TMailContent = {
|
type TMailContent = {
|
||||||
to: string,
|
to: string,
|
||||||
subject: string,
|
subject: string,
|
||||||
|
|||||||
+2
-2
@@ -1,7 +1,7 @@
|
|||||||
import app from "./app";
|
import app from "./app";
|
||||||
import { configs } from "./app/errors/configs/index";
|
import { configs } from "./app/configs/index";
|
||||||
import { prisma } from "./app/lib/prisma";
|
import { prisma } from "./app/lib/prisma";
|
||||||
// import "./app/queues/worker";
|
import "./app/queues/worker";
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { fileURLToPath } from "node:url";
|
import { fileURLToPath } from "node:url";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { configs } from "./app/errors/configs";
|
import { configs } from "./app/configs";
|
||||||
import { accountSwaggerDocs } from "./app/modules/account/account.swagger";
|
import { accountSwaggerDocs } from "./app/modules/account/account.swagger";
|
||||||
import { orderSwaggerDocs } from "./app/modules/order/order.swagger";
|
import { orderSwaggerDocs } from "./app/modules/order/order.swagger";
|
||||||
import { planSwaggerDocs } from "./app/modules/plan/plan.swagger";
|
import { planSwaggerDocs } from "./app/modules/plan/plan.swagger";
|
||||||
|
|||||||
Reference in New Issue
Block a user