diff --git a/.vscode/settings.json b/.vscode/settings.json index d01044931..b043ade4f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,5 +2,11 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "editor.formatOnSave": true, "editor.formatOnPaste": true, - "eslint.enable": false -} \ No newline at end of file + "eslint.enable": false, + "[prisma]": { + "editor.defaultFormatter": "Prisma.prisma" + }, + "[sql]": { + "editor.defaultFormatter": "inferrinizzard.prettier-sql-vscode" + } +} diff --git a/backend-external/src/v1/routes/pay-transparency-routes.ts b/backend-external/src/v1/routes/pay-transparency-routes.ts index 59af1aa8e..302b35311 100644 --- a/backend-external/src/v1/routes/pay-transparency-routes.ts +++ b/backend-external/src/v1/routes/pay-transparency-routes.ts @@ -21,7 +21,7 @@ const router = express.Router(); * type: boolean * calculation_code: * type: string - * Report: + * ReportItem: * type: object * properties: * report_id: @@ -70,6 +70,15 @@ const router = express.Router(); * type: array * items: * $ref: "#/components/schemas/CalculatedData" + * Report: + * allOf: + * - $ref: "#/components/schemas/ReportItem" + * - type: object + * properties: + * history: + * type: array + * items: + * $ref: "#/components/schemas/ReportItem" * * PaginatedReports: * type: object diff --git a/backend/db/migrations/V1.0.22__report_id_fk_in_history.sql b/backend/db/migrations/V1.0.22__report_id_fk_in_history.sql new file mode 100644 index 000000000..565725dd6 --- /dev/null +++ b/backend/db/migrations/V1.0.22__report_id_fk_in_history.sql @@ -0,0 +1,4 @@ +SET + search_path TO pay_transparency; + +ALTER TABLE report_history ADD CONSTRAINT report_history_report_id FOREIGN KEY (report_id) REFERENCES pay_transparency_report (report_id); \ No newline at end of file diff --git a/backend/src/v1/prisma/schema.prisma b/backend/src/v1/prisma/schema.prisma index 0a2b1a7cf..7e4309607 100644 --- a/backend/src/v1/prisma/schema.prisma +++ b/backend/src/v1/prisma/schema.prisma @@ -121,18 +121,21 @@ model pay_transparency_report { revision Decimal @db.Decimal data_constraints String? @db.VarChar(3000) is_unlocked Boolean @default(true) - report_unlock_date DateTime? @db.Timestamp(6) reporting_year Decimal @db.Decimal + report_unlock_date DateTime? @db.Timestamp(6) pay_transparency_calculated_data pay_transparency_calculated_data[] naics_code_pay_transparency_report_naics_codeTonaics_code naics_code @relation("pay_transparency_report_naics_codeTonaics_code", fields: [naics_code], references: [naics_code], onDelete: NoAction, onUpdate: NoAction, map: "pay_transparency_report_naics_code_fk") employee_count_range employee_count_range @relation(fields: [employee_count_range_id], references: [employee_count_range_id], onDelete: NoAction, onUpdate: NoAction, map: "pay_transparency_report_employee_count_range_id_fk") pay_transparency_company pay_transparency_company @relation(fields: [company_id], references: [company_id], onDelete: NoAction, onUpdate: NoAction, map: "report_pt_company_id_fk") pay_transparency_user pay_transparency_user @relation(fields: [user_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction, map: "report_pt_user_id_fk") + report_history report_history[] + @@unique([company_id, user_id, reporting_year, report_status], map: "pay_transparency_report_uk") @@index([report_id, company_id]) @@index([create_date, report_status]) - @@index([company_id, report_start_date, report_end_date, report_status]) - @@index([company_id, create_date, report_start_date, report_end_date, report_status]) + @@index([company_id, create_date, reporting_year, report_status], map: "pay_transparency_report_company_id_create_date_report_year_idx") + @@index([company_id, reporting_year, report_status], map: "pay_transparency_report_company_id_reporting_year_report_idx") + @@index([report_unlock_date, is_unlocked]) } model pay_transparency_user { @@ -165,13 +168,14 @@ model report_history { naics_code String @db.VarChar(5) data_constraints String? @db.VarChar(3000) revision Decimal @db.Decimal - report_unlock_date DateTime? @db.Timestamp(6) is_unlocked Boolean @default(true) reporting_year Decimal @db.Decimal + report_unlock_date DateTime? @db.Timestamp(6) calculated_data_history calculated_data_history[] pay_transparency_company pay_transparency_company @relation(fields: [company_id], references: [company_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_company_id_fk") employee_count_range employee_count_range @relation(fields: [employee_count_range_id], references: [employee_count_range_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_employee_count_range_id_fk") naics_code_report_history_naics_codeTonaics_code naics_code @relation("report_history_naics_codeTonaics_code", fields: [naics_code], references: [naics_code], onDelete: NoAction, onUpdate: NoAction) + pay_transparency_report pay_transparency_report @relation(fields: [report_id], references: [report_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_report_id") pay_transparency_user pay_transparency_user @relation(fields: [user_id], references: [user_id], onDelete: NoAction, onUpdate: NoAction, map: "report_history_user_id_fk") } diff --git a/backend/src/v1/routes/external-consumer-routes.spec.ts b/backend/src/v1/routes/external-consumer-routes.spec.ts index 08e4f85ee..051196f67 100644 --- a/backend/src/v1/routes/external-consumer-routes.spec.ts +++ b/backend/src/v1/routes/external-consumer-routes.spec.ts @@ -55,9 +55,10 @@ const REPORT = { { value: faker.number.float(), is_suppressed: false, - calculation_code: `${faker.number.int()}`, + calculation_code: { calculation_code: `${faker.number.int()}` }, }, ], + report_history: [], }; describe('external-consumer-routes', () => { @@ -84,7 +85,7 @@ describe('external-consumer-routes', () => { { calculation_code: REPORT.pay_transparency_calculated_data[0] - .calculation_code, + .calculation_code.calculation_code, is_suppressed: REPORT.pay_transparency_calculated_data[0].is_suppressed, value: REPORT.pay_transparency_calculated_data[0].value, @@ -122,6 +123,7 @@ describe('external-consumer-routes', () => { revision: REPORT.revision, update_date: REPORT.update_date.toISOString(), user_comment: REPORT.user_comment, + history: [], }, ], totalRecords: 1, diff --git a/backend/src/v1/routes/report-routes.ts b/backend/src/v1/routes/report-routes.ts index 17e0fd98c..aa28b3627 100644 --- a/backend/src/v1/routes/report-routes.ts +++ b/backend/src/v1/routes/report-routes.ts @@ -86,7 +86,7 @@ reportRouter.put( return res.status(HttpStatus.INTERNAL_SERVER_ERROR).end(); } - const reportId: string = req.params.reportId; + let reportId: string = req.params.reportId; const report_to_publish = await reportService.getReportById( bceidBusinessGuid, reportId, @@ -105,15 +105,18 @@ reportRouter.put( } try { - await reportService.publishReport(report_to_publish); + reportId = await reportService.publishReport(report_to_publish); } catch (error) { logger.error(error); return res.status(HttpStatus.BAD_REQUEST).send(error.message); } try { - const reportHtml = await reportService.getReportHtml(req, reportId); - res.type('html').status(200).send(reportHtml); + const report = await reportService.getReportById( + bceidBusinessGuid, + reportId, + ); + res.status(200).send(report); } catch (e) { logger.error(e); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).end(); diff --git a/backend/src/v1/services/external-consumer-service.spec.ts b/backend/src/v1/services/external-consumer-service.spec.ts index 530bfa01b..9cdf46f8f 100644 --- a/backend/src/v1/services/external-consumer-service.spec.ts +++ b/backend/src/v1/services/external-consumer-service.spec.ts @@ -51,7 +51,46 @@ const testData = { { value: faker.number.float(), is_suppressed: false, - calculation_code: `${faker.number.int()}`, + calculation_code: { calculation_code: `${faker.number.int()}` }, + }, + ], + report_history: [ + { + report_id: faker.string.uuid(), + company_id: faker.string.uuid(), + naics_code: '11', + create_date: faker.date.past(), + update_date: faker.date.past(), + data_constraints: faker.lorem.sentence(), + user_comment: faker.lorem.sentence(), + revision: '12', + report_start_date: faker.date.past(), + report_end_date: faker.date.past(), + report_status: 'Published', + pay_transparency_company: { + company_name: faker.company.name(), + province: faker.location.state(), + bceid_business_guid: faker.string.uuid(), + country: faker.location.country(), + city: faker.location.city(), + postal_code: faker.location.zipCode(), + address_line1: faker.location.streetAddress(), + address_line2: faker.location.streetAddress(), + }, + employee_count_range: { + employee_count_range: '50-299', + }, + naics_code_report_history_naics_codeTonaics_code: { + naics_code: '11', + naics_label: faker.lorem.words(3), + }, + calculated_data_history: [ + { + value: faker.number.float(), + is_suppressed: false, + calculation_code: { calculation_code: `${faker.number.int()}` }, + }, + ], }, ], }; @@ -66,67 +105,131 @@ describe('external-consumer-service', () => { mockFindMany.mockReturnValue([testData]); const results = await externalConsumerService.exportDataWithPagination(); expect(results.page).toBe(0); - expect(results.pageSize).toBe(1000), - expect(results.totalRecords).toBe(1); - expect(results.records[0]).toEqual({ - calculated_data: [ - { - calculation_code: testData.pay_transparency_calculated_data[0].calculation_code, - is_suppressed: testData.pay_transparency_calculated_data[0].is_suppressed, - value: testData.pay_transparency_calculated_data[0].value, - }, - ], - company_address_line1: testData.pay_transparency_company.address_line1, - company_address_line2: testData.pay_transparency_company.address_line2, - company_bceid_business_guid: testData.pay_transparency_company.bceid_business_guid, - company_city: testData.pay_transparency_company.city, - company_country: testData.pay_transparency_company.country, - company_id: testData.company_id, - company_name:testData.pay_transparency_company.company_name, - company_postal_code: testData.pay_transparency_company.postal_code, - company_province: testData.pay_transparency_company.province, - create_date: testData.create_date, - data_constraints: testData.data_constraints, - employee_count_range: testData.employee_count_range.employee_count_range, - naics_code: testData.naics_code_pay_transparency_report_naics_codeTonaics_code.naics_code, - naics_code_label: testData.naics_code_pay_transparency_report_naics_codeTonaics_code.naics_label, - report_end_date: testData.report_end_date, - report_id: testData.report_id, - report_start_date: testData.report_start_date, - report_status: testData.report_status, - revision: testData.revision, - update_date: testData.update_date, - user_comment: testData.user_comment, - }) + expect(results.pageSize).toBe(1000), expect(results.totalRecords).toBe(1); + expect(results.records[0]).toStrictEqual({ + calculated_data: [ + { + is_suppressed: + testData.pay_transparency_calculated_data[0].is_suppressed, + value: testData.pay_transparency_calculated_data[0].value, + calculation_code: + testData.pay_transparency_calculated_data[0].calculation_code + .calculation_code, + }, + ], + company_address_line1: testData.pay_transparency_company.address_line1, + company_address_line2: testData.pay_transparency_company.address_line2, + company_bceid_business_guid: + testData.pay_transparency_company.bceid_business_guid, + company_city: testData.pay_transparency_company.city, + company_country: testData.pay_transparency_company.country, + company_id: testData.company_id, + company_name: testData.pay_transparency_company.company_name, + company_postal_code: testData.pay_transparency_company.postal_code, + company_province: testData.pay_transparency_company.province, + create_date: testData.create_date, + data_constraints: testData.data_constraints, + employee_count_range: testData.employee_count_range.employee_count_range, + naics_code: + testData.naics_code_pay_transparency_report_naics_codeTonaics_code + .naics_code, + naics_code_label: + testData.naics_code_pay_transparency_report_naics_codeTonaics_code + .naics_label, + report_end_date: testData.report_end_date, + report_id: testData.report_id, + report_start_date: testData.report_start_date, + report_status: testData.report_status, + revision: testData.revision, + update_date: testData.update_date, + user_comment: testData.user_comment, + history: [ + { + calculated_data: [ + { + is_suppressed: + testData.report_history[0].calculated_data_history[0].is_suppressed, + value: testData.report_history[0].calculated_data_history[0].value, + calculation_code: + testData.report_history[0].calculated_data_history[0].calculation_code + .calculation_code, + }, + ], + company_address_line1: + testData.report_history[0].pay_transparency_company.address_line1, + company_address_line2: + testData.report_history[0].pay_transparency_company.address_line2, + company_bceid_business_guid: + testData.report_history[0].pay_transparency_company.bceid_business_guid, + company_city: testData.report_history[0].pay_transparency_company.city, + company_country: testData.report_history[0].pay_transparency_company.country, + company_id: testData.report_history[0].company_id, + company_name: testData.report_history[0].pay_transparency_company.company_name, + company_postal_code: testData.report_history[0].pay_transparency_company.postal_code, + company_province: testData.report_history[0].pay_transparency_company.province, + create_date: testData.report_history[0].create_date, + data_constraints: testData.report_history[0].data_constraints, + employee_count_range: + testData.report_history[0].employee_count_range.employee_count_range, + naics_code: + testData.report_history[0].naics_code_report_history_naics_codeTonaics_code + .naics_code, + naics_code_label: + testData.report_history[0].naics_code_report_history_naics_codeTonaics_code + .naics_label, + report_end_date: testData.report_history[0].report_end_date, + report_id: testData.report_history[0].report_id, + report_start_date: testData.report_history[0].report_start_date, + report_status: testData.report_history[0].report_status, + revision: testData.report_history[0].revision, + update_date: testData.report_history[0].update_date, + user_comment: testData.report_history[0].user_comment, + }, + ], + }); }); it('should parse date strings', async () => { mockCount.mockReturnValue(1); mockFindMany.mockReturnValue([testData]); - const results = await externalConsumerService.exportDataWithPagination("2024-01-01", '2024-01-01', -1, -1); + const results = await externalConsumerService.exportDataWithPagination( + '2024-01-01', + '2024-01-01', + -1, + -1, + ); expect(results.page).toBe(0); - expect(results.pageSize).toBe(1000), - expect(results.totalRecords).toBe(1); + expect(results.pageSize).toBe(1000), expect(results.totalRecords).toBe(1); }); it('should fail parse invalid date strings', async () => { mockCount.mockReturnValue(1); mockFindMany.mockReturnValue([testData]); try { - await externalConsumerService.exportDataWithPagination("20241-01-01", '20241-01-01', -1, -1); + await externalConsumerService.exportDataWithPagination( + '20241-01-01', + '20241-01-01', + -1, + -1, + ); } catch (error) { - expect(error.message).toBe( - 'Failed to parse dates. Please use date format YYYY-MM-dd', - ); + expect(error.message).toBe( + 'Failed to parse dates. Please use date format YYYY-MM-dd', + ); } }); it('should fail when endDate is before the startDate', async () => { mockCount.mockReturnValue(1); mockFindMany.mockReturnValue([testData]); try { - await externalConsumerService.exportDataWithPagination("2024-01-01", '2023-01-01', -1, -1); + await externalConsumerService.exportDataWithPagination( + '2024-01-01', + '2023-01-01', + -1, + -1, + ); } catch (error) { - expect(error.message).toBe('Start date must be before the end date.'); + expect(error.message).toBe('Start date must be before the end date.'); } }); }); diff --git a/backend/src/v1/services/external-consumer-service.ts b/backend/src/v1/services/external-consumer-service.ts index 645c1bdf9..0450c6a37 100644 --- a/backend/src/v1/services/external-consumer-service.ts +++ b/backend/src/v1/services/external-consumer-service.ts @@ -1,11 +1,57 @@ import prismaReadOnlyReplica from '../prisma/prisma-client-readonly-replica'; -import { - LocalDate, - convert, -} from '@js-joda/core'; +import { LocalDate, convert } from '@js-joda/core'; import pick from 'lodash/pick'; import { PayTransparencyUserError } from './file-upload-service'; +const denormalizeCompany = (company) => { + return { + company_name: company.company_name, + company_province: company.province, + company_bceid_business_guid: company.bceid_business_guid, + company_city: company.city, + company_country: company.country, + company_postal_code: company.postal_code, + company_address_line1: company.address_line1, + company_address_line2: company.address_line2, + }; +}; + +const denormalizeReport = ( + report, + getNaicsCode: (report) => { naics_code: string; naics_label: string }, + getCalculatedData: (report) => { + value: string; + is_suppressed: string; + calculation_code: any; + }[], +) => { + return { + ...pick(report, [ + 'report_id', + 'company_id', + 'naics_code', + 'create_date', + 'update_date', + 'data_constraints', + 'user_comment', + 'revision', + 'report_start_date', + 'report_end_date', + 'report_status', + 'reporting_year', + ]), + ...denormalizeCompany(report.pay_transparency_company), + employee_count_range: report.employee_count_range.employee_count_range, + naics_code: getNaicsCode(report).naics_code, + naics_code_label: getNaicsCode(report).naics_label, + calculated_data: getCalculatedData(report).map((data) => ({ + value: data.value, + is_suppressed: data.is_suppressed, + calculation_code: data.calculation_code.calculation_code, + })), + }; +}; + const externalConsumerService = { /** * This function returns a list of objects with pagination details to support the analytics team. @@ -27,8 +73,7 @@ const externalConsumerService = { offset?: number, limit?: number, ) { - let startDt = LocalDate.now() - .minusMonths(1); + let startDt = LocalDate.now().minusMonths(1); let endDt = LocalDate.now().plusDays(1); if (limit > 1000 || !limit || limit <= 0) { limit = 1000; @@ -87,6 +132,18 @@ const externalConsumerService = { }, }, pay_transparency_company: true, + report_history: { + include: { + naics_code_report_history_naics_codeTonaics_code: true, + employee_count_range: true, + calculated_data_history: { + include: { + calculation_code: true, + }, + }, + pay_transparency_company: true, + }, + }, }, skip: offset, take: limit, @@ -96,51 +153,22 @@ const externalConsumerService = { totalRecords: totalCount, page: offset, pageSize: limit, - records: results.map( - ({ - naics_code_pay_transparency_report_naics_codeTonaics_code, - pay_transparency_calculated_data, - employee_count_range, - pay_transparency_company, - ...report - }) => { - return { - ...pick(report, [ - 'report_id', - 'company_id', - 'naics_code', - 'create_date', - 'update_date', - 'data_constraints', - 'user_comment', - 'revision', - 'report_start_date', - 'report_end_date', - 'report_status', - 'reporting_year', - ]), - company_name: pay_transparency_company.company_name, - company_province: pay_transparency_company.province, - company_bceid_business_guid: - pay_transparency_company.bceid_business_guid, - company_city: pay_transparency_company.city, - company_country: pay_transparency_company.country, - company_postal_code: pay_transparency_company.postal_code, - company_address_line1: pay_transparency_company.address_line1, - company_address_line2: pay_transparency_company.address_line2, - employee_count_range: employee_count_range.employee_count_range, - naics_code: - naics_code_pay_transparency_report_naics_codeTonaics_code.naics_code, - naics_code_label: - naics_code_pay_transparency_report_naics_codeTonaics_code.naics_label, - calculated_data: pay_transparency_calculated_data.map((data) => ({ - value: data.value, - is_suppressed: data.is_suppressed, - calculation_code: data.calculation_code, - })), - }; - }, - ), + records: results.map((report) => { + return { + ...denormalizeReport( + report, + (r) => r.naics_code_pay_transparency_report_naics_codeTonaics_code, + (r) => r.pay_transparency_calculated_data, + ), + history: report.report_history.map((report) => { + return denormalizeReport( + report, + (r) => r.naics_code_report_history_naics_codeTonaics_code, + (r) => r.calculated_data_history, + ); + }), + }; + }), }; }, }; diff --git a/backend/src/v1/services/report-service.spec.ts b/backend/src/v1/services/report-service.spec.ts index c21d35481..1f0b007ef 100644 --- a/backend/src/v1/services/report-service.spec.ts +++ b/backend/src/v1/services/report-service.spec.ts @@ -32,6 +32,7 @@ const actualMovePublishedReportToHistory = jest.mock('./utils-service'); const mockCompanyFindFirst = jest.fn(); const mockReportFindFirst = jest.fn(); +const mockReportFindUnique = jest.fn(); jest.mock('../prisma/prisma-client', () => { return { pay_transparency_company: { @@ -40,6 +41,7 @@ jest.mock('../prisma/prisma-client', () => { update: jest.fn(), }, pay_transparency_report: { + findUnique: (...args) => mockReportFindUnique(...args), findFirst: (...args) => mockReportFindFirst(...args), create: jest.fn(), update: jest.fn(), @@ -764,7 +766,7 @@ describe('publishReport', () => { it('throws an error', async () => { await expect( reportService.publishReport(mockPublishedReportInApi), - ).rejects.toThrow(); + ).rejects.toEqual(new Error('Only draft reports can be published')); }); }); @@ -809,6 +811,10 @@ describe('publishReport', () => { describe('if the given report has status=Draft, and there is an existing Published report', () => { it('archives the existing published report in history, and changes the status of the Draft to Published', async () => { mockReportFindFirst.mockResolvedValue(mockPublishedReportInDb); + mockReportFindUnique.mockResolvedValue({ + ...mockPublishedReportInDb, + pay_transparency_calculated_data: [], + }); jest .spyOn(reportServicePrivate, 'movePublishedReportToHistory') .mockReturnValueOnce(null); @@ -834,14 +840,8 @@ describe('publishReport', () => { // Expect only one record to be updated (the report that was passed to // publishReport(...) expect(updateStatement.where.report_id).toBe( - mockDraftReportInApi.report_id, + mockPublishedReportInDb.report_id, ); - - // Expect only one column to be updated (the report status_column) - expect(updateStatement.data).toStrictEqual({ - report_status: enumReportStatus.Published, - create_date: mockPublishedReportInDb.create_date, - }); }); }); describe('if the given report has status=Draft, and there is an existing Published and locked report', () => { @@ -850,17 +850,21 @@ describe('publishReport', () => { ...mockPublishedReportInDb, is_unlocked: false, }); + mockReportFindUnique.mockReturnValue({ + ...mockDraftReportInDb, + is_unlocked: false, + pay_transparency_calculated_data: [], + }); + jest .spyOn(reportServicePrivate, 'movePublishedReportToHistory') .mockReturnValueOnce(null); - try { - await reportService.publishReport(mockDraftReportInApi); - } catch (error) { - expect(error.message).toBe( - 'A report for this time period already exists and cannot be updated.', - ); - } + await expect( + reportService.publishReport(mockDraftReportInApi), + ).rejects.toEqual(new Error( + 'A report for this time period already exists and cannot be updated.', + )); }); }); }); @@ -926,14 +930,6 @@ describe('movePublishedReportToHistory', () => { expect(deleteCalcData.where.report_id).toBe( mockPublishedReportInDb.report_id, ); - - // Confirm that the original report was deleted from the reports table - expect(prisma.pay_transparency_report.delete).toHaveBeenCalledTimes(1); - const deleteReport = (prisma.pay_transparency_report.delete as jest.Mock) - .mock.calls[0][0]; - expect(deleteReport.where.report_id).toBe( - mockPublishedReportInDb.report_id, - ); }); }); }); diff --git a/backend/src/v1/services/report-service.ts b/backend/src/v1/services/report-service.ts index c15085669..e58bae407 100644 --- a/backend/src/v1/services/report-service.ts +++ b/backend/src/v1/services/report-service.ts @@ -417,13 +417,6 @@ const reportServicePrivate = { report_id: report.report_id, }, }); - - // Delete the original report - await tx.pay_transparency_report.delete({ - where: { - report_id: report.report_id, - }, - }); }, /** @@ -1170,16 +1163,18 @@ const reportService = { return reportsAdjusted; }, - async publishReport(report_to_publish: Report) { + async publishReport(report_to_publish: Report): Promise { // Check preconditions if (report_to_publish.report_status != enumReportStatus.Draft) { throw new Error('Only draft reports can be published'); } + let reportId = report_to_publish.report_id; await prisma.$transaction(async (tx) => { // Check if there is an existing published report that // corresponds to the same company_id and reporting year as // the draft "report_to_publish". (Should be 1 published at most.) + const existing_published_report = await tx.pay_transparency_report.findFirst({ where: { @@ -1191,32 +1186,82 @@ const reportService = { if (existing_published_report && !existing_published_report.is_unlocked) { throw new Error( - 'A report for this time period already exists and cannot be updated.', - ); + 'A report for this time period already exists and cannot be updated.', + ); } // If there is an existing Published report, move it into // report_history if (existing_published_report) { + const full_report_to_publish = + await tx.pay_transparency_report.findUnique({ + where: { report_id: report_to_publish.report_id }, + include: { + pay_transparency_calculated_data: { + select: { + calculation_code_id: true, + value: true, + is_suppressed: true, + create_date: true, + update_date: true, + create_user: true, + update_user: true, + }, + }, + }, + }); await reportServicePrivate.movePublishedReportToHistory( tx, existing_published_report, ); - } - // Change report's status to Published - await tx.pay_transparency_report.update({ - where: { - report_id: report_to_publish.report_id, - }, - data: { - report_status: enumReportStatus.Published, - create_date: - existing_published_report?.create_date || - report_to_publish.create_date, - }, - }); + // Update existing report + await tx.pay_transparency_report.update({ + where: { report_id: existing_published_report.report_id }, + data: { + naics_code_pay_transparency_report_naics_codeTonaics_code: { + connect: { + naics_code: full_report_to_publish.naics_code, + }, + }, + employee_count_range: { + connect: { + employee_count_range_id: + full_report_to_publish.employee_count_range_id, + }, + }, + report_start_date: full_report_to_publish.report_start_date, + report_end_date: full_report_to_publish.report_end_date, + user_comment: full_report_to_publish.user_comment, + data_constraints: full_report_to_publish.data_constraints, + revision: parseInt(existing_published_report.revision as any) + 1, + update_date: full_report_to_publish.update_date, + update_user: full_report_to_publish.update_user, + pay_transparency_calculated_data: { + createMany: { + data: full_report_to_publish.pay_transparency_calculated_data, + }, + }, + }, + }); + reportId = existing_published_report.report_id; + } else { + // Change report's status to Published + await tx.pay_transparency_report.update({ + where: { + report_id: report_to_publish.report_id, + }, + data: { + report_status: enumReportStatus.Published, + create_date: + existing_published_report?.create_date || + report_to_publish.create_date, + }, + }); + } }); + + return reportId; }, /** diff --git a/frontend/e2e/pages/report.ts b/frontend/e2e/pages/report.ts index 6fe605de0..fce6bcf27 100644 --- a/frontend/e2e/pages/report.ts +++ b/frontend/e2e/pages/report.ts @@ -80,12 +80,15 @@ export class DraftReportPage extends BaseReportPage { } async finalizedReport(reportId: string) { + const publishReportRequest = this.instance.waitForResponse(res => + res.url().includes('/api/v1/report') && res.request().method().toLowerCase() === 'put' + ) const finalizeReportResponse = this.instance.waitForResponse((res) => - res.url().includes(`/api/v1/report/${reportId}`), + res.url().includes(`/api/v1/report/${reportId}`) && res.request().method().toLowerCase() === 'get', ); const getReportsRequest = this.instance.waitForResponse( (res) => - res.url().includes('/api/v1/report/?reporting_year=') && + res.url().includes('reporting_year=') && res.status() === 200, ); await this.finalReportCheckBox.scrollIntoViewIfNeeded(); @@ -110,10 +113,13 @@ export class DraftReportPage extends BaseReportPage { await yesButton.click(); await expect(confirmTitle).not.toBeVisible(); } + const publishReportResponse = await publishReportRequest; const finalize = await finalizeReportResponse; await finalize.text(); + const reportData = await publishReportResponse.json(); await this.instance.waitForTimeout(5000); await this.instance.waitForURL(PagePaths.VIEW_REPORT); + return reportData; } async validateCanGoBack(generateReportPage: GenerateReportPage) { diff --git a/frontend/e2e/report-generation.spec.ts b/frontend/e2e/report-generation.spec.ts index 23d7564b9..b0742a7f9 100644 --- a/frontend/e2e/report-generation.spec.ts +++ b/frontend/e2e/report-generation.spec.ts @@ -1,9 +1,4 @@ -import { expect, test } from '@playwright/test'; -import { DashboardPage } from './pages/dashboard'; -import { GenerateReportPage } from './pages/generate-report'; -import { PagePaths } from './utils'; -import { DraftReportPage, PublishedReportPage } from './pages/report'; -import { waitForApiResponses, waitForUserAndReports } from './utils/report'; +import { test } from '@playwright/test'; import { generateReport } from './utils/generate-report'; import { checkDashboardReports } from './utils/check-dashboard-reports'; import { editReport } from './utils/edit-report'; diff --git a/frontend/e2e/utils/edit-report.ts b/frontend/e2e/utils/edit-report.ts index 498bcaf0d..a5b1515ce 100644 --- a/frontend/e2e/utils/edit-report.ts +++ b/frontend/e2e/utils/edit-report.ts @@ -28,12 +28,12 @@ export const editReport = async (page: Page) => { await formPage.checkDefaultFormValues(reportDetails); // edit form and submit form - const report = await formPage.editReportAndSubmit(reportDetails); + let report = await formPage.editReportAndSubmit(reportDetails); const draftReportPage = new DraftReportPage(formPage.instance, user); await draftReportPage.setup(); await draftReportPage.verifyEmployeerDetails(user, null); - await draftReportPage.finalizedReport(report.report_id); + report = await draftReportPage.finalizedReport(reportId); const publishedReportPage = new PublishedReportPage(page, user); await publishedReportPage.setup(); diff --git a/frontend/e2e/utils/generate-report.ts b/frontend/e2e/utils/generate-report.ts index 2284263b1..47d1dfe49 100644 --- a/frontend/e2e/utils/generate-report.ts +++ b/frontend/e2e/utils/generate-report.ts @@ -28,8 +28,8 @@ export const generateReport = async (page: Page) => { await draftReportPage.verifyEmployeerDetails(user, reportDetails); await draftReportPage.validateCanGoBack(generateReportPage); - await draftReportPage.finalizedReport(reportDetails.report_id); + const report = await draftReportPage.finalizedReport(reportDetails.report_id); const publishedReportPage = new PublishedReportPage(page, user); await publishedReportPage.setup(); - await publishedReportPage.verifyEmployeerDetails(user, reportDetails); + await publishedReportPage.verifyEmployeerDetails(user, report); } \ No newline at end of file diff --git a/frontend/src/common/apiService.ts b/frontend/src/common/apiService.ts index 833b8fcb5..92a43c580 100644 --- a/frontend/src/common/apiService.ts +++ b/frontend/src/common/apiService.ts @@ -261,7 +261,7 @@ export default { * Change the status of an existing report from Draft to Published. * @param {string} reportId The id of a Draft report that should be Published */ - async publishReport(reportId: string): Promise { + async publishReport(reportId: string): Promise { try { const resp = await apiAxios.put(`${ApiRoutes.REPORT}/${reportId}`); if (resp?.data) { diff --git a/frontend/src/components/DraftReportPage.vue b/frontend/src/components/DraftReportPage.vue index d13f8e295..2d6b97136 100644 --- a/frontend/src/components/DraftReportPage.vue +++ b/frontend/src/components/DraftReportPage.vue @@ -164,7 +164,8 @@ async function tryGenerateReport() { if (shouldGenerateReport) { isProcessing.value = true; try { - await ApiService.publishReport(ReportStepperStore.reportId ?? ''); + const reportData = await ApiService.publishReport(ReportStepperStore.reportId ?? ''); + await ReportStepperStore.setReportInfo(reportData); NotificationService.pushNotificationSuccess( 'You have created a pay transparency report.', );