diff --git a/cypress/e2e/ui/reports/standard.csv.reports.spec.js b/cypress/e2e/ui/reports/standard.csv.reports.spec.js index 48d096beae..3086d92ac4 100644 --- a/cypress/e2e/ui/reports/standard.csv.reports.spec.js +++ b/cypress/e2e/ui/reports/standard.csv.reports.spec.js @@ -24,6 +24,86 @@ describe("csv export", () => { expect(response.body).to.not.include("Parse error"); }); }); + + it("should include address columns in CSV export with family fallback", () => { + // Test default format includes address fields when requested + // This also validates the family fallback logic for addresses + cy.request({ + method: "POST", + url: "/CSVCreateFile.php", + form: true, + body: { + output: "csv", + Format: "default", + familyonly: "false", + Source: "all", + Address1: "1", + Address2: "1", + City: "1", + State: "1", + Zip: "1" + } + }).then((response) => { + expect(response.status).to.eq(200); + expect(response.headers["content-type"]).to.include("text/csv"); + + // Verify no PHP errors in response + expect(response.body).to.not.include("Fatal error"); + expect(response.body).to.not.include("Parse error"); + + // Verify CSV header contains address columns + const lines = response.body.split('\n'); + expect(lines.length).to.be.greaterThan(1); + const header = lines[0].toLowerCase(); + expect(header).to.include("address 1"); + expect(header).to.include("city"); + expect(header).to.include("state"); + + // Verify at least one data row exists (family fallback should populate addresses) + // Filter out empty lines + const dataLines = lines.slice(1).filter(line => line.trim().length > 0); + expect(dataLines.length).to.be.greaterThan(0, "Expected at least one data row in export"); + }); + }); + + it("should include address columns in rollup format CSV export", () => { + // Test rollup format includes address fields when requested + cy.request({ + method: "POST", + url: "/CSVCreateFile.php", + form: true, + body: { + output: "csv", + Format: "rollup", + familyonly: "false", + Source: "all", + Address1: "1", + Address2: "1", + City: "1", + State: "1", + Zip: "1" + } + }).then((response) => { + expect(response.status).to.eq(200); + expect(response.headers["content-type"]).to.include("text/csv"); + + // Verify no PHP errors in response + expect(response.body).to.not.include("Fatal error"); + expect(response.body).to.not.include("Parse error"); + + // Verify CSV header contains address columns + const lines = response.body.split('\n'); + expect(lines.length).to.be.greaterThan(1); + const header = lines[0].toLowerCase(); + expect(header).to.include("address 1"); + expect(header).to.include("city"); + expect(header).to.include("state"); + + // Verify at least one data row exists + const dataLines = lines.slice(1).filter(line => line.trim().length > 0); + expect(dataLines.length).to.be.greaterThan(0, "Expected at least one data row in export"); + }); + }); }); describe("advanced deposit report", () => { diff --git a/src/CSVCreateFile.php b/src/CSVCreateFile.php index f2fa8c35d7..d395478b9b 100644 --- a/src/CSVCreateFile.php +++ b/src/CSVCreateFile.php @@ -374,32 +374,20 @@ extract($aRow); $person = PersonQuery::create()->findOneById($per_ID); - // Use person data only - each person must enter their own information - if ($sFormat === 'rollup') { - // Even in rollup format, use person data (no family inheritance) - $sHomePhone = $per_HomePhone ?? ''; - $sWorkPhone = $per_WorkPhone ?? ''; - $sCellPhone = $per_CellPhone ?? ''; - $sCountry = $per_Country ?? ''; - $sAddress1 = $per_Address1 ?? ''; - $sAddress2 = $per_Address2 ?? ''; - $sCity = $per_City ?? ''; - $sState = $per_State ?? ''; - $sZip = $per_Zip ?? ''; - $sEmail = $per_Email ?? ''; - } else { - // Individual data - use person data only - $sHomePhone = $per_HomePhone ?? ''; - $sWorkPhone = $per_WorkPhone ?? ''; - $sCellPhone = $per_CellPhone ?? ''; - $sCountry = $per_Country ?? ''; - $sAddress1 = $per_Address1 ?? ''; - $sAddress2 = $per_Address2 ?? ''; - $sCity = $per_City ?? ''; - $sState = $per_State ?? ''; - $sZip = $per_Zip ?? ''; - $sEmail = $per_Email ?? ''; - } + // Use person data with fallback to family data for addresses/contact + // This ensures members without personal addresses still get exported with their family's address + // Note: Similar logic exists in DirectoryReports.php - consider centralizing in PersonService + // if this pattern needs to be reused elsewhere (see issue #7937) + $sHomePhone = !empty($per_HomePhone) ? $per_HomePhone : ($fam_HomePhone ?? ''); + $sWorkPhone = $per_WorkPhone ?? ''; + $sCellPhone = $per_CellPhone ?? ''; + $sCountry = !empty($per_Country) ? $per_Country : ($fam_Country ?? ''); + $sAddress1 = !empty($per_Address1) ? $per_Address1 : ($fam_Address1 ?? ''); + $sAddress2 = !empty($per_Address2) ? $per_Address2 : ($fam_Address2 ?? ''); + $sCity = !empty($per_City) ? $per_City : ($fam_City ?? ''); + $sState = !empty($per_State) ? $per_State : ($fam_State ?? ''); + $sZip = !empty($per_Zip) ? $per_Zip : ($fam_Zip ?? ''); + $sEmail = !empty($per_Email) ? $per_Email : ($fam_Email ?? ''); // Check if we're filtering out people with incomplete addresses if (!($bSkipIncompleteAddr && (strlen($sCity) === 0 || strlen($sState) === 0 || strlen($sZip) === 0 || (strlen($sAddress1) === 0 && strlen($sAddress2) === 0)))) { diff --git a/src/Reports/DirectoryReport.php b/src/Reports/DirectoryReport.php index 940ea3bbf4..e44b85ae09 100644 --- a/src/Reports/DirectoryReport.php +++ b/src/Reports/DirectoryReport.php @@ -246,16 +246,17 @@ $pdf->sRecordName .= ' ' . MiscUtils::formatBirthDate($per_BirthYear, $per_BirthMonth, $per_BirthDay, $per_Flags); } - // Use person data only - each person must enter their own information - $sAddress1 = $per_Address1 ?? ''; - $sAddress2 = $per_Address2 ?? ''; - $sCity = $per_City ?? ''; - $sState = $per_State ?? ''; - $sZip = $per_Zip ?? ''; - $sHomePhone = $per_HomePhone ?? ''; + // Use person data with fallback to family data for addresses + // This ensures members without personal addresses still appear in the directory with their family's address + $sAddress1 = !empty($per_Address1) ? $per_Address1 : ($fam_Address1 ?? ''); + $sAddress2 = !empty($per_Address2) ? $per_Address2 : ($fam_Address2 ?? ''); + $sCity = !empty($per_City) ? $per_City : ($fam_City ?? ''); + $sState = !empty($per_State) ? $per_State : ($fam_State ?? ''); + $sZip = !empty($per_Zip) ? $per_Zip : ($fam_Zip ?? ''); + $sHomePhone = !empty($per_HomePhone) ? $per_HomePhone : ($fam_HomePhone ?? ''); $sWorkPhone = $per_WorkPhone ?? ''; $sCellPhone = $per_CellPhone ?? ''; - $sEmail = $per_Email ?? ''; + $sEmail = !empty($per_Email) ? $per_Email : ($fam_Email ?? ''); if ($bDirAddress) { if (strlen($sAddress1)) {