commit working state
Too many changes to split them up. This commit contains roughly two months worth of work. This commit introduces the first aggregate and rewrites the ticket system to follow DDD patterns. It adds Spartan NG as the new component library. TanStack Query to replace GraphQL. Oh and Prisma is almost over as well.
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@avicenna/shared-dtos",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc --watch"
|
||||
},
|
||||
"devDependencies": {
|
||||
"typescript": "^5.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/swagger": "^11.2.5",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.3",
|
||||
"date-fns": "^4.1.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
export interface OptimizationTourDto {
|
||||
id: string;
|
||||
operationId: string;
|
||||
ordinanceType: string;
|
||||
startDate: string;
|
||||
optimizationInMinutes: number;
|
||||
|
||||
departedForPickup: string | null;
|
||||
arrivedAtPickup: string | null;
|
||||
departedPickup: string | null;
|
||||
arrivedAtTarget: string | null;
|
||||
completed: string | null;
|
||||
}
|
||||
|
||||
export interface OptimizationCarDto {
|
||||
name: string;
|
||||
tours: OptimizationTourDto[];
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
export interface AccountingListDto {
|
||||
tourId: string;
|
||||
operationId: string;
|
||||
ordinanceType: string;
|
||||
startDate: Date;
|
||||
revenue: number;
|
||||
|
||||
isTicketOpen: boolean;
|
||||
hasRevenueDeviation: boolean;
|
||||
hasOptimizationPotential: boolean;
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
||||
import { CostTypeVariant } from '@prisma/client';
|
||||
import { Type } from 'class-transformer';
|
||||
|
||||
export class TourDto {
|
||||
@ApiProperty()
|
||||
id!: string;
|
||||
|
||||
@ApiProperty()
|
||||
operationId!: string;
|
||||
|
||||
@ApiProperty()
|
||||
done!: boolean;
|
||||
|
||||
@ApiProperty()
|
||||
direction!: string;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
carName!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
driverId!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
driverName!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
codriverId!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
codriverName!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientId!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientName!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientSurname!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientStreet!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientZip!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
patientCity!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
healthInsurance!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
healthInsuranceNumber!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
category!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
transportType!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
ordinanceType!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startInstitution!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startStreet!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startZip!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startCity!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
targetInstitution!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
targetStreet!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
targetZip!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
targetCity!: string | null;
|
||||
|
||||
@ApiProperty({ type: Date })
|
||||
@Type(() => Date)
|
||||
startDate!: Date;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startBegin!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
startEnd!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
target!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
targetBegin!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
empty!: string | null;
|
||||
|
||||
@ApiProperty()
|
||||
check!: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
occupiedKm!: number | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
totalKm!: number | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
type!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true, type: Date })
|
||||
@Type(() => Date)
|
||||
rangeEndDate!: Date | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
revenueDispoLive!: number | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
revenue!: number | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
consumptionCosts!: number | null;
|
||||
|
||||
@ApiProperty()
|
||||
revenueDeviation!: number;
|
||||
|
||||
@ApiProperty({ nullable: true, type: Date })
|
||||
@Type(() => Date)
|
||||
billDate!: Date | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
billNumber!: string | null;
|
||||
|
||||
@ApiProperty({ nullable: true, type: Date })
|
||||
@Type(() => Date)
|
||||
createdAt!: Date | null;
|
||||
|
||||
@ApiProperty({ nullable: true, type: Date })
|
||||
@Type(() => Date)
|
||||
updatedAt!: Date | null;
|
||||
|
||||
@ApiProperty({ nullable: true, type: Date })
|
||||
@Type(() => Date)
|
||||
deletedAt!: Date | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
hasInfection!: boolean | null;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
infectionName!: string | null;
|
||||
|
||||
@ApiProperty()
|
||||
optimizationInMinutes!: number;
|
||||
|
||||
@ApiProperty({ nullable: true })
|
||||
ticketId!: string | null;
|
||||
|
||||
// Optional!: Wenn das Ticket eingebettet wird (war vorher ein ResolveField)
|
||||
// Du musst hier ggf. dein TicketDto importieren oder 'any' nutzen.
|
||||
@ApiPropertyOptional()
|
||||
ticket?: any;
|
||||
}
|
||||
|
||||
export class PaginatedTourDto {
|
||||
@ApiProperty({ type: [TourDto] })
|
||||
@Type(() => TourDto)
|
||||
nodes!: TourDto[];
|
||||
|
||||
@ApiProperty()
|
||||
prevCursor!: string;
|
||||
|
||||
@ApiProperty()
|
||||
endCursor!: string;
|
||||
|
||||
@ApiProperty()
|
||||
totalCount!: number;
|
||||
|
||||
@ApiProperty()
|
||||
hasNextPage!: boolean;
|
||||
}
|
||||
|
||||
// --- COST BREAKDOWN ---
|
||||
|
||||
export class ServicePositionDto {
|
||||
@ApiProperty({ enum: CostTypeVariant })
|
||||
variant!: CostTypeVariant;
|
||||
|
||||
@ApiProperty()
|
||||
description!: string;
|
||||
|
||||
@ApiProperty()
|
||||
value!: number;
|
||||
}
|
||||
|
||||
export class TourCostBreakdownDto {
|
||||
@ApiProperty()
|
||||
tariffDescription!: string;
|
||||
|
||||
@ApiProperty()
|
||||
tariffValidityPeriod!: string;
|
||||
|
||||
@ApiProperty({ type: [ServicePositionDto] })
|
||||
@Type(() => ServicePositionDto)
|
||||
servicePositions!: ServicePositionDto[];
|
||||
}
|
||||
|
||||
// --- STATE META ---
|
||||
|
||||
export class TourStateMetaDto {
|
||||
@ApiProperty()
|
||||
count!: number;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
revenueSum?: number;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
positiveRevenueDeviation?: number;
|
||||
|
||||
@ApiPropertyOptional()
|
||||
negativeRevenueDeviation?: number;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './accounting-car.dto';
|
||||
export * from './accounting-list.dto';
|
||||
export * from './accounting.dto';
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface AddressDto {
|
||||
street: string;
|
||||
city: string;
|
||||
zip: string;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './address.dto';
|
||||
export * from './interval.dto';
|
||||
export * from './kpi.dto';
|
||||
export * from './paginated-result.dto';
|
||||
export * from './validation-error';
|
||||
@@ -0,0 +1,12 @@
|
||||
import { ApiProperty } from "@nestjs/swagger";
|
||||
import { Type } from "class-transformer";
|
||||
|
||||
export class IntervalDto {
|
||||
@ApiProperty()
|
||||
@Type(() => Date)
|
||||
start!: Date;
|
||||
|
||||
@ApiProperty()
|
||||
@Type(() => Date)
|
||||
end!: Date;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class MetricDto {
|
||||
@ApiProperty()
|
||||
label!: string;
|
||||
@ApiProperty()
|
||||
value!: number;
|
||||
}
|
||||
|
||||
export class KpiInfoDto {
|
||||
@ApiProperty()
|
||||
main!: MetricDto;
|
||||
@ApiProperty()
|
||||
secondary!: MetricDto;
|
||||
@ApiProperty()
|
||||
additional!: MetricDto[];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
export interface PaginatedResult<T> {
|
||||
data: T[];
|
||||
meta: {
|
||||
total: number;
|
||||
page: number;
|
||||
limit: number;
|
||||
totalPages: number;
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
export interface ValidationError {
|
||||
field: string;
|
||||
message: string;
|
||||
source: { type: 'TOUR' | 'PATIENT'; id: string };
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './accounting';
|
||||
export * from './common';
|
||||
export * from './manager';
|
||||
export * from './tickets';
|
||||
@@ -0,0 +1 @@
|
||||
export * from './manager.dto';
|
||||
@@ -0,0 +1,79 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { Type } from 'class-transformer';
|
||||
import { IntervalDto } from '../common';
|
||||
|
||||
export class KPIMetadataDto {
|
||||
@ApiProperty()
|
||||
tourAmount!: number;
|
||||
|
||||
@ApiProperty()
|
||||
availableTime!: number;
|
||||
|
||||
@ApiProperty()
|
||||
baseValue!: number;
|
||||
|
||||
@ApiProperty()
|
||||
breakEven!: number;
|
||||
}
|
||||
|
||||
export class KPIDto {
|
||||
@ApiProperty()
|
||||
value!: number;
|
||||
|
||||
@ApiProperty({ type: KPIMetadataDto })
|
||||
@Type(() => KPIMetadataDto)
|
||||
meta!: KPIMetadataDto;
|
||||
}
|
||||
|
||||
export class KPIByCarDto {
|
||||
@ApiProperty()
|
||||
carName!: string;
|
||||
|
||||
@ApiProperty({ type: KPIDto })
|
||||
@Type(() => KPIDto)
|
||||
kpi!: KPIDto;
|
||||
}
|
||||
|
||||
export class TimePerTourKPISummaryDto {
|
||||
@ApiProperty({ type: IntervalDto })
|
||||
@Type(() => IntervalDto)
|
||||
interval!: IntervalDto;
|
||||
|
||||
@ApiProperty({ type: KPIDto })
|
||||
@Type(() => KPIDto)
|
||||
kpi!: KPIDto;
|
||||
|
||||
@ApiProperty({ type: [KPIByCarDto] })
|
||||
@Type(() => KPIByCarDto)
|
||||
cars!: KPIByCarDto[];
|
||||
}
|
||||
|
||||
// --- Metrics ---
|
||||
|
||||
export class TimePerTourMetricCategoryAmountDto {
|
||||
@ApiProperty()
|
||||
category!: string;
|
||||
|
||||
@ApiProperty()
|
||||
amount!: number;
|
||||
}
|
||||
|
||||
export class TimePerTourMetricDto {
|
||||
@ApiProperty({ type: IntervalDto })
|
||||
@Type(() => IntervalDto)
|
||||
interval!: IntervalDto;
|
||||
|
||||
@ApiProperty() availableTime!: number;
|
||||
@ApiProperty() tourAmount!: number;
|
||||
@ApiProperty() breakEven!: number;
|
||||
@ApiProperty() revenue!: number;
|
||||
@ApiProperty() averageRevenue!: number;
|
||||
@ApiProperty() personnelCost!: number;
|
||||
@ApiProperty() consumptionCost!: number;
|
||||
@ApiProperty() forecast!: number;
|
||||
@ApiProperty() forecastRevenue!: number;
|
||||
|
||||
@ApiProperty({ type: [TimePerTourMetricCategoryAmountDto] })
|
||||
@Type(() => TimePerTourMetricCategoryAmountDto)
|
||||
categoryAmounts!: TimePerTourMetricCategoryAmountDto[];
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export * from './ticket-detail.dto';
|
||||
export * from './ticket-list.dto';
|
||||
export * from './ticket-queries.dto';
|
||||
export * from './ticket.enum';
|
||||
@@ -0,0 +1,101 @@
|
||||
import { ValidationError } from '../common';
|
||||
import { AddressDto } from '../common/address.dto';
|
||||
import {
|
||||
ApprovalRequestStatus,
|
||||
ApprovalRequirement,
|
||||
TicketStage,
|
||||
} from './ticket.enum';
|
||||
|
||||
export interface TicketTourSummaryDto {
|
||||
id: string;
|
||||
operationId: string;
|
||||
date: string;
|
||||
startAddress: AddressDto;
|
||||
targetAddress: AddressDto;
|
||||
}
|
||||
|
||||
export interface TicketApprovalViewDto {
|
||||
requirement: ApprovalRequirement;
|
||||
status: ApprovalRequestStatus;
|
||||
statusLabel: string;
|
||||
|
||||
actions: {
|
||||
override: {
|
||||
isPossible: boolean;
|
||||
label: string;
|
||||
confirmationMessage?: string;
|
||||
};
|
||||
|
||||
registerExternalRequest: {
|
||||
isPossible: boolean;
|
||||
label: string;
|
||||
}
|
||||
|
||||
sendRequest: {
|
||||
isPossible: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
sendReminder: {
|
||||
isPossible: boolean;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
approve: {
|
||||
isPossible: boolean;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface TicketTransportDocumentView {
|
||||
isDocumentPresent: boolean;
|
||||
label: string;
|
||||
|
||||
digitalDownloadLink?: string;
|
||||
|
||||
actions: {
|
||||
upload: {
|
||||
isPossible: boolean;
|
||||
};
|
||||
|
||||
receivePhysical: {
|
||||
isPossible: boolean;
|
||||
label: string;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface TicketDetailDto {
|
||||
id: string;
|
||||
stage: TicketStage;
|
||||
|
||||
general: {
|
||||
ordinanceType: string;
|
||||
transportType: string;
|
||||
category: string;
|
||||
hasInfection: boolean;
|
||||
infectionName: string | null;
|
||||
isSeries: boolean;
|
||||
seriesEndDate: Date;
|
||||
};
|
||||
|
||||
validationErrors: ValidationError[];
|
||||
|
||||
patient: {
|
||||
id: string;
|
||||
name: string;
|
||||
healthInsurance: string;
|
||||
address: AddressDto;
|
||||
};
|
||||
|
||||
approval: TicketApprovalViewDto;
|
||||
|
||||
transportDocument: TicketTransportDocumentView;
|
||||
|
||||
tours: TicketTourSummaryDto[];
|
||||
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export interface TicketListItemDto {
|
||||
id: string;
|
||||
|
||||
ordinanceType: string;
|
||||
patientName: string;
|
||||
stage: string;
|
||||
nextStart: Date;
|
||||
tourCount: number;
|
||||
errorCount: number;
|
||||
approvalRequirement: string;
|
||||
approvalStatus: string;
|
||||
isOverdue: boolean;
|
||||
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { Type } from 'class-transformer';
|
||||
import { IsEnum, IsInt, IsOptional, IsString, Max, Min, ValidationArguments } from 'class-validator';
|
||||
import { TicketStage } from './ticket.enum';
|
||||
|
||||
export class GetBoardQueryDto {
|
||||
@IsString()
|
||||
@IsEnum(TicketStage, {'message': (args: ValidationArguments) => `${args.value} is no part of ${args.object}`})
|
||||
stage!: string;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
page: number = 1;
|
||||
|
||||
@IsOptional()
|
||||
@Type(() => Number)
|
||||
@IsInt()
|
||||
@Min(1)
|
||||
@Max(50)
|
||||
limit: number = 10;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
export enum TicketStage {
|
||||
DRAFT = 'DRAFT',
|
||||
DATA_INCOMPLETE = 'DATA_INCOMPLETE',
|
||||
ASSESSMENT_PENDING = 'ASSESSMENT',
|
||||
APPROVAL_IN_PROGRESS = 'APPROVAL',
|
||||
DOCUMENTS_PENDING = 'DOCS_PENDING',
|
||||
READY = 'READY',
|
||||
// Normaler Durchlauf
|
||||
ARCHIVED = 'ARCHIVED',
|
||||
// Ticket wurde vorzeitig geschlossen
|
||||
CLOSED = 'CLOSED'
|
||||
}
|
||||
|
||||
export enum ApprovalRequirement {
|
||||
UNKOWN = 'UNKNOWN',
|
||||
NOT_REQUIRED = 'NOT_REQUIRED',
|
||||
REQUIRED = 'REQUIRED',
|
||||
}
|
||||
|
||||
export enum ApprovalRequestStatus {
|
||||
NOT_STARTED = 'NOT_STARTED',
|
||||
PRE_INQUIRY_SENT = 'PRE_INQUIRY_SENT',
|
||||
FULL_REQUEST_SENT = 'FULL_REQUEST_SENT',
|
||||
EXTERNAL_REQUEST = 'EXTERNAL_REQUEST',
|
||||
GRANTED = 'GRANTED',
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"target": "ES2020",
|
||||
"moduleResolution": "node",
|
||||
|
||||
"outDir": "./dist",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true
|
||||
},
|
||||
"include": ["src/**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.spec.ts", "**/*.test.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user