first commit
Docker-Images bauen und veroeffentlichen / build-and-push (push) Failing after 1m32s

This commit is contained in:
Nick Szaunig committed 2026-09-27 06:06:56 +02:00
commit 59bb5a38a3
132 files changed
+19435

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
/**
* Verbindliche Preislogik (Briefing Abschnitt 5). Reine Funktionen ohne
* Datenbank — Grundlage fuer Anzeige, Checkout und Tests.
*
* Grundpreis 2.750,00 €
* Early Bird (-20 %) 2.200,00 € 01.11.2026 00:00 – 31.12.2026 24:00 Berlin
* Regulaer 2.750,00 € ab 01.01.2027 00:00 Berlin
* Stammmodel-Code weitere -10 % auf den jeweils gueltigen Preis
* (Early Bird 1.980,00 € = -28 % gesamt, Regulaer 2.475,00 €)
*/
import { assertCents } from "./money";
import { berlinToUtc } from "./time";
export type PricePhase = "PREVIEW" | "EARLY_BIRD" | "REGULAR";
export type SellablePhase = Exclude<PricePhase, "PREVIEW">;
export const BASE_PRICE_CENTS = 275_000;
export const EARLY_BIRD_DISCOUNT_PERCENT = 20;
export const STAMMMODEL_DISCOUNT_PERCENT = 10;
/** 01.11.2026 00:00 Europe/Berlin */
export const EARLY_BIRD_START = berlinToUtc(2026, 11, 1);
/** 01.01.2027 00:00 Europe/Berlin — Ende Early Bird, Beginn Regulaer. */
export const REGULAR_START = berlinToUtc(2027, 1, 1);
export function pricePhaseAt(now: Date): PricePhase {
const t = now.getTime();
if (t < EARLY_BIRD_START.getTime()) return "PREVIEW";
if (t < REGULAR_START.getTime()) return "EARLY_BIRD";
return "REGULAR";
}
export type PriceBreakdown = {
phase: SellablePhase;
basePriceCents: number;
phaseDiscountCents: number;
/** Preis nach Phasenrabatt, vor Gutschein. */
phasePriceCents: number;
voucherDiscountCents: number;
totalCents: number;
hasStammmodelCode: boolean;
};
export function calculatePrice(phase: SellablePhase, hasStammmodelCode: boolean): PriceBreakdown {
const basePriceCents = BASE_PRICE_CENTS;
const phaseDiscountCents = phase === "EARLY_BIRD" ? percentOf(basePriceCents, EARLY_BIRD_DISCOUNT_PERCENT) : 0;
const phasePriceCents = basePriceCents - phaseDiscountCents;
// Der Code wirkt NACH dem Phasenrabatt auf den dann gueltigen Preis
// (multiplikativ, nicht additiv: 28 % statt 30 % im Early Bird).
const voucherDiscountCents = hasStammmodelCode ? percentOf(phasePriceCents, STAMMMODEL_DISCOUNT_PERCENT) : 0;
const totalCents = phasePriceCents - voucherDiscountCents;
assertCents(totalCents);
return {
phase,
basePriceCents,
phaseDiscountCents,
phasePriceCents,
voucherDiscountCents,
totalCents,
hasStammmodelCode,
};
}
/**
* Prozentanteil in ganzen Cent. Die vorgegebenen Preise ergeben immer glatte
* Cent-Betraege; alles andere waere ein Konfigurationsfehler und bricht ab,
* statt still zu runden.
*/
function percentOf(cents: number, percent: number): number {
const raw = cents * percent;
if (raw % 100 !== 0) {
throw new Error(`Rabatt ergibt keinen glatten Cent-Betrag: ${cents} × ${percent} %`);
}
return raw / 100;
}
export const PHASE_LABEL: Record<PricePhase, string> = {
PREVIEW: "Vorschau",
EARLY_BIRD: "Early Bird",
REGULAR: "Regulär",
};