Skip to content

Commit

Permalink
Merge pull request #102 from cuappdev/ashley/ptf-transaction
Browse files Browse the repository at this point in the history
PTF - Added Transaction Entity, Post Sold Marking, and Notifications
  • Loading branch information
ashleynhs authored Dec 26, 2024
2 parents 0265073 + 3102398 commit f86c4e5
Show file tree
Hide file tree
Showing 33 changed files with 1,357 additions and 13 deletions.
4 changes: 2 additions & 2 deletions ormconfig.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//import { SnakeNamingStrategy } from 'typeorm-naming-strategies';
import { SnakeNamingStrategy } from 'typeorm-naming-strategies';

module.exports = {
type: 'postgres',
Expand All @@ -11,7 +11,7 @@ module.exports = {
'src/models/*.ts',
],
synchronize: false,
//namingStrategy: new SnakeNamingStrategy(),
namingStrategy: new SnakeNamingStrategy(),
migrations: [
'src/migrations/*.ts',
],
Expand Down
69 changes: 69 additions & 0 deletions src/api/controllers/TransactionController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Body, CurrentUser, Delete, Get, JsonController, Params, Post } from "routing-controllers";

import { TransactionModel } from "../../models/TransactionModel";
import { TransactionService } from "../../services/TransactionService";
import {
CreateTransactionRequest,
UpdateTransactionStatusRequest,
} from "../../types/ApiRequests";
import { UuidParam } from "../validators/GenericRequests";

@JsonController("transaction/")
export class TransactionController {
private transactionService: TransactionService;

constructor(transactionService: TransactionService) {
this.transactionService = transactionService;
}

@Get()
async getAllTransactions(): Promise<{ transactions: TransactionModel[] }> {
return { transactions: await this.transactionService.getAllTransactions() };
}

@Get("id/:id/")
async getTransactionById(
@Params() params: UuidParam
): Promise<{ transaction: TransactionModel }> {
return { transaction: await this.transactionService.getTransactionById(params) };
}

@Get("buyerId/:id/")
async getTransactionsByBuyerId(
@Params() params: UuidParam
): Promise<{ transactions: TransactionModel[] }> {
return { transactions: await this.transactionService.getTransactionsByBuyerId(params) };
}

@Get("sellerId/:id/")
async getTransactionsBySellerId(
@Params() params: UuidParam
): Promise<{ transactions: TransactionModel[] }> {
return { transactions: await this.transactionService.getTransactionsBySellerId(params) };
}

@Post()
async createTransaction(
@Body() createTransactionRequest: CreateTransactionRequest
): Promise<{ transaction: TransactionModel }> {
return { transaction: await this.transactionService.createTransaction(createTransactionRequest) };
}

@Post("complete/id/:id/")
async completeTransaction(
@Params() params: UuidParam,
@Body() updateTransactionStatusRequest: UpdateTransactionStatusRequest
): Promise<{ transaction: TransactionModel }> {
return {
transaction: await this.transactionService.completeTransaction(params, updateTransactionStatusRequest),
};
}

@Get("postId/:id/")
async getTransactionByPostId(
@Params() params: UuidParam
): Promise<{ transaction: TransactionModel }> {
return { transaction: await this.transactionService.getTransactionByPostId(params) };
}

}
39 changes: 39 additions & 0 deletions src/api/controllers/TransactionReviewController.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Body, Delete, Get, JsonController, Params, Post, QueryParam } from 'routing-controllers';
import { CreateTransactionReviewRequest } from '../../types';
import { TransactionReviewModel } from '../../models/TransactionReviewModel';
import { TransactionReviewService } from '../../services/TransactionReviewService';
import { UuidParam } from '../validators/GenericRequests';

@JsonController('transactionReview/')
export class TransactionReviewController {
private transactionReviewService: TransactionReviewService;

constructor(transactionReviewService: TransactionReviewService) {
this.transactionReviewService = transactionReviewService;
}

@Get()
async getTransactionReviews(): Promise<TransactionReviewModel[]> {
return await this.transactionReviewService.getAllTransactionReviews();
}

@Get('id/:id/')
async getTransactionReviewById(@Params() params: UuidParam): Promise<TransactionReviewModel> {
return await this.transactionReviewService.getTransactionReviewById(params);
}

@Get('transactionId/:transactionId/')
async getTransactionReviewByTransactionId(@Params() params: UuidParam): Promise<TransactionReviewModel> {
return await this.transactionReviewService.getTransactionReviewByTransactionId(params);
}

@Post()
async createTransactionReview(@Body() createTransactionReviewRequest: CreateTransactionReviewRequest): Promise<TransactionReviewModel> {
return await this.transactionReviewService.createTransactionReview(createTransactionReviewRequest);
}

@Delete('id/:id/')
async deleteTransactionReview(@Params() params: UuidParam): Promise<TransactionReviewModel> {
return await this.transactionReviewService.deleteTransactionReviewById(params);
}
}
3 changes: 3 additions & 0 deletions src/api/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UserController } from './UserController';
import { UserReviewController } from './UserReviewController';
import { NotifController } from './NotifController'
import { ReportController } from './ReportController';
import { TransactionController } from './TransactionController';

export const controllers = [
AuthController,
Expand All @@ -18,4 +19,6 @@ export const controllers = [
ReportController,
UserController,
UserReviewController,
TransactionController
TransactionReviewController
];
33 changes: 33 additions & 0 deletions src/factories/TransactionFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// TransactionFactory.ts
import { define } from 'typeorm-seeding';
import { TransactionModel } from '../models/TransactionModel';
import { UserModel } from '../models/UserModel';
import { PostModel } from '../models/PostModel';

// Define a factory for TransactionModel
define(TransactionModel, (_, context?: { index?: number; buyer?: UserModel; seller?: UserModel; post?: PostModel }) => {
if (
context === undefined ||
context.buyer === undefined ||
context.seller === undefined ||
context.post === undefined ||
context.index === undefined
) {
throw "Context, buyer, seller, post, and index cannot be undefined";
}

const transaction = new TransactionModel();
const index = context.index; // Use the provided index for uniqueness

transaction.buyer = context.buyer;
transaction.seller = context.seller;
transaction.post = context.post;

// Use consistent data for transaction fields, utilizing index for uniqueness
transaction.location = `Sample Location - Buyer ${transaction.buyer.givenName} - Seller ${transaction.seller.givenName} - Index: ${index}`;
transaction.amount = 100 + index; // Increment amount for uniqueness
transaction.transactionDate = new Date(`2023-01-${String(index).padStart(2, '0')}T12:00:00Z`); // Unique date for each transaction
transaction.completed = index % 2 === 0; // Alternate between true and false for completed

return transaction;
});
26 changes: 26 additions & 0 deletions src/factories/TransactionReviewFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// TransactionReviewFactory.ts
import { define } from 'typeorm-seeding';
import { TransactionReviewModel } from '../models/TransactionReviewModel';
import { TransactionModel } from '../models/TransactionModel';

// Define a factory for TransactionReviewModel
define(TransactionReviewModel, (_, context?: { index?: number; transaction?: TransactionModel }) => {
if (context === undefined || context.transaction === undefined || context.index === undefined) {
throw "Context, transaction, and index cannot be undefined";
}

const transactionReview = new TransactionReviewModel();
const index = context.index; // Use the provided index for uniqueness

transactionReview.transaction = context.transaction;

// Use consistent data for transaction review fields, utilizing index for uniqueness
transactionReview.stars = (index % 5) + 1; // Stars from 1 to 5
transactionReview.comments = index % 2 === 0 ? `Great transaction! - Index: ${index}` : null; // Alternate between having comments or not
transactionReview.hadIssues = index % 3 === 0; // Alternate having issues
transactionReview.issueCategory = transactionReview.hadIssues ? `Issue Category - Index: ${index}` : null; // Issue category if hadIssues is true
transactionReview.issueDetails = transactionReview.hadIssues ? `Detailed issue description for transaction review - Index: ${index}` : null; // Issue details if hadIssues is true
transactionReview.createdAt = new Date(`2023-01-${String(index).padStart(2, '0')}T12:00:00Z`); // Unique creation date

return transactionReview;
});
44 changes: 44 additions & 0 deletions src/migrations/1732906578369-AddTransactionTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {MigrationInterface, QueryRunner} from "typeorm";

export class AddTransactionTable1732906578369 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "Transaction" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"location" character varying NOT NULL,
"amount" numeric NOT NULL,
"transaction_date" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"completed" boolean NOT NULL DEFAULT false,
"buyer_id" uuid,
"seller_id" uuid,
"post_id" uuid,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
CONSTRAINT "PK_Transaction" PRIMARY KEY ("id")
)
`);

await queryRunner.query(`
ALTER TABLE "Transaction"
ADD CONSTRAINT "FK_buyer" FOREIGN KEY ("buyer_id") REFERENCES "User"("id") ON DELETE NO ACTION ON UPDATE NO ACTION
`);

await queryRunner.query(`
ALTER TABLE "Transaction"
ADD CONSTRAINT "FK_seller" FOREIGN KEY ("seller_id") REFERENCES "User"("id") ON DELETE NO ACTION ON UPDATE NO ACTION
`);

await queryRunner.query(`
ALTER TABLE "Transaction"
ADD CONSTRAINT "FK_post" FOREIGN KEY ("post_id") REFERENCES "Post"("id") ON DELETE NO ACTION ON UPDATE NO ACTION
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "Transaction" DROP CONSTRAINT "FK_post"`);
await queryRunner.query(`ALTER TABLE "Transaction" DROP CONSTRAINT "FK_seller"`);
await queryRunner.query(`ALTER TABLE "Transaction" DROP CONSTRAINT "FK_buyer"`);
await queryRunner.query(`DROP TABLE "Transaction"`);
}

}
13 changes: 13 additions & 0 deletions src/migrations/1732924592033-AddSoldColumnToPost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {MigrationInterface, QueryRunner} from "typeorm";

export class AddSoldColumnToPost1732924592033 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "Post" ADD COLUMN IF NOT EXISTS "sold" boolean NOT NULL DEFAULT false`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "Post" DROP COLUMN IF EXISTS "sold"`);
}

}
31 changes: 31 additions & 0 deletions src/migrations/1732975238671-AddTransactionReviewTable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {MigrationInterface, QueryRunner} from "typeorm";

export class AddTransactionReviewTable1732975238671 implements MigrationInterface {

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
CREATE TABLE "TransactionReview" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"stars" integer NOT NULL,
"comments" character varying,
"had_issues" boolean NOT NULL DEFAULT false,
"issue_category" character varying,
"issue_details" character varying,
"created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
"transaction_id" uuid,
CONSTRAINT "PK_TransactionReview" PRIMARY KEY ("id")
)
`);

await queryRunner.query(`
ALTER TABLE "TransactionReview"
ADD CONSTRAINT "FK_transaction" FOREIGN KEY ("transaction_id") REFERENCES "Transaction"("id") ON DELETE CASCADE ON UPDATE NO ACTION
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "TransactionReview" DROP CONSTRAINT "FK_transaction"`);
await queryRunner.query(`DROP TABLE "TransactionReview"`);
}

}
4 changes: 4 additions & 0 deletions src/models/PostModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export class PostModel {
})
matched: RequestModel[];

@Column({ default: false })
sold: boolean;

@OneToMany(() => ReportModel, (report) => report.post)
public reports: ReportModel[];

Expand All @@ -88,6 +91,7 @@ export class PostModel {
user: this.user.getUserProfile(),
savers: this.savers?.map((user) => user.getUserProfile()),
matched: this.matched?.map((request) => request.getRequestInfo()),
sold: this.sold,
};
}
}
59 changes: 59 additions & 0 deletions src/models/TransactionModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import {
Entity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
ManyToOne,
JoinColumn,
} from "typeorm";
import { PostModel } from "./PostModel";
import { UserModel } from "./UserModel";

@Entity("Transaction")
export class TransactionModel {
@PrimaryGeneratedColumn("uuid")
id: string;

@Column()
location: string; // Transaction location

@Column("numeric", { scale: 2 })
amount: number; // Transaction amount

@Column({ type: "timestamptz" })
transactionDate: Date; // Date and time of the transaction

@Column({ default: false })
completed: boolean; // Whether the transaction is completed

@ManyToOne(() => PostModel, { cascade: false })
@JoinColumn({ name: "post_id" })
post: PostModel; // The item/listing itself

@ManyToOne(() => UserModel, { cascade: false })
@JoinColumn({ name: "buyer_id" })
buyer: UserModel; // The buyer

@ManyToOne(() => UserModel, { cascade: false })
@JoinColumn({ name: "seller_id" })
seller: UserModel; // The seller

@CreateDateColumn({ type: "timestamptz" })
createdAt: Date; // Automatically store when the transaction record was created


public getTransactionInfo() {
return {
id: this.id,
location: this.location,
amount: this.amount,
transactionDate: this.transactionDate,
completed: this.completed,
post: this.post?.getPostInfo(),
buyer: this.buyer?.getUserProfile(),
seller: this.seller?.getUserProfile(),
createdAt: this.createdAt,
};
}
}

Loading

0 comments on commit f86c4e5

Please sign in to comment.