diff --git a/src/models/actions/create/CreateDistanceDonation.ts b/src/models/actions/create/CreateDistanceDonation.ts index b1ba1dd..133e71b 100644 --- a/src/models/actions/create/CreateDistanceDonation.ts +++ b/src/models/actions/create/CreateDistanceDonation.ts @@ -33,6 +33,7 @@ export class CreateDistanceDonation extends CreateDonation { let newDonation = new DistanceDonation; newDonation.amountPerDistance = this.amountPerDistance; + newDonation.paidAmount = this.paidAmount; newDonation.donor = await this.getDonor(); newDonation.runner = await this.getRunner(); diff --git a/src/models/actions/create/CreateDonation.ts b/src/models/actions/create/CreateDonation.ts index 50d7cd7..e70455e 100644 --- a/src/models/actions/create/CreateDonation.ts +++ b/src/models/actions/create/CreateDonation.ts @@ -1,4 +1,4 @@ -import { IsInt, IsPositive } from 'class-validator'; +import { IsInt, IsOptional, IsPositive } from 'class-validator'; import { getConnection } from 'typeorm'; import { DonorNotFoundError } from '../../../errors/DonorErrors'; import { Donation } from '../../entities/Donation'; @@ -16,6 +16,13 @@ export abstract class CreateDonation { @IsPositive() donor: number; + /** + * The donation's paid amount in the smalles unit of your currency (default: euro cent). + */ + @IsInt() + @IsOptional() + paidAmount?: number; + /** * Creates a new Donation entity from this. */ diff --git a/src/models/actions/create/CreateFixedDonation.ts b/src/models/actions/create/CreateFixedDonation.ts index 4d73f50..bfdca9f 100644 --- a/src/models/actions/create/CreateFixedDonation.ts +++ b/src/models/actions/create/CreateFixedDonation.ts @@ -21,6 +21,7 @@ export class CreateFixedDonation extends CreateDonation { let newDonation = new FixedDonation; newDonation.amount = this.amount; + newDonation.paidAmount = this.paidAmount; newDonation.donor = await this.getDonor(); return newDonation; diff --git a/src/models/actions/update/UpdateDistanceDonation.ts b/src/models/actions/update/UpdateDistanceDonation.ts index 67407b9..c9a66ce 100644 --- a/src/models/actions/update/UpdateDistanceDonation.ts +++ b/src/models/actions/update/UpdateDistanceDonation.ts @@ -32,6 +32,7 @@ export class UpdateDistanceDonation extends UpdateDonation { */ public async update(donation: DistanceDonation): Promise { donation.amountPerDistance = this.amountPerDistance; + donation.paidAmount = this.paidAmount; donation.donor = await this.getDonor(); donation.runner = await this.getRunner(); diff --git a/src/models/actions/update/UpdateDonation.ts b/src/models/actions/update/UpdateDonation.ts index 569454a..3acf2b9 100644 --- a/src/models/actions/update/UpdateDonation.ts +++ b/src/models/actions/update/UpdateDonation.ts @@ -1,4 +1,4 @@ -import { IsInt, IsPositive } from 'class-validator'; +import { IsInt, IsOptional, IsPositive } from 'class-validator'; import { getConnection } from 'typeorm'; import { DonorNotFoundError } from '../../../errors/DonorErrors'; import { Donation } from '../../entities/Donation'; @@ -23,6 +23,13 @@ export abstract class UpdateDonation { @IsPositive() donor: number; + /** + * The donation's paid amount in the smalles unit of your currency (default: euro cent). + */ + @IsInt() + @IsOptional() + paidAmount?: number; + /** * Creates a new Donation entity from this. */ diff --git a/src/models/actions/update/UpdateFixedDonation.ts b/src/models/actions/update/UpdateFixedDonation.ts index 5e31068..9e0401f 100644 --- a/src/models/actions/update/UpdateFixedDonation.ts +++ b/src/models/actions/update/UpdateFixedDonation.ts @@ -20,6 +20,7 @@ export class UpdateFixedDonation extends UpdateDonation { */ public async update(donation: FixedDonation): Promise { donation.amount = this.amount; + donation.paidAmount = this.paidAmount; donation.donor = await this.getDonor(); return donation; diff --git a/src/models/entities/Donation.ts b/src/models/entities/Donation.ts index 1dd023d..f693d01 100644 --- a/src/models/entities/Donation.ts +++ b/src/models/entities/Donation.ts @@ -2,7 +2,7 @@ import { IsInt, IsNotEmpty } from "class-validator"; -import { Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; +import { Column, Entity, ManyToOne, PrimaryGeneratedColumn, TableInheritance } from "typeorm"; import { ResponseDonation } from '../responses/ResponseDonation'; import { Donor } from './Donor'; @@ -34,6 +34,13 @@ export abstract class Donation { */ public abstract get amount(): number; + /** + * The donation's paid amount in cents (or whatever your currency's smallest unit is.). + * Used to mark donations as paid. + */ + @Column({ nullable: true }) + @IsInt() + paidAmount: number; /** * Turns this entity into it's response class. diff --git a/src/models/entities/Donor.ts b/src/models/entities/Donor.ts index acf5536..4d42769 100644 --- a/src/models/entities/Donor.ts +++ b/src/models/entities/Donor.ts @@ -33,6 +33,15 @@ export class Donor extends Participant { return this.donations.reduce((sum, current) => sum + current.amount, 0); } + /** + * Returns the total paid donations of a donor based on his linked donations. + */ + @IsInt() + public get paidDonationAmount(): number { + if (!this.donations) { return 0; } + return this.donations.reduce((sum, current) => sum + current.paidAmount, 0); + } + /** * Turns this entity into it's response class. */ diff --git a/src/models/enums/DonationStatus.ts b/src/models/enums/DonationStatus.ts new file mode 100644 index 0000000..0f2323f --- /dev/null +++ b/src/models/enums/DonationStatus.ts @@ -0,0 +1,7 @@ +/** + * This enum contains all status a donation can inherit regarding it's payment status. + */ +export enum DonationStatus { + OPEN = 'OPEN', + PAID = 'PAID' +} \ No newline at end of file diff --git a/src/models/responses/ResponseDonation.ts b/src/models/responses/ResponseDonation.ts index e29f11f..e917e96 100644 --- a/src/models/responses/ResponseDonation.ts +++ b/src/models/responses/ResponseDonation.ts @@ -1,5 +1,6 @@ import { IsInt, IsNotEmpty, IsPositive } from "class-validator"; import { Donation } from '../entities/Donation'; +import { DonationStatus } from '../enums/DonationStatus'; import { ResponseObjectType } from '../enums/ResponseObjectType'; import { IResponse } from './IResponse'; import { ResponseDonor } from './ResponseDonor'; @@ -15,6 +16,12 @@ export class ResponseDonation implements IResponse { */ responseType: ResponseObjectType = ResponseObjectType.DONATION; + /** + * The donation's payment status. + * Provides you with a quick indicator of it's payment status. + */ + status: DonationStatus; + /** * The donation's id. */ @@ -34,6 +41,12 @@ export class ResponseDonation implements IResponse { @IsInt() amount: number; + /** + * The donation's paid amount in the smalles unit of your currency (default: euro cent). + */ + @IsInt() + paidAmount: number; + /** * Creates a ResponseDonation object from a scan. * @param donation The donation the response shall be build for. @@ -42,5 +55,12 @@ export class ResponseDonation implements IResponse { this.id = donation.id; this.donor = donation.donor.toResponse(); this.amount = donation.amount; + this.paidAmount = donation.paidAmount || 0; + if (this.paidAmount < this.amount) { + this.status = DonationStatus.OPEN; + } + else { + this.status = DonationStatus.PAID; + } } } diff --git a/src/models/responses/ResponseDonor.ts b/src/models/responses/ResponseDonor.ts index 2b1e3fc..07f12dc 100644 --- a/src/models/responses/ResponseDonor.ts +++ b/src/models/responses/ResponseDonor.ts @@ -28,6 +28,12 @@ export class ResponseDonor extends ResponseParticipant implements IResponse { @IsInt() donationAmount: number; + /** + * Returns the total paid donations of a donor based on his linked donations. + */ + @IsInt() + paidDonationAmount: number; + /** * Creates a ResponseRunner object from a runner. * @param runner The user the response shall be build for. @@ -36,5 +42,6 @@ export class ResponseDonor extends ResponseParticipant implements IResponse { super(donor); this.receiptNeeded = donor.receiptNeeded; this.donationAmount = donor.donationAmount; + this.paidDonationAmount = donor.paidDonationAmount; } } diff --git a/src/tests/donations/donations_add.spec.ts b/src/tests/donations/donations_add.spec.ts index c851d8d..1f9bfc2 100644 --- a/src/tests/donations/donations_add.spec.ts +++ b/src/tests/donations/donations_add.spec.ts @@ -170,7 +170,7 @@ describe('POST /api/donations/fixed successfully', () => { expect(res.status).toEqual(200); expect(res.headers['content-type']).toContain("application/json") }); - it('creating a new fixed donation should return 200', async () => { + it('creating a new fixed donation with more params should return 200', async () => { const res = await axios.post(base + '/api/donations/fixed', { "donor": added_donor.id, "amount": 1000 @@ -181,6 +181,25 @@ describe('POST /api/donations/fixed successfully', () => { expect(res.data).toEqual({ "donor": added_donor, "amount": 1000, + "paidAmount": 0, + "status": "OPEN", + "responseType": "DONATION" + }); + }); + it('creating a new fixed donation with all params should return 200', async () => { + const res = await axios.post(base + '/api/donations/fixed', { + "donor": added_donor.id, + "amount": 1000, + "paidAmount": 1000 + }, axios_config); + delete res.data.id; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ + "donor": added_donor, + "amount": 1000, + "paidAmount": 1000, + "status": "PAID", "responseType": "DONATION" }); }); @@ -219,7 +238,7 @@ describe('POST /api/donations/distance successfully', () => { expect(res.status).toEqual(200); expect(res.headers['content-type']).toContain("application/json") }); - it('creating a new fixed donation should return 200', async () => { + it('creating a new distance donation with most params should return 200', async () => { const res = await axios.post(base + '/api/donations/distance', { "runner": added_runner.id, "amountPerDistance": 100, @@ -233,6 +252,28 @@ describe('POST /api/donations/distance successfully', () => { "amountPerDistance": 100, "runner": added_runner, "amount": 0, + "paidAmount": 0, + "status": "PAID", + "responseType": "DISTANCEDONATION" + }) + }); + it('creating a new distance donation with all params should return 200', async () => { + const res = await axios.post(base + '/api/donations/distance', { + "runner": added_runner.id, + "amountPerDistance": 100, + "donor": added_donor.id, + "paidAmount": 1000 + }, axios_config); + delete res.data.id; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data).toEqual({ + "donor": added_donor, + "amountPerDistance": 100, + "runner": added_runner, + "amount": 0, + "paidAmount": 1000, + "status": "PAID", "responseType": "DISTANCEDONATION" }) }); diff --git a/src/tests/donations/donations_update.spec.ts b/src/tests/donations/donations_update.spec.ts index 0ac7e86..c7f4fc7 100644 --- a/src/tests/donations/donations_update.spec.ts +++ b/src/tests/donations/donations_update.spec.ts @@ -213,6 +213,17 @@ describe('adding + updating fixed donation valid', () => { expect(res.headers['content-type']).toContain("application/json"); expect(res.data.amount).toEqual(42); }); + it('updating paidAmount should return 200', async () => { + const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { + "id": added_donation.id, + "donor": added_donor.id, + "amount": 42, + "paidAmount": 10 + }, axios_config); + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data.paidAmount).toEqual(10); + }); it('updating donor should return 200', async () => { const res = await axios.put(base + '/api/donations/fixed/' + added_donation.id, { "id": added_donation.id, @@ -317,6 +328,19 @@ describe('adding + updating distance donation valid', () => { expect(res.headers['content-type']).toContain("application/json"); expect(res.data.amountPerDistance).toEqual(69); }); + it('updating paidAmount should return 200', async () => { + const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { + "id": added_donation.id, + "runner": added_runner.id, + "amountPerDistance": 69, + "donor": added_donor.id, + "paidAmount": 10 + }, axios_config); + delete res.data.donor.donationAmount; + expect(res.status).toEqual(200); + expect(res.headers['content-type']).toContain("application/json"); + expect(res.data.paidAmount).toEqual(10); + }); it('updating runner should return 200', async () => { const res = await axios.put(base + '/api/donations/distance/' + added_donation.id, { "id": added_donation.id,