diff --git a/src/controllers/statsController.ts b/src/controllers/statsController.ts new file mode 100644 index 0000000..8c4dff9 --- /dev/null +++ b/src/controllers/statsController.ts @@ -0,0 +1,21 @@ +import { Request, Response } from 'express'; +import { StatisticsService } from '../services/stats.service'; +// import { StatisticsService } from './statisticsService'; + +export async function getYearlyStats(req:Request, res:Response) { + const sellerId = (req as any).user.id; + const now = new Date(); + const startOfYear = new Date(now.getFullYear(), now.getMonth() - 11, 1); + + const stats = []; + + for (let month = startOfYear; month <= now; month.setMonth(month.getMonth() + 1)) { + const startOfMonth = new Date(month.getFullYear(), month.getMonth(), 1); + const endOfMonth = new Date(month.getFullYear(), month.getMonth() + 1, 0); + + const monthlyStats = await StatisticsService.calculateMonthlyStats(sellerId, startOfMonth, endOfMonth); + stats.push(monthlyStats); + } + + res.json(stats); +} \ No newline at end of file diff --git a/src/docs/stats.ts b/src/docs/stats.ts new file mode 100644 index 0000000..4f727bb --- /dev/null +++ b/src/docs/stats.ts @@ -0,0 +1,16 @@ +export const getSellerStats = { + tags: ["stats"], + security: [{ bearerAuth: [] }], + summary: "Get seller statistics", + responses: { + 200: { + description: "success", + }, + 401: { + description: "Unauthorized", + }, + 500: { + description: "Internal Server Error", + }, + }, +} \ No newline at end of file diff --git a/src/docs/swagger.ts b/src/docs/swagger.ts index 4a44b60..1b49c80 100644 --- a/src/docs/swagger.ts +++ b/src/docs/swagger.ts @@ -43,6 +43,7 @@ import { createReviewProduct, deleteReview, getReviewProduct, reviewSchema, upda import { getAdProducts } from "./products"; import {PrivateChatSchema, getAllUserPrivateChats, getUserToUserPrivateMessages, createPrivateChat } from "./privateChatDoc" import { StatusSchema, buyerAndSellerOrder, statusUpdate } from "./orders"; +import { getSellerStats } from "./stats"; const docRouter = express.Router(); @@ -80,7 +81,8 @@ const options = { { name: "Carts", description: "Endpoints related to Cart" }, { name: "Payments", description: "Endpoints related to payments" }, { name: "PrivateChat", description: "Endpoints related to Private Chat" }, - {name: "orders", description: "Endpoints related to orders"} + {name: "orders", description: "Endpoints related to orders"}, + {name: "stats", description: "Endpoints related to Seller statistics"} ], paths: { @@ -205,6 +207,9 @@ const options = { }, "/api/v1/orders/{id}/status": { patch: statusUpdate + }, + "/api/v1/stats":{ + get: getSellerStats } }, components: { diff --git a/src/routes/index.ts b/src/routes/index.ts index 2be3cae..5977b86 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -9,6 +9,7 @@ import notificationRoutes from "./notificationRoutes"; import paymentRouter from "./paymentRoutes"; import PrivateChatRoutes from "./privateChatRoutes"; import orderRouter from "./orderRoute"; +import statsRouter from "./statsRoutes"; const appROutes = Router(); appROutes.use("/chats", PrivateChatRoutes); @@ -21,4 +22,5 @@ appROutes.use("/carts", cartRoutes); appROutes.use("/notifications", notificationRoutes); appROutes.use("/payment", paymentRouter); appROutes.use("/orders", orderRouter) +appROutes.use("/stats", statsRouter); export default appROutes; diff --git a/src/routes/statsRoutes.ts b/src/routes/statsRoutes.ts new file mode 100644 index 0000000..c7fbe44 --- /dev/null +++ b/src/routes/statsRoutes.ts @@ -0,0 +1,11 @@ +import express from "express"; + +import { getYearlyStats } from "../controllers/statsController"; +import { isLoggedIn } from "../middlewares/isLoggedIn"; +import { isAseller } from "../middlewares/sellerAuth"; + +const statsRouter = express.Router(); + +statsRouter.get("/", isLoggedIn, isAseller , getYearlyStats); + +export default statsRouter; \ No newline at end of file diff --git a/src/services/stats.service.ts b/src/services/stats.service.ts new file mode 100644 index 0000000..b4a3db9 --- /dev/null +++ b/src/services/stats.service.ts @@ -0,0 +1,50 @@ +import { Op, Sequelize } from 'sequelize'; +import Product from '../sequelize/models/products'; +import OrderItem from '../sequelize/models/orderItems'; + +interface OrderItemWithProduct extends OrderItem { + product: Product; + } + +export class StatisticsService { + static async calculateMonthlyStats(sellerId: string, startOfMonth: Date, endOfMonth: Date) { + + const totalProducts = await Product.count({ where: { userId: sellerId } }); + + const itemsOrdered= (await OrderItem.findAll({ + include: [{ + model: Product, + as: 'product', + where: { userId: sellerId }, + required: true + }], + where: { + createdAt: { [Op.gte]: startOfMonth, [Op.lte]: endOfMonth } + } + })) as OrderItemWithProduct[]; + + const monthlySales = itemsOrdered.reduce((sum, item) => sum + item.quantity, 0); + + const monthlyRevenue = itemsOrdered.reduce((sum, item) => sum + (item.product.price * item.quantity), 0); + + + const expiredProducts = await Product.findAll({ + where: { + userId: sellerId, + expiryDate: { [Op.gte]: startOfMonth, [Op.lte]: endOfMonth } + } + }); + + + const losses = expiredProducts.reduce((sum, product) => sum + product.price, 0); + const monthName = startOfMonth.toLocaleString('default', { month: 'long' }); + return { + month: monthName, + totalProducts, + monthlySales, + monthlyRevenue, + expiredProducts, + losses, + }; + } +} \ No newline at end of file