(dc): data-connector enhancements
This commit is contained in:
parent
44f627499a
commit
08e3b3247c
|
|
@ -1,26 +1,28 @@
|
||||||
FROM node:20.15.1-alpine AS builder
|
FROM node:20.15.1-alpine AS builder
|
||||||
|
|
||||||
WORKDIR /app/builder
|
WORKDIR /app/builder
|
||||||
|
COPY package.json .
|
||||||
|
COPY package-lock.json .
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
RUN npm run build
|
RUN npm run build
|
||||||
|
|
||||||
FROM node:20.15.1-alpine
|
FROM node:20.15.1-alpine
|
||||||
|
|
||||||
ENV OTEL_TRACES_EXPORTER="otlp"
|
# ENV OTEL_TRACES_EXPORTER="otlp"
|
||||||
ENV OTEL_METRICS_EXPORTER="otlp"
|
# ENV OTEL_METRICS_EXPORTER="otlp"
|
||||||
ENV OTEL_LOGS_EXPORTER="otlp"
|
# ENV OTEL_LOGS_EXPORTER="otlp"
|
||||||
ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
|
# ENV NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"
|
||||||
|
|
||||||
# To setup your own Service Instance ID
|
# To setup your own Service Instance ID
|
||||||
ENV OTEL_RESOURCE_ATTRIBUTES="service.instance.id=avicenna.data-connector"
|
# ENV OTEL_RESOURCE_ATTRIBUTES="service.instance.id=avicenna.data-connector"
|
||||||
# ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
|
# ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os"
|
||||||
|
|
||||||
# To have a Service Instance ID generated using a random UUID
|
# To have a Service Instance ID generated using a random UUID
|
||||||
# Requires auto-instrumentations-node version 0.46.0+
|
# Requires auto-instrumentations-node version 0.46.0+
|
||||||
ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os,serviceinstance,container"
|
# ENV OTEL_NODE_RESOURCE_DETECTORS="env,host,os,serviceinstance,container"
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -35,12 +35,9 @@
|
||||||
"@nestjs/platform-express": "^11.0.15",
|
"@nestjs/platform-express": "^11.0.15",
|
||||||
"@nestjs/schedule": "^5.0.1",
|
"@nestjs/schedule": "^5.0.1",
|
||||||
"@opentelemetry/api": "^1.9.0",
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"@opentelemetry/auto-instrumentations-node": "^0.57.1",
|
"@opentelemetry/auto-instrumentations-node": "^0.60.0",
|
||||||
"@opentelemetry/exporter-metrics-otlp-proto": "^0.200.0",
|
"@opentelemetry/exporter-trace-otlp-http": "^0.202.0",
|
||||||
"@opentelemetry/exporter-trace-otlp-proto": "^0.200.0",
|
"@opentelemetry/sdk-node": "^0.202.0",
|
||||||
"@opentelemetry/resources": "^2.0.0",
|
|
||||||
"@opentelemetry/sdk-node": "^0.200.0",
|
|
||||||
"@opentelemetry/semantic-conventions": "^1.31.0",
|
|
||||||
"@prisma/client": "^6.6.0",
|
"@prisma/client": "^6.6.0",
|
||||||
"date-fns": "^4.1.0",
|
"date-fns": "^4.1.0",
|
||||||
"date-fns-tz": "^3.2.0",
|
"date-fns-tz": "^3.2.0",
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const BTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 1665,
|
costAmount: 1665,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -53,7 +53,7 @@ export const BTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 1665,
|
costAmount: 1665,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -87,7 +87,7 @@ export const BTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 1765,
|
costAmount: 1765,
|
||||||
timeRange: '18:00-07:30',
|
timeRange: '18:00-07:30',
|
||||||
|
|
@ -107,7 +107,7 @@ export const BTW_AOK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'AOK/BKK/IKK/Knappschaft BTW',
|
description: 'AOK/BKK/IKK/Knappschaft BTW',
|
||||||
validFrom: startOfDay(new Date('2023-05-01')),
|
validFrom: startOfDay(new Date('2023-05-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2024-06-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -121,7 +121,7 @@ export const BTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 1865,
|
costAmount: 1865,
|
||||||
timeRange: '18:00-07:30',
|
timeRange: '18:00-07:30',
|
||||||
|
|
@ -138,4 +138,145 @@ export const BTW_AOK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft BTW',
|
||||||
|
validFrom: startOfDay(new Date('2024-07-01')),
|
||||||
|
validTo: endOfDay(new Date('2024-12-31')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4000,
|
||||||
|
timeRange: '00:00-14:29',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4000,
|
||||||
|
timeRange: '18:01-23:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt (14:30-18:00)',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4500,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 2500,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 2500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 2500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft BTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-01-01')),
|
||||||
|
validTo: endOfDay(new Date('2025-12-31')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4250,
|
||||||
|
timeRange: '00:00-14:29',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4250,
|
||||||
|
timeRange: '18:01-23:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt (14:30-18:00)',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 5000,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft BTW',
|
||||||
|
validFrom: startOfDay(new Date('2026-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4500,
|
||||||
|
timeRange: '00:00-14:29',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4500,
|
||||||
|
timeRange: '18:01-23:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt (14:30-18:00)',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 5000,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 3000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ export const BTW_DAK_VDEK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'DAK + VDEK BTW',
|
description: 'DAK + VDEK BTW',
|
||||||
validFrom: startOfDay(new Date('2023-06-01')),
|
validFrom: startOfDay(new Date('2023-06-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2024-08-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -38,4 +38,22 @@ export const BTW_DAK_VDEK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'DAK + VDEK BTW',
|
||||||
|
validFrom: startOfDay(new Date('2024-09-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 4400,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 180,
|
||||||
|
kmInclusive: 21,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { CostTypeVariant } from '@prisma/client';
|
import { CostTypeVariant } from '@prisma/client';
|
||||||
import { startOfDay } from 'date-fns';
|
import { endOfDay, startOfDay } from 'date-fns';
|
||||||
|
|
||||||
export const BTW_PRIVAT_TARIFFS = [
|
export const BTW_PRIVAT_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'Privat BTW',
|
description: 'Privat BTW',
|
||||||
validFrom: startOfDay(new Date('2020-01-01')),
|
validFrom: startOfDay(new Date('2020-01-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2023-12-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -24,7 +24,46 @@ export const BTW_PRIVAT_TARIFFS = [
|
||||||
kmInclusive: 15,
|
kmInclusive: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 4000,
|
||||||
|
timeRange: '19:00-07:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 4000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 4000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Privat BTW',
|
||||||
|
validFrom: startOfDay(new Date('2024-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 8500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 16. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 250,
|
||||||
|
kmInclusive: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 4000,
|
costAmount: 4000,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
|
||||||
|
|
@ -5,45 +5,6 @@ export const KTW_AOK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
||||||
validFrom: startOfDay(new Date('2019-04-01')),
|
validFrom: startOfDay(new Date('2019-04-01')),
|
||||||
validTo: endOfDay(new Date('2021-04-22')),
|
|
||||||
costTypes: [
|
|
||||||
{
|
|
||||||
description: 'Grundpauschale je Fahrt',
|
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
|
||||||
costAmount: 8936,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Desinfektionszuschlag',
|
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
|
||||||
costAmount: 70,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'ab dem 18. km',
|
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
|
||||||
costAmount: 286,
|
|
||||||
kmInclusive: 18,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Nachtzuschlag',
|
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
|
||||||
costAmount: 4003,
|
|
||||||
timeRange: '19:00-07:00',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Wochenendzuschlag',
|
|
||||||
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
|
||||||
costAmount: 4003,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Feiertagszuschlag',
|
|
||||||
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
|
||||||
costAmount: 4003,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
|
||||||
validFrom: startOfDay(new Date('2021-04-23')),
|
|
||||||
validTo: endOfDay(new Date('2021-06-30')),
|
validTo: endOfDay(new Date('2021-06-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
|
|
@ -51,18 +12,24 @@ export const KTW_AOK_TARIFFS = [
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
costAmount: 8936,
|
costAmount: 8936,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: 'QMS Zuschlag',
|
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
|
||||||
costAmount: 177,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: 'Desinfektionszuschlag',
|
description: 'Desinfektionszuschlag',
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
costAmount: 70,
|
costAmount: 70,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 177,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 18. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 286,
|
||||||
|
kmInclusive: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 4003,
|
costAmount: 4003,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -77,12 +44,6 @@ export const KTW_AOK_TARIFFS = [
|
||||||
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
costAmount: 4003,
|
costAmount: 4003,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: 'ab dem 18. km',
|
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
|
||||||
costAmount: 286,
|
|
||||||
kmInclusive: 18,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -106,7 +67,13 @@ export const KTW_AOK_TARIFFS = [
|
||||||
costAmount: 181,
|
costAmount: 181,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'ab dem 18. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 293,
|
||||||
|
kmInclusive: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 4104,
|
costAmount: 4104,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -121,12 +88,6 @@ export const KTW_AOK_TARIFFS = [
|
||||||
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
costAmount: 4104,
|
costAmount: 4104,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
description: 'ab dem 18. km',
|
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
|
||||||
costAmount: 293,
|
|
||||||
kmInclusive: 18,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -174,7 +135,7 @@ export const KTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 17,
|
kmInclusive: 17,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 6500,
|
costAmount: 6500,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -218,7 +179,7 @@ export const KTW_AOK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
||||||
validFrom: startOfDay(new Date('2024-01-01')),
|
validFrom: startOfDay(new Date('2024-01-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2024-12-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -260,7 +221,7 @@ export const KTW_AOK_TARIFFS = [
|
||||||
kmInclusive: 17,
|
kmInclusive: 17,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 6919,
|
costAmount: 6919,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -301,4 +262,128 @@ export const KTW_AOK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-01-01')),
|
||||||
|
validTo: endOfDay(new Date('2025-06-30')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 7500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 3000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.FAHRZEUGZUSCHLAG,
|
||||||
|
costAmount: 1600,
|
||||||
|
// excludeCarNames: [
|
||||||
|
// '1/KTW-01',
|
||||||
|
// '1/KTW-02',
|
||||||
|
// '1/KTW-03',
|
||||||
|
// '1/KTW-04',
|
||||||
|
// '1/KTW-05-E',
|
||||||
|
// '1/KTW-06',
|
||||||
|
// ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Leitstellenmanagement',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 900,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag pauschal',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 18. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 385,
|
||||||
|
kmInclusive: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 8500,
|
||||||
|
timeRange: '18:30-06:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 8500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 8500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft KTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-07-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 7831,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 3132,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.FAHRZEUGZUSCHLAG,
|
||||||
|
costAmount: 1671,
|
||||||
|
// excludeCarNames: [
|
||||||
|
// '1/KTW-01',
|
||||||
|
// '1/KTW-02',
|
||||||
|
// '1/KTW-03',
|
||||||
|
// '1/KTW-04',
|
||||||
|
// '1/KTW-05-E',
|
||||||
|
// '1/KTW-06',
|
||||||
|
// ],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Leitstellenmanagement',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 940,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag pauschal',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2610,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 19. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 402,
|
||||||
|
kmInclusive: 18,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 8875,
|
||||||
|
timeRange: '19:00-07:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 8875,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 8875,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -5,40 +5,6 @@ export const KTW_DAK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'DAK KTW',
|
description: 'DAK KTW',
|
||||||
validFrom: startOfDay(new Date('2018-02-01')),
|
validFrom: startOfDay(new Date('2018-02-01')),
|
||||||
validTo: endOfDay(new Date('2021-04-22')),
|
|
||||||
costTypes: [
|
|
||||||
{
|
|
||||||
description: 'Grundpauschale je Fahrt',
|
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
|
||||||
costAmount: 8507,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'ab dem 18. km',
|
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
|
||||||
costAmount: 284,
|
|
||||||
kmInclusive: 17,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Nachtzuschlag',
|
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
|
||||||
costAmount: 4728,
|
|
||||||
timeRange: '18:30-06:30',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Wochenendzuschlag',
|
|
||||||
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
|
||||||
costAmount: 4728,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Feiertagszuschlag',
|
|
||||||
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
|
||||||
costAmount: 4728,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'DAK KTW',
|
|
||||||
validFrom: startOfDay(new Date('2021-04-23')),
|
|
||||||
validTo: endOfDay(new Date('2021-09-30')),
|
validTo: endOfDay(new Date('2021-09-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +24,7 @@ export const KTW_DAK_TARIFFS = [
|
||||||
kmInclusive: 17,
|
kmInclusive: 17,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 4728,
|
costAmount: 4728,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -102,7 +68,7 @@ export const KTW_DAK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5300,
|
costAmount: 5300,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -170,7 +136,7 @@ export const KTW_DAK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5485,
|
costAmount: 5485,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -214,7 +180,7 @@ export const KTW_DAK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'DAK KTW',
|
description: 'DAK KTW',
|
||||||
validFrom: startOfDay(new Date('2024-01-01')),
|
validFrom: startOfDay(new Date('2024-01-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2024-12-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -238,25 +204,25 @@ export const KTW_DAK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5728,
|
costAmount: 5728,
|
||||||
timeRange: '19:00-19:59',
|
timeRange: '19:00-19:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 6644,
|
costAmount: 6644,
|
||||||
timeRange: '20:00-21:59',
|
timeRange: '20:00-21:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 7198,
|
costAmount: 7198,
|
||||||
timeRange: '22:00-04:59',
|
timeRange: '22:00-04:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5728,
|
costAmount: 5728,
|
||||||
timeRange: '05:00-06:00',
|
timeRange: '05:00-06:00',
|
||||||
|
|
@ -297,4 +263,262 @@ export const KTW_DAK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'DAK KTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-01-01')),
|
||||||
|
validTo: endOfDay(new Date('2025-04-30')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 9648,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2144,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 1072,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 17. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 354,
|
||||||
|
kmInclusive: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6140,
|
||||||
|
timeRange: '19:00-19:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7122,
|
||||||
|
timeRange: '20:00-21:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7716,
|
||||||
|
timeRange: '22:00-04:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6140,
|
||||||
|
timeRange: '05:00-06:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 5800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 5800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.A',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 3981,
|
||||||
|
infectionType: '2.A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.B',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 15914,
|
||||||
|
infectionType: '2.B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.1',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 18298,
|
||||||
|
infectionType: '3.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.2',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 18298,
|
||||||
|
infectionType: '3.2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'DAK KTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-05-01')),
|
||||||
|
validTo: endOfDay(new Date('2025-12-31')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 10073,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2239,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 1119,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 17. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 369,
|
||||||
|
kmInclusive: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6411,
|
||||||
|
timeRange: '19:00-19:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7436,
|
||||||
|
timeRange: '20:00-21:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 8057,
|
||||||
|
timeRange: '22:00-04:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6411,
|
||||||
|
timeRange: '05:00-06:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 6055,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 6055,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.A',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 4157,
|
||||||
|
infectionType: '2.A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.B',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 16616,
|
||||||
|
infectionType: '2.B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.1',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 19105,
|
||||||
|
infectionType: '3.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.2',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 19105,
|
||||||
|
infectionType: '3.2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'DAK KTW',
|
||||||
|
validFrom: startOfDay(new Date('2026-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 10142,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2239,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 1119,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 17. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 369,
|
||||||
|
kmInclusive: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6411,
|
||||||
|
timeRange: '19:00-19:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7436,
|
||||||
|
timeRange: '20:00-21:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 8057,
|
||||||
|
timeRange: '22:00-04:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6411,
|
||||||
|
timeRange: '05:00-06:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 6055,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 6055,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.A',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 4157,
|
||||||
|
infectionType: '2.A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.B',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 16616,
|
||||||
|
infectionType: '2.B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.1',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 19105,
|
||||||
|
infectionType: '3.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.2',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 19105,
|
||||||
|
infectionType: '3.2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { CostTypeVariant } from '@prisma/client';
|
import { CostTypeVariant } from '@prisma/client';
|
||||||
import { startOfDay } from 'date-fns';
|
import { endOfDay, startOfDay } from 'date-fns';
|
||||||
|
|
||||||
export const KTW_PRIVAT_TARIFFS = [
|
export const KTW_PRIVAT_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'Privat KTW',
|
description: 'Privat KTW',
|
||||||
validFrom: startOfDay(new Date('2020-01-01')),
|
validFrom: startOfDay(new Date('2020-01-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2023-12-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -65,4 +65,72 @@ export const KTW_PRIVAT_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'Privat KTW',
|
||||||
|
validFrom: startOfDay(new Date('2024-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 19000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 3300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Hygienemaßnahmen',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 1500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 16. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 390,
|
||||||
|
kmInclusive: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:30-07:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 8800,
|
||||||
|
timeRange: '18:30-07:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 8800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 8800,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.A',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 8800,
|
||||||
|
infectionType: '2.A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.B',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 17500,
|
||||||
|
infectionType: '2.B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.1',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 17500,
|
||||||
|
infectionType: '3.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.2',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 17500,
|
||||||
|
infectionType: '3.2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -5,40 +5,6 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'VDEK KTW',
|
description: 'VDEK KTW',
|
||||||
validFrom: startOfDay(new Date('2017-09-01')),
|
validFrom: startOfDay(new Date('2017-09-01')),
|
||||||
validTo: endOfDay(new Date('2021-04-22')),
|
|
||||||
costTypes: [
|
|
||||||
{
|
|
||||||
description: 'Grundpauschale je Fahrt',
|
|
||||||
variant: CostTypeVariant.GRUNDPREIS,
|
|
||||||
costAmount: 8150,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'ab dem 18. km',
|
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
|
||||||
costAmount: 273,
|
|
||||||
kmInclusive: 17,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Nachtzuschlag',
|
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
|
||||||
costAmount: 4550,
|
|
||||||
timeRange: '18:30-06:30',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Wochenendzuschlag',
|
|
||||||
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
|
||||||
costAmount: 4550,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Feiertagszuschlag',
|
|
||||||
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
|
||||||
costAmount: 4550,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'VDEK KTW',
|
|
||||||
validFrom: startOfDay(new Date('2021-04-23')),
|
|
||||||
validTo: endOfDay(new Date('2021-06-30')),
|
validTo: endOfDay(new Date('2021-06-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
|
|
@ -58,7 +24,7 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
kmInclusive: 17,
|
kmInclusive: 17,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 4550,
|
costAmount: 4550,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -97,7 +63,7 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
kmInclusive: 17,
|
kmInclusive: 17,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5028,
|
costAmount: 5028,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -146,7 +112,7 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:30-06:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5537,
|
costAmount: 5537,
|
||||||
timeRange: '18:30-06:30',
|
timeRange: '18:30-06:30',
|
||||||
|
|
@ -190,7 +156,7 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'VDEK KTW',
|
description: 'VDEK KTW',
|
||||||
validFrom: startOfDay(new Date('2023-05-01')),
|
validFrom: startOfDay(new Date('2023-05-01')),
|
||||||
validTo: endOfDay(new Date('2024-04-31')),
|
validTo: endOfDay(new Date('2024-04-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -219,25 +185,25 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5728,
|
costAmount: 5728,
|
||||||
timeRange: '19:00-19:59',
|
timeRange: '19:00-19:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 6644,
|
costAmount: 6644,
|
||||||
timeRange: '20:00-21:59',
|
timeRange: '20:00-21:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 7198,
|
costAmount: 7198,
|
||||||
timeRange: '22:00-04:59',
|
timeRange: '22:00-04:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5728,
|
costAmount: 5728,
|
||||||
timeRange: '05:00-06:00',
|
timeRange: '05:00-06:00',
|
||||||
|
|
@ -281,7 +247,7 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'VDEK KTW',
|
description: 'VDEK KTW',
|
||||||
validFrom: startOfDay(new Date('2024-05-01')),
|
validFrom: startOfDay(new Date('2024-05-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2025-04-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -310,25 +276,25 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
kmInclusive: 16,
|
kmInclusive: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5970,
|
costAmount: 5970,
|
||||||
timeRange: '19:00-19:59',
|
timeRange: '19:00-19:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 6924,
|
costAmount: 6924,
|
||||||
timeRange: '20:00-21:59',
|
timeRange: '20:00-21:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 7502,
|
costAmount: 7502,
|
||||||
timeRange: '22:00-04:59',
|
timeRange: '22:00-04:59',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 5970,
|
costAmount: 5970,
|
||||||
timeRange: '05:00-06:00',
|
timeRange: '05:00-06:00',
|
||||||
|
|
@ -369,4 +335,95 @@ export const KTW_VDEK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'VDEK KTW',
|
||||||
|
validFrom: startOfDay(new Date('2025-05-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 7971,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 3141,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Modernes Einsatzfahrzeug',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 1885,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Leitstellenmanagement',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 730,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 17. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 402,
|
||||||
|
kmInclusive: 16,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 19:00-19:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6233,
|
||||||
|
timeRange: '19:00-19:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 20:00-21:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7229,
|
||||||
|
timeRange: '20:00-21:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 22:00-04:59',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7833,
|
||||||
|
timeRange: '22:00-04:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 05:00-06:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6233,
|
||||||
|
timeRange: '05:00-06:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 6992,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 6992,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.A',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 4654,
|
||||||
|
infectionType: '2.A',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 2.B',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 19070,
|
||||||
|
infectionType: '2.B',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.1',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 22052,
|
||||||
|
infectionType: '3.1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Desinfektionszuschlag 3.2',
|
||||||
|
variant: CostTypeVariant.DESINFEKTIONSZUSCHLAG,
|
||||||
|
costAmount: 22052,
|
||||||
|
infectionType: '3.2',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const TSW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 2445,
|
costAmount: 2445,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -53,7 +53,7 @@ export const TSW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag s19:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 2445,
|
costAmount: 2445,
|
||||||
timeRange: '19:00-07:00',
|
timeRange: '19:00-07:00',
|
||||||
|
|
@ -87,7 +87,7 @@ export const TSW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 2600,
|
costAmount: 2600,
|
||||||
timeRange: '18:00-07:30',
|
timeRange: '18:00-07:30',
|
||||||
|
|
@ -107,7 +107,7 @@ export const TSW_AOK_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'AOK/BKK/IKK/Knappschaft TSW',
|
description: 'AOK/BKK/IKK/Knappschaft TSW',
|
||||||
validFrom: startOfDay(new Date('2023-05-01')),
|
validFrom: startOfDay(new Date('2023-05-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2024-06-30')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -121,7 +121,7 @@ export const TSW_AOK_TARIFFS = [
|
||||||
kmInclusive: 20,
|
kmInclusive: 20,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 2700,
|
costAmount: 2700,
|
||||||
timeRange: '18:00-07:30',
|
timeRange: '18:00-07:30',
|
||||||
|
|
@ -138,4 +138,138 @@ export const TSW_AOK_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft TSW',
|
||||||
|
validFrom: startOfDay(new Date('2024-07-01')),
|
||||||
|
validTo: endOfDay(new Date('2024-12-31')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 6000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachmittagszuschlag 14:30-18:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7000,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 5000,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 5000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 5000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft TSW',
|
||||||
|
validFrom: startOfDay(new Date('2025-01-01')),
|
||||||
|
validTo: endOfDay(new Date('2025-12-31')),
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6250,
|
||||||
|
timeRange: '00:00-14:29',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6250,
|
||||||
|
timeRange: '18:01-23:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachmittagspauschale 14:30-18:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7500,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'AOK/BKK/IKK/Knappschaft TSW',
|
||||||
|
validFrom: startOfDay(new Date('2026-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6500,
|
||||||
|
timeRange: '00:00-14:29',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6500,
|
||||||
|
timeRange: '18:01-23:59',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 21. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 200,
|
||||||
|
kmInclusive: 20,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachmittagszuschlag 14:30-18:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 7500,
|
||||||
|
timeRange: '14:30-18:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag 18:00-07:30',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
timeRange: '18:00-07:30',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 6000,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -26,33 +26,33 @@ export const TSW_DAK_VDEK_TARIFFS = [
|
||||||
validTo: endOfDay(new Date('2024-08-31')),
|
validTo: endOfDay(new Date('2024-08-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt (TSW 2)',
|
description: 'Grundpauschale je Fahrt',
|
||||||
variant: CostTypeVariant.TSWDIFFERENZIERUNG,
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
costAmount: 5880,
|
costAmount: 5880,
|
||||||
},
|
},
|
||||||
{
|
// {
|
||||||
description: 'Grundpauschale je Fahrt (TSW 1)',
|
// description: 'Grundpauschale je Fahrt (TSW 1)',
|
||||||
variant: CostTypeVariant.TSWDIFFERENZIERUNG,
|
// variant: CostTypeVariant.TSWDIFFERENZIERUNG,
|
||||||
costAmount: 4380,
|
// costAmount: 4380,
|
||||||
},
|
// },
|
||||||
{
|
|
||||||
description: 'Nachtzuschlag',
|
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
|
||||||
costAmount: 407,
|
|
||||||
timeRange: '18:30-20:00',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: 'Nachtzuschlag',
|
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
|
||||||
costAmount: 407,
|
|
||||||
timeRange: '06:00-07:00',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
description: 'ab dem 22. km',
|
description: 'ab dem 22. km',
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
costAmount: 159,
|
costAmount: 159,
|
||||||
kmInclusive: 21,
|
kmInclusive: 21,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'Morgenzuschlag 06:00-07:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 407,
|
||||||
|
timeRange: '06:00-07:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Abendzuschlag 18:30-20:00',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 407,
|
||||||
|
timeRange: '18:30-20:00',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -76,21 +76,26 @@ export const TSW_DAK_VDEK_TARIFFS = [
|
||||||
costAmount: 5793,
|
costAmount: 5793,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Nachtzuschlag',
|
description: 'Fallmanagementzuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 750,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Morgenzuschlag 05:00-07:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 407,
|
costAmount: 450,
|
||||||
timeRange: '05:00-07:00',
|
timeRange: '05:00-07:00',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Fallmanagementzuschlag',
|
description: 'Abendzuschlag 19:00-23:00',
|
||||||
variant: CostTypeVariant.NACHTZUSCHLAG,
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
costAmount: 750,
|
costAmount: 407,
|
||||||
timeRange: '14:30-23:00',
|
timeRange: '19:00-23:00',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'ab dem 22. km',
|
description: 'ab dem 22. km',
|
||||||
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
costAmount: 180,
|
costAmount: 159,
|
||||||
kmInclusive: 21,
|
kmInclusive: 21,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import { CostTypeVariant } from '@prisma/client';
|
import { CostTypeVariant } from '@prisma/client';
|
||||||
import { startOfDay } from 'date-fns';
|
import { endOfDay, startOfDay } from 'date-fns';
|
||||||
|
|
||||||
export const TSW_PRIVAT_TARIFFS = [
|
export const TSW_PRIVAT_TARIFFS = [
|
||||||
{
|
{
|
||||||
description: 'Privat TSW',
|
description: 'Privat TSW',
|
||||||
validFrom: startOfDay(new Date('2020-01-01')),
|
validFrom: startOfDay(new Date('2020-01-01')),
|
||||||
validTo: null,
|
validTo: endOfDay(new Date('2023-12-31')),
|
||||||
costTypes: [
|
costTypes: [
|
||||||
{
|
{
|
||||||
description: 'Grundpauschale je Fahrt',
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
|
@ -41,4 +41,43 @@ export const TSW_PRIVAT_TARIFFS = [
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
description: 'Privat TSW',
|
||||||
|
validFrom: startOfDay(new Date('2024-01-01')),
|
||||||
|
validTo: null,
|
||||||
|
costTypes: [
|
||||||
|
{
|
||||||
|
description: 'Grundpauschale je Fahrt',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 13500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'QMS Zuschlag',
|
||||||
|
variant: CostTypeVariant.GRUNDPREIS,
|
||||||
|
costAmount: 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'ab dem 16. km',
|
||||||
|
variant: CostTypeVariant.KILOMETERZUSCHLAG,
|
||||||
|
costAmount: 250,
|
||||||
|
kmInclusive: 15,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Nachtzuschlag',
|
||||||
|
variant: CostTypeVariant.NACHTZUSCHLAG,
|
||||||
|
costAmount: 5500,
|
||||||
|
timeRange: '19:00-07:00',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Wochenendzuschlag',
|
||||||
|
variant: CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
|
costAmount: 5500,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: 'Feiertagszuschlag',
|
||||||
|
variant: CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
|
costAmount: 5500,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -13,18 +13,18 @@ import { DatabaseModule } from './core/database/database.module';
|
||||||
import { PrismaModule } from './core/database/prisma.module';
|
import { PrismaModule } from './core/database/prisma.module';
|
||||||
import { ApplicationAccountingModule } from './modules/app-accounting/app-accounting.module';
|
import { ApplicationAccountingModule } from './modules/app-accounting/app-accounting.module';
|
||||||
import { ApplicationControlCenterModule } from './modules/app-control-center/app-control-center.module';
|
import { ApplicationControlCenterModule } from './modules/app-control-center/app-control-center.module';
|
||||||
import { ApplicationManagerModule } from './modules/app-manager/app-manager.module';
|
import { ManagerModule } from './modules/app-manager/app-manager.module';
|
||||||
import { ApplicationTicketSystemModule } from './modules/app-ticket-system/app-ticket-system.module';
|
import { ApplicationTicketSystemModule } from './modules/app-ticket-system/app-ticket-system.module';
|
||||||
import { AnomalyDetectionModule } from './modules/feat-anomalies/anomaly-detection.module';
|
import { AnomalyDetectionModule } from './modules/feat-anomalies/anomaly-detection.module';
|
||||||
import { FeatureEtlModule } from './modules/feat-etl/feat-etl.module';
|
import { FeatureEtlModule } from './modules/feat-etl/feat-etl.module';
|
||||||
import { FeatReportsModule } from './modules/feat-reports/feat-reports.module';
|
import { FeatReportsModule } from './modules/feat-reports/feat-reports.module';
|
||||||
import { TicketSystemModule } from './modules/feat-tickets/ticket-system.module';
|
import { TicketSystemModule } from './modules/feat-tickets/ticket-system.module';
|
||||||
import { FeatureCalculationModule } from './modules/feat-calculation/feat-calculation.module';
|
import { FeatCalculationModule } from './modules/feat-calculation/feat-calculation.module';
|
||||||
|
import { AppHomeModule } from './modules/app-home/app-home.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
ConfigModule,
|
ConfigModule,
|
||||||
|
|
||||||
// DatabaseModule.forRootAsync({
|
// DatabaseModule.forRootAsync({
|
||||||
// isGlobal: true,
|
// isGlobal: true,
|
||||||
// inject: [ConfigService],
|
// inject: [ConfigService],
|
||||||
|
|
@ -81,13 +81,14 @@ import { FeatureCalculationModule } from './modules/feat-calculation/feat-calcul
|
||||||
}),
|
}),
|
||||||
FeatureEtlModule,
|
FeatureEtlModule,
|
||||||
FeatReportsModule,
|
FeatReportsModule,
|
||||||
FeatureCalculationModule,
|
FeatCalculationModule,
|
||||||
TicketSystemModule,
|
TicketSystemModule,
|
||||||
AnomalyDetectionModule,
|
AnomalyDetectionModule,
|
||||||
ApplicationManagerModule,
|
ManagerModule,
|
||||||
ApplicationAccountingModule,
|
ApplicationAccountingModule,
|
||||||
ApplicationControlCenterModule,
|
ApplicationControlCenterModule,
|
||||||
ApplicationTicketSystemModule,
|
ApplicationTicketSystemModule,
|
||||||
|
AppHomeModule,
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { SchedulerRegistry } from '@nestjs/schedule';
|
import { Cron, SchedulerRegistry } from '@nestjs/schedule';
|
||||||
import {
|
import {
|
||||||
Interval as TimeInterval,
|
Interval as TimeInterval,
|
||||||
addDays,
|
addDays,
|
||||||
|
|
@ -13,12 +13,20 @@ import { concatMap, from, lastValueFrom, switchMap, take, tap } from 'rxjs';
|
||||||
import { ImporterService } from './modules/feat-etl/feat-importers/importer.service';
|
import { ImporterService } from './modules/feat-etl/feat-importers/importer.service';
|
||||||
import { TicketService } from './modules/feat-tickets/tickets.service';
|
import { TicketService } from './modules/feat-tickets/tickets.service';
|
||||||
import { chunk } from './utils';
|
import { chunk } from './utils';
|
||||||
|
import { AnomaliesService } from './modules/feat-anomalies/anomalies.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {
|
export class AppService {
|
||||||
private readonly logger = new Logger(AppService.name);
|
private readonly logger = new Logger(AppService.name);
|
||||||
|
|
||||||
// @Cron('0 */5 2-20 * * *')
|
constructor(
|
||||||
|
private readonly importer: ImporterService,
|
||||||
|
private readonly ticketService: TicketService,
|
||||||
|
private readonly anomalieService: AnomaliesService,
|
||||||
|
private readonly schedulerRegistry: SchedulerRegistry,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Cron('0 */5 2-20 * * *')
|
||||||
async fetchDataContinously() {
|
async fetchDataContinously() {
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const fetchInterval = {
|
const fetchInterval = {
|
||||||
|
|
@ -35,6 +43,13 @@ export class AppService {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
switchMap(() => from(this.ticketService.createTickets(fetchInterval))),
|
switchMap(() => from(this.ticketService.createTickets(fetchInterval))),
|
||||||
|
switchMap(() =>
|
||||||
|
from(
|
||||||
|
this.anomalieService
|
||||||
|
.detectAnomalies(fetchInterval)
|
||||||
|
.then(() => this.anomalieService.validateAnomalies()),
|
||||||
|
),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
@ -142,12 +157,6 @@ export class AppService {
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(
|
|
||||||
private readonly importer: ImporterService,
|
|
||||||
private readonly ticketService: TicketService,
|
|
||||||
private readonly schedulerRegistry: SchedulerRegistry,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
private splitIntervalIntoPeriods(
|
private splitIntervalIntoPeriods(
|
||||||
interval: TimeInterval,
|
interval: TimeInterval,
|
||||||
chunkSize = 30,
|
chunkSize = 30,
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,18 @@ type DocumentInfo {
|
||||||
tdLocation: String
|
tdLocation: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HomeDashboardKPIs {
|
||||||
|
anomalyCount: Int!
|
||||||
|
billableTourCount: Int!
|
||||||
|
billableTourRevenueSum: Float!
|
||||||
|
billedTourCount: Int!
|
||||||
|
billedTourNegativeRevenueDeviation: Float!
|
||||||
|
billedTourPositiveRevenueDeviation: Float!
|
||||||
|
employeesWithoutTourCount: Int!
|
||||||
|
revenueTrend: [RevenueTrend!]!
|
||||||
|
toursWithoutApprovalCount: Int!
|
||||||
|
}
|
||||||
|
|
||||||
type Interval {
|
type Interval {
|
||||||
end: DateTime!
|
end: DateTime!
|
||||||
start: DateTime!
|
start: DateTime!
|
||||||
|
|
@ -88,6 +100,17 @@ type KPIMetadata {
|
||||||
tourAmount: Float!
|
tourAmount: Float!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type KpiInfo {
|
||||||
|
additional: [Metric!]!
|
||||||
|
main: Metric!
|
||||||
|
secondary: Metric!
|
||||||
|
}
|
||||||
|
|
||||||
|
type Metric {
|
||||||
|
label: String!
|
||||||
|
value: Float!
|
||||||
|
}
|
||||||
|
|
||||||
type Mutation {
|
type Mutation {
|
||||||
acceptAnomaly(id: String!): Anomaly!
|
acceptAnomaly(id: String!): Anomaly!
|
||||||
setAnomalyToSleep(id: String!): Anomaly!
|
setAnomalyToSleep(id: String!): Anomaly!
|
||||||
|
|
@ -133,14 +156,31 @@ type Query {
|
||||||
anomaliesProto: [Anomaly!]!
|
anomaliesProto: [Anomaly!]!
|
||||||
anomaly(id: String!): Anomaly!
|
anomaly(id: String!): Anomaly!
|
||||||
groupedAnomalies(cursor: String, take: Int! = 10): PaginatedAnomalyGroup!
|
groupedAnomalies(cursor: String, take: Int! = 10): PaginatedAnomalyGroup!
|
||||||
|
homeDashboardKpis: HomeDashboardKPIs!
|
||||||
|
managerKpi(ordinanceType: String!): KpiInfo!
|
||||||
searchTickets(query: String!): [Ticket!]!
|
searchTickets(query: String!): [Ticket!]!
|
||||||
ticket(id: String!): Ticket!
|
ticket(id: String!): Ticket!
|
||||||
ticketStateKeyFigures(state: TicketValidationState!): TicketStateKeyFigures!
|
ticketStateKeyFigures(state: TicketValidationState!): TicketStateKeyFigures!
|
||||||
|
ticketStateKpi(state: TicketValidationState!): KpiInfo!
|
||||||
ticketStateMeta(state: TicketValidationState!): TicketStateMeta!
|
ticketStateMeta(state: TicketValidationState!): TicketStateMeta!
|
||||||
tickets(cursor: String, filters: TicketFilterArgs, state: TicketValidationState!, take: Int! = 10): PaginatedTicket!
|
tickets(cursor: String, filters: TicketFilterArgs, state: TicketValidationState!, take: Int! = 10): PaginatedTicket!
|
||||||
timePerTourMetricForDay(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
|
timePerTourMetricForDay(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
|
||||||
timePerTourMetricForMonth(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
|
timePerTourMetricForMonth(day: DateTime!, ordinanceType: String!): [TimePerTourMetric!]!
|
||||||
|
tourCostBreakdown(operationId: String!): TourCostBreakdown!
|
||||||
toursPerTimeKPI(ordinanceTypes: String!): [TimePerTourKPISummary!]!
|
toursPerTimeKPI(ordinanceTypes: String!): [TimePerTourKPISummary!]!
|
||||||
|
upcomingTours: [Tour!]!
|
||||||
|
}
|
||||||
|
|
||||||
|
type RevenueTrend {
|
||||||
|
date: DateTime!
|
||||||
|
ordinanceType: String!
|
||||||
|
revenueSum: Float!
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServicePosition {
|
||||||
|
description: String!
|
||||||
|
value: Float!
|
||||||
|
variant: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
type SolutionTarget {
|
type SolutionTarget {
|
||||||
|
|
@ -287,7 +327,15 @@ type Tour {
|
||||||
type: String!
|
type: String!
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TourCostBreakdown {
|
||||||
|
servicePositions: [ServicePosition!]!
|
||||||
|
tariffDescription: String!
|
||||||
|
tariffValidityPeriod: String!
|
||||||
|
}
|
||||||
|
|
||||||
type TourStateMeta {
|
type TourStateMeta {
|
||||||
count: Int!
|
count: Int!
|
||||||
|
negativeRevenueDeviation: Float
|
||||||
|
positiveRevenueDeviation: Float
|
||||||
revenueSum: Float
|
revenueSum: Float
|
||||||
}
|
}
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
|
// const tracer = require('./tracer');
|
||||||
|
import tracer from './tracer'
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
import { ConsoleLogger } from '@nestjs/common';
|
import { ConsoleLogger } from '@nestjs/common';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
|
await tracer.start();
|
||||||
|
|
||||||
const app = await NestFactory.create(AppModule, {
|
const app = await NestFactory.create(AppModule, {
|
||||||
logger: new ConsoleLogger({ json: true }),
|
logger: new ConsoleLogger({ json: true }),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -34,4 +34,8 @@ export class TourStateMetaObjectType {
|
||||||
count!: number;
|
count!: number;
|
||||||
@Field(() => Float, { nullable: true })
|
@Field(() => Float, { nullable: true })
|
||||||
revenueSum?: number;
|
revenueSum?: number;
|
||||||
|
@Field(() => Float, { nullable: true })
|
||||||
|
positiveRevenueDeviation?: number;
|
||||||
|
@Field(() => Float, { nullable: true })
|
||||||
|
negativeRevenueDeviation?: number;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,12 @@ export class AccountingValidationRepository {
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
const [count, revenueSum] = await this.prisma.$transaction([
|
const [
|
||||||
|
count,
|
||||||
|
revenueSum,
|
||||||
|
positiveRevenueDeviation,
|
||||||
|
negativeRevenueDeviation,
|
||||||
|
] = await this.prisma.$transaction([
|
||||||
this.prisma.tour.count({
|
this.prisma.tour.count({
|
||||||
where: { ...ticketWithCurrentStateAndTours },
|
where: { ...ticketWithCurrentStateAndTours },
|
||||||
}),
|
}),
|
||||||
|
|
@ -182,11 +187,37 @@ export class AccountingValidationRepository {
|
||||||
...ticketWithCurrentStateAndTours,
|
...ticketWithCurrentStateAndTours,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
this.prisma.tour.aggregate({
|
||||||
|
_sum: {
|
||||||
|
revenueDeviation: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...ticketWithCurrentStateAndTours,
|
||||||
|
revenueDeviation: {
|
||||||
|
gt: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
this.prisma.tour.aggregate({
|
||||||
|
_sum: {
|
||||||
|
revenueDeviation: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...ticketWithCurrentStateAndTours,
|
||||||
|
revenueDeviation: {
|
||||||
|
lt: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
count,
|
count,
|
||||||
revenueSum: revenueSum._sum.revenue ?? 0,
|
revenueSum: revenueSum._sum.revenue ?? 0,
|
||||||
|
positiveRevenueDeviation:
|
||||||
|
positiveRevenueDeviation._sum.revenueDeviation ?? 0,
|
||||||
|
negativeRevenueDeviation:
|
||||||
|
negativeRevenueDeviation._sum.revenueDeviation ?? 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import {
|
||||||
} from './accounting-arg.types';
|
} from './accounting-arg.types';
|
||||||
import { AccountingState } from './accounting-state';
|
import { AccountingState } from './accounting-state';
|
||||||
import { AccountingValidationService } from './accounting-validation.service';
|
import { AccountingValidationService } from './accounting-validation.service';
|
||||||
import { PaginatedTourObjectType } from './accounting.object-type';
|
import { PaginatedTourObjectType, TourCostBreakdownObjectType } from './accounting.object-type';
|
||||||
import { TourObjectType } from './tour.object-type';
|
import { TourObjectType } from './tour.object-type';
|
||||||
|
|
||||||
@Resolver(() => TourObjectType)
|
@Resolver(() => TourObjectType)
|
||||||
|
|
@ -28,6 +28,11 @@ export class AccountingValidationResolver {
|
||||||
return this.service.getAccountingStateMeta(accountingState);
|
return this.service.getAccountingStateMeta(accountingState);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Query(() => TourCostBreakdownObjectType)
|
||||||
|
tourCostBreakdown(@Args('operationId') operationId: string) {
|
||||||
|
return this.service.getTourCostBreakdown(operationId)
|
||||||
|
}
|
||||||
|
|
||||||
@ResolveField(() => TicketObjectType, { nullable: true })
|
@ResolveField(() => TicketObjectType, { nullable: true })
|
||||||
ticket(@Parent() tour: TourObjectType) {
|
ticket(@Parent() tour: TourObjectType) {
|
||||||
return this.service.findTicketByTicketId(tour.ticketId);
|
return this.service.findTicketByTicketId(tour.ticketId);
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,21 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
decodeOffsetCursor,
|
||||||
|
encodeCursor,
|
||||||
|
} from 'src/core/base/pagination.util';
|
||||||
import { TourStateMetaObjectType } from './accounting-arg.types';
|
import { TourStateMetaObjectType } from './accounting-arg.types';
|
||||||
import { AccountingFilter } from './accounting-filter';
|
import { AccountingFilter } from './accounting-filter';
|
||||||
import { AccountingState } from './accounting-state';
|
import { AccountingState } from './accounting-state';
|
||||||
import { AccountingValidationRepository } from './accounting-validation.repository';
|
import { AccountingValidationRepository } from './accounting-validation.repository';
|
||||||
import { PaginatedTourObjectType } from './accounting.object-type';
|
import { PaginatedTourObjectType } from './accounting.object-type';
|
||||||
import {
|
import { AccountingService } from './accounting.service';
|
||||||
decodeOffsetCursor,
|
|
||||||
encodeCursor,
|
|
||||||
} from 'src/core/base/pagination.util';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccountingValidationService {
|
export class AccountingValidationService {
|
||||||
constructor(private readonly repository: AccountingValidationRepository) {}
|
constructor(
|
||||||
|
private readonly repository: AccountingValidationRepository,
|
||||||
|
private readonly accounting: AccountingService,
|
||||||
|
) {}
|
||||||
|
|
||||||
async paginateToursByState(
|
async paginateToursByState(
|
||||||
state: AccountingState,
|
state: AccountingState,
|
||||||
|
|
@ -49,4 +53,8 @@ export class AccountingValidationService {
|
||||||
if (!ticketId) return;
|
if (!ticketId) return;
|
||||||
return this.repository.findTicketByTicketId(ticketId);
|
return this.repository.findTicketByTicketId(ticketId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getTourCostBreakdown(operationId: string) {
|
||||||
|
return this.accounting.tourCostBreakdown(operationId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,33 @@
|
||||||
import { ObjectType, registerEnumType } from '@nestjs/graphql';
|
import { Field, Float, ObjectType, registerEnumType } from '@nestjs/graphql';
|
||||||
import { AccountingState } from './accounting-state';
|
import { AccountingState } from './accounting-state';
|
||||||
import { Paginated } from 'src/core/base/pagination.input';
|
import { Paginated } from 'src/core/base/pagination.input';
|
||||||
import { TourObjectType } from './tour.object-type';
|
import { TourObjectType } from './tour.object-type';
|
||||||
|
import { CostTypeVariant } from '@prisma/client';
|
||||||
@ObjectType('PaginatedTour')
|
|
||||||
export class PaginatedTourObjectType extends Paginated(TourObjectType) {}
|
|
||||||
|
|
||||||
registerEnumType(AccountingState, {
|
registerEnumType(AccountingState, {
|
||||||
name: 'AccountingState',
|
name: 'AccountingState',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ObjectType('PaginatedTour')
|
||||||
|
export class PaginatedTourObjectType extends Paginated(TourObjectType) {}
|
||||||
|
|
||||||
|
@ObjectType('ServicePosition')
|
||||||
|
export class ServicePositionObjectType {
|
||||||
|
@Field()
|
||||||
|
variant: CostTypeVariant;
|
||||||
|
@Field()
|
||||||
|
description: string;
|
||||||
|
@Field(() => Float)
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType('TourCostBreakdown')
|
||||||
|
export class TourCostBreakdownObjectType {
|
||||||
|
@Field()
|
||||||
|
tariffDescription: string;
|
||||||
|
@Field()
|
||||||
|
tariffValidityPeriod: string;
|
||||||
|
|
||||||
|
@Field(() => [ServicePositionObjectType])
|
||||||
|
servicePositions: ServicePositionObjectType[];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import { PrismaService } from 'src/core/database/prisma.service';
|
||||||
|
import { CalculationService } from '../feat-calculation/calculation.service';
|
||||||
|
import { TourCostBreakdownObjectType } from './accounting.object-type';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AccountingService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly calculation: CalculationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async tourCostBreakdown(
|
||||||
|
operationId: string,
|
||||||
|
): Promise<TourCostBreakdownObjectType> {
|
||||||
|
const tour = await this.prisma.tour.findFirst({
|
||||||
|
where: { operationId },
|
||||||
|
});
|
||||||
|
|
||||||
|
const { tariff, servicePositions } =
|
||||||
|
await this.calculation.listProvidedServicePositions(tour);
|
||||||
|
|
||||||
|
if (!tariff || servicePositions.length === 0) {
|
||||||
|
return {
|
||||||
|
tariffDescription: '!-- Tarif kann nicht ermittelt werden --!',
|
||||||
|
tariffValidityPeriod: '--',
|
||||||
|
servicePositions: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
tariffDescription: tariff.tariffGroup?.description,
|
||||||
|
tariffValidityPeriod: this.formatTariffValidityPeriod(
|
||||||
|
tariff.validFrom,
|
||||||
|
tariff.validTo,
|
||||||
|
),
|
||||||
|
servicePositions: servicePositions.map(([costType, value]) => ({
|
||||||
|
value,
|
||||||
|
variant: costType.variant,
|
||||||
|
description: costType.description,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private formatTariffValidityPeriod(from: Date, to?: Date): string {
|
||||||
|
if (!to) return `ab ${format(new Date(from), 'yyyy-MM-dd')}`;
|
||||||
|
return `${format(new Date(from), 'yyyy-MM-dd')} - ${format(new Date(to), 'yyyy-MM-dd')}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,15 +1,18 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { PrismaModule } from 'src/core/database/prisma.module';
|
import { PrismaModule } from 'src/core/database/prisma.module';
|
||||||
|
import { FeatCalculationModule } from '../feat-calculation/feat-calculation.module';
|
||||||
import { AccountingValidationRepository } from './accounting-validation.repository';
|
import { AccountingValidationRepository } from './accounting-validation.repository';
|
||||||
import { AccountingValidationResolver } from './accounting-validation.resolver';
|
import { AccountingValidationResolver } from './accounting-validation.resolver';
|
||||||
import { AccountingValidationService } from './accounting-validation.service';
|
import { AccountingValidationService } from './accounting-validation.service';
|
||||||
|
import { AccountingService } from './accounting.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule],
|
imports: [PrismaModule, FeatCalculationModule],
|
||||||
providers: [
|
providers: [
|
||||||
AccountingValidationRepository,
|
AccountingValidationRepository,
|
||||||
AccountingValidationResolver,
|
AccountingValidationResolver,
|
||||||
AccountingValidationService,
|
AccountingValidationService,
|
||||||
|
AccountingService,
|
||||||
],
|
],
|
||||||
exports: [],
|
exports: [],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,10 @@ import { Module } from '@nestjs/common';
|
||||||
import { ControlCenterResolver } from './control-center.resolver';
|
import { ControlCenterResolver } from './control-center.resolver';
|
||||||
import { SolutionTargetsResolver } from './solution-targets.resolver';
|
import { SolutionTargetsResolver } from './solution-targets.resolver';
|
||||||
import { AnomalyDetectionModule } from '../feat-anomalies/anomaly-detection.module';
|
import { AnomalyDetectionModule } from '../feat-anomalies/anomaly-detection.module';
|
||||||
|
import { FeatKpiModule } from '../feat-kpis/feat-kpis.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [AnomalyDetectionModule],
|
imports: [AnomalyDetectionModule, FeatKpiModule],
|
||||||
providers: [ControlCenterResolver, SolutionTargetsResolver],
|
providers: [ControlCenterResolver, SolutionTargetsResolver],
|
||||||
})
|
})
|
||||||
export class ApplicationControlCenterModule {}
|
export class ApplicationControlCenterModule {}
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,16 @@ import {
|
||||||
PaginatedAnomalyObjectType,
|
PaginatedAnomalyObjectType,
|
||||||
SolutionTargetObjectType,
|
SolutionTargetObjectType,
|
||||||
} from './anomaly.object-type';
|
} from './anomaly.object-type';
|
||||||
|
import { KpiService } from '../feat-kpis/kpi.service';
|
||||||
|
|
||||||
@Resolver(() => AnomalyObjectType)
|
@Resolver(() => AnomalyObjectType)
|
||||||
export class ControlCenterResolver {
|
export class ControlCenterResolver {
|
||||||
constructor(private readonly anomaliesService: AnomaliesService) {}
|
constructor(private readonly anomaliesService: AnomaliesService, private readonly kpiService: KpiService) {}
|
||||||
|
|
||||||
|
@Query(() => [TourObjectType])
|
||||||
|
async upcomingTours() {
|
||||||
|
return this.kpiService.upcomingTours();
|
||||||
|
}
|
||||||
|
|
||||||
@Query(() => AnomalyObjectType)
|
@Query(() => AnomalyObjectType)
|
||||||
async anomaly(@Args('id') id: string) {
|
async anomaly(@Args('id') id: string) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { PrismaModule } from 'src/core/database/prisma.module';
|
||||||
|
import { HomeResolver } from './home.resolver';
|
||||||
|
import { HomeService } from './home.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule],
|
||||||
|
providers: [HomeResolver, HomeService],
|
||||||
|
})
|
||||||
|
export class AppHomeModule {}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { Field, Float, Int, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@ObjectType('RevenueTrend')
|
||||||
|
export class RevenueTrendObjectType {
|
||||||
|
@Field(() => Date)
|
||||||
|
date: Date;
|
||||||
|
@Field()
|
||||||
|
ordinanceType: string;
|
||||||
|
@Field(() => Float)
|
||||||
|
revenueSum: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType('HomeDashboardKPIs')
|
||||||
|
export class HomeDashboardKPIsObjectType {
|
||||||
|
@Field(() => Int)
|
||||||
|
anomalyCount: number;
|
||||||
|
@Field(() => Int)
|
||||||
|
employeesWithoutTourCount: number;
|
||||||
|
@Field(() => Int)
|
||||||
|
toursWithoutApprovalCount: number;
|
||||||
|
@Field(() => Int)
|
||||||
|
billableTourCount: number;
|
||||||
|
@Field(() => Float)
|
||||||
|
billableTourRevenueSum: number;
|
||||||
|
@Field(() => Int)
|
||||||
|
billedTourCount: number;
|
||||||
|
@Field(() => Float)
|
||||||
|
billedTourPositiveRevenueDeviation: number;
|
||||||
|
@Field(() => Float)
|
||||||
|
billedTourNegativeRevenueDeviation: number;
|
||||||
|
@Field(() => [RevenueTrendObjectType])
|
||||||
|
revenueTrend: RevenueTrendObjectType[];
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Query, Resolver } from '@nestjs/graphql';
|
||||||
|
import { HomeDashboardKPIsObjectType } from './home.object-type';
|
||||||
|
import { HomeService } from './home.service';
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
export class HomeResolver {
|
||||||
|
constructor(private readonly service: HomeService) {}
|
||||||
|
|
||||||
|
@Query(() => HomeDashboardKPIsObjectType)
|
||||||
|
homeDashboardKpis() {
|
||||||
|
return this.service.homeDashboardKpis();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,444 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ApprovalState, Prisma, Tour } from '@prisma/client';
|
||||||
|
import { addDays, endOfDay, format, startOfDay, subDays } from 'date-fns';
|
||||||
|
import { PrismaService } from 'src/core/database/prisma.service';
|
||||||
|
import { AnomalyTypes } from '../feat-anomalies/anomaly-detectors/anomaly-types.enum';
|
||||||
|
import {
|
||||||
|
HomeDashboardKPIsObjectType,
|
||||||
|
RevenueTrendObjectType,
|
||||||
|
} from './home.object-type';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class HomeService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async homeDashboardKpis(): Promise<HomeDashboardKPIsObjectType> {
|
||||||
|
const anomalyCount = await this.getAnomalyCount();
|
||||||
|
const employeesWithoutTourCount = await this.getEmployeesWithoutTourCount();
|
||||||
|
const toursWithoutApprovalCount = await this.getToursWithoutApprovalCount();
|
||||||
|
const billableTourCount = await this.getBillableTourCount();
|
||||||
|
const billableTourRevenueSum = await this.getBillableTourRevenueSum();
|
||||||
|
const {
|
||||||
|
tourCount: billedTourCount,
|
||||||
|
positiveRevenueDeviation: billedTourPositiveRevenueDeviation,
|
||||||
|
negativeRevenueDeviation: billedTourNegativeRevenueDeviation,
|
||||||
|
} = await this.getBilledTourKPIs();
|
||||||
|
const revenueTrend = await this.getRevenueTrend();
|
||||||
|
|
||||||
|
return {
|
||||||
|
anomalyCount,
|
||||||
|
employeesWithoutTourCount,
|
||||||
|
toursWithoutApprovalCount,
|
||||||
|
billableTourCount,
|
||||||
|
billableTourRevenueSum,
|
||||||
|
billedTourCount,
|
||||||
|
billedTourPositiveRevenueDeviation,
|
||||||
|
billedTourNegativeRevenueDeviation,
|
||||||
|
revenueTrend,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getAnomalyCount(): Promise<number> {
|
||||||
|
return await this.prisma.anomaly.count({
|
||||||
|
where: {
|
||||||
|
resolvedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getEmployeesWithoutTourCount(): Promise<number> {
|
||||||
|
return await this.prisma.anomaly.count({
|
||||||
|
where: {
|
||||||
|
type: AnomalyTypes.WorkingEmployeeNotInDistributionSystem,
|
||||||
|
resolvedAt: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getToursWithoutApprovalCount(): Promise<number> {
|
||||||
|
return await this.prisma.tour.count({
|
||||||
|
where: {
|
||||||
|
startDate: {
|
||||||
|
gte: startOfDay(addDays(new Date(), 3)),
|
||||||
|
},
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
Ticket: {
|
||||||
|
approvalState: ApprovalState.UNKNOWN,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Ticket: {
|
||||||
|
approvalState: ApprovalState.REQUIRED,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBillableTourCount(): Promise<number> {
|
||||||
|
return await this.prisma.tour.count({
|
||||||
|
where: {
|
||||||
|
done: true,
|
||||||
|
check: 0,
|
||||||
|
empty: { not: null },
|
||||||
|
ticketId: { not: null },
|
||||||
|
occupiedKm: {
|
||||||
|
lt: 9000,
|
||||||
|
},
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
carName: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Storno',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Abgabe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{ patientId: null },
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Pause
|
||||||
|
not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Feierabend
|
||||||
|
not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBillableTourRevenueSum(): Promise<number> {
|
||||||
|
const aggr = await this.prisma.tour.aggregate({
|
||||||
|
_sum: {
|
||||||
|
revenue: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
done: true,
|
||||||
|
check: 0,
|
||||||
|
empty: { not: null },
|
||||||
|
ticketId: { not: null },
|
||||||
|
occupiedKm: {
|
||||||
|
lt: 9000,
|
||||||
|
},
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
carName: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Storno',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Abgabe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{ patientId: null },
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Pause
|
||||||
|
not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Feierabend
|
||||||
|
not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return aggr._sum.revenue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getBilledTourKPIs(): Promise<{
|
||||||
|
tourCount: number;
|
||||||
|
positiveRevenueDeviation: number;
|
||||||
|
negativeRevenueDeviation: number;
|
||||||
|
}> {
|
||||||
|
const toursWithCurrentState: Prisma.TourWhereInput = {
|
||||||
|
check: 2,
|
||||||
|
revenueDeviation: { not: 0 },
|
||||||
|
empty: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
ticketId: {
|
||||||
|
not: null,
|
||||||
|
},
|
||||||
|
occupiedKm: {
|
||||||
|
lt: 9000,
|
||||||
|
},
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
carName: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Storno',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Abgabe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{ patientId: null },
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Pause
|
||||||
|
not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Feierabend
|
||||||
|
not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const [tourCount, positiveRevenueDeviation, negativeRevenueDeviation] =
|
||||||
|
await this.prisma.$transaction([
|
||||||
|
this.prisma.tour.count({
|
||||||
|
where: { ...toursWithCurrentState },
|
||||||
|
}),
|
||||||
|
this.prisma.tour.aggregate({
|
||||||
|
_sum: {
|
||||||
|
revenueDeviation: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...toursWithCurrentState,
|
||||||
|
revenueDeviation: {
|
||||||
|
gt: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
this.prisma.tour.aggregate({
|
||||||
|
_sum: {
|
||||||
|
revenueDeviation: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...toursWithCurrentState,
|
||||||
|
revenueDeviation: {
|
||||||
|
lt: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tourCount,
|
||||||
|
positiveRevenueDeviation:
|
||||||
|
positiveRevenueDeviation._sum.revenueDeviation ?? 0,
|
||||||
|
negativeRevenueDeviation:
|
||||||
|
negativeRevenueDeviation._sum.revenueDeviation ?? 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getRevenueTrend(): Promise<RevenueTrendObjectType[]> {
|
||||||
|
const tours = await this.getTours();
|
||||||
|
|
||||||
|
const toursPerDay = tours.reduce(
|
||||||
|
(acc, tour) => {
|
||||||
|
const tourDay = format(new Date(tour.startDate), 'yyyy-MM-dd');
|
||||||
|
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[tourDay]: [...(acc[tourDay]?.length ? acc[tourDay] : []), tour],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{} as Record<string, Tour[]>,
|
||||||
|
) as Record<string, Tour[]>;
|
||||||
|
|
||||||
|
const revenueTrend = Object.entries(toursPerDay).flatMap(([day, tours]) => {
|
||||||
|
const toursByOrdinanceType = tours.reduce(
|
||||||
|
(acc, tour) => {
|
||||||
|
return {
|
||||||
|
...acc,
|
||||||
|
[tour.ordinanceType]: [
|
||||||
|
...(acc[tour.ordinanceType] ? acc[tour.ordinanceType] : []),
|
||||||
|
tour,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
{} as Record<string, Tour[]>,
|
||||||
|
);
|
||||||
|
return Object.entries(toursByOrdinanceType).map(([ot, tours]) => {
|
||||||
|
const revenueSum = tours.reduce(
|
||||||
|
(sum, { revenue }) => (sum += revenue),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
revenueSum,
|
||||||
|
date: new Date(day),
|
||||||
|
ordinanceType: ot,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return revenueTrend;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTours(): Promise<Tour[]> {
|
||||||
|
return this.prisma.tour.findMany({
|
||||||
|
where: {
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
startDate: {
|
||||||
|
gte: startOfDay(subDays(new Date(), 10)),
|
||||||
|
lte: endOfDay(new Date()),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{ordinanceType: {not: null}},
|
||||||
|
{ordinanceType: {not: 'Tragehilfe'}},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
carName: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Storno',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Abgabe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
check: {
|
||||||
|
not: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
occupiedKm: {
|
||||||
|
lt: 9999,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
patientId: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Pause
|
||||||
|
not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Feierabend
|
||||||
|
not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,22 +1,19 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { PrismaModule } from 'src/core/database/prisma.module';
|
import { PrismaModule } from 'src/core/database/prisma.module';
|
||||||
import { FeatureCalculationModule } from '../feat-calculation/feat-calculation.module';
|
import { FeatBusinessObjectsModule } from '../feat-business-objects/feat-business-objects.module';
|
||||||
import { TourAmountForecastService } from './tour-amount-forecast.service';
|
import { FeatCalculationModule } from '../feat-calculation/feat-calculation.module';
|
||||||
|
import { ManagerResolver } from './manager.resolver';
|
||||||
import { TimePerTourKpiService } from './time-per-tour-kpi.service';
|
import { TimePerTourKpiService } from './time-per-tour-kpi.service';
|
||||||
import { TimePerTourMetricsService } from './time-per-tour-metrics.service';
|
import { TimePerTourMetricsService } from './time-per-tour-metrics.service';
|
||||||
import { ManagerResolver } from './manager.resolver';
|
import { TourAmountForecastService } from './tour-amount-forecast.service';
|
||||||
import { CarEntityService } from './car-entity/car-entity.service';
|
|
||||||
import { CarEntityRepository } from './car-entity/car-entity.repository';
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, FeatureCalculationModule],
|
imports: [PrismaModule, FeatCalculationModule, FeatBusinessObjectsModule],
|
||||||
providers: [
|
providers: [
|
||||||
TourAmountForecastService,
|
TourAmountForecastService,
|
||||||
TimePerTourKpiService,
|
TimePerTourKpiService,
|
||||||
TimePerTourMetricsService,
|
TimePerTourMetricsService,
|
||||||
CarEntityService,
|
|
||||||
CarEntityRepository,
|
|
||||||
ManagerResolver,
|
ManagerResolver,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class ApplicationManagerModule {}
|
export class ManagerModule {}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ import { OnEvent } from '@nestjs/event-emitter';
|
||||||
import { addMilliseconds } from 'date-fns';
|
import { addMilliseconds } from 'date-fns';
|
||||||
import { PubSub } from 'graphql-subscriptions';
|
import { PubSub } from 'graphql-subscriptions';
|
||||||
import { generateIntervalForTimeRange } from 'src/utils';
|
import { generateIntervalForTimeRange } from 'src/utils';
|
||||||
import { CarEntityService } from './car-entity/car-entity.service';
|
|
||||||
import { CalculationService } from '../feat-calculation/calculation.service';
|
import { CalculationService } from '../feat-calculation/calculation.service';
|
||||||
import { ETLEventTypes } from '../feat-etl/feat-importers/events/event-types.enum';
|
import { ETLEventTypes } from '../feat-etl/feat-importers/events/event-types.enum';
|
||||||
import { LoadingDoneEvent } from '../feat-etl/feat-importers/events/loading-done.event';
|
import { LoadingDoneEvent } from '../feat-etl/feat-importers/events/loading-done.event';
|
||||||
|
import { CarEntityService } from '../feat-business-objects/car-entity/car-entity.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimePerTourKpiService {
|
export class TimePerTourKpiService {
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,11 @@ import {
|
||||||
startOfDay,
|
startOfDay,
|
||||||
startOfMonth,
|
startOfMonth,
|
||||||
} from 'date-fns';
|
} from 'date-fns';
|
||||||
import { CarEntityService } from './car-entity/car-entity.service';
|
|
||||||
import { TourAmountForecastService } from './tour-amount-forecast.service';
|
import { TourAmountForecastService } from './tour-amount-forecast.service';
|
||||||
import { TimePerTourMetricObjectType } from './manager.object-type';
|
import { TimePerTourMetricObjectType } from './manager.object-type';
|
||||||
import { CalculationService } from '../feat-calculation/calculation.service';
|
import { CalculationService } from '../feat-calculation/calculation.service';
|
||||||
import { generateIntervalForTimeRange, groupBy } from 'src/utils';
|
import { generateIntervalForTimeRange, groupBy } from 'src/utils';
|
||||||
|
import { CarEntityService } from '../feat-business-objects/car-entity/car-entity.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimePerTourMetricsService {
|
export class TimePerTourMetricsService {
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,21 @@ export class TicketLifecycleService {
|
||||||
? ApprovalState.UNKNOWN
|
? ApprovalState.UNKNOWN
|
||||||
: ApprovalState.REQUIRED,
|
: ApprovalState.REQUIRED,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// NOTE: Condition required to get these tickets out of USER_INTERACTION_REQUIRED
|
||||||
|
if (
|
||||||
|
ticket.tours.every(
|
||||||
|
({ ordinanceType }) =>
|
||||||
|
ordinanceType.indexOf('BTW') > -1 ||
|
||||||
|
ordinanceType.indexOf('TSW') > -1,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await this.repository.updateTicket(ticket.id, {
|
||||||
|
errors,
|
||||||
|
notes,
|
||||||
|
state: TicketValidationState.TD_UNCERTAIN,
|
||||||
|
approvalState: ApprovalState.FREE,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.repository.updateTicket(ticket.id, {
|
await this.repository.updateTicket(ticket.id, {
|
||||||
errors,
|
errors,
|
||||||
|
|
@ -119,6 +134,7 @@ export class TicketLifecycleService {
|
||||||
approvalState: ApprovalState.FREE,
|
approvalState: ApprovalState.FREE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.repository.setActionablesOnTicket(ticket.id, errors, notes);
|
await this.repository.setActionablesOnTicket(ticket.id, errors, notes);
|
||||||
}
|
}
|
||||||
|
|
@ -186,6 +202,9 @@ export class TicketLifecycleService {
|
||||||
const approvalRequirements = await Promise.all(
|
const approvalRequirements = await Promise.all(
|
||||||
ticket.tours.map((tour) => this.checkTourApprovalRequirement(tour)),
|
ticket.tours.map((tour) => this.checkTourApprovalRequirement(tour)),
|
||||||
);
|
);
|
||||||
|
if(ticket.tours.some(({ordinanceType}) => ordinanceType.indexOf('TSW') > -1)) {
|
||||||
|
console.log(approvalRequirements)
|
||||||
|
}
|
||||||
const isApprovalRequired = approvalRequirements.reduce(
|
const isApprovalRequired = approvalRequirements.reduce(
|
||||||
(result, requirement) => result || requirement,
|
(result, requirement) => result || requirement,
|
||||||
false,
|
false,
|
||||||
|
|
@ -204,6 +223,14 @@ export class TicketLifecycleService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkTourApprovalRequirement(tour: Tour): Promise<boolean> {
|
private async checkTourApprovalRequirement(tour: Tour): Promise<boolean> {
|
||||||
|
// NOTE: Requirement by Avicenna
|
||||||
|
if (
|
||||||
|
tour.ordinanceType.indexOf('BTW') > -1 ||
|
||||||
|
tour.ordinanceType.indexOf('TSW') > -1
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
'Einweisung',
|
'Einweisung',
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,15 @@ export function validatePatient(patient: Patient): ValidationResult {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
errors.push('Patient: Krankenkasse ist nicht gefüllt');
|
errors.push('Patient: Krankenkasse ist nicht gefüllt');
|
||||||
}
|
}
|
||||||
if (!patient?.careDegree) {
|
// if (!patient?.careDegree) {
|
||||||
notes.push('Patient: Pflegegrad ist nicht gefüllt');
|
// notes.push('Patient: Pflegegrad ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
if (!patient?.disabilityMark1) {
|
// if (!patient?.disabilityMark1) {
|
||||||
notes.push('Patient: Merkzeichen 1 ist nicht gefüllt');
|
// notes.push('Patient: Merkzeichen 1 ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
if (!patient?.disabilityMark2) {
|
// if (!patient?.disabilityMark2) {
|
||||||
notes.push('Patient: Merkzeichen 2 ist nicht gefüllt');
|
// notes.push('Patient: Merkzeichen 2 ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: isValid,
|
result: isValid,
|
||||||
|
|
|
||||||
|
|
@ -200,11 +200,11 @@ export class EmployeesLoggedInDetector extends AnomalyDetector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!employee.personnelNumber) {
|
if (!employee.personnelNumber) {
|
||||||
this.logger.warn(`Employee has no personnelNumber`, {
|
this.logger.warn(`Employee has no personnelNumber ${JSON.stringify({
|
||||||
id: employee.id,
|
id: employee.id,
|
||||||
surname: employee.surname,
|
surname: employee.surname,
|
||||||
name: employee.name,
|
name: employee.name,
|
||||||
});
|
})}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -86,9 +86,16 @@ export class WorkingEmployeeNotInDistributionSystemDetector extends AnomalyDetec
|
||||||
const employeeId = employeeIdParts.join('-');
|
const employeeId = employeeIdParts.join('-');
|
||||||
const employee = await this.dataService.findEmployeeById(employeeId);
|
const employee = await this.dataService.findEmployeeById(employeeId);
|
||||||
|
|
||||||
|
if (!employee || !employee?.personnelNumber) {
|
||||||
|
this.logger.error(
|
||||||
|
`Cannot verify anomaly ${anomaly.id}. Employee (${employeeId}) does not exist or cannot be synced ${JSON.stringify(employee)}`,
|
||||||
|
);
|
||||||
|
return anomaly;
|
||||||
|
}
|
||||||
|
|
||||||
const plannedTime =
|
const plannedTime =
|
||||||
await this.dataService.findPlannedTimeByPersonnelNumberForDay(
|
await this.dataService.findPlannedTimeByPersonnelNumberForDay(
|
||||||
`${employee.personnelNumber}`,
|
`${employee?.personnelNumber}`,
|
||||||
attendanceRegistration.dateTime,
|
attendanceRegistration.dateTime,
|
||||||
);
|
);
|
||||||
if (
|
if (
|
||||||
|
|
|
||||||
|
|
@ -98,11 +98,15 @@ export class CarEntityRepository {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
AND: [
|
AND: [
|
||||||
|
...(ordinanceType !== 'ALL'
|
||||||
|
? [
|
||||||
{
|
{
|
||||||
department: {
|
department: {
|
||||||
contains: ordinanceType,
|
contains: ordinanceType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
{
|
{
|
||||||
department: {
|
department: {
|
||||||
not: 'Leitstelle',
|
not: 'Leitstelle',
|
||||||
|
|
@ -113,6 +117,11 @@ export class CarEntityRepository {
|
||||||
not: 'Büro',
|
not: 'Büro',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
department: {
|
||||||
|
not: 'Fortbildung',
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -138,7 +138,7 @@ export class CarEntity {
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private plannedTimeIntervalInInterval(interval: Interval): Interval {
|
public plannedTimeIntervalInInterval(interval: Interval): Interval {
|
||||||
return this.plannedTimes
|
return this.plannedTimes
|
||||||
.map(({ startDate, endDate }) => ({
|
.map(({ startDate, endDate }) => ({
|
||||||
start: new Date(startDate),
|
start: new Date(startDate),
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { Module } from "@nestjs/common";
|
||||||
|
import { PrismaModule } from "src/core/database/prisma.module";
|
||||||
|
import { CarEntityRepository } from "./car-entity/car-entity.repository";
|
||||||
|
import { CarEntityService } from "./car-entity/car-entity.service";
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule],
|
||||||
|
exports: [CarEntityService],
|
||||||
|
providers: [CarEntityRepository, CarEntityService]
|
||||||
|
})
|
||||||
|
export class FeatBusinessObjectsModule {}
|
||||||
|
|
@ -6,22 +6,21 @@ import {
|
||||||
TariffGroup,
|
TariffGroup,
|
||||||
Tour,
|
Tour,
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
import {
|
import { Interval, addDays, isSaturday, isWithinInterval } from 'date-fns';
|
||||||
Interval,
|
|
||||||
addDays,
|
|
||||||
isSameDay,
|
|
||||||
isSaturday,
|
|
||||||
isWeekend,
|
|
||||||
isWithinInterval,
|
|
||||||
} from 'date-fns';
|
|
||||||
import { toZonedTime } from 'date-fns-tz';
|
|
||||||
|
|
||||||
import { PrismaService } from 'src/core/database/prisma.service';
|
import { PrismaService } from 'src/core/database/prisma.service';
|
||||||
import { generateIntervalForTimeRange } from 'src/utils';
|
import { generateIntervalForTimeRange } from 'src/utils';
|
||||||
import { BILLING_INFORMATION_BY_ORDINANCE_TYPE } from './billing-information-by-ordinance-type';
|
import { BILLING_INFORMATION_BY_ORDINANCE_TYPE } from './billing-information-by-ordinance-type';
|
||||||
import { INFECTION_TYPES } from './infection-types';
|
import {
|
||||||
|
calculateDesinfektionszuschlag,
|
||||||
|
calculateFahrzeugzuschlag,
|
||||||
|
calculateFeiertagszuschlag,
|
||||||
|
calculateKilometerzuschlag,
|
||||||
|
calculateNachtzuschlag,
|
||||||
|
calculateTSW,
|
||||||
|
calculateWochenendzuschlag,
|
||||||
|
} from './cost-type-calculations';
|
||||||
import { PersonnelCostCalculationService } from './personnel-cost-calculation.service';
|
import { PersonnelCostCalculationService } from './personnel-cost-calculation.service';
|
||||||
import { PUBLIC_HOLIDAYS_HH } from './public-holidays';
|
|
||||||
|
|
||||||
enum OrdinanceTypes {
|
enum OrdinanceTypes {
|
||||||
BTW = 'BTW',
|
BTW = 'BTW',
|
||||||
|
|
@ -56,10 +55,39 @@ export class CalculationService {
|
||||||
);
|
);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const revenue = this.calculateRevenue(tour, tariff) / 100;
|
const revenue =
|
||||||
|
this.calculateProvidedServicePositions(tour, tariff).reduce(
|
||||||
|
(sum, [, value]) => (sum += value),
|
||||||
|
0,
|
||||||
|
) / 100;
|
||||||
return revenue;
|
return revenue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async listProvidedServicePositions(tour: Tour): Promise<{
|
||||||
|
tariff: Tariff & { costTypes: CostType[]; tariffGroup: TariffGroup };
|
||||||
|
servicePositions: [CostType, number][];
|
||||||
|
}> {
|
||||||
|
const { healthInsurance, ordinanceType, startDate } = tour;
|
||||||
|
if (!ordinanceType) {
|
||||||
|
return { tariff: undefined, servicePositions: [] };
|
||||||
|
}
|
||||||
|
const tariff = await this.findTariff(
|
||||||
|
healthInsurance,
|
||||||
|
ordinanceType.match(/([A-Z])\w+/).at(0),
|
||||||
|
new Date(startDate),
|
||||||
|
);
|
||||||
|
if (!tariff) {
|
||||||
|
this.logger.warn(
|
||||||
|
`No tariff found for tour ${tour.operationId} (${healthInsurance}, ${ordinanceType}, ${startDate})`,
|
||||||
|
);
|
||||||
|
return { tariff: undefined, servicePositions: [] };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
tariff,
|
||||||
|
servicePositions: this.calculateProvidedServicePositions(tour, tariff),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public calculatePersonnelCostInInterval(
|
public calculatePersonnelCostInInterval(
|
||||||
interval: Interval,
|
interval: Interval,
|
||||||
employeeAmount: number,
|
employeeAmount: number,
|
||||||
|
|
@ -172,15 +200,16 @@ export class CalculationService {
|
||||||
return billingInformation[1];
|
return billingInformation[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateRevenue(
|
private calculateProvidedServicePositions(
|
||||||
tour: Tour,
|
tour: Tour,
|
||||||
tariff: Tariff & { costTypes: CostType[]; tariffGroup: TariffGroup },
|
tariff: Tariff & { costTypes: CostType[]; tariffGroup: TariffGroup },
|
||||||
): number {
|
): [CostType, number][] {
|
||||||
const highestZuschlag = this.getHighestZuschlag(tour, tariff);
|
const highestZuschlag = this.getHighestZuschlag(tour, tariff);
|
||||||
// if TariffGroup is VDEK KTW, sum up costTypes
|
// if TariffGroup is VDEK KTW, sum up costTypes
|
||||||
const isVdek = tariff.tariffGroup?.description?.indexOf('VDEK KTW') > -1;
|
const isVdek = tariff.tariffGroup?.description?.indexOf('VDEK KTW') > -1;
|
||||||
|
|
||||||
return tariff.costTypes.reduce((sum, costType) => {
|
return tariff.costTypes
|
||||||
|
.map((costType) => {
|
||||||
switch (costType.variant) {
|
switch (costType.variant) {
|
||||||
case CostTypeVariant.GRUNDPREIS:
|
case CostTypeVariant.GRUNDPREIS:
|
||||||
// TODO: decide if you need a CostTypeVariant for QMS Zuschlag and exclude the category there
|
// TODO: decide if you need a CostTypeVariant for QMS Zuschlag and exclude the category there
|
||||||
|
|
@ -188,34 +217,41 @@ export class CalculationService {
|
||||||
costType.description.indexOf('QMS') > -1 &&
|
costType.description.indexOf('QMS') > -1 &&
|
||||||
tour.category === 'Privatfahrt Rechnung'
|
tour.category === 'Privatfahrt Rechnung'
|
||||||
) {
|
) {
|
||||||
return sum;
|
return [costType, 0];
|
||||||
}
|
}
|
||||||
return sum + costType.costAmount;
|
return [costType, costType.costAmount];
|
||||||
case CostTypeVariant.TSWDIFFERENZIERUNG:
|
case CostTypeVariant.TSWDIFFERENZIERUNG:
|
||||||
return sum + this.calculateTSW(tour, costType);
|
return [costType, calculateTSW(tour, costType)];
|
||||||
case CostTypeVariant.DESINFEKTIONSZUSCHLAG:
|
case CostTypeVariant.DESINFEKTIONSZUSCHLAG:
|
||||||
return sum + this.calculateDesinfektionszuschlag(tour, costType);
|
return [costType, calculateDesinfektionszuschlag(tour, costType)];
|
||||||
case CostTypeVariant.KILOMETERZUSCHLAG:
|
case CostTypeVariant.KILOMETERZUSCHLAG:
|
||||||
return sum + this.calculateKilometerzuschlag(tour, costType);
|
return [costType, calculateKilometerzuschlag(tour, costType)];
|
||||||
case CostTypeVariant.FAHRZEUGZUSCHLAG:
|
case CostTypeVariant.FAHRZEUGZUSCHLAG:
|
||||||
return sum + this.calculateFahrzeugzuschlag(tour, costType);
|
return [costType, calculateFahrzeugzuschlag(tour, costType)];
|
||||||
case CostTypeVariant.NACHTZUSCHLAG:
|
case CostTypeVariant.NACHTZUSCHLAG:
|
||||||
if (highestZuschlag === CostTypeVariant.NACHTZUSCHLAG || isVdek) {
|
if (highestZuschlag === CostTypeVariant.NACHTZUSCHLAG || isVdek) {
|
||||||
return sum + this.calculateNachtzuschlag(tour, costType);
|
return [costType, calculateNachtzuschlag(tour, costType)];
|
||||||
}
|
}
|
||||||
return sum;
|
return [costType, 0];
|
||||||
case CostTypeVariant.WOCHENENDZUSCHLAG:
|
case CostTypeVariant.WOCHENENDZUSCHLAG:
|
||||||
if (highestZuschlag === CostTypeVariant.WOCHENENDZUSCHLAG || isVdek) {
|
if (
|
||||||
return sum + this.calculateWochenendzuschlag(tour, costType);
|
highestZuschlag === CostTypeVariant.WOCHENENDZUSCHLAG ||
|
||||||
|
isVdek
|
||||||
|
) {
|
||||||
|
return [costType, calculateWochenendzuschlag(tour, costType)];
|
||||||
}
|
}
|
||||||
return sum;
|
return [costType, 0];
|
||||||
case CostTypeVariant.FEIERTAGSZUSCHLAG:
|
case CostTypeVariant.FEIERTAGSZUSCHLAG:
|
||||||
if (highestZuschlag === CostTypeVariant.FEIERTAGSZUSCHLAG || isVdek) {
|
if (
|
||||||
return sum + this.calculateFeiertagszuschlag(tour, costType);
|
highestZuschlag === CostTypeVariant.FEIERTAGSZUSCHLAG ||
|
||||||
|
isVdek
|
||||||
|
) {
|
||||||
|
return [costType, calculateFeiertagszuschlag(tour, costType)];
|
||||||
}
|
}
|
||||||
return sum;
|
return [costType, 0];
|
||||||
}
|
}
|
||||||
}, 0);
|
})
|
||||||
|
.filter(([, value]) => value !== 0) as [CostType, number][];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getHighestZuschlag(
|
private getHighestZuschlag(
|
||||||
|
|
@ -235,17 +271,17 @@ export class CalculationService {
|
||||||
case CostTypeVariant.NACHTZUSCHLAG:
|
case CostTypeVariant.NACHTZUSCHLAG:
|
||||||
return [
|
return [
|
||||||
CostTypeVariant.NACHTZUSCHLAG,
|
CostTypeVariant.NACHTZUSCHLAG,
|
||||||
this.calculateNachtzuschlag(tour, costType),
|
calculateNachtzuschlag(tour, costType),
|
||||||
];
|
];
|
||||||
case CostTypeVariant.WOCHENENDZUSCHLAG:
|
case CostTypeVariant.WOCHENENDZUSCHLAG:
|
||||||
return [
|
return [
|
||||||
CostTypeVariant.WOCHENENDZUSCHLAG,
|
CostTypeVariant.WOCHENENDZUSCHLAG,
|
||||||
this.calculateWochenendzuschlag(tour, costType),
|
calculateWochenendzuschlag(tour, costType),
|
||||||
];
|
];
|
||||||
case CostTypeVariant.FEIERTAGSZUSCHLAG:
|
case CostTypeVariant.FEIERTAGSZUSCHLAG:
|
||||||
return [
|
return [
|
||||||
CostTypeVariant.FEIERTAGSZUSCHLAG,
|
CostTypeVariant.FEIERTAGSZUSCHLAG,
|
||||||
this.calculateFeiertagszuschlag(tour, costType),
|
calculateFeiertagszuschlag(tour, costType),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -260,99 +296,6 @@ export class CalculationService {
|
||||||
return zuschlaegeHighestToLowest[0][0];
|
return zuschlaegeHighestToLowest[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private calculateDesinfektionszuschlag(
|
|
||||||
tour: Tour,
|
|
||||||
costType: CostType,
|
|
||||||
): number {
|
|
||||||
if (tour.hasInfection && !tour.infectionName) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
tour.hasInfection &&
|
|
||||||
(tour.infectionName.indexOf(costType.infectionType) > -1 ||
|
|
||||||
INFECTION_TYPES[tour.infectionName] === costType.infectionType)
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateTSW(tour: Tour, costType: CostType): number {
|
|
||||||
if (
|
|
||||||
costType.description.includes('TS+R') &&
|
|
||||||
tour.transportType === 'TS+R'
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
} else if (
|
|
||||||
(costType.description.includes('TS 1') ||
|
|
||||||
costType.description.includes('TSW 1')) &&
|
|
||||||
tour.driverId === tour.codriverId
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
} else if (
|
|
||||||
(costType.description.includes('TS 2') ||
|
|
||||||
costType.description.includes('TSW 2')) &&
|
|
||||||
tour.driverId !== tour.codriverId
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateFeiertagszuschlag(tour: Tour, costType: CostType): number {
|
|
||||||
if (
|
|
||||||
PUBLIC_HOLIDAYS_HH.some((day) =>
|
|
||||||
isSameDay(
|
|
||||||
new Date(day),
|
|
||||||
new Date(toZonedTime(tour.startDate, 'Europe/Berlin')),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateNachtzuschlag(tour: Tour, costType: CostType): number {
|
|
||||||
const firstInterval = generateIntervalForTimeRange(
|
|
||||||
costType.timeRange,
|
|
||||||
'Europe/Berlin',
|
|
||||||
addDays(tour.startDate, -1),
|
|
||||||
);
|
|
||||||
const secondInterval = generateIntervalForTimeRange(
|
|
||||||
costType.timeRange,
|
|
||||||
'Europe/Berlin',
|
|
||||||
tour.startDate,
|
|
||||||
);
|
|
||||||
if (
|
|
||||||
isWithinInterval(tour.startDate, firstInterval) ||
|
|
||||||
isWithinInterval(tour.startDate, secondInterval)
|
|
||||||
) {
|
|
||||||
return costType.costAmount;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateKilometerzuschlag(tour: Tour, costType: CostType): number {
|
|
||||||
const kilometerOverLimit = Math.ceil(
|
|
||||||
tour.occupiedKm - costType.kmInclusive,
|
|
||||||
);
|
|
||||||
if (kilometerOverLimit <= 0) return 0;
|
|
||||||
return costType.costAmount * kilometerOverLimit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateFahrzeugzuschlag(tour: Tour, costType: CostType): number {
|
|
||||||
const isCarNameSupported = !costType.excludeCarNames.includes(tour.carName);
|
|
||||||
if (!isCarNameSupported) return 0;
|
|
||||||
return costType.costAmount;
|
|
||||||
}
|
|
||||||
|
|
||||||
private calculateWochenendzuschlag(tour: Tour, costType: CostType): number {
|
|
||||||
if (isWeekend(toZonedTime(tour.startDate, 'Europe/Berlin')))
|
|
||||||
return costType.costAmount;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async findTariff(
|
private async findTariff(
|
||||||
healthInsurance: string,
|
healthInsurance: string,
|
||||||
ordinanceType: string,
|
ordinanceType: string,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { CostType, Tour } from '@prisma/client';
|
||||||
|
import { addDays, isSameDay, isWeekend, isWithinInterval } from 'date-fns';
|
||||||
|
import { toZonedTime } from 'date-fns-tz';
|
||||||
|
import { generateIntervalForTimeRange } from 'src/utils';
|
||||||
|
import { INFECTION_TYPES } from './infection-types';
|
||||||
|
import { PUBLIC_HOLIDAYS_HH } from './public-holidays';
|
||||||
|
|
||||||
|
export function calculateDesinfektionszuschlag(
|
||||||
|
tour: Tour,
|
||||||
|
costType: CostType,
|
||||||
|
): number {
|
||||||
|
if (tour.hasInfection && !tour.infectionName) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
tour.hasInfection &&
|
||||||
|
(tour.infectionName.indexOf(costType.infectionType) > -1 ||
|
||||||
|
INFECTION_TYPES[tour.infectionName] === costType.infectionType)
|
||||||
|
) {
|
||||||
|
return costType.costAmount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateTSW(tour: Tour, costType: CostType): number {
|
||||||
|
if (costType.description.includes('TS+R') && tour.transportType === 'TS+R') {
|
||||||
|
return costType.costAmount;
|
||||||
|
} else if (
|
||||||
|
(costType.description.includes('TS 1') ||
|
||||||
|
costType.description.includes('TSW 1')) &&
|
||||||
|
tour.transportType === 'tsw'
|
||||||
|
) {
|
||||||
|
return costType.costAmount;
|
||||||
|
} else if (
|
||||||
|
(costType.description.includes('TS 2') ||
|
||||||
|
costType.description.includes('TSW 2')) &&
|
||||||
|
tour.transportType === 'TS+T'
|
||||||
|
) {
|
||||||
|
return costType.costAmount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateFeiertagszuschlag(
|
||||||
|
tour: Tour,
|
||||||
|
costType: CostType,
|
||||||
|
): number {
|
||||||
|
if (
|
||||||
|
PUBLIC_HOLIDAYS_HH.some((day) =>
|
||||||
|
isSameDay(
|
||||||
|
new Date(day),
|
||||||
|
new Date(toZonedTime(tour.startDate, 'Europe/Berlin')),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return costType.costAmount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateNachtzuschlag(tour: Tour, costType: CostType): number {
|
||||||
|
const firstInterval = generateIntervalForTimeRange(
|
||||||
|
costType.timeRange,
|
||||||
|
'Europe/Berlin',
|
||||||
|
addDays(tour.startDate, -1),
|
||||||
|
);
|
||||||
|
const secondInterval = generateIntervalForTimeRange(
|
||||||
|
costType.timeRange,
|
||||||
|
'Europe/Berlin',
|
||||||
|
tour.startDate,
|
||||||
|
);
|
||||||
|
if (
|
||||||
|
isWithinInterval(tour.startDate, firstInterval) ||
|
||||||
|
isWithinInterval(tour.startDate, secondInterval)
|
||||||
|
) {
|
||||||
|
return costType.costAmount;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateKilometerzuschlag(
|
||||||
|
tour: Tour,
|
||||||
|
costType: CostType,
|
||||||
|
): number {
|
||||||
|
const kilometerOverLimit = Math.ceil(tour.occupiedKm - costType.kmInclusive);
|
||||||
|
if (kilometerOverLimit <= 0) return 0;
|
||||||
|
return costType.costAmount * kilometerOverLimit;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateFahrzeugzuschlag(
|
||||||
|
tour: Tour,
|
||||||
|
costType: CostType,
|
||||||
|
): number {
|
||||||
|
const isCarNameSupported = !costType.excludeCarNames.includes(tour.carName);
|
||||||
|
if (!isCarNameSupported) return 0;
|
||||||
|
return costType.costAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function calculateWochenendzuschlag(
|
||||||
|
tour: Tour,
|
||||||
|
costType: CostType,
|
||||||
|
): number {
|
||||||
|
if (isWeekend(toZonedTime(tour.startDate, 'Europe/Berlin')))
|
||||||
|
return costType.costAmount;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -9,4 +9,4 @@ import { PersonnelCostCalculationService } from './personnel-cost-calculation.se
|
||||||
providers: [CalculationService, PersonnelCostCalculationService],
|
providers: [CalculationService, PersonnelCostCalculationService],
|
||||||
exports: [CalculationService],
|
exports: [CalculationService],
|
||||||
})
|
})
|
||||||
export class FeatureCalculationModule {}
|
export class FeatCalculationModule {}
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ export class TourLoader {
|
||||||
|
|
||||||
private static tourStagingToFinal(tour: TourStaging, ticketId?: string) {
|
private static tourStagingToFinal(tour: TourStaging, ticketId?: string) {
|
||||||
const {
|
const {
|
||||||
|
id,
|
||||||
done,
|
done,
|
||||||
direction,
|
direction,
|
||||||
carName,
|
carName,
|
||||||
|
|
@ -96,6 +97,7 @@ export class TourLoader {
|
||||||
} = tour;
|
} = tour;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
id,
|
||||||
done,
|
done,
|
||||||
direction,
|
direction,
|
||||||
carName,
|
carName,
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,13 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { PrismaModule } from 'src/core/database/prisma.module';
|
import { PrismaModule } from 'src/core/database/prisma.module';
|
||||||
import { FeatureCalculationModule } from 'src/modules/feat-calculation/feat-calculation.module';
|
import { FeatCalculationModule } from 'src/modules/feat-calculation/feat-calculation.module';
|
||||||
import { DataAccessExtractorsModule } from '../feat-extractors/data-access-extractors.module';
|
import { DataAccessExtractorsModule } from '../feat-extractors/data-access-extractors.module';
|
||||||
import { ToursCalculationAdapater } from './adapter/tours-calculation.adapter';
|
import { ToursCalculationAdapater } from './adapter/tours-calculation.adapter';
|
||||||
import { StagingRepository } from './staging.repository';
|
import { StagingRepository } from './staging.repository';
|
||||||
import { TransformersService } from './transformers.service';
|
import { TransformersService } from './transformers.service';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [PrismaModule, DataAccessExtractorsModule, FeatureCalculationModule],
|
imports: [PrismaModule, DataAccessExtractorsModule, FeatCalculationModule],
|
||||||
providers: [TransformersService, StagingRepository, ToursCalculationAdapater],
|
providers: [TransformersService, StagingRepository, ToursCalculationAdapater],
|
||||||
exports: [TransformersService],
|
exports: [TransformersService],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,9 @@ export class PatientTransformer {
|
||||||
public static transform(patientSource: PatientSource): PatientPreStaging {
|
public static transform(patientSource: PatientSource): PatientPreStaging {
|
||||||
const patientParsed = {
|
const patientParsed = {
|
||||||
...patientSource,
|
...patientSource,
|
||||||
surname: faker.person.lastName(),
|
// surname: faker.person.lastName(),
|
||||||
name: faker.person.firstName(),
|
// name: faker.person.firstName(),
|
||||||
street: faker.location.streetAddress(),
|
// street: faker.location.streetAddress(),
|
||||||
...transformProperties(parseDateTime('yyyy-MM-dd'))(
|
...transformProperties(parseDateTime('yyyy-MM-dd'))(
|
||||||
pick(['birthday'])(patientSource),
|
pick(['birthday'])(patientSource),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ export class PlannedTimeTransformer {
|
||||||
): PlannedTimePreStaging {
|
): PlannedTimePreStaging {
|
||||||
const plannedTimeParsed = {
|
const plannedTimeParsed = {
|
||||||
...plannedTimeSource,
|
...plannedTimeSource,
|
||||||
firstname: faker.person.firstName(),
|
// firstname: faker.person.firstName(),
|
||||||
surname: faker.person.lastName(),
|
// surname: faker.person.lastName(),
|
||||||
...transformProperties(parseInteger)(
|
...transformProperties(parseInteger)(
|
||||||
pick(['id', 'user_id'])(plannedTimeSource),
|
pick(['id', 'user_id'])(plannedTimeSource),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -16,16 +16,16 @@ export class TourTransformer {
|
||||||
public static transform(tourSource: TourSource): TourPreStaging {
|
public static transform(tourSource: TourSource): TourPreStaging {
|
||||||
const tourParsed = {
|
const tourParsed = {
|
||||||
...tourSource,
|
...tourSource,
|
||||||
operationId: faker.number.int({ min: 10000, max: 19999 }),
|
// operationId: faker.number.int({ min: 10000, max: 19999 }),
|
||||||
driverName: faker.person.fullName(),
|
// driverName: faker.person.fullName(),
|
||||||
codriverName: faker.person.fullName(),
|
// codriverName: faker.person.fullName(),
|
||||||
patientName: faker.person.firstName(),
|
// patientName: faker.person.firstName(),
|
||||||
patientSurname: faker.person.lastName(),
|
// patientSurname: faker.person.lastName(),
|
||||||
patientStreet: faker.location.streetAddress(),
|
// patientStreet: faker.location.streetAddress(),
|
||||||
startInstitution: faker.company.name(),
|
// startInstitution: faker.company.name(),
|
||||||
startStreet: faker.location.streetAddress(),
|
// startStreet: faker.location.streetAddress(),
|
||||||
targetInstitution: faker.company.name(),
|
// targetInstitution: faker.company.name(),
|
||||||
targetStreet: faker.location.streetAddress(),
|
// targetStreet: faker.location.streetAddress(),
|
||||||
...transformProperties(parseInteger)(pick(['check'])(tourSource)),
|
...transformProperties(parseInteger)(pick(['check'])(tourSource)),
|
||||||
...transformProperties(parseFloat)(
|
...transformProperties(parseFloat)(
|
||||||
pick(['besetztKm', 'gesamtPreis'])(tourSource),
|
pick(['besetztKm', 'gesamtPreis'])(tourSource),
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@ export class WorkTimeTransformer {
|
||||||
public static transform(workTimeSource: WorkTimeSource): WorkTimePreStaging {
|
public static transform(workTimeSource: WorkTimeSource): WorkTimePreStaging {
|
||||||
const workTimeParsed = {
|
const workTimeParsed = {
|
||||||
...workTimeSource,
|
...workTimeSource,
|
||||||
firstname: faker.person.firstName(),
|
// firstname: faker.person.firstName(),
|
||||||
surname: faker.person.lastName(),
|
// surname: faker.person.lastName(),
|
||||||
...transformProperties(parseInteger)(
|
...transformProperties(parseInteger)(
|
||||||
pick(['id', 'user_id'])(workTimeSource),
|
pick(['id', 'user_id'])(workTimeSource),
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { PrismaModule } from 'src/core/database/prisma.module';
|
||||||
|
import { FeatBusinessObjectsModule } from '../feat-business-objects/feat-business-objects.module';
|
||||||
|
import { FeatCalculationModule } from '../feat-calculation/feat-calculation.module';
|
||||||
|
import { KpiResolver } from './kpi.resolver';
|
||||||
|
import { KpiService } from './kpi.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [PrismaModule, FeatBusinessObjectsModule, FeatCalculationModule],
|
||||||
|
providers: [KpiService, KpiResolver],
|
||||||
|
exports: [KpiService],
|
||||||
|
})
|
||||||
|
export class FeatKpiModule {}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { Args, Query, Resolver } from '@nestjs/graphql';
|
||||||
|
import { TicketValidationState } from '@prisma/client';
|
||||||
|
import { KpiService } from './kpi.service';
|
||||||
|
import { KpiInfoObjectType } from './object-types';
|
||||||
|
|
||||||
|
@Resolver()
|
||||||
|
export class KpiResolver {
|
||||||
|
constructor(private readonly kpiService: KpiService) {}
|
||||||
|
|
||||||
|
@Query(() => KpiInfoObjectType)
|
||||||
|
ticketStateKpi(
|
||||||
|
@Args('state', { type: () => TicketValidationState })
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
) {
|
||||||
|
return this.kpiService.ticketStateKpi(ticketState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Query(() => KpiInfoObjectType)
|
||||||
|
managerKpi(
|
||||||
|
@Args('ordinanceType', { type: () => String }) ordinanceType: string,
|
||||||
|
) {
|
||||||
|
return this.kpiService.managerKpi(ordinanceType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,384 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApprovalState,
|
||||||
|
Prisma,
|
||||||
|
TicketValidationState,
|
||||||
|
Tour,
|
||||||
|
} from '@prisma/client';
|
||||||
|
import {
|
||||||
|
add,
|
||||||
|
differenceInHours,
|
||||||
|
differenceInMinutes,
|
||||||
|
set,
|
||||||
|
subDays,
|
||||||
|
subHours,
|
||||||
|
subMonths,
|
||||||
|
} from 'date-fns';
|
||||||
|
import { PrismaService } from 'src/core/database/prisma.service';
|
||||||
|
import { generateIntervalForTimeRange } from 'src/utils';
|
||||||
|
import { CarEntityService } from '../feat-business-objects/car-entity/car-entity.service';
|
||||||
|
import { CalculationService } from '../feat-calculation/calculation.service';
|
||||||
|
import { KpiInfoObjectType, MetricObjectType } from './object-types';
|
||||||
|
|
||||||
|
function ticketWithCurrentStateAndTours(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Prisma.TicketWhereInput {
|
||||||
|
return { currentState: ticketState, tours: { some: { check: 0 } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
function dateFromTime(timeString: string): Date {
|
||||||
|
const [hours, minutes] = timeString.split(':');
|
||||||
|
return set(new Date(), {
|
||||||
|
hours: parseInt(hours),
|
||||||
|
minutes: parseInt(minutes),
|
||||||
|
seconds: 0,
|
||||||
|
milliseconds: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class KpiService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly carEntity: CarEntityService,
|
||||||
|
private readonly calculationService: CalculationService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async calcCityTourAvgTime(ordinanceType: string) {
|
||||||
|
const tourTimestamps = await this.prisma.tour.findMany({
|
||||||
|
select: {
|
||||||
|
startBegin: true,
|
||||||
|
startEnd: true,
|
||||||
|
target: true,
|
||||||
|
targetBegin: true,
|
||||||
|
empty: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...(ordinanceType !== 'ALL'
|
||||||
|
? {
|
||||||
|
ordinanceType: {
|
||||||
|
contains: ordinanceType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
done: true,
|
||||||
|
totalKm: {
|
||||||
|
lte: 17,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: 5000,
|
||||||
|
});
|
||||||
|
const tourTimes = tourTimestamps
|
||||||
|
.filter((timings) => Object.values(timings).filter(Boolean).length > 0)
|
||||||
|
.map((timings) => {
|
||||||
|
const times = Object.values(timings).filter(Boolean).sort();
|
||||||
|
const startTime = times.at(0);
|
||||||
|
const endTime = times.at(-1);
|
||||||
|
return differenceInMinutes(
|
||||||
|
dateFromTime(endTime),
|
||||||
|
dateFromTime(startTime),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return tourTimes.reduce((sum, cur) => (sum += cur), 0) / tourTimes.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
async calcCityTourAvgRevenue(ordinanceType: string) {
|
||||||
|
const tourRevenues = await this.prisma.tour.findMany({
|
||||||
|
select: {
|
||||||
|
revenue: true,
|
||||||
|
},
|
||||||
|
where: {
|
||||||
|
...(ordinanceType !== 'ALL'
|
||||||
|
? {
|
||||||
|
ordinanceType: {
|
||||||
|
contains: ordinanceType,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {}),
|
||||||
|
done: true,
|
||||||
|
totalKm: {
|
||||||
|
lte: 17,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: 5000,
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
tourRevenues.reduce((sum, { revenue }) => (sum += revenue), 0) /
|
||||||
|
tourRevenues.length
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
upcomingTours(): Promise<Tour[]> {
|
||||||
|
const now = new Date();
|
||||||
|
const endUpcoming = add(now, { minutes: 20 });
|
||||||
|
return this.prisma.tour.findMany({
|
||||||
|
where: {
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
startDate: {
|
||||||
|
gte: subHours(now, 4),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startDate: {
|
||||||
|
lt: endUpcoming,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
startBegin: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
carName: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Storno',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Löschen',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
carName: {
|
||||||
|
not: 'Abgabe',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
AND: [
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Pause
|
||||||
|
not: 'b7424477-a4b2-42f7-b7e7-a4bef24f6979',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
patientId: {
|
||||||
|
// Feierabend
|
||||||
|
not: '3554e1a9-9c07-45e5-b53c-692a77e4fa9a',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deletedAt: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async managerKpi(ordinanceType: string): Promise<KpiInfoObjectType> {
|
||||||
|
const avgRevenueCityTour = await this.calcCityTourAvgRevenue(ordinanceType);
|
||||||
|
const avgCityTourTimeInMinutes =
|
||||||
|
await this.calcCityTourAvgTime(ordinanceType);
|
||||||
|
const forDay = subDays(new Date(), 0);
|
||||||
|
const cars = await this.carEntity.getCarsForOperationDay(
|
||||||
|
forDay,
|
||||||
|
ordinanceType,
|
||||||
|
);
|
||||||
|
const workdayInterval = generateIntervalForTimeRange(
|
||||||
|
'05:00-04:59',
|
||||||
|
'Europe/Berlin',
|
||||||
|
forDay,
|
||||||
|
);
|
||||||
|
|
||||||
|
// console.log(cars.map(car => ))
|
||||||
|
|
||||||
|
const workCapacity = cars
|
||||||
|
.map((car) => car.availableTimeInInterval(workdayInterval))
|
||||||
|
.reduce((sum, cur) => (sum += cur), 0);
|
||||||
|
|
||||||
|
const tourAmount = cars
|
||||||
|
.map((car) => car.tourAmountInInterval(workdayInterval))
|
||||||
|
.reduce((sum, cur) => (sum += cur), 0);
|
||||||
|
|
||||||
|
const possibleRevenue =
|
||||||
|
((workCapacity * 60) / avgCityTourTimeInMinutes) * avgRevenueCityTour;
|
||||||
|
const carRevenues = await Promise.all([
|
||||||
|
...cars.map((car) =>
|
||||||
|
car.breakEvenInInterval(workdayInterval, this.calculationService),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
const totalRevenue = carRevenues.reduce(
|
||||||
|
(sum, { toursRevenue }) => (sum += toursRevenue),
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
main: {
|
||||||
|
label: 'Realisierbarer Umsatz',
|
||||||
|
value: Math.floor(
|
||||||
|
(100 / (possibleRevenue == 0 ? 1 : possibleRevenue)) * totalRevenue,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
label: 'Realisierbare Touren',
|
||||||
|
value:
|
||||||
|
possibleRevenue > totalRevenue
|
||||||
|
? (possibleRevenue - totalRevenue) / avgRevenueCityTour
|
||||||
|
: 0,
|
||||||
|
},
|
||||||
|
additional: [
|
||||||
|
{
|
||||||
|
label: 'Anzahl Touren',
|
||||||
|
value: tourAmount,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Kapazität (in h)',
|
||||||
|
value: workCapacity,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '⌀ Zeit Stadttour (in min)',
|
||||||
|
value: avgCityTourTimeInMinutes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '⌀ Umsatz Stadttour',
|
||||||
|
value: avgRevenueCityTour,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async ticketStateKpi(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<KpiInfoObjectType> {
|
||||||
|
return {
|
||||||
|
main: await this.fastMovingTicketPercentMetric(ticketState),
|
||||||
|
secondary: await this.averageTimePerStateMetric(ticketState),
|
||||||
|
additional: [
|
||||||
|
await this.ticketCountPerStateMetric(ticketState),
|
||||||
|
...(ticketState === TicketValidationState.USER_INTERACTION_REQUIRED
|
||||||
|
? [await this.ticketApprovalRequiredCountMetric(ticketState)]
|
||||||
|
: []),
|
||||||
|
await this.tourCountPerTicketStateMetric(ticketState),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async fastMovingTicketPercentMetric(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<MetricObjectType> {
|
||||||
|
const ticketsGotState24HoursAgo = await this.prisma.ticket.findMany({
|
||||||
|
where: {
|
||||||
|
state: {
|
||||||
|
some: {
|
||||||
|
state: ticketState,
|
||||||
|
createdAt: {
|
||||||
|
gte: subHours(new Date(), 24),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const ticketsMoved = ticketsGotState24HoursAgo.filter(
|
||||||
|
({ currentState }) => currentState !== ticketState,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: 'Neue Tickets bearbeitet innerhalb von 24 Stunden',
|
||||||
|
value:
|
||||||
|
ticketsMoved.length === 0
|
||||||
|
? 0
|
||||||
|
: Math.floor(
|
||||||
|
(100 / ticketsGotState24HoursAgo.length) * ticketsMoved.length,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async averageTimePerStateMetric(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<MetricObjectType> {
|
||||||
|
const movedTickets = await this.prisma.ticket.findMany({
|
||||||
|
where: {
|
||||||
|
currentState: {
|
||||||
|
not: ticketState,
|
||||||
|
},
|
||||||
|
state: {
|
||||||
|
some: {
|
||||||
|
state: ticketState,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
gte: subMonths(new Date(), 3),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
state: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const timePerTickets = movedTickets
|
||||||
|
.map(({ state }) => {
|
||||||
|
const statesSorted = [...state].sort(
|
||||||
|
(a, b) =>
|
||||||
|
new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime(),
|
||||||
|
);
|
||||||
|
const currentStateIdx = statesSorted.findIndex(
|
||||||
|
(s) => s.state === ticketState,
|
||||||
|
);
|
||||||
|
if (currentStateIdx === -1) return;
|
||||||
|
// if (statesSorted.length - 1 === currentStateIdx) return;
|
||||||
|
return differenceInHours(
|
||||||
|
new Date(statesSorted.at(currentStateIdx + 1).createdAt),
|
||||||
|
new Date(statesSorted.at(currentStateIdx).createdAt),
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.filter((v) => !!v);
|
||||||
|
if (timePerTickets.length === 0) {
|
||||||
|
return { label: '⌀ Verweildauer (in h)', value: 0 };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: '⌀ Verweildauer (in h)',
|
||||||
|
value:
|
||||||
|
timePerTickets.reduce((sum, cur) => (sum += cur), 0) /
|
||||||
|
timePerTickets.length,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ticketCountPerStateMetric(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<MetricObjectType> {
|
||||||
|
const count = await this.prisma.ticket.count({
|
||||||
|
where: { ...ticketWithCurrentStateAndTours(ticketState) },
|
||||||
|
});
|
||||||
|
return { label: 'Tickets', value: count };
|
||||||
|
}
|
||||||
|
|
||||||
|
private async ticketApprovalRequiredCountMetric(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<MetricObjectType> {
|
||||||
|
const count = await this.prisma.ticket.count({
|
||||||
|
where: {
|
||||||
|
...ticketWithCurrentStateAndTours(ticketState),
|
||||||
|
approvalState: ApprovalState.REQUIRED,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return { label: 'Genehmigungspflicht', value: count };
|
||||||
|
}
|
||||||
|
|
||||||
|
private async tourCountPerTicketStateMetric(
|
||||||
|
ticketState: TicketValidationState,
|
||||||
|
): Promise<MetricObjectType> {
|
||||||
|
const count = await this.prisma.tour.count({
|
||||||
|
where: {
|
||||||
|
Ticket: {
|
||||||
|
...ticketWithCurrentStateAndTours(ticketState),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
label: 'Touren',
|
||||||
|
value: count,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { Field, Float, ObjectType } from '@nestjs/graphql';
|
||||||
|
|
||||||
|
@ObjectType('Metric')
|
||||||
|
export class MetricObjectType {
|
||||||
|
@Field()
|
||||||
|
label: string;
|
||||||
|
@Field(() => Float)
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ObjectType('KpiInfo')
|
||||||
|
export class KpiInfoObjectType {
|
||||||
|
@Field(() => MetricObjectType)
|
||||||
|
main: MetricObjectType;
|
||||||
|
@Field(() => MetricObjectType)
|
||||||
|
secondary: MetricObjectType;
|
||||||
|
@Field(() => [MetricObjectType])
|
||||||
|
additional: MetricObjectType[];
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ import {
|
||||||
TicketValidationState,
|
TicketValidationState,
|
||||||
Tour,
|
Tour,
|
||||||
} from '@prisma/client';
|
} from '@prisma/client';
|
||||||
|
import { Interval } from 'date-fns';
|
||||||
|
import { DocumentInfoArgs } from './ticket-arg.types';
|
||||||
import {
|
import {
|
||||||
ValidationResult,
|
ValidationResult,
|
||||||
validatePatient,
|
validatePatient,
|
||||||
|
|
@ -12,8 +14,6 @@ import {
|
||||||
validateTour,
|
validateTour,
|
||||||
} from './ticket-validation.functions';
|
} from './ticket-validation.functions';
|
||||||
import { TicketRepository } from './ticket.repository';
|
import { TicketRepository } from './ticket.repository';
|
||||||
import { DocumentInfoArgs } from './ticket-arg.types';
|
|
||||||
import { Interval } from 'date-fns';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TicketLifecycleService {
|
export class TicketLifecycleService {
|
||||||
|
|
@ -117,6 +117,21 @@ export class TicketLifecycleService {
|
||||||
? ApprovalState.UNKNOWN
|
? ApprovalState.UNKNOWN
|
||||||
: ApprovalState.REQUIRED,
|
: ApprovalState.REQUIRED,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
// NOTE: Condition required to get these tickets out of USER_INTERACTION_REQUIRED
|
||||||
|
if (
|
||||||
|
ticket.tours.every(
|
||||||
|
({ ordinanceType }) =>
|
||||||
|
ordinanceType.indexOf('BTW') > -1 ||
|
||||||
|
ordinanceType.indexOf('TSW') > -1,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
await this.repository.updateTicket(ticket.id, {
|
||||||
|
errors,
|
||||||
|
notes,
|
||||||
|
state: TicketValidationState.TD_UNCERTAIN,
|
||||||
|
approvalState: ApprovalState.FREE,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
await this.repository.updateTicket(ticket.id, {
|
await this.repository.updateTicket(ticket.id, {
|
||||||
errors,
|
errors,
|
||||||
|
|
@ -125,6 +140,7 @@ export class TicketLifecycleService {
|
||||||
approvalState: ApprovalState.FREE,
|
approvalState: ApprovalState.FREE,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.repository.setActionablesOnTicket(ticket.id, errors, notes);
|
await this.repository.setActionablesOnTicket(ticket.id, errors, notes);
|
||||||
}
|
}
|
||||||
|
|
@ -210,6 +226,14 @@ export class TicketLifecycleService {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async checkTourApprovalRequirement(tour: Tour): Promise<boolean> {
|
private async checkTourApprovalRequirement(tour: Tour): Promise<boolean> {
|
||||||
|
// NOTE: Requirement by Avicenna
|
||||||
|
if (
|
||||||
|
tour.ordinanceType.indexOf('BTW') > -1 ||
|
||||||
|
tour.ordinanceType.indexOf('TSW') > -1
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
[
|
[
|
||||||
'Einweisung',
|
'Einweisung',
|
||||||
|
|
|
||||||
|
|
@ -27,15 +27,15 @@ export function validatePatient(patient: Patient): ValidationResult {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
errors.push('Patient: Krankenkasse ist nicht gefüllt');
|
errors.push('Patient: Krankenkasse ist nicht gefüllt');
|
||||||
}
|
}
|
||||||
if (!patient?.careDegree) {
|
// if (!patient?.careDegree) {
|
||||||
notes.push('Patient: Pflegegrad ist nicht gefüllt');
|
// notes.push('Patient: Pflegegrad ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
if (!patient?.disabilityMark1) {
|
// if (!patient?.disabilityMark1) {
|
||||||
notes.push('Patient: Merkzeichen 1 ist nicht gefüllt');
|
// notes.push('Patient: Merkzeichen 1 ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
if (!patient?.disabilityMark2) {
|
// if (!patient?.disabilityMark2) {
|
||||||
notes.push('Patient: Merkzeichen 2 ist nicht gefüllt');
|
// notes.push('Patient: Merkzeichen 2 ist nicht gefüllt');
|
||||||
}
|
// }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: isValid,
|
result: isValid,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
|
||||||
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
||||||
|
import { resourceFromAttributes } from '@opentelemetry/resources';
|
||||||
|
import * as opentelemetry from '@opentelemetry/sdk-node';
|
||||||
|
import {
|
||||||
|
ATTR_SERVICE_NAME,
|
||||||
|
ATTR_SERVICE_VERSION,
|
||||||
|
} from '@opentelemetry/semantic-conventions';
|
||||||
|
|
||||||
|
// Configure the SDK to export telemetry data to the console
|
||||||
|
// Enable all auto-instrumentations from the meta package
|
||||||
|
const exporterOptions = {
|
||||||
|
url: process.env.TRACE_EXPORTER_URL ?? 'http://localhost:4318/v1/traces',
|
||||||
|
};
|
||||||
|
|
||||||
|
const traceExporter = new OTLPTraceExporter(exporterOptions);
|
||||||
|
const sdk = new opentelemetry.NodeSDK({
|
||||||
|
traceExporter,
|
||||||
|
instrumentations: [getNodeAutoInstrumentations()],
|
||||||
|
resource: resourceFromAttributes({
|
||||||
|
[ATTR_SERVICE_NAME]: process.env.OTLP_SERVICE_NAME ?? 'avicenna.data-connector',
|
||||||
|
// [ATTR_SERVICE_NAMESPACE]: 'avicenna',
|
||||||
|
[ATTR_SERVICE_VERSION]: '1.0',
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
sdk.start();
|
||||||
|
|
||||||
|
// gracefully shut down the SDK on process exit
|
||||||
|
process.on('SIGTERM', () => {
|
||||||
|
sdk
|
||||||
|
.shutdown()
|
||||||
|
.then(() => console.log('Tracing terminated'))
|
||||||
|
.catch((error) => console.log('Error terminating tracing', error))
|
||||||
|
.finally(() => process.exit(0));
|
||||||
|
});
|
||||||
|
|
||||||
|
export default sdk;
|
||||||
Loading…
Reference in New Issue