From 8b1c4ac0017d7bffd8305c691de52773000e9a10 Mon Sep 17 00:00:00 2001 From: Abdulrahman Goni Date: Thu, 4 Apr 2024 20:15:08 +0300 Subject: [PATCH] feat: create a route for managing the monthly targets of earnings Create `POST api/statistics/monthly-targets` route for managing the monthly targets of earnings by the admins. Create `statistics_setMonthTarget_get` route handler for dealing with the requests on `POST api/statistics/monthly-targets` route. Create `setMonthTarget` function for handling business logic. Create tests file for testing `POST api/statistics/monthly-targets` route. --- .../setMonthTarget.test.js | 83 +++++++++++++++++++ .../StatisticsController.js | 2 + .../statistics-controllers/setMonthTarget.js | 14 ++++ src/routers/statisticsRouter.js | 3 + .../statistics_setMonthTarget_get.js | 26 ++++++ 5 files changed, 128 insertions(+) create mode 100644 __tests__/integration_testing/statistics_routes_tests/setMonthTarget.test.js create mode 100644 src/controllers/statistics-controllers/setMonthTarget.js create mode 100644 src/routes/statistics_routes/statistics_setMonthTarget_get.js diff --git a/__tests__/integration_testing/statistics_routes_tests/setMonthTarget.test.js b/__tests__/integration_testing/statistics_routes_tests/setMonthTarget.test.js new file mode 100644 index 00000000..63b562ff --- /dev/null +++ b/__tests__/integration_testing/statistics_routes_tests/setMonthTarget.test.js @@ -0,0 +1,83 @@ +import ProductsModel from "../../../src/models/Products.js" +import YearlyStatisticsModel from "../../../src/models/YearlyStatistics.js"; +import { getFakeYearStatistics } from "../../fakes/fakeYearlyStatistics.js"; +import { closeTestingServer, adminRequest } from "../../helpers/testRequest.js" + +afterAll(async () => { + await YearlyStatisticsModel.deleteMany({}); + await closeTestingServer(); +}) + +beforeAll(async () => { + const year = new Date().getFullYear(); + await YearlyStatisticsModel.insertMany([ + getFakeYearStatistics(year - 1), + getFakeYearStatistics(year), + getFakeYearStatistics(year + 1) + ]); +}) + +afterEach(async () => { + await ProductsModel.deleteMany({}); +}) + +const routePath = `/api/statistics/monthly-targets` + +describe(`POST ${routePath}`, () => { + + it("Should sets the target of the current month to `4000`", async () => { + await setMonthTargetTest(4000, "current") + }) + + it("Should sets the target of the next month to `5000`", async () => { + await setMonthTargetTest(5000, "next") + }) + + it("Should returns an error with \"Can't set a target for a passed month\" message", async () => { + const newTarget = 4750; + const { year, monthIndex } = prepareDate(-1) + const requestBody = { year, monthIndex, newTarget }; + + const response = await adminRequest(routePath, "post", { body: requestBody }); + expect(response.statusCode).toBe(400); + expect(response.body.message).toMatch("Can't set a target for a passed month"); + }) + + it("Should returns an error with \"Invalid Date\" message", async () => { + const newTarget = 6000; + const requestBody = { + year: "any invalid year", + monthIndex: "any invalid number", + newTarget + }; + + const response = await adminRequest(routePath, "post", { body: requestBody }); + expect(response.statusCode).toBe(400); + expect(response.body.message).toMatch("Invalid Date"); + }) + +}) + +async function setMonthTargetTest(target, month) { + const newTarget = target; + const { year, monthIndex } = prepareDate(month === "next" ? 1 : 0) + const requestBody = { year, monthIndex, newTarget }; + + const response = await adminRequest(routePath, "post", { body: requestBody }); + expect(response.statusCode).toBe(200); + expect(response.body).toBe(true); + + const { monthes } = await YearlyStatisticsModel.findOne({ year }, { monthes: true }); + + expect(monthes[monthIndex].earningsTarget).toBe(newTarget); +} + +const prepareDate = (moveMonth = 0) => { + const date = new Date(new Date().setMonth(new Date().getMonth() + moveMonth)) + const year = date.getFullYear(); + const monthIndex = date.getMonth(); + return { + year, + monthIndex + } +} \ No newline at end of file diff --git a/src/controllers/statistics-controllers/StatisticsController.js b/src/controllers/statistics-controllers/StatisticsController.js index f7b79d32..efe7ec69 100644 --- a/src/controllers/statistics-controllers/StatisticsController.js +++ b/src/controllers/statistics-controllers/StatisticsController.js @@ -14,6 +14,7 @@ import usersStatistics from "./usersStatistics.js" import registerOrderStatistics from "./registerOrderStatistics.js" import registerCategoriesStatistics from "./registerCategoriesStatistics.js" import registerProductsStatistics from "./registerProductsStatistics.js" +import setMonthTarget from "./setMonthTarget.js" class StatisticsController { constructor() { } @@ -36,4 +37,5 @@ export default Object.assign(StatisticsController.prototype, { registerOrderStatistics, registerCategoriesStatistics, registerProductsStatistics, + setMonthTarget }) diff --git a/src/controllers/statistics-controllers/setMonthTarget.js b/src/controllers/statistics-controllers/setMonthTarget.js new file mode 100644 index 00000000..b5f30b03 --- /dev/null +++ b/src/controllers/statistics-controllers/setMonthTarget.js @@ -0,0 +1,14 @@ +import YearlyStatisticsModel from "../../models/YearlyStatistics.js"; + +export default async function setMonthTarget({ year, monthIndex, newTarget }) { + try { + const respond = await YearlyStatisticsModel.updateOne( + { year }, + { [`monthes.${monthIndex}.earningsTarget`]: newTarget } + ) + return !!respond.modifiedCount + } catch (error) { + console.log(error) + return; + } +} \ No newline at end of file diff --git a/src/routers/statisticsRouter.js b/src/routers/statisticsRouter.js index 12a949f2..8b5030c9 100644 --- a/src/routers/statisticsRouter.js +++ b/src/routers/statisticsRouter.js @@ -1,9 +1,12 @@ import { Router } from "express"; import statistics_get from "../routes/statistics_routes/statistics_get.js"; +import statistics_setMonthTarget_get from "../routes/statistics_routes/statistics_setMonthTarget_get.js"; const router = Router(); router.get("/", statistics_get); +router.post("/monthly-targets", statistics_setMonthTarget_get); + export default router; \ No newline at end of file diff --git a/src/routes/statistics_routes/statistics_setMonthTarget_get.js b/src/routes/statistics_routes/statistics_setMonthTarget_get.js new file mode 100644 index 00000000..fe5247a6 --- /dev/null +++ b/src/routes/statistics_routes/statistics_setMonthTarget_get.js @@ -0,0 +1,26 @@ +import StatisticsController from "../../controllers/statistics-controllers/StatisticsController.js"; +import asyncRouteHandler from "../../utilities/asyncRouteHandler.js"; +import { getCurrentDate } from "../../utilities/dateMaker.js"; +import ErrorGenerator from "../../utilities/ErrorGenerator.js"; + +export default asyncRouteHandler( + async function statistics_setMonthTarget_get(req, res, next) { + const { year, monthIndex, newTarget } = req.body; + const currentDate = getCurrentDate(); + if (!isNaN(+year) && !isNaN(+monthIndex)) { + // if the given month is future or current month + if (currentDate.year <= +year && (currentDate.monthIndex <= +monthIndex || currentDate.year < +year)) { + if (!isNaN(+newTarget)) { + const response = await StatisticsController.setMonthTarget({ year, monthIndex, newTarget }); + res.status(response === undefined ? 400 : 200).json(response); + } else { + next(new ErrorGenerator("Invalid Target", 400)); + } + } else { + next(new ErrorGenerator("Can't set a target for a passed month", 400)); + } + } else { + next(new ErrorGenerator("Invalid Date", 400)); + } + } +)