import { IsBoolean, IsInt, IsOptional } from "class-validator"; import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm"; import { RunnerCardIdOutOfRangeError } from '../../errors/RunnerCardErrors'; import { ResponseRunnerCard } from '../responses/ResponseRunnerCard'; import { Runner } from "./Runner"; import { TrackScan } from "./TrackScan"; /** * Defines the RunnerCard entity. * A runnerCard is a physical representation for a runner. * It can be associated with a runner to create scans via the scan station's. */ @Entity() export class RunnerCard { /** * Autogenerated unique id (primary key). */ @PrimaryGeneratedColumn() @IsInt() id: number; /** * The card's currently associated runner. * To increase reusability a card can be reassigned. */ @IsOptional() @ManyToOne(() => Runner, runner => runner.cards, { nullable: true }) runner: Runner; /** * Is the card enabled (for fraud reasons)? * Default: true */ @Column() @IsBoolean() enabled: boolean = true; /** * The card's associated scans. * Used to link cards to track scans. */ @OneToMany(() => TrackScan, scan => scan.track, { nullable: true }) scans: TrackScan[]; /** * Generates a ean-13 compliant string for barcode generation. */ public get code(): string { const multiply = [1, 3]; let total = 0; this.paddedId.split('').forEach((letter, index) => { total += parseInt(letter, 10) * multiply[index % 2]; }); const checkSum = (Math.ceil(total / 10) * 10) - total; return this.paddedId + checkSum.toString(); } /** * Returns this card's id as a string padded to the length of 12 characters with leading zeros. */ private get paddedId(): string { let id: string = this.id.toString(); if (id.length > 11) { throw new RunnerCardIdOutOfRangeError(); } while (id.length < 11) { id = '0' + id; } id = '2' + id; return id; } /** * Turns this entity into it's response class. */ public toResponse() { return new ResponseRunnerCard(this); } }