diff --git a/package-lock.json b/package-lock.json index 71a4ebb..7437ec4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2371,6 +2371,19 @@ "real-require": "^0.2.0" } }, + "node_modules/@nestjs/schedule": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-4.0.2.tgz", + "integrity": "sha512-po9oauE7fO0CjhDKvVC2tzEgjOUwhxYoIsXIVkgfu+xaDMmzzpmXY2s1LT4oP90Z+PaTtPoAHmhslnYmo4mSZg==", + "dependencies": { + "cron": "3.1.7", + "uuid": "9.0.1" + }, + "peerDependencies": { + "@nestjs/common": "^8.0.0 || ^9.0.0 || ^10.0.0", + "@nestjs/core": "^8.0.0 || ^9.0.0 || ^10.0.0" + } + }, "node_modules/@nestjs/schematics": { "version": "10.1.1", "resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-10.1.1.tgz", diff --git a/prisma/migrations/20240605140756_/migration.sql b/prisma/migrations/20240605140756_/migration.sql new file mode 100644 index 0000000..3f59660 --- /dev/null +++ b/prisma/migrations/20240605140756_/migration.sql @@ -0,0 +1,14 @@ +-- CreateTable +CREATE TABLE "shelter_users" ( + "user_id" TEXT NOT NULL, + "shelter_id" TEXT NOT NULL, + "created_at" VARCHAR(32) NOT NULL, + + CONSTRAINT "shelter_users_pkey" PRIMARY KEY ("user_id","shelter_id") +); + +-- AddForeignKey +ALTER TABLE "shelter_users" ADD CONSTRAINT "shelter_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "shelter_users" ADD CONSTRAINT "shelter_users_shelter_id_fkey" FOREIGN KEY ("shelter_id") REFERENCES "shelters"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 57447c7..1a4b82f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -48,6 +48,7 @@ model User { shelterManagers ShelterManagers[] suppliesHistory SupplyHistory[] donationOrders DonationOrder[] + shelterUsers ShelterUsers[] @@map("users") } @@ -134,7 +135,8 @@ model Shelter { shelterManagers ShelterManagers[] shelterSupplies ShelterSupply[] supplyHistories SupplyHistory[] - DonationOrder DonationOrder[] + donationOrders DonationOrder[] + shelterUsers ShelterUsers[] @@map("shelters") } @@ -222,3 +224,15 @@ model DonationOrder { @@map("donation_orders") } + +model ShelterUsers { + userId String @map("user_id") + shelterId String @map("shelter_id") + createdAt String @map("created_at") @db.VarChar(32) + + user User @relation(fields: [userId], references: [id]) + shelter Shelter @relation(fields: [shelterId], references: [id]) + + @@id([userId, shelterId]) + @@map("shelter_users") +} diff --git a/src/dashboard/dashboard.controller.spec.ts b/src/dashboard/dashboard.controller.spec.ts index 312a8e2..5776539 100644 --- a/src/dashboard/dashboard.controller.spec.ts +++ b/src/dashboard/dashboard.controller.spec.ts @@ -2,8 +2,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DashboardController } from './dashboard.controller'; import { DashboardService } from './dashboard.service'; -import { PrismaService } from '../../prisma/prisma.service'; -import { PrismaModule } from '../../prisma/prisma.module'; +import { PrismaService } from '../prisma/prisma.service'; +import { PrismaModule } from '../prisma/prisma.module'; describe('DashboardController', () => { let controller: DashboardController; diff --git a/src/dashboard/dashboard.service.spec.ts b/src/dashboard/dashboard.service.spec.ts index d02863a..674d766 100644 --- a/src/dashboard/dashboard.service.spec.ts +++ b/src/dashboard/dashboard.service.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { DashboardService } from './dashboard.service'; -import { PrismaService } from '../../prisma/prisma.service'; -import { PrismaModule } from '../../prisma/prisma.module'; +import { PrismaService } from '../prisma/prisma.service'; +import { PrismaModule } from '../prisma/prisma.module'; describe('DashboardService', () => { let service: DashboardService; diff --git a/src/donation-order/donation-order.controller.ts b/src/donation-order/donation-order.controller.ts index 52997ec..fea6327 100644 --- a/src/donation-order/donation-order.controller.ts +++ b/src/donation-order/donation-order.controller.ts @@ -39,9 +39,14 @@ export class DonationOrderController { @Put(':orderId') @UseGuards(UserGuard) - async update(@Param('orderId') orderId: string, @Body() body) { + async update( + @Request() req, + @Param('orderId') orderId: string, + @Body() body, + ) { try { - await this.donationOrderService.update(orderId, body); + const { userId } = req.user; + await this.donationOrderService.update(orderId, userId, body); return new ServerResponse(200, 'Successfully updated donation order'); } catch (err: any) { this.logger.error(`Failed to update donation order: ${err}`); diff --git a/src/donation-order/donation-order.service.ts b/src/donation-order/donation-order.service.ts index 6bcb11d..5738c54 100644 --- a/src/donation-order/donation-order.service.ts +++ b/src/donation-order/donation-order.service.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { Injectable } from '@nestjs/common'; +import { HttpException, Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { DefaultArgs } from '@prisma/client/runtime/library'; @@ -9,16 +9,44 @@ import { SearchSchema } from '../types'; @Injectable() export class DonationOrderService { + private donationOrderVisibleFields: Prisma.DonationOrderSelect = { + id: true, + status: true, + userId: true, + shelter: { + select: { + id: true, + name: true, + }, + }, + donationOrderSupplies: { + select: { + quantity: true, + supply: { + select: { + name: true, + measure: true, + }, + }, + }, + }, + createdAt: true, + updatedAt: true, + }; + constructor(private readonly prismaService: PrismaService) {} async index(userId: string, query: any) { - const { shelterId } = query; + const { shelterId, op } = query; const { order, orderBy, page, perPage } = SearchSchema.parse(query); - const where: Prisma.DonationOrderWhereInput = { - shelterId, - userId, - }; + let where = {}; + + if (op === 'received') { + where = await this.getAllReceivedDonations(userId); + } else { + where = this.getAllDonationsMade(userId, shelterId); + } const count = await this.prismaService.donationOrder.count({ where }); @@ -34,30 +62,7 @@ export class DonationOrderService { const results = await this.prismaService.donationOrder.findMany({ ...whereData, - select: { - id: true, - status: true, - userId: true, - shelter: { - select: { - id: true, - name: true, - }, - }, - donationOrderSupplies: { - select: { - quantity: true, - supply: { - select: { - name: true, - measure: true, - }, - }, - }, - }, - createdAt: true, - updatedAt: true, - }, + select: this.donationOrderVisibleFields, orderBy: { createdAt: 'desc', }, @@ -127,17 +132,94 @@ export class DonationOrderService { async update( orderId: string, + userId: string, body: z.infer, ) { const { status } = UpdateDonationOrderScheme.parse(body); - await this.prismaService.donationOrder.update({ - where: { - id: orderId, - }, - data: { - status, - updatedAt: new Date().toISOString(), + const order = await this.prismaService.donationOrder.findFirst({ + where: { id: orderId }, + select: { + shelterId: true, + userId: true, + donationOrderSupplies: true, }, }); + + if (!order) return new HttpException('Donation not found', 404); + + if (order.userId !== userId) { + const isEmployer = await this.prismaService.shelterUsers.findFirst({ + where: { + userId, + shelterId: order.shelterId, + }, + }); + + if (!isEmployer) + return new HttpException( + 'User not allowed to update this donation', + 404, + ); + } + + await this.prismaService.$transaction([ + ...order.donationOrderSupplies.map((d) => + this.prismaService.shelterSupply.update({ + where: { + shelterId_supplyId: { + shelterId: order.shelterId, + supplyId: d.supplyId, + }, + }, + data: { + quantity: { + decrement: d.quantity, + }, + }, + }), + ), + this.prismaService.donationOrder.update({ + where: { + id: orderId, + }, + data: { + status, + updatedAt: new Date().toISOString(), + }, + }), + ]); + } + + private async getAllReceivedDonations(userId: string, shelterId?: string) { + const where: Prisma.DonationOrderWhereInput = { + shelterId, + }; + + if (!shelterId) { + const sheltersByUser = await this.prismaService.shelterUsers.findMany({ + where: { + userId, + }, + select: { + shelterId: true, + }, + }); + + const shelterIds = sheltersByUser.map((s) => s.shelterId); + where.shelterId = { + in: shelterIds, + }; + } + + return where; + } + + private getAllDonationsMade(userId: string, shelterId?: string) { + const where: Prisma.DonationOrderWhereInput = { + userId, + shelterId, + }; + + return where; } }