diff --git a/app/domains/fluggastrechte/services/pdf/sections/__test__/getFullPlaintiffName.test.ts b/app/domains/fluggastrechte/services/pdf/sections/__test__/getFullPlaintiffName.test.ts index 568f4232e..f34f37757 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/__test__/getFullPlaintiffName.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/__test__/getFullPlaintiffName.test.ts @@ -1,49 +1,26 @@ -import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; import { getFullPlaintiffName } from "../getFullPlaintiffName"; describe("getFullPlaintiffName", () => { it("should return the full plaintiff name given the vorname and nachname", () => { - const userData: FluggastrechtContext = { - vorname: "Test", - nachname: "Test", - }; - - const actual = getFullPlaintiffName(userData); + const actual = getFullPlaintiffName(undefined, undefined, "Test", "Test"); expect(actual).toEqual("Test Test"); }); it("should return the full plaintiff name with capitalized vorname given the vorname lower case and nachname", () => { - const userData: FluggastrechtContext = { - vorname: "test", - nachname: "Test", - }; - - const actual = getFullPlaintiffName(userData); + const actual = getFullPlaintiffName(undefined, undefined, "test", "Test"); expect(actual).toEqual("Test Test"); }); it("should return the full plaintiff name given the title, vorname and nachname", () => { - const userData: FluggastrechtContext = { - title: "dr", - vorname: "Test", - nachname: "Test", - }; - - const actual = getFullPlaintiffName(userData); + const actual = getFullPlaintiffName(undefined, "dr", "Test", "Test"); expect(actual).toEqual("Dr. Test Test"); }); it("should return the full plaintiff name given the anrede, vorname and nachname", () => { - const userData: FluggastrechtContext = { - anrede: "Herr", - vorname: "Test", - nachname: "Test", - }; - - const actual = getFullPlaintiffName(userData); + const actual = getFullPlaintiffName("Herr", undefined, "Test", "Test"); expect(actual).toEqual("Herr Test Test"); }); diff --git a/app/domains/fluggastrechte/services/pdf/sections/firstPage/__test__/createStatementClaim.test.ts b/app/domains/fluggastrechte/services/pdf/sections/firstPage/__test__/createStatementClaim.test.ts index d82dbf175..b30187969 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/firstPage/__test__/createStatementClaim.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/firstPage/__test__/createStatementClaim.test.ts @@ -4,7 +4,7 @@ import { mockPdfKitDocumentStructure, } from "tests/factories/mockPdfKit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; -import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { PDF_MARGIN_HORIZONTAL } from "~/services/pdf/createPdfKitDocument"; import { createStatementClaim, @@ -30,9 +30,9 @@ function assertDefendantPartyList( describe("createStatementClaim", () => { beforeEach(() => { vi.mock( - "~/domains/fluggastrechte/services/airports/getCompensationPayment", + "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim", ); - vi.mocked(getCompensationPayment).mockReturnValue("600"); + vi.mocked(getTotalCompensationClaim).mockReturnValue(600); }); afterEach(() => { @@ -52,7 +52,7 @@ describe("createStatementClaim", () => { expect(mockDoc.text).toHaveBeenCalledWith(STATEMENT_CLAIM_TITLE_TEXT); expect(mockDoc.text).toHaveBeenCalledWith(STATEMENT_CLAIM_SUBTITLE_TEXT); - const compensation = getCompensationPayment({ + const compensation = getTotalCompensationClaim({ startAirport: userDataMock.startAirport, endAirport: userDataMock.endAirport, }); @@ -76,7 +76,7 @@ describe("createStatementClaim", () => { const defendantPartyList = getDefendantPartyList( userDataMock.prozesszinsen, - "600", + 600, ); assertDefendantPartyList(mockDoc, defendantPartyList); }); diff --git a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/__test__/addPlannedFlightDetails.test.ts b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/__test__/addPlannedFlightDetails.test.ts index 83c9c3d11..05cd37552 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/__test__/addPlannedFlightDetails.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/__test__/addPlannedFlightDetails.test.ts @@ -3,7 +3,7 @@ import { mockPdfKitDocument, mockPdfKitDocumentStructure, } from "tests/factories/mockPdfKit"; -import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { addPlannedFlightDetails, AFFECTED_FLIGHT_TEXT, @@ -12,7 +12,7 @@ import { PLANNED_DEPARTURE_DATE_TEXT, } from "../addPlannedFlightDetails"; -vi.mock("~/domains/fluggastrechte/services/airports/getCompensationPayment"); +vi.mock("~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"); describe("addPlannedFlightDetails", () => { it("should create document with flight details", () => { @@ -39,15 +39,12 @@ describe("addPlannedFlightDetails", () => { it("should calculate compensation based on start and end airport", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); - const mockCompensation = "400"; - vi.mocked(getCompensationPayment).mockReturnValue(mockCompensation); + const mockCompensation = 400; + vi.mocked(getTotalCompensationClaim).mockReturnValue(mockCompensation); addPlannedFlightDetails(mockDoc, userDataMock); - expect(getCompensationPayment).toHaveBeenCalledWith({ - startAirport: userDataMock.startAirport, - endAirport: userDataMock.endAirport, - }); + expect(getTotalCompensationClaim).toHaveBeenCalled(); expect(mockDoc.text).toHaveBeenCalledWith( `Streitwert: ${mockCompensation}€`, ); diff --git a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlaintiffDetails.ts b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlaintiffDetails.ts index 8eaee773c..8d762b939 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlaintiffDetails.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlaintiffDetails.ts @@ -11,13 +11,22 @@ export const SEPARATOR = " | "; export const addPlaintiffDetails = ( doc: typeof PDFDocument, - userData: FluggastrechtContext, + { + anrede, + title, + vorname, + nachname, + strasseHausnummer, + telefonnummer, + plz, + ort, + }: FluggastrechtContext, ) => { - const plaintiffName = getFullPlaintiffName(userData); - const address = userData.strasseHausnummer ?? ""; - const phoneNumber = userData.telefonnummer ?? ""; - const zipCode = userData.plz ?? ""; - const city = userData.ort ?? ""; + const plaintiffName = getFullPlaintiffName(anrede, title, vorname, nachname); + const address = strasseHausnummer ?? ""; + const phoneNumber = telefonnummer ?? ""; + const zipCode = plz ?? ""; + const city = ort ?? ""; doc .fontSize(10) diff --git a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlannedFlightDetails.ts b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlannedFlightDetails.ts index 1bfcaa6d0..f01e2d5cb 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlannedFlightDetails.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/firstPage/claimData/addPlannedFlightDetails.ts @@ -1,6 +1,6 @@ import type PDFDocument from "pdfkit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; -import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { FONTS_BUNDESSANS_BOLD, FONTS_BUNDESSANS_REGULAR, @@ -17,11 +17,7 @@ export const addPlannedFlightDetails = ( doc: typeof PDFDocument, userData: FluggastrechtContext, ) => { - const { startAirport, endAirport } = userData; - const compensationByDistance = getCompensationPayment({ - startAirport, - endAirport, - }); + const compensationByDistance = getTotalCompensationClaim(userData); doc.fontSize(12).font(FONTS_BUNDESSANS_BOLD).text(DUE_REASON_TEXT); doc.font(FONTS_BUNDESSANS_BOLD).text(AFFECTED_FLIGHT_TEXT); doc.moveDown(0.5); diff --git a/app/domains/fluggastrechte/services/pdf/sections/firstPage/createStatementClaim.ts b/app/domains/fluggastrechte/services/pdf/sections/firstPage/createStatementClaim.ts index 4ebbce516..8b2f90017 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/firstPage/createStatementClaim.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/firstPage/createStatementClaim.ts @@ -1,6 +1,6 @@ import type PDFDocument from "pdfkit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; -import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { FONTS_BUNDESSANS_BOLD, FONTS_BUNDESSANS_REGULAR, @@ -9,7 +9,7 @@ import { export const getDefendantPartyList = ( prozesszinsen: string, - streitwert: string, + streitwert: number, ): Record => { const interestClause = prozesszinsen === "yes" @@ -35,12 +35,8 @@ export const createStatementClaim = ( documentStruct: PDFKit.PDFStructureElement, userData: FluggastrechtContext, ) => { - const { startAirport, endAirport, prozesszinsen, versaeumnisurteil } = - userData; - const compensationByDistance = getCompensationPayment({ - startAirport, - endAirport, - }); + const { prozesszinsen, versaeumnisurteil } = userData; + const compensationByDistance = getTotalCompensationClaim(userData); const defendantPartyList = getDefendantPartyList( prozesszinsen ?? "", diff --git a/app/domains/fluggastrechte/services/pdf/sections/getFullPlaintiffName.ts b/app/domains/fluggastrechte/services/pdf/sections/getFullPlaintiffName.ts index e89521991..5a1a137a8 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/getFullPlaintiffName.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/getFullPlaintiffName.ts @@ -1,9 +1,11 @@ import capitalize from "lodash/capitalize"; -import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; - -export const getFullPlaintiffName = (userData: FluggastrechtContext) => { - const { anrede, title, vorname, nachname } = userData; +export const getFullPlaintiffName = ( + anrede?: string, + title?: "" | "dr", + vorname?: string, + nachname?: string, +) => { const mappedTitle = title === "dr" ? "Dr." : title; const capitalizedVorname = capitalize(vorname); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/addNewPageInCaseMissingVerticalSpace.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/addNewPageInCaseMissingVerticalSpace.ts index 22e0c4ba0..60bdeb7bc 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/addNewPageInCaseMissingVerticalSpace.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/addNewPageInCaseMissingVerticalSpace.ts @@ -5,8 +5,9 @@ const MAX_VERTICAL_SPACE = PDF_HEIGHT_SEIZE - 90; export const addNewPageInCaseMissingVerticalSpace = ( doc: typeof PDFDocument, + extraYPosition = 0, ): void => { - if (doc.y >= MAX_VERTICAL_SPACE) { + if (doc.y + extraYPosition >= MAX_VERTICAL_SPACE) { doc.addPage(); } }; diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addCompensationAmount.test.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addCompensationAmount.test.ts index a371d788e..3677a586f 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addCompensationAmount.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addCompensationAmount.test.ts @@ -16,8 +16,10 @@ import { DEMANDED_COMPENSATION_PAYMENT_TEXT, OTHER_DETAILS_ITINERARY, OTHER_PASSENGERS_DEMANDED_COMPENSATION_PAYMENT_TEXT, + PLAINTIFF_WITNESSES_MULTIPLE_PERSONS_TEXT, PLAINTIFF_WITNESSES_TEXT, } from "../addCompensationAmount"; +import { addMultiplePersonsInfo } from "../addMultiplePersonsInfo"; vi.mock("~/domains/fluggastrechte/services/airports/getCompensationPayment"); vi.mock("~/domains/fluggastrechte/services/airports/getAirportNameByIataCode"); @@ -25,6 +27,7 @@ vi.mock( "~/domains/fluggastrechte/services/airports/calculateDistanceBetweenAirports", ); vi.mock("../../addNewPageInCaseMissingVerticalSpace"); +vi.mock("../addMultiplePersonsInfo"); const distanceValueMock = 100; @@ -137,7 +140,7 @@ describe("addCompensationAmount", () => { const userDataWeiterePersonenMock = { ...userDataMock, - isWeiterePersonen: YesNoAnswer.Enum.yes, + isWeiterePersonen: YesNoAnswer.Enum.no, }; addCompensationAmount(mockDoc, mockStruct, userDataWeiterePersonenMock, 0); @@ -149,18 +152,58 @@ describe("addCompensationAmount", () => { ); }); - it("should have the text for plaintiff witnesses in case the hasZeugen is yes", () => { + it("should have the text distance airport for multiple persons", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonenMock = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.Enum.yes, + }; + + addCompensationAmount(mockDoc, mockStruct, userDataWeiterePersonenMock, 0); + + expect(mockDoc.text).toHaveBeenCalledWith( + `Die Distanz zwischen ${startAirportMock} und ${endAirportMock} beträgt nach Großkreismethode ca. ${distanceValueMock} km. ${ARTICLE_AIR_PASSENGER_REGULATION_TEXT} ${compensationValueMock} € pro Person, insgesamt aus eigenem und abgetretenem Recht damit eine Gesamtsumme von ${compensationValueMock} €.`, + PDF_MARGIN_HORIZONTAL, + undefined, + ); + }); + + it("should have the text for plaintiff witnesses in case the hasZeugen is yes and weitere person is no", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataHasZeugenMock = { + ...userDataMock, + hasZeugen: YesNoAnswer.Enum.yes, + isWeiterePersonen: YesNoAnswer.Enum.no, + }; + + addCompensationAmount(mockDoc, mockStruct, userDataHasZeugenMock, 0); + + expect(mockDoc.text).toHaveBeenCalledWith( + PLAINTIFF_WITNESSES_TEXT, + PDF_MARGIN_HORIZONTAL, + ); + }); + + it("should have the text for plaintiff witnesses for multiple persons in case the hasZeugen and weitere personen is yes", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); const userDataHasZeugenMock = { ...userDataMock, hasZeugen: YesNoAnswer.Enum.yes, + isWeiterePersonen: YesNoAnswer.Enum.yes, }; addCompensationAmount(mockDoc, mockStruct, userDataHasZeugenMock, 0); - expect(mockDoc.text).toHaveBeenCalledWith(PLAINTIFF_WITNESSES_TEXT); + expect(mockDoc.text).toHaveBeenCalledWith( + PLAINTIFF_WITNESSES_MULTIPLE_PERSONS_TEXT, + PDF_MARGIN_HORIZONTAL, + ); }); it("should not have the text for plaintiff witnesses in case the hasZeugen is no", () => { @@ -177,7 +220,7 @@ describe("addCompensationAmount", () => { expect(mockDoc.text).not.toHaveBeenCalledWith(PLAINTIFF_WITNESSES_TEXT); }); - it("should call addNewPageInCaseMissingVerticalSpace three times in case the hasZeugen is yes ", () => { + it("should call addNewPageInCaseMissingVerticalSpace four times in case the hasZeugen is yes ", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); @@ -188,10 +231,24 @@ describe("addCompensationAmount", () => { addCompensationAmount(mockDoc, mockStruct, userDataHasZeugenMock, 0); + expect(addNewPageInCaseMissingVerticalSpace).toBeCalledTimes(4); + }); + + it("should call addNewPageInCaseMissingVerticalSpace three times in case the hasZeugen is no", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataHasZeugenMock = { + ...userDataMock, + hasZeugen: YesNoAnswer.Enum.no, + }; + + addCompensationAmount(mockDoc, mockStruct, userDataHasZeugenMock, 0); + expect(addNewPageInCaseMissingVerticalSpace).toBeCalledTimes(3); }); - it("should call addNewPageInCaseMissingVerticalSpace two times in case the hasZeugen is no", () => { + it("should call addMultiplePersonsInfo once", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); @@ -202,6 +259,6 @@ describe("addCompensationAmount", () => { addCompensationAmount(mockDoc, mockStruct, userDataHasZeugenMock, 0); - expect(addNewPageInCaseMissingVerticalSpace).toBeCalledTimes(2); + expect(addMultiplePersonsInfo).toBeCalledTimes(1); }); }); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addDetailedReason.test.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addDetailedReason.test.ts index 7e5169907..b1319abd0 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addDetailedReason.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addDetailedReason.test.ts @@ -5,11 +5,14 @@ import { } from "tests/factories/mockPdfKit"; import { getAirportNameByIataCode } from "~/domains/fluggastrechte/services/airports/getAirportNameByIataCode"; import { PDF_MARGIN_HORIZONTAL } from "~/services/pdf/createPdfKitDocument"; +import { YesNoAnswer } from "~/services/validation/YesNoAnswer"; import { addDetailedReason, ATTACHMENT_CONFIRM_BOOKING_TEXT, + CONFIRM_BOOKING_MULTIPLE_PERSONS_TEXT, CONFIRM_BOOKING_TEXT, MARGIN_RIGHT, + PLAINTIFF_ON_TIME_MULTIPLE_PERSONS_TEXT, PLAINTIFF_ON_TIME_TEXT, } from "../addDetailedReason"; @@ -47,6 +50,27 @@ describe("addDetailedReason", () => { ); }); + it("should have the text for booking confirm and attachment booking confirm for multiple persons", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataMultiplePersons = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.enum.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataMultiplePersons); + + expect(mockDoc.text).toHaveBeenCalledWith( + CONFIRM_BOOKING_MULTIPLE_PERSONS_TEXT, + ); + + expect(mockDoc.text).toHaveBeenCalledWith( + ATTACHMENT_CONFIRM_BOOKING_TEXT, + PDF_MARGIN_HORIZONTAL + MARGIN_RIGHT, + ); + }); + it("should have the text for plaintiff on time for verspaetet bereich", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); @@ -59,6 +83,23 @@ describe("addDetailedReason", () => { ); }); + it("should have the text for plaintiff on time for verspaetet bereich for multiple persons", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataMultiplePersons = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.enum.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataMultiplePersons); + + expect(mockDoc.text).toHaveBeenCalledWith( + PLAINTIFF_ON_TIME_MULTIPLE_PERSONS_TEXT, + PDF_MARGIN_HORIZONTAL, + ); + }); + it("should not have the text for plaintiff on time for annullierung bereich", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); @@ -93,6 +134,24 @@ describe("addDetailedReason", () => { ); }); + it("should have the text for plaintiff on time for nichtbefoerderung bereich for multiple persons", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataNichtBefoerderungMock = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.enum.yes, + bereich: "nichtbefoerderung", + }; + + addDetailedReason(mockDoc, mockStruct, userDataNichtBefoerderungMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + PLAINTIFF_ON_TIME_MULTIPLE_PERSONS_TEXT, + PDF_MARGIN_HORIZONTAL, + ); + }); + it("should have the text for start and end airport for verspaetet bereich", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); @@ -138,4 +197,128 @@ describe("addDetailedReason", () => { PDF_MARGIN_HORIZONTAL, ); }); + + it("should have the text for following persons given weiter personen and verspaetet bereich", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonenMock = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataWeiterePersonenMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + "Folgende Personen waren von dieser Verspätung betroffen:", + PDF_MARGIN_HORIZONTAL, + ); + }); + + it("should have the text for following persons given weiter personen and annullierung bereich", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonenMock = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + bereich: "annullierung", + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataWeiterePersonenMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + "Folgende Personen waren von dieser Annullierung betroffen:", + PDF_MARGIN_HORIZONTAL, + ); + }); + + it("should have the text for plaintiff name given weiter personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonenMock = { + ...userDataMock, + anrede: undefined, + title: undefined, + vorname: "Test", + nachname: "Test", + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataWeiterePersonenMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + "1. Die klagende Partei Test Test", + expect.anything(), + ); + }); + + it("should have the text for persons names given weiter personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonenMock = { + ...userDataMock, + anrede: undefined, + title: undefined, + vorname: "Test", + nachname: "Test", + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + { + vorname: "vorname2", + nachname: "nachname2", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + buchungsnummer: "123456", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addDetailedReason(mockDoc, mockStruct, userDataWeiterePersonenMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + "2. Vorname nachname, strasseHausnummer, plz, ort", + ); + + expect(mockDoc.text).toHaveBeenCalledWith( + "3. Vorname2 nachname2, strasseHausnummer, plz, ort, abweichende Buchungsnummer: 123456", + ); + }); }); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addMultiplePersonsInfo.test.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addMultiplePersonsInfo.test.ts new file mode 100644 index 000000000..00400a946 --- /dev/null +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addMultiplePersonsInfo.test.ts @@ -0,0 +1,247 @@ +import { userDataMock } from "tests/factories/fluggastrechte/userDataMock"; +import { + mockPdfKitDocument, + mockPdfKitDocumentStructure, +} from "tests/factories/mockPdfKit"; +import { YesNoAnswer } from "~/services/validation/YesNoAnswer"; +import { + addMultiplePersonsInfo, + ATTACHMENT_ASSIGNMENTS_TEXT, + CLAIM_FOLLOWING_PERSONS_TRANSFERER_TEXT, + EVIDENCE_QUESTION_WITNESSES_TEXT, + INFORMATION_BOOKING_AND_ASSIGNMENTS_ANNULLIERUNG_TEXT, + INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT, +} from "../addMultiplePersonsInfo"; + +describe("addMultiplePersonsInfo", () => { + it("should not call any print text given an user data is not weitere personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + addMultiplePersonsInfo(mockDoc, userDataMock); + + expect(mockDoc.text).not.toBeCalled(); + }); + + it("should not call any print text given an user data with empty weitere personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).not.toBeCalled(); + }); + + it("should have the text for CLAIM_FOLLOWING_PERSONS_TRANSFERER_TEXT given an user data with weitere personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + CLAIM_FOLLOWING_PERSONS_TRANSFERER_TEXT, + ); + }); + + it("should have the text for persons names given an user data with weitere personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith("Vorname nachname"); + }); + + it("should have the text for ATTACHMENT_ASSIGNMENTS_TEXT given an user data with weitere personen", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + ATTACHMENT_ASSIGNMENTS_TEXT, + expect.anything(), + ); + }); + + it("should have the text for INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT given an user data with weitere personen, zeugen yes and bereich verspaetet", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + hasZeugen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT, + expect.anything(), + ); + }); + + it("should have the text for INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT given an user data with weitere personen, zeugen yes and bereich nichtbefoerderung", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + hasZeugen: YesNoAnswer.Values.yes, + bereich: "nichtbefoerderung", + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT, + expect.anything(), + ); + }); + + it("should have the text for INFORMATION_BOOKING_AND_ASSIGNMENTS_ANNULLIERUNG_TEXT given an user data with weitere personen, zeugen yes and bereich annullierung", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + hasZeugen: YesNoAnswer.Values.yes, + bereich: "annullierung", + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + INFORMATION_BOOKING_AND_ASSIGNMENTS_ANNULLIERUNG_TEXT, + expect.anything(), + ); + }); + + it("should have the text for EVIDENCE_QUESTION_WITNESSES_TEXT given an user data with weitere personen and zeugen yes", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + hasZeugen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith( + EVIDENCE_QUESTION_WITNESSES_TEXT, + expect.anything(), + ); + }); + + it("should have call text for persons names given an user data with weitere personen and zeugen yes", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataWeiterePersonen = { + ...userDataMock, + weiterePersonen: [ + { + vorname: "vorname", + nachname: "nachname", + strasseHausnummer: "strasseHausnummer", + ort: "ort", + plz: "plz", + }, + ], + isWeiterePersonen: YesNoAnswer.Values.yes, + hasZeugen: YesNoAnswer.Values.yes, + }; + + addMultiplePersonsInfo(mockDoc, userDataWeiterePersonen); + + expect(mockDoc.text).toHaveBeenCalledWith("Vorname nachname"); + }); +}); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addReason.test.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addReason.test.ts index e17d2698b..735d95d85 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addReason.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/__test__/addReason.test.ts @@ -3,6 +3,7 @@ import { mockPdfKitDocument, mockPdfKitDocumentStructure, } from "tests/factories/mockPdfKit"; +import { YesNoAnswer } from "~/services/validation/YesNoAnswer"; import { addReason, ARTICLE_DELAY_CANCEL_TEXT, @@ -11,6 +12,7 @@ import { DELAY_TEXT, NOT_MOVE_TEXT, PASSIVE_VERB_TEXT, + PLAINTIFF_BOOKED_MULTIPLE_PERSONS_TEXT, PLAINTIFF_BOOKED_TEXT, } from "../addReason"; @@ -26,6 +28,25 @@ describe("addReason", () => { }); }); + it("should render document with PLAINTIFF_BOOKED_MULTIPLE_PERSONS_TEXT in case is multiple persons", () => { + const mockStruct = mockPdfKitDocumentStructure(); + const mockDoc = mockPdfKitDocument(mockStruct); + + const userDataMultiplePersonsMock = { + ...userDataMock, + isWeiterePersonen: YesNoAnswer.enum.yes, + }; + + addReason(mockDoc, mockStruct, userDataMultiplePersonsMock); + + expect(mockDoc.text).toHaveBeenCalledWith( + PLAINTIFF_BOOKED_MULTIPLE_PERSONS_TEXT, + { + continued: true, + }, + ); + }); + it("should render document for verspaetet claim", () => { const mockStruct = mockPdfKitDocumentStructure(); const mockDoc = mockPdfKitDocument(mockStruct); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addCompensationAmount.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addCompensationAmount.ts index 7af656d62..ef4112a59 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addCompensationAmount.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addCompensationAmount.ts @@ -1,5 +1,6 @@ import type PDFDocument from "pdfkit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { calculateDistanceBetweenAirportsInKilometers } from "~/domains/fluggastrechte/services/airports/calculateDistanceBetweenAirports"; import { getAirportNameByIataCode } from "~/domains/fluggastrechte/services/airports/getAirportNameByIataCode"; import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; @@ -8,34 +9,54 @@ import { PDF_MARGIN_HORIZONTAL, } from "~/services/pdf/createPdfKitDocument"; import { addNewPageInCaseMissingVerticalSpace } from "../addNewPageInCaseMissingVerticalSpace"; +import { addMultiplePersonsInfo } from "./addMultiplePersonsInfo"; const COMPENSATION_PAYMENT_TEXT = "gemäß Art. 7 der Fluggastrechteverordnung (EG) 261/2004 von der beklagten Partei mit einer Frist zum Datum der Frist ein. Die beklagte Partei hat jedoch bisher keine Zahlung geleistet."; export const DEMANDED_COMPENSATION_PAYMENT_TEXT = `Die klagende Partei forderte außergerichtlich die Ausgleichszahlung ${COMPENSATION_PAYMENT_TEXT}`; -export const OTHER_PASSENGERS_DEMANDED_COMPENSATION_PAYMENT_TEXT = `Die klagende Partei and the other passengers affected, demanded compensation payments out of court ${COMPENSATION_PAYMENT_TEXT}`; +export const OTHER_PASSENGERS_DEMANDED_COMPENSATION_PAYMENT_TEXT = `Die klagende Partei sowie die weiteren betroffenen Fluggäste, forderten außergerichtlich die Ausgleichszahlungen ${COMPENSATION_PAYMENT_TEXT}`; export const OTHER_DETAILS_ITINERARY = "Weitere Angaben zum Reiseverlauf:"; export const ARTICLE_AIR_PASSENGER_REGULATION_TEXT = "Damit ergibt sich nach Art. 7 der Fluggastrechteverordnung (EG) 261/2004 eine Entschädigung in Höhe von"; export const PLAINTIFF_WITNESSES_TEXT = "Zum Beweis dieses Sachverhalt wird die klagende Partei im Prozessverlauf bei Bedarf Zeugen benennen."; +export const PLAINTIFF_WITNESSES_MULTIPLE_PERSONS_TEXT = + "Zum Beweis dieses Sachverhalt wird die klagende Partei im Prozessverlauf bei Bedarf weitere Zeugen benennen."; -const getDistanceText = (startAirport: string, endAirport: string): string => { - const startAirportName = getAirportNameByIataCode(startAirport); - const endAirportName = getAirportNameByIataCode(endAirport); +const getDistanceText = (userData: FluggastrechtContext): string => { + const startAirportName = getAirportNameByIataCode(userData.startAirport); + const endAirportName = getAirportNameByIataCode(userData.endAirport); const distanceKmBetweenAirportsResult = - calculateDistanceBetweenAirportsInKilometers(startAirport, endAirport); + calculateDistanceBetweenAirportsInKilometers( + userData.startAirport, + userData.endAirport, + ); const distanceKmBetweenAirportValue = distanceKmBetweenAirportsResult.isOk ? Math.round(distanceKmBetweenAirportsResult.value) : 0; const compensationAmountValue = getCompensationPayment({ - startAirport: startAirport, - endAirport: endAirport, + startAirport: userData.startAirport, + endAirport: userData.endAirport, }); - return `Die Distanz zwischen ${startAirportName} und ${endAirportName} beträgt nach Großkreismethode ca. ${distanceKmBetweenAirportValue} km. ${ARTICLE_AIR_PASSENGER_REGULATION_TEXT} ${compensationAmountValue} €.`; + const compensationTotalAmountValue = getTotalCompensationClaim(userData); + + const distanceText = `Die Distanz zwischen ${startAirportName} und ${endAirportName} beträgt nach Großkreismethode ca. ${distanceKmBetweenAirportValue} km. ${ARTICLE_AIR_PASSENGER_REGULATION_TEXT} ${compensationAmountValue} €`; + if (userData.isWeiterePersonen === "no") { + return `${distanceText}.`; + } + return `${distanceText} pro Person, insgesamt aus eigenem und abgetretenem Recht damit eine Gesamtsumme von ${compensationTotalAmountValue} €.`; +}; + +// check if should use the current Y position before to use the compensation start y position +const getStartYPosition = ( + compensationStartYPosition: number, + currentYPosition: number, +) => { + return currentYPosition < 150 ? currentYPosition : compensationStartYPosition; }; const addOtherDetailsItinerary = ( @@ -50,7 +71,7 @@ const addOtherDetailsItinerary = ( doc.text( OTHER_DETAILS_ITINERARY, PDF_MARGIN_HORIZONTAL, - compensationStartYPosition, // start to print this text from this line + getStartYPosition(compensationStartYPosition, doc.y), // start to print this text from this line ); doc.text(zusaetzlicheAngaben).moveDown(1); @@ -66,19 +87,13 @@ const getYPositionDistanceText = ( return typeof zusaetzlicheAngaben !== "undefined" && zusaetzlicheAngaben.length > 0 ? doc.y - : compensationStartYPosition; + : getStartYPosition(compensationStartYPosition, doc.y); }; export const addCompensationAmount = ( doc: typeof PDFDocument, documentStruct: PDFKit.PDFStructureElement, - { - zusaetzlicheAngaben, - startAirport, - endAirport, - isWeiterePersonen, - hasZeugen, - }: FluggastrechtContext, + userData: FluggastrechtContext, compensationStartYPosition: number, ) => { const compensationSect = doc.struct("Sect"); @@ -86,21 +101,23 @@ export const addCompensationAmount = ( doc.struct("P", {}, () => { doc.font(FONTS_BUNDESSANS_REGULAR).fontSize(10); + addNewPageInCaseMissingVerticalSpace(doc); + addOtherDetailsItinerary( doc, compensationStartYPosition, - zusaetzlicheAngaben, + userData.zusaetzlicheAngaben, ); addNewPageInCaseMissingVerticalSpace(doc); doc.text( - getDistanceText(startAirport, endAirport), + getDistanceText(userData), PDF_MARGIN_HORIZONTAL, getYPositionDistanceText( doc, compensationStartYPosition, - zusaetzlicheAngaben, + userData.zusaetzlicheAngaben, ), ); @@ -110,14 +127,21 @@ export const addCompensationAmount = ( doc .text( - `${isWeiterePersonen === "no" ? DEMANDED_COMPENSATION_PAYMENT_TEXT : OTHER_PASSENGERS_DEMANDED_COMPENSATION_PAYMENT_TEXT}`, + `${userData.isWeiterePersonen === "no" ? DEMANDED_COMPENSATION_PAYMENT_TEXT : OTHER_PASSENGERS_DEMANDED_COMPENSATION_PAYMENT_TEXT}`, ) .moveDown(1); - if (hasZeugen === "yes") { + addMultiplePersonsInfo(doc, userData); + + if (userData.hasZeugen === "yes") { addNewPageInCaseMissingVerticalSpace(doc); - doc.text(PLAINTIFF_WITNESSES_TEXT).moveDown(1); + doc.text( + `${userData.isWeiterePersonen === "no" ? PLAINTIFF_WITNESSES_TEXT : PLAINTIFF_WITNESSES_MULTIPLE_PERSONS_TEXT}`, + PDF_MARGIN_HORIZONTAL, + ); } + + doc.moveDown(2); }), ); documentStruct.add(compensationSect); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addDetailedReason.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addDetailedReason.ts index 57855e3bc..88222889e 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addDetailedReason.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addDetailedReason.ts @@ -1,19 +1,33 @@ import type PDFDocument from "pdfkit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; import { getAirportNameByIataCode } from "~/domains/fluggastrechte/services/airports/getAirportNameByIataCode"; +import type { FluggastrechtBereichType } from "~/domains/fluggastrechte/vorabcheck/context"; import { FONTS_BUNDESSANS_BOLD, FONTS_BUNDESSANS_REGULAR, PDF_MARGIN_HORIZONTAL, } from "~/services/pdf/createPdfKitDocument"; +import { arrayIsNonEmpty } from "~/util/array"; +import { getFullPlaintiffName } from "../../getFullPlaintiffName"; export const CONFIRM_BOOKING_TEXT = "Eine bestätigte Buchung liegt vor."; +export const CONFIRM_BOOKING_MULTIPLE_PERSONS_TEXT = + "Bestätigte Buchungen der klagenden Partei und der weiteren Fluggäste liegen vor."; export const ATTACHMENT_CONFIRM_BOOKING_TEXT = "Beweis: Anlage Buchungsbestätigung"; export const PLAINTIFF_ON_TIME_TEXT = "Die klagende Partei war pünktlich zum Check-in."; +export const PLAINTIFF_ON_TIME_MULTIPLE_PERSONS_TEXT = + "Die klagende Partei und die weiteren Fluggäste waren pünktlich zum Check-in."; export const MARGIN_RIGHT = 10; +const bereichMappingText = { + verspaetet: "Verspätung", + annullierung: "Annullierung", + nichtbefoerderung: "Nicht-Beförderung", + anderes: "", +} as const; + const getFlightTextByBereich = ({ bereich, startAirport, @@ -30,6 +44,68 @@ const getFlightTextByBereich = ({ return `Die Nicht-Beförderung fand auf dem Flug von ${getAirportNameByIataCode(startAirport)} nach ${getAirportNameByIataCode(endAirport)} statt. Aufgrund der Nicht-Beförderung wurde der Anschlussflug verpasst.`; }; +const getTextBookingNumber = (buchungsnummer?: string) => { + if (typeof buchungsnummer === "undefined" || buchungsnummer.length === 0) { + return ""; + } + + return `, abweichende Buchungsnummer: ${buchungsnummer}`; +}; + +const addMultiplePersonsText = ( + doc: typeof PDFDocument, + userData: FluggastrechtContext, +) => { + if ( + userData.isWeiterePersonen === "no" || + !arrayIsNonEmpty(userData.weiterePersonen) + ) { + return; + } + + doc + .text( + `Folgende Personen waren von dieser ${bereichMappingText[userData.bereich as FluggastrechtBereichType] ?? ""} betroffen:`, + PDF_MARGIN_HORIZONTAL, + ) + .text( + `1. Die klagende Partei ${getFullPlaintiffName(userData.anrede, userData.title, userData.vorname, userData.nachname)}`, + PDF_MARGIN_HORIZONTAL + MARGIN_RIGHT - 5, + ); + + userData.weiterePersonen.forEach( + ( + { + anrede, + title, + nachname, + vorname, + buchungsnummer, + strasseHausnummer, + ort, + plz, + }, + index, + ) => { + doc.text( + `${index + 2}. ${getFullPlaintiffName(anrede, title, vorname, nachname)}, ${strasseHausnummer}, ${plz}, ${ort}${getTextBookingNumber(buchungsnummer)}`, + ); + }, + ); + + doc.moveDown(1); +}; + +const getConfirmationBookingText = ({ + isWeiterePersonen, +}: FluggastrechtContext) => { + if (isWeiterePersonen === "yes") { + return CONFIRM_BOOKING_MULTIPLE_PERSONS_TEXT; + } + + return CONFIRM_BOOKING_TEXT; +}; + export const addDetailedReason = ( doc: typeof PDFDocument, documentStruct: PDFKit.PDFStructureElement, @@ -41,18 +117,24 @@ export const addDetailedReason = ( doc .font(FONTS_BUNDESSANS_REGULAR) .fontSize(10) - .text(CONFIRM_BOOKING_TEXT) + .text(getConfirmationBookingText(userData)) .font(FONTS_BUNDESSANS_BOLD) .text( ATTACHMENT_CONFIRM_BOOKING_TEXT, PDF_MARGIN_HORIZONTAL + MARGIN_RIGHT, ) + .font(FONTS_BUNDESSANS_REGULAR) .moveDown(1); + addMultiplePersonsText(doc, userData); + if (userData.bereich !== "annullierung") { - doc - .font(FONTS_BUNDESSANS_REGULAR) - .text(PLAINTIFF_ON_TIME_TEXT, PDF_MARGIN_HORIZONTAL); + doc.text( + userData.isWeiterePersonen === "no" + ? PLAINTIFF_ON_TIME_TEXT + : PLAINTIFF_ON_TIME_MULTIPLE_PERSONS_TEXT, + PDF_MARGIN_HORIZONTAL, + ); } doc.text(getFlightTextByBereich(userData), PDF_MARGIN_HORIZONTAL); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addMultiplePersonsInfo.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addMultiplePersonsInfo.ts new file mode 100644 index 000000000..7eb80ba91 --- /dev/null +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addMultiplePersonsInfo.ts @@ -0,0 +1,74 @@ +import type PDFDocument from "pdfkit"; +import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; +import { + FONTS_BUNDESSANS_BOLD, + FONTS_BUNDESSANS_REGULAR, + PDF_MARGIN_HORIZONTAL, +} from "~/services/pdf/createPdfKitDocument"; +import { arrayIsNonEmpty } from "~/util/array"; +import { getFullPlaintiffName } from "../../getFullPlaintiffName"; +import { addNewPageInCaseMissingVerticalSpace } from "../addNewPageInCaseMissingVerticalSpace"; + +export const CLAIM_FOLLOWING_PERSONS_TRANSFERER_TEXT = + "Die Ansprüche folgender Personen wurden durch Abtretung gemäß § 398 BGB an die klagende Partei übertragen:"; +export const ATTACHMENT_ASSIGNMENTS_TEXT = "Beweis: Anlage Abtretungen"; +const INFORMATION_BOOKING_AND_ASSIGNMENTS_SECOND_PART_TEXT = + "Reiseverlauf, Beteiligung der genannten Personen und Abtretungen wird für den Fall des Bestreitens"; +export const INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT = `Für sämtliche Angaben, insbesondere zu Buchungen, Check-in, Boarding, ${INFORMATION_BOOKING_AND_ASSIGNMENTS_SECOND_PART_TEXT}`; +export const INFORMATION_BOOKING_AND_ASSIGNMENTS_ANNULLIERUNG_TEXT = `Für sämtliche Angaben, insbesondere zu Buchungen, ${INFORMATION_BOOKING_AND_ASSIGNMENTS_SECOND_PART_TEXT}`; +export const EVIDENCE_QUESTION_WITNESSES_TEXT = + "Beweis angeboten durch Vernehmung der folgenden Personen als Zeugen:"; + +export const MARGIN_RIGHT = 10; + +export const addMultiplePersonsInfo = ( + doc: typeof PDFDocument, + { + isWeiterePersonen, + weiterePersonen, + hasZeugen, + bereich, + }: FluggastrechtContext, +) => { + if (isWeiterePersonen === "no" || !arrayIsNonEmpty(weiterePersonen)) { + return; + } + + addNewPageInCaseMissingVerticalSpace(doc); + + const personsNames = weiterePersonen + .flatMap(({ anrede, title, nachname, vorname }) => { + return `${getFullPlaintiffName(anrede, title, vorname, nachname)}`; + }) + .join(", "); + + doc + .text(CLAIM_FOLLOWING_PERSONS_TRANSFERER_TEXT) + .text(personsNames) + .font(FONTS_BUNDESSANS_BOLD) + .moveDown(0.5) + .text(ATTACHMENT_ASSIGNMENTS_TEXT, PDF_MARGIN_HORIZONTAL + MARGIN_RIGHT) + .font(FONTS_BUNDESSANS_REGULAR); + + addNewPageInCaseMissingVerticalSpace(doc); + + if (hasZeugen === "yes") { + doc + .moveDown(1) + .text( + bereich === "annullierung" + ? INFORMATION_BOOKING_AND_ASSIGNMENTS_ANNULLIERUNG_TEXT + : INFORMATION_BOOKING_AND_ASSIGNMENTS_TEXT, + PDF_MARGIN_HORIZONTAL, + ) + .font(FONTS_BUNDESSANS_BOLD) + .moveDown(0.5) + .text( + EVIDENCE_QUESTION_WITNESSES_TEXT, + PDF_MARGIN_HORIZONTAL + MARGIN_RIGHT, + ) + .font(FONTS_BUNDESSANS_REGULAR) + .text(personsNames) + .moveDown(0.5); + } +}; diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addReason.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addReason.ts index 60b047f58..3ba825f63 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addReason.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/addReason.ts @@ -7,6 +7,8 @@ import { export const PLAINTIFF_BOOKED_TEXT = "Die klagende Partei buchte den folgenden Flug"; +export const PLAINTIFF_BOOKED_MULTIPLE_PERSONS_TEXT = + "Die klagende Partei buchte gemeinsam mit weiteren Fluggästen den folgenden Flug"; export const ARTICLE_DELAY_CANCEL_TEXT = "der "; export const ARTICLE_NOT_MOVE_TEXT = "und wurde "; export const DELAY_TEXT = "nicht pünktlich ausgeführt "; @@ -15,6 +17,16 @@ export const NOT_MOVE_TEXT = "und wurde von der beklagten Partei nicht befördert"; export const PASSIVE_VERB_TEXT = "wurde: "; +const getPlaintiffBookedText = ({ + isWeiterePersonen, +}: FluggastrechtContext) => { + if (isWeiterePersonen === "yes") { + return PLAINTIFF_BOOKED_MULTIPLE_PERSONS_TEXT; + } + + return PLAINTIFF_BOOKED_TEXT; +}; + const getBereichArticleText = ({ bereich }: FluggastrechtContext) => { if (bereich === "nichtbefoerderung") { return ARTICLE_NOT_MOVE_TEXT; @@ -50,7 +62,7 @@ export const addReason = ( doc .fontSize(10) .font(FONTS_BUNDESSANS_REGULAR) - .text(PLAINTIFF_BOOKED_TEXT, { + .text(getPlaintiffBookedText(userData), { continued: true, }) .text(getBereichArticleText(userData), { diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/createFactsOfCases.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/createFactsOfCases.ts index 40faa3421..d6204a4ad 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/createFactsOfCases.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/factsOfCases/createFactsOfCases.ts @@ -5,12 +5,13 @@ import { addCompensationAmount } from "./addCompensationAmount"; import { addDetailedReason } from "./addDetailedReason"; import { addFlightDetails } from "./addFlightDetails"; import { addReason } from "./addReason"; +import { addNewPageInCaseMissingVerticalSpace } from "../addNewPageInCaseMissingVerticalSpace"; import { addTable } from "./table/addTable"; import { addTableInfo } from "./table/addTableInfo"; import { COLUMN_HEIGHT } from "./table/tableConfigurations"; export const FACTS_OF_CASES_TEXT = "I. Sachverhalt"; -const MARGIN_TOP = 5; +const MARGIN_TOP = 15; export const createFactsOfCases = ( doc: typeof PDFDocument, @@ -32,6 +33,7 @@ export const createFactsOfCases = ( doc.moveDown(1); addDetailedReason(doc, documentStruct, userData); doc.moveDown(1); + addNewPageInCaseMissingVerticalSpace(doc, COLUMN_HEIGHT * 4 + MARGIN_TOP); const startTableY = doc.y; addTable(doc, documentStruct, startTableY, userData); diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/__test__/createLegalAssessment.test.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/__test__/createLegalAssessment.test.ts index c79de76ac..2a2e097d8 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/__test__/createLegalAssessment.test.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/__test__/createLegalAssessment.test.ts @@ -3,6 +3,7 @@ import { mockPdfKitDocument, mockPdfKitDocumentStructure, } from "tests/factories/mockPdfKit"; +import { PDF_MARGIN_HORIZONTAL } from "~/services/pdf/createPdfKitDocument"; import { ASSUMED_SETTLEMENT_SECTION_TEXT, CLAIM_FULL_JUSTIFIED_TEXT, @@ -16,7 +17,10 @@ describe("createLegalAssessment", () => { const mockDoc = mockPdfKitDocument(mockStruct); createLegalAssessment(mockDoc, mockStruct, userDataMock); - expect(mockDoc.text).toHaveBeenCalledWith(LEGAL_ASSESSMENT_TEXT); + expect(mockDoc.text).toHaveBeenCalledWith( + LEGAL_ASSESSMENT_TEXT, + PDF_MARGIN_HORIZONTAL, + ); }); it("should render document with claim full justified text", () => { diff --git a/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/createLegalAssessment.ts b/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/createLegalAssessment.ts index f89675836..72f75f87d 100644 --- a/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/createLegalAssessment.ts +++ b/app/domains/fluggastrechte/services/pdf/sections/reason/legalAssessment/createLegalAssessment.ts @@ -1,12 +1,14 @@ import type PDFDocument from "pdfkit"; import type { FluggastrechtContext } from "~/domains/fluggastrechte/formular/context"; -import { getCompensationPayment } from "~/domains/fluggastrechte/services/airports/getCompensationPayment"; +import { getTotalCompensationClaim } from "~/domains/fluggastrechte/formular/services/getTotalCompensationClaim"; import { gerichtskostenFromBetrag } from "~/domains/geldEinklagen/shared/gerichtskosten"; import { FONTS_BUNDESSANS_BOLD, FONTS_BUNDESSANS_REGULAR, + PDF_MARGIN_HORIZONTAL, } from "~/services/pdf/createPdfKitDocument"; import { getFullPlaintiffName } from "../../getFullPlaintiffName"; +import { addNewPageInCaseMissingVerticalSpace } from "../addNewPageInCaseMissingVerticalSpace"; export const LEGAL_ASSESSMENT_TEXT = "II. Rechtliche Würdigung"; export const CLAIM_FULL_JUSTIFIED_TEXT = @@ -26,17 +28,17 @@ export const createLegalAssessment = ( const legalAssessmentSect = doc.struct("Sect"); legalAssessmentSect.add( doc.struct("H2", {}, () => { - doc.fontSize(14).font(FONTS_BUNDESSANS_BOLD).text(LEGAL_ASSESSMENT_TEXT); + doc + .fontSize(14) + .font(FONTS_BUNDESSANS_BOLD) + .text(LEGAL_ASSESSMENT_TEXT, PDF_MARGIN_HORIZONTAL); doc.moveDown(1); }), ); documentStruct.add(legalAssessmentSect); - const compensationByDistance = getCompensationPayment({ - startAirport: userData.startAirport, - endAirport: userData.endAirport, - }); + const compensationByDistance = getTotalCompensationClaim(userData); const courtCostValue = gerichtskostenFromBetrag( Number(compensationByDistance), @@ -50,13 +52,24 @@ export const createLegalAssessment = ( .font(FONTS_BUNDESSANS_REGULAR) .text(CLAIM_FULL_JUSTIFIED_TEXT) .text(ASSUMED_SETTLEMENT_SECTION_TEXT) - .moveDown(4) - .text( - `${ADVANCE_COURT_COSTS_FIRST_TEXT} ${courtCostValue} ${ADVANCE_COURT_COSTS_SECOND_TEXT}`, - ) + .moveDown(4); + + const advanceCourtText = `${ADVANCE_COURT_COSTS_FIRST_TEXT} ${courtCostValue} ${ADVANCE_COURT_COSTS_SECOND_TEXT}`; + const advanceCourtTextHeight = doc.heightOfString(advanceCourtText); + addNewPageInCaseMissingVerticalSpace(doc, advanceCourtTextHeight); + + doc + .text(advanceCourtText) .moveDown(2) .font(FONTS_BUNDESSANS_BOLD) - .text(getFullPlaintiffName(userData)); + .text( + getFullPlaintiffName( + userData.anrede, + userData.title, + userData.vorname, + userData.nachname, + ), + ); }), ); documentStruct.add(reasonSect); diff --git a/tests/factories/mockPdfKit.ts b/tests/factories/mockPdfKit.ts index 48e695434..0376bca05 100644 --- a/tests/factories/mockPdfKit.ts +++ b/tests/factories/mockPdfKit.ts @@ -33,5 +33,6 @@ export const mockPdfKitDocument = ( return mockStructure; }), addPage: vi.fn().mockReturnThis(), + heightOfString: vi.fn().mockReturnThis(), } as unknown as PDFKit.PDFDocument; };