diff --git a/src/errors/AddressErrors.ts b/src/errors/AddressErrors.ts new file mode 100644 index 0000000..300bb31 --- /dev/null +++ b/src/errors/AddressErrors.ts @@ -0,0 +1,57 @@ +import { IsString } from 'class-validator'; +import { BadRequestError } from 'routing-controllers'; + +/** + * Error to throw when an address's postal code fails validation. + */ +export class AddressPostalCodeInvalidError extends BadRequestError { + @IsString() + name = "AddressPostalCodeInvalidError" + + @IsString() + message = "The postal code you provided is invalid. \n Please check if your postal code follows the postal code validation guidelines." +} + +/** + * Error to throw when an non-empty address's first line isn't set. + */ +export class AddressFirstLineEmptyError extends BadRequestError { + @IsString() + name = "AddressFirstLineEmptyError" + + @IsString() + message = "You provided a empty first address line. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's postal code isn't set. + */ +export class AddressPostalCodeEmptyError extends BadRequestError { + @IsString() + name = "AddressPostalCodeEmptyError" + + @IsString() + message = "You provided a empty postal code. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's city isn't set. + */ +export class AddressCityEmptyError extends BadRequestError { + @IsString() + name = "AddressCityEmptyError" + + @IsString() + message = "You provided a empty city. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} + +/** + * Error to throw when an non-empty address's country isn't set. + */ +export class AddressCountryEmptyError extends BadRequestError { + @IsString() + name = "AddressCountryEmptyError" + + @IsString() + message = "You provided a empty country. \n If you want an empty address please set all propertys to null. \n For non-empty addresses the following fields have to be set: address1, postalcode, city, country" +} \ No newline at end of file diff --git a/src/models/Address.ts b/src/models/Address.ts new file mode 100644 index 0000000..dc66f10 --- /dev/null +++ b/src/models/Address.ts @@ -0,0 +1,80 @@ +import { + IsPostalCode, + IsString +} from "class-validator"; +import ValidatorJS from 'validator'; +import { config } from '../../config'; +import { AddressCityEmptyError, AddressCountryEmptyError, AddressFirstLineEmptyError, AddressPostalCodeEmptyError, AddressPostalCodeInvalidError } from '../../errors/AddressErrors'; + +/** + * Defines the Address class. + * Implemented this way to prevent any formatting differences. +*/ +export class Address { + /** + * The address's first line. + * Containing the street and house number. + */ + @IsString() + address1?: string; + + /** + * The address's second line. + * Containing optional information. + */ + @IsString() + address2?: string; + + /** + * The address's postal code. + * This will get checked against the postal code syntax for the configured country. + */ + @IsString() + @IsPostalCode(config.postalcode_validation_countrycode) + postalcode: string; + + /** + * The address's city. + */ + @IsString() + city: string; + + /** + * The address's country. + */ + @IsString() + country: string; + + public reset() { + this.address1 = null; + this.address2 = null; + this.city = null; + this.country = null; + this.postalcode = null; + } + + /** + * Checks if this is a valid address + */ + public static isValidAddress(address: Address): Boolean { + if (address == null) { return false; } + if (address.address1 == null || address.city == null || address.country == null || address.postalcode == null) { return false; } + if (ValidatorJS.isPostalCode(address.postalcode, config.postalcode_validation_countrycode) == false) { return false; } + return true; + } + + /** + * This function validates addresses. + * This is a workaround for non-existant class validation for embedded entities. + * @param address The address that shall get validated. + */ + public static validate(address: Address) { + if (address == null) { return; } + if (address.address1 == null && address.city == null && address.country == null && address.postalcode == null) { return; } + if (address.address1 == null) { throw new AddressFirstLineEmptyError(); } + if (address.postalcode == null) { throw new AddressPostalCodeEmptyError(); } + if (address.city == null) { throw new AddressCityEmptyError(); } + if (address.country == null) { throw new AddressCountryEmptyError(); } + if (ValidatorJS.isPostalCode(address.postalcode.toString(), config.postalcode_validation_countrycode) == false) { throw new AddressPostalCodeInvalidError(); } + } +} diff --git a/src/models/CertificateRunner.ts b/src/models/CertificateRunner.ts new file mode 100644 index 0000000..a30b356 --- /dev/null +++ b/src/models/CertificateRunner.ts @@ -0,0 +1,16 @@ +import { + IsArray +} from "class-validator"; +import { DistanceDonation } from './DistanceDonation'; +import { Runner } from './Runner'; + +/** + * Defines the certificate runner class (from which the runner certificates get generated). +*/ +export class CertificateRunner extends Runner { + /** + * Array containing all distance donations associated with the runner. + */ + @IsArray() + distanceDonations: DistanceDonation[]; +} diff --git a/src/models/DistanceDonation.ts b/src/models/DistanceDonation.ts new file mode 100644 index 0000000..a4e35e1 --- /dev/null +++ b/src/models/DistanceDonation.ts @@ -0,0 +1,40 @@ +import { IsInt, IsNotEmpty, IsObject, IsPositive } from "class-validator"; +import { Donation } from "./Donation"; +import { Runner } from "./Runner"; + +/** + * Defines the DistanceDonation class. + * For distanceDonations a donor pledges to donate a certain amount for each kilometer ran by a runner. +*/ +export class DistanceDonation extends Donation { + /** + * The donation's associated runner. + * Used as the source of the donation's distance. + */ + @IsObject() + @IsNotEmpty() + runner: Runner; + + /** + * The donation's amount donated per distance. + * The amount the donor set to be donated per kilometer that the runner ran. + */ + @IsInt() + @IsPositive() + amountPerDistance: number; + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + * Get's calculated from the runner's distance ran and the amount donated per kilometer. + */ + public get amount(): number { + let calculatedAmount = 0; + try { + calculatedAmount = this.amountPerDistance * (this.runner.distance / 1000); + } catch (error) { + throw error; + } + return calculatedAmount; + } + +} diff --git a/src/models/Donation.ts b/src/models/Donation.ts new file mode 100644 index 0000000..1beaf0c --- /dev/null +++ b/src/models/Donation.ts @@ -0,0 +1,32 @@ +import { + IsInt, + IsNotEmpty, + IsObject +} from "class-validator"; +import { Donor } from './Donor'; + +/** + * Defines the Donation base calss. + * A donation just associates a donor with a donation amount. + * The specifics of the amoun's determination has to be implemented in child classes. +*/ +export abstract class Donation { + /** + * Autogenerated unique id (primary key). + */ + @IsInt() + id: number; + + /** + * The donations's donor. + */ + @IsNotEmpty() + @IsObject() + donor: Donor; + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + * The exact implementation may differ for each type of donation. + */ + public abstract get amount(): number; +} \ No newline at end of file diff --git a/src/models/Donor.ts b/src/models/Donor.ts new file mode 100644 index 0000000..cb9d9df --- /dev/null +++ b/src/models/Donor.ts @@ -0,0 +1,37 @@ +import { + + IsInt, + + + + IsString +} from "class-validator"; + +/** + * Defines the Donor class. +*/ +export class Donor { + /** + * The donor's id. + */ + @IsInt() + id: number; + + /** + * The donor's first name. + */ + @IsString() + firstname: string; + + /** + * The donor's middle name. + */ + @IsString() + middlename?: string; + + /** + * The donor's last name. + */ + @IsString() + lastname: string; +} diff --git a/src/models/FixedDonation.ts b/src/models/FixedDonation.ts new file mode 100644 index 0000000..18c8a23 --- /dev/null +++ b/src/models/FixedDonation.ts @@ -0,0 +1,16 @@ +import { IsInt, IsPositive } from "class-validator"; +import { Donation } from "./Donation"; + +/** + * Defines the FixedDonation entity. + * In the past there was no easy way to track fixed donations (eg. for creating donation receipts). +*/ +export class FixedDonation extends Donation { + + /** + * The donation's amount in cents (or whatever your currency's smallest unit is.). + */ + @IsInt() + @IsPositive() + amount: number; +} \ No newline at end of file diff --git a/src/models/Runner.ts b/src/models/Runner.ts new file mode 100644 index 0000000..f7968a8 --- /dev/null +++ b/src/models/Runner.ts @@ -0,0 +1,47 @@ +import { + IsInt, + IsObject, + IsString +} from "class-validator"; +import { RunnerGroup } from './RunnerGroup'; + +/** + * Defines the runner class (from which the runner sponsoring contracts get generated). +*/ +export class Runner { + /** + * The runner's id. + */ + @IsInt() + id: number; + + /** + * The runner's first name. + */ + @IsString() + firstname: string; + + /** + * The runner's middle name. + */ + @IsString() + middlename?: string; + + /** + * The runner's last name. + */ + @IsString() + lastname: string; + + /** + * The runner's group. + */ + @IsObject() + group: RunnerGroup; + + /** + * The total distance ran by the runner. + */ + @IsInt() + distance: number; +} diff --git a/src/models/RunnerCard.ts b/src/models/RunnerCard.ts new file mode 100644 index 0000000..3a4cffb --- /dev/null +++ b/src/models/RunnerCard.ts @@ -0,0 +1,33 @@ +import { + IsEAN, + IsInt, + IsNotEmpty, + IsObject, + IsString +} from "class-validator"; +import { Runner } from './Runner'; + +/** + * Defines the runner card class (used to create runner card pdfs). +*/ +export class RunnerCard { + /** + * The cards's id. + */ + @IsInt() + id: number; + + /** + * The card's associated runner. + */ + @IsObject() + runner: Runner | null; + + /** + * The card's code. + */ + @IsEAN() + @IsString() + @IsNotEmpty() + code: string; +} diff --git a/src/models/RunnerGroup.ts b/src/models/RunnerGroup.ts new file mode 100644 index 0000000..f50a179 --- /dev/null +++ b/src/models/RunnerGroup.ts @@ -0,0 +1,36 @@ +import { IsInt, IsNotEmpty, IsObject, IsOptional, IsString } from "class-validator"; + +/** + * Defines the runner group class - a simplified version of the backend's ResponseRunnerTeam/-Organization +*/ +export abstract class RunnerGroup { + /** + * The group's id. + */ + @IsInt() + @IsNotEmpty() + id: number;; + + /** + * The group's name. + */ + @IsString() + @IsNotEmpty() + name: string; + + /** + * The group's parent group. + * If it is set this implies that the object is a team. + */ + @IsObject() + @IsOptional() + parentGroup?: RunnerGroup + + /** + * Returns the groups full name in the format: org.name/team.name (or just org). + */ + public get fullName(): string { + if (!this.parentGroup) { return this.name; } + return `${this.name}/${this.parentGroup.fullName}`; + } +}