From 874c654911469473e0fb9e9ca383b3003d10e7bc Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Sun, 1 Feb 2026 19:39:52 -0500 Subject: [PATCH 1/4] add tracking link and shipping cost to order --- apps/backend/src/config/typeorm.ts | 2 ++ .../1769990652833-UpdateOrderEntity.ts | 19 ++++++++++++++++++ .../src/orders/order.controller.spec.ts | 20 +++++++++++++++++++ apps/backend/src/orders/order.controller.ts | 13 ++++++++++++ apps/backend/src/orders/order.entity.ts | 17 ++++++++++++++++ apps/backend/src/orders/order.service.ts | 18 +++++++++++++++++ 6 files changed, 89 insertions(+) create mode 100644 apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts diff --git a/apps/backend/src/config/typeorm.ts b/apps/backend/src/config/typeorm.ts index 82384673..9f4faf76 100644 --- a/apps/backend/src/config/typeorm.ts +++ b/apps/backend/src/config/typeorm.ts @@ -27,6 +27,7 @@ import { RemoveMultipleVolunteerTypes1764811878152 } from '../migrations/1764811 import { RemoveUnusedStatuses1764816885341 } from '../migrations/1764816885341-RemoveUnusedStatuses'; import { UpdatePantryFields1763762628431 } from '../migrations/1763762628431-UpdatePantryFields'; import { PopulateDummyData1768501812134 } from '../migrations/1768501812134-populateDummyData'; +import { UpdateOrderEntity1769990652833 } from '../migrations/1769990652833-UpdateOrderEntity'; const config = { type: 'postgres', @@ -67,6 +68,7 @@ const config = { RemoveMultipleVolunteerTypes1764811878152, RemoveUnusedStatuses1764816885341, PopulateDummyData1768501812134, + UpdateOrderEntity1769990652833, ], }; diff --git a/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts b/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts new file mode 100644 index 00000000..0360fc8a --- /dev/null +++ b/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts @@ -0,0 +1,19 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateOrderEntity1769990652833 implements MigrationInterface { + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE orders + ADD COLUMN IF NOT EXISTS tracking_link VARCHAR(255), + ADD COLUMN IF NOT EXISTS shipping_cost NUMERIC(10,2); + `); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE orders + DROP COLUMN IF EXISTS tracking_link, + DROP COLUMN IF EXISTS shipping_cost; + `); + } +} diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 4c06f83a..a8288985 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -73,4 +73,24 @@ describe('OrdersController', () => { ).toHaveBeenCalledWith(orderId); }); }); + + describe('updateTrackingAndCost', () => { + it('should call ordersService.updateTrackingAndCost with correct parameters', async () => { + const orderId = 1; + const trackingLink = 'www.samplelink/samplelink'; + const shippingCost = '15.99'; + + await controller.updateTrackingAndCost( + orderId, + trackingLink, + shippingCost, + ); + + expect(mockOrdersService.updateTrackingAndCost).toHaveBeenCalledWith( + orderId, + trackingLink, + shippingCost, + ); + }); + }); }); diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 870dc1ef..f4081fff 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -99,4 +99,17 @@ export class OrdersController { } return this.ordersService.updateStatus(orderId, newStatus as OrderStatus); } + + @Patch('/:orderId/update-tracking-and-cost') + async updateTrackingAndCost( + @Param('orderId', ParseIntPipe) orderId: number, + @Body('trackingLink') trackingLink: string, + @Body('shippingCost') shippingCost: string, + ): Promise { + return this.ordersService.updateTrackingAndCost( + orderId, + trackingLink, + shippingCost, + ); + } } diff --git a/apps/backend/src/orders/order.entity.ts b/apps/backend/src/orders/order.entity.ts index 7c40fdb4..6ef531a8 100644 --- a/apps/backend/src/orders/order.entity.ts +++ b/apps/backend/src/orders/order.entity.ts @@ -77,4 +77,21 @@ export class Order { @OneToMany(() => Allocation, (allocation) => allocation.order) allocations: Allocation[]; + + @Column({ + name: 'tracking_link', + type: 'varchar', + length: 255, + nullable: true, + }) + trackingLink?: string; + + @Column({ + name: 'shipping_cost', + type: 'numeric', + precision: 10, + scale: 2, + nullable: true, + }) + shippingCost?: string; } diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index 05f37bca..a03fc5c6 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -147,4 +147,22 @@ export class OrdersService { return orders; } + + async updateTrackingAndCost( + orderId: number, + trackingLink: string, + shippingCost: string, + ) { + validateId(orderId, 'Order'); + + await this.repo + .createQueryBuilder() + .update(Order) + .set({ + trackingLink: trackingLink, + shippingCost: shippingCost, + }) + .where('order_id = :orderId', { orderId }) + .execute(); + } } From 700b1245a0ade1bc4d2421e21d19b84a7e556c91 Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Sun, 1 Feb 2026 23:09:08 -0500 Subject: [PATCH 2/4] add dummy data --- .../src/migrations/1769990652833-UpdateOrderEntity.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts b/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts index 0360fc8a..780483df 100644 --- a/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts +++ b/apps/backend/src/migrations/1769990652833-UpdateOrderEntity.ts @@ -6,6 +6,11 @@ export class UpdateOrderEntity1769990652833 implements MigrationInterface { ALTER TABLE orders ADD COLUMN IF NOT EXISTS tracking_link VARCHAR(255), ADD COLUMN IF NOT EXISTS shipping_cost NUMERIC(10,2); + + UPDATE orders + SET tracking_link = 'www.samplelink/samplelink', + shipping_cost = 20.00 + WHERE status = 'delivered' OR status = 'shipped' AND shipped_at IS NOT NULL; `); } From e23efe2b824957b6187d69ceb5339e0be8bee245 Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Tue, 3 Feb 2026 23:12:42 -0500 Subject: [PATCH 3/4] add dto and review comments --- apps/backend/src/orders/dtos/tracking-cost.dto.ts | 10 ++++++++++ apps/backend/src/orders/order.controller.spec.ts | 10 +++++----- apps/backend/src/orders/order.controller.ts | 9 +++++---- apps/backend/src/orders/order.entity.ts | 2 +- apps/backend/src/orders/order.service.ts | 8 ++++---- apps/frontend/src/types/types.ts | 2 ++ 6 files changed, 27 insertions(+), 14 deletions(-) create mode 100644 apps/backend/src/orders/dtos/tracking-cost.dto.ts diff --git a/apps/backend/src/orders/dtos/tracking-cost.dto.ts b/apps/backend/src/orders/dtos/tracking-cost.dto.ts new file mode 100644 index 00000000..58e817b4 --- /dev/null +++ b/apps/backend/src/orders/dtos/tracking-cost.dto.ts @@ -0,0 +1,10 @@ +import { IsUrl, IsNumber, Min } from "class-validator"; + +export class TrackingCostDto { + @IsUrl({}, { message: 'Tracking link must be a valid URL' }) + trackingLink: string; + + @IsNumber({ maxDecimalPlaces: 2, }, { message: 'Shipping cost must have at most 2 decimal places' }) + @Min(0, { message: 'Shipping cost cannot be negative' }) + shippingCost: number; +} \ No newline at end of file diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index a8288985..68be728e 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -6,6 +6,7 @@ import { Order } from './order.entity'; import { Allocation } from '../allocations/allocations.entity'; import { mock } from 'jest-mock-extended'; import { OrderStatus } from './types'; +import { TrackingCostDto } from './dtos/tracking-cost.dto'; const mockOrdersService = mock(); const mockAllocationsService = mock(); @@ -78,18 +79,17 @@ describe('OrdersController', () => { it('should call ordersService.updateTrackingAndCost with correct parameters', async () => { const orderId = 1; const trackingLink = 'www.samplelink/samplelink'; - const shippingCost = '15.99'; + const shippingCost = 15.99; + const dto: TrackingCostDto = { trackingLink, shippingCost }; await controller.updateTrackingAndCost( orderId, - trackingLink, - shippingCost, + dto, ); expect(mockOrdersService.updateTrackingAndCost).toHaveBeenCalledWith( orderId, - trackingLink, - shippingCost, + dto, ); }); }); diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index f4081fff..33068010 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -7,6 +7,7 @@ import { Body, Query, BadRequestException, + ValidationPipe, } from '@nestjs/common'; import { OrdersService } from './order.service'; import { Order } from './order.entity'; @@ -15,6 +16,7 @@ import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; import { FoodRequest } from '../foodRequests/request.entity'; import { AllocationsService } from '../allocations/allocations.service'; import { OrderStatus } from './types'; +import { TrackingCostDto } from './dtos/tracking-cost.dto'; @Controller('orders') export class OrdersController { @@ -103,13 +105,12 @@ export class OrdersController { @Patch('/:orderId/update-tracking-and-cost') async updateTrackingAndCost( @Param('orderId', ParseIntPipe) orderId: number, - @Body('trackingLink') trackingLink: string, - @Body('shippingCost') shippingCost: string, + @Body(new ValidationPipe()) + dto: TrackingCostDto, ): Promise { return this.ordersService.updateTrackingAndCost( orderId, - trackingLink, - shippingCost, + dto, ); } } diff --git a/apps/backend/src/orders/order.entity.ts b/apps/backend/src/orders/order.entity.ts index 6ef531a8..1cb49f84 100644 --- a/apps/backend/src/orders/order.entity.ts +++ b/apps/backend/src/orders/order.entity.ts @@ -93,5 +93,5 @@ export class Order { scale: 2, nullable: true, }) - shippingCost?: string; + shippingCost?: number; } diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index a03fc5c6..c4fad40c 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -7,6 +7,7 @@ import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity'; import { FoodRequest } from '../foodRequests/request.entity'; import { validateId } from '../utils/validation.utils'; import { OrderStatus } from './types'; +import { TrackingCostDto } from './dtos/tracking-cost.dto'; @Injectable() export class OrdersService { @@ -150,8 +151,7 @@ export class OrdersService { async updateTrackingAndCost( orderId: number, - trackingLink: string, - shippingCost: string, + dto: TrackingCostDto, ) { validateId(orderId, 'Order'); @@ -159,8 +159,8 @@ export class OrdersService { .createQueryBuilder() .update(Order) .set({ - trackingLink: trackingLink, - shippingCost: shippingCost, + trackingLink: dto.trackingLink, + shippingCost: dto.shippingCost, }) .where('order_id = :orderId', { orderId }) .execute(); diff --git a/apps/frontend/src/types/types.ts b/apps/frontend/src/types/types.ts index eb98e3a2..8aba145b 100644 --- a/apps/frontend/src/types/types.ts +++ b/apps/frontend/src/types/types.ts @@ -198,6 +198,8 @@ export interface Order { createdAt: string; shippedAt: string | null; deliveredAt: string | null; + trackingLink: string | null; + shippingCost: number | null; } export interface OrderItemDetails { From bbf6f459b83cc229cf947d0faa13c15ef8441083 Mon Sep 17 00:00:00 2001 From: amywng <147568742+amywng@users.noreply.github.com> Date: Tue, 3 Feb 2026 23:15:29 -0500 Subject: [PATCH 4/4] prettier --- apps/backend/src/orders/order.controller.spec.ts | 5 +---- apps/backend/src/orders/order.controller.ts | 5 +---- apps/backend/src/orders/order.service.ts | 5 +---- 3 files changed, 3 insertions(+), 12 deletions(-) diff --git a/apps/backend/src/orders/order.controller.spec.ts b/apps/backend/src/orders/order.controller.spec.ts index 68be728e..400e322b 100644 --- a/apps/backend/src/orders/order.controller.spec.ts +++ b/apps/backend/src/orders/order.controller.spec.ts @@ -82,10 +82,7 @@ describe('OrdersController', () => { const shippingCost = 15.99; const dto: TrackingCostDto = { trackingLink, shippingCost }; - await controller.updateTrackingAndCost( - orderId, - dto, - ); + await controller.updateTrackingAndCost(orderId, dto); expect(mockOrdersService.updateTrackingAndCost).toHaveBeenCalledWith( orderId, diff --git a/apps/backend/src/orders/order.controller.ts b/apps/backend/src/orders/order.controller.ts index 33068010..994617b5 100644 --- a/apps/backend/src/orders/order.controller.ts +++ b/apps/backend/src/orders/order.controller.ts @@ -108,9 +108,6 @@ export class OrdersController { @Body(new ValidationPipe()) dto: TrackingCostDto, ): Promise { - return this.ordersService.updateTrackingAndCost( - orderId, - dto, - ); + return this.ordersService.updateTrackingAndCost(orderId, dto); } } diff --git a/apps/backend/src/orders/order.service.ts b/apps/backend/src/orders/order.service.ts index c4fad40c..36975108 100644 --- a/apps/backend/src/orders/order.service.ts +++ b/apps/backend/src/orders/order.service.ts @@ -149,10 +149,7 @@ export class OrdersService { return orders; } - async updateTrackingAndCost( - orderId: number, - dto: TrackingCostDto, - ) { + async updateTrackingAndCost(orderId: number, dto: TrackingCostDto) { validateId(orderId, 'Order'); await this.repo