diff --git a/app/src/controllers/submission.ts b/app/src/controllers/submission.ts index f656b4a0..013c89af 100644 --- a/app/src/controllers/submission.ts +++ b/app/src/controllers/submission.ts @@ -378,6 +378,10 @@ const controller = { req.currentContext ); + // Create contacts + if (req.body.contacts) + await contactService.upsertContacts(submission.activityId, req.body.contacts, req.currentContext); + // Create new submission const result = await submissionService.createSubmission({ ...submission, diff --git a/app/src/db/migrations/20241028000000_012-user-contacts.ts b/app/src/db/migrations/20241028000000_012-user-contacts.ts deleted file mode 100644 index 240c2d7b..00000000 --- a/app/src/db/migrations/20241028000000_012-user-contacts.ts +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Two new tables are created: - * contact - * activity_contact - * - * Contact information from submission & enquiry is moved to these tables and then dropped from originals - */ - -/* eslint-disable max-len */ -import stamps from '../stamps'; - -import type { Knex } from 'knex'; - -export async function up(knex: Knex): Promise { - return ( - Promise.resolve() - - // Create public schema tables - .then(() => - knex.schema.createTable('contact', (table) => { - table.uuid('contact_id').primary(); - table - .uuid('user_id') - .nullable() - .references('user_id') - .inTable('user') - .onUpdate('CASCADE') - .onDelete('CASCADE'); - table.text('first_name'); - table.text('last_name'); - table.text('phone_number'); - table.text('email'); - table.text('contact_preference'); - table.text('contact_applicant_relationship'); - stamps(knex, table); - }) - ) - - .then(() => - knex.schema.createTable('activity_contact', (table) => { - table.primary(['activity_id', 'contact_id']); - table - .text('activity_id') - .notNullable() - .references('activity_id') - .inTable('activity') - .onUpdate('CASCADE') - .onDelete('CASCADE'); - table - .uuid('contact_id') - .notNullable() - .references('contact_id') - .inTable('contact') - .onUpdate('CASCADE') - .onDelete('CASCADE'); - stamps(knex, table); - }) - ) - - // Create public schema table triggers - .then(() => - knex.schema.raw(`CREATE TRIGGER before_update_contact_trigger - BEFORE UPDATE ON public."contact" - FOR EACH ROW EXECUTE PROCEDURE public.set_updated_at();`) - ) - - .then(() => - knex.schema.raw(`CREATE TRIGGER before_update_activity_contact_trigger - BEFORE UPDATE ON public."activity_contact" - FOR EACH ROW EXECUTE PROCEDURE public.set_updated_at();`) - ) - - // Create audit triggers - .then(() => - knex.schema.raw(`CREATE TRIGGER audit_contact_trigger - AFTER UPDATE OR DELETE ON public."contact" - FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func();`) - ) - - .then(() => - knex.schema.raw(`CREATE TRIGGER audit_activity_contact_trigger - AFTER UPDATE OR DELETE ON public."activity_contact" - FOR EACH ROW EXECUTE PROCEDURE audit.if_modified_func();`) - ) - - // Split data - .then(() => - knex.schema.raw(`DROP TABLE IF EXISTS temp; - DELETE FROM public.activity_contact; - DELETE FROM public.contact; - - CREATE TABLE temp (contact_id uuid, activity_id text, contact_first_name text, contact_last_name text, contact_email text, contact_phone_number text, contact_preference text, contact_applicant_relationship text); - - INSERT INTO temp (contact_id, activity_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship) - SELECT gen_random_uuid(), activity_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship - FROM public.submission; - - INSERT INTO temp (contact_id, activity_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship) - SELECT gen_random_uuid(), activity_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship - FROM public.enquiry; - - INSERT INTO public.contact (contact_id, first_name, last_name, email, phone_number, contact_preference, contact_applicant_relationship) - SELECT contact_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship - FROM public.temp; - - INSERT INTO public.activity_contact (activity_id, contact_id) - SELECT activity_id, contact_id - FROM public.temp; - - DROP TABLE IF EXISTS temp;`) - ) - - // Drop old columns - .then(() => - knex.schema.raw(`ALTER TABLE public.submission - DROP COLUMN IF EXISTS contact_name;`) - ) - - .then(() => - knex.schema.alterTable('submission', function (table) { - table.dropColumn('contact_first_name'); - table.dropColumn('contact_last_name'); - table.dropColumn('contact_email'); - table.dropColumn('contact_phone_number'); - table.dropColumn('contact_preference'); - table.dropColumn('contact_applicant_relationship'); - }) - ) - - .then(() => - knex.schema.alterTable('enquiry', function (table) { - table.dropColumn('contact_first_name'); - table.dropColumn('contact_last_name'); - table.dropColumn('contact_email'); - table.dropColumn('contact_phone_number'); - table.dropColumn('contact_preference'); - table.dropColumn('contact_applicant_relationship'); - }) - ) - ); -} - -export async function down(knex: Knex): Promise { - return ( - Promise.resolve() - // Add columns - .then(() => - knex.schema.alterTable('enquiry', function (table) { - table.text('contact_first_name'); - table.text('contact_last_name'); - table.text('contact_email'); - table.text('contact_phone_number'); - table.text('contact_preference'); - table.text('contact_applicant_relationship'); - }) - ) - - .then(() => - knex.schema.alterTable('submission', function (table) { - // Don't bring back contact_name, this should've been dropped long ago - table.text('contact_first_name'); - table.text('contact_last_name'); - table.text('contact_email'); - table.text('contact_phone_number'); - table.text('contact_preference'); - table.text('contact_applicant_relationship'); - }) - ) - - // Retrieve data - .then(() => - knex.schema.raw(`DROP TABLE IF EXISTS temp; - - CREATE TABLE temp (contact_id uuid, activity_id text, contact_first_name text, contact_last_name text, contact_email text, contact_phone_number text, contact_preference text, contact_applicant_relationship text); - - INSERT INTO temp (contact_id, activity_id, contact_first_name, contact_last_name, contact_email, contact_phone_number, contact_preference, contact_applicant_relationship) - SELECT c.contact_id, ac.activity_id, c.first_name, c.last_name, c.email, c.phone_number, c.contact_preference, c.contact_applicant_relationship - FROM public.contact c - JOIN public.activity_contact ac on ac.contact_id = c.contact_id; - - UPDATE public.submission AS s - set contact_first_name = t.contact_first_name, - contact_last_name = t.contact_last_name, - contact_email = t.contact_email, - contact_phone_number = t.contact_phone_number, - contact_preference = t.contact_preference, - contact_applicant_relationship = t.contact_applicant_relationship - FROM public.temp t - WHERE s.activity_id = t.activity_id; - - UPDATE public.enquiry AS e - set contact_first_name = t.contact_first_name, - contact_last_name = t.contact_last_name, - contact_email = t.contact_email, - contact_phone_number = t.contact_phone_number, - contact_preference = t.contact_preference, - contact_applicant_relationship = t.contact_applicant_relationship - FROM public.temp t - WHERE e.activity_id = t.activity_id; - - DROP TABLE IF EXISTS temp;`) - ) - - // Drop audit triggers - .then(() => knex.schema.raw('DROP TRIGGER IF EXISTS audit_activity_contact_trigger ON public."activity_contact"')) - .then(() => knex.schema.raw('DROP TRIGGER IF EXISTS audit_contact_trigger ON public."contact"')) - - // Drop public schema table triggers - .then(() => - knex.schema.raw('DROP TRIGGER IF EXISTS before_update_activity_contact_trigger ON public."activity_contact"') - ) - .then(() => knex.schema.raw('DROP TRIGGER IF EXISTS before_update_contact_trigger ON public."contact"')) - - // Drop public schema tables - .then(() => knex.schema.dropTableIfExists('activity_contact')) - .then(() => knex.schema.dropTableIfExists('contact')) - ); -} diff --git a/app/src/db/migrations/20241107000000_013-draft-tables.ts b/app/src/db/migrations/20241125000000_015-draft-tables.ts similarity index 100% rename from app/src/db/migrations/20241107000000_013-draft-tables.ts rename to app/src/db/migrations/20241125000000_015-draft-tables.ts diff --git a/app/src/db/prisma/schema.prisma b/app/src/db/prisma/schema.prisma index 6734484a..caa484da 100644 --- a/app/src/db/prisma/schema.prisma +++ b/app/src/db/prisma/schema.prisma @@ -299,6 +299,19 @@ model submission { @@schema("public") } +model submission_draft { + submission_draft_id String @id @db.Uuid + activity_id String + data Json @db.Json + created_by String? @default("00000000-0000-0000-0000-000000000000") + created_at DateTime? @default(now()) @db.Timestamptz(6) + updated_by String? + updated_at DateTime? @db.Timestamptz(6) + activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "submission_draft_activity_id_foreign") + + @@schema("public") +} + model user { user_id String @id @db.Uuid identity_id String? @db.Uuid @@ -483,52 +496,6 @@ model subject_group { @@schema("yars") } -model activity_contact { - activity_id String - contact_id String @db.Uuid - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "activity_contact_activity_id_foreign") - contact contact @relation(fields: [contact_id], references: [contact_id], onDelete: Cascade, map: "activity_contact_contact_id_foreign") - - @@id([activity_id, contact_id]) - @@schema("public") -} - -model contact { - contact_id String @id @db.Uuid - user_id String? @db.Uuid - first_name String? - last_name String? - phone_number String? - email String? - contact_preference String? - contact_applicant_relationship String? - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - activity_contact activity_contact[] - user user? @relation(fields: [user_id], references: [user_id], onDelete: Cascade, map: "contact_user_id_foreign") - - @@schema("public") -} - -model submission_draft { - submission_draft_id String @id @db.Uuid - activity_id String - data Json @db.Json - created_by String? @default("00000000-0000-0000-0000-000000000000") - created_at DateTime? @default(now()) @db.Timestamptz(6) - updated_by String? - updated_at DateTime? @db.Timestamptz(6) - activity activity @relation(fields: [activity_id], references: [activity_id], onDelete: Cascade, map: "submission_draft_activity_id_foreign") - - @@schema("public") -} - view group_role_policy_vw { row_number BigInt @unique group_id Int? diff --git a/app/src/middleware/authorization.ts b/app/src/middleware/authorization.ts index fefe1086..936d36a9 100644 --- a/app/src/middleware/authorization.ts +++ b/app/src/middleware/authorization.ts @@ -8,6 +8,7 @@ import { noteService, permitService, submissionService, + submissionDraftService, userService, yarsService } from '../services'; @@ -112,7 +113,7 @@ const paramMap = new Map any>([ ['noteId', noteService.getNote], ['permitId', permitService.getPermit], ['submissionId', submissionService.getSubmission], - ['submissionDraftId', submissionService.getDraft] + ['submissionDraftId', submissionDraftService.getDraft] ]); /** diff --git a/app/tests/unit/controllers/submission.spec.ts b/app/tests/unit/controllers/submission.spec.ts index 76af4de2..b46600b1 100644 --- a/app/tests/unit/controllers/submission.spec.ts +++ b/app/tests/unit/controllers/submission.spec.ts @@ -6,10 +6,11 @@ import { contactService, enquiryService, permitService, + submissionDraftService, submissionService } from '../../../src/services'; -import type { Permit, Submission } from '../../../src/types'; -import { ApplicationStatus, IntakeStatus } from '../../../src/utils/enums/housing'; +import type { Permit, Submission, SubmissionDraft } from '../../../src/types'; +import { ApplicationStatus, IntakeStatus, PermitNeeded, PermitStatus } from '../../../src/utils/enums/housing'; import { AuthType, BasicResponse, Initiative } from '../../../src/utils/enums/application'; // Mock config library - @see {@link https://stackoverflow.com/a/64819698} @@ -38,6 +39,7 @@ afterEach(() => { }); const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; +const uuidv4Pattern = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/; const CURRENT_CONTEXT = { authType: AuthType.BEARER, tokenPayload: undefined, userId: 'abc-123' }; @@ -723,38 +725,14 @@ describe('getSubmissions', () => { describe('submitDraft', () => { // Mock service calls const createPermitSpy = jest.spyOn(permitService, 'createPermit'); - const updateSubmissionSpy = jest.spyOn(submissionService, 'updateSubmission'); + const createSubmissionSpy = jest.spyOn(submissionService, 'createSubmission'); const createActivitySpy = jest.spyOn(activityService, 'createActivity'); - const deletePermitsByActivitySpy = jest.spyOn(permitService, 'deletePermitsByActivity'); const upsertContacts = jest.spyOn(contactService, 'upsertContacts'); - it('updates submission with the given activity ID', async () => { - const req = { - body: { activityId: '000000000', submissionId: '11111111' }, - currentContext: CURRENT_CONTEXT - }; - const next = jest.fn(); - - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000', submissionId: '11111111' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); - upsertContacts.mockResolvedValue(); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.submitDraft(req as any, res as any, next); - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - expect(upsertContacts).toHaveBeenCalledTimes(1); - expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ activityId: '00000000', submissionId: '11111111' }); - }); - it('populates data from body if it exists', async () => { const req = { body: { - activityId: '00000000', - submissionId: '11111111', + contacts: [{ firstName: 'test', lastName: 'person' }], basic: { isDevelopedByCompanyOrOrg: true }, @@ -772,104 +750,53 @@ describe('submitDraft', () => { }; const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); + createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.submitDraft(req as any, res as any, next); - expect(createActivitySpy).toHaveBeenCalledTimes(0); + expect(createActivitySpy).toHaveBeenCalledTimes(1); expect(upsertContacts).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledWith( + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledWith( expect.objectContaining({ isDevelopedByCompanyOrOrg: true, projectName: 'TheProject', projectLocation: 'Some place', hasAppliedProvincialPermits: true, - submissionId: '11111111', + submissionId: expect.stringMatching(uuidv4Pattern), activityId: '00000000', submittedAt: expect.stringMatching(isoPattern), intakeStatus: IntakeStatus.SUBMITTED, applicationStatus: ApplicationStatus.NEW }) ); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); }); it('sets intake status to Submitted', async () => { const req = { - body: { - activityId: '00000000', - submissionId: '11111111' - }, + body: {}, currentContext: CURRENT_CONTEXT }; const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); + createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); upsertContacts.mockResolvedValue(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.submitDraft(req as any, res as any, next); - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(upsertContacts).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledWith( - expect.objectContaining({ - intakeStatus: IntakeStatus.SUBMITTED - }) - ); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - }); - - it('deletes all existing permits before creating new ones', async () => { - const now = new Date().toISOString(); - - const req = { - body: { - activityId: '00000000', - submissionId: '11111111', - appliedPermits: [ - { - permitTypeId: 1, - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now - } - ] - }, - currentContext: CURRENT_CONTEXT - }; - const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000', submissionId: '11111111' } as Submission); - createPermitSpy.mockResolvedValue({} as Permit); - deletePermitsByActivitySpy.mockResolvedValue(0); - upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.submitDraft(req as any, res as any, next); - const deleteOrder = deletePermitsByActivitySpy.mock.invocationCallOrder[0]; - const createOrder = createPermitSpy.mock.invocationCallOrder[0]; - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(upsertContacts).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - expect(createPermitSpy).toHaveBeenCalledTimes(1); - expect(createPermitSpy).toHaveBeenCalledWith( + expect(createActivitySpy).toHaveBeenCalledTimes(1); + expect(upsertContacts).toHaveBeenCalledTimes(0); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledWith( expect.objectContaining({ - permitTypeId: 1, - activityId: '00000000', - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now + intakeStatus: IntakeStatus.SUBMITTED }) ); - expect(deleteOrder).toBeLessThan(createOrder); }); it('creates permits if they exist', async () => { @@ -877,8 +804,6 @@ describe('submitDraft', () => { const req = { body: { - activityId: '00000000', - submissionId: '11111111', appliedPermits: [ { permitTypeId: 1, @@ -905,17 +830,17 @@ describe('submitDraft', () => { }; const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); + createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); + createSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); createPermitSpy.mockResolvedValue({} as Permit); - deletePermitsByActivitySpy.mockResolvedValue(0); upsertContacts.mockResolvedValue(); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.submitDraft(req as any, res as any, next); - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); + expect(createActivitySpy).toHaveBeenCalledTimes(1); + expect(upsertContacts).toHaveBeenCalledTimes(0); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); + expect(createSubmissionSpy).toHaveBeenCalledTimes(1); expect(createPermitSpy).toHaveBeenCalledTimes(3); expect(createPermitSpy).toHaveBeenNthCalledWith( 1, @@ -951,37 +876,15 @@ describe('submitDraft', () => { describe('updateDraft', () => { // Mock service calls - const createPermitSpy = jest.spyOn(permitService, 'createPermit'); - const updateSubmissionSpy = jest.spyOn(submissionService, 'updateSubmission'); + const createDraftSpy = jest.spyOn(submissionDraftService, 'createDraft'); + const updateDraftSpy = jest.spyOn(submissionDraftService, 'updateDraft'); const createActivitySpy = jest.spyOn(activityService, 'createActivity'); - const deletePermitsByActivitySpy = jest.spyOn(permitService, 'deletePermitsByActivity'); - it('updates submission with the given activity ID', async () => { - const req = { - body: { activityId: '000000000', submissionId: '11111111' }, - currentContext: CURRENT_CONTEXT - }; - const next = jest.fn(); - - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000', submissionId: '11111111' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateDraft(req as any, res as any, next); - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - expect(res.status).toHaveBeenCalledWith(200); - expect(res.json).toHaveBeenCalledWith({ activityId: '00000000', submissionId: '11111111' }); - }); - - it('populates data from body if it exists', async () => { + it('creates a new draft', async () => { const req = { body: { - activityId: '00000000', - submissionId: '11111111', - applicant: {}, + contactFirstName: 'test', + contactLastName: 'person', basic: { isDevelopedByCompanyOrOrg: true }, @@ -999,173 +902,63 @@ describe('updateDraft', () => { }; const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateDraft(req as any, res as any, next); - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledWith( - expect.objectContaining({ - isDevelopedByCompanyOrOrg: true, - projectName: 'TheProject', - projectLocation: 'Some place', - hasAppliedProvincialPermits: true, - submissionId: '11111111', - activityId: '00000000', - submittedAt: expect.stringMatching(isoPattern), - intakeStatus: IntakeStatus.DRAFT, - applicationStatus: ApplicationStatus.NEW - }) - ); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - }); - - it('sets intake status to Draft', async () => { - const req = { - body: { - activityId: '00000000', - submissionId: '11111111', - submit: true - }, - currentContext: CURRENT_CONTEXT - }; - const next = jest.fn(); + createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); + createDraftSpy.mockResolvedValue({ submissionDraftId: '11111111', activityId: '00000000' } as SubmissionDraft); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - deletePermitsByActivitySpy.mockResolvedValue(0); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.updateDraft(req as any, res as any, next); - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(updateSubmissionSpy).toHaveBeenCalledWith( + expect(createActivitySpy).toHaveBeenCalledTimes(1); + expect(createDraftSpy).toHaveBeenCalledTimes(1); + expect(createDraftSpy).toHaveBeenCalledWith( expect.objectContaining({ - intakeStatus: IntakeStatus.DRAFT + submissionDraftId: expect.stringMatching(uuidv4Pattern), + activityId: '00000000' }) ); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); + expect(res.status).toHaveBeenCalledWith(201); + expect(res.json).toHaveBeenCalledWith({ submissionDraftId: '11111111', activityId: '00000000' }); }); - it('deletes all existing permits before creating new ones', async () => { - const now = new Date().toISOString(); - + it('updates draft with the given submissionDraftId and activityId', async () => { const req = { body: { + submissionDraftId: '11111111', activityId: '00000000', - submissionId: '11111111', - appliedPermits: [ - { - permitTypeId: 1, - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now - } - ] + contactFirstName: 'test', + contactLastName: 'person', + basic: { + isDevelopedByCompanyOrOrg: true + }, + housing: { + projectName: 'TheProject' + }, + location: { + projectLocation: 'Some place' + }, + permits: { + hasAppliedProvincialPermits: true + } }, currentContext: CURRENT_CONTEXT }; const next = jest.fn(); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000', submissionId: '11111111' } as Submission); - createPermitSpy.mockResolvedValue({} as Permit); - deletePermitsByActivitySpy.mockResolvedValue(0); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await submissionController.updateDraft(req as any, res as any, next); - - const deleteOrder = deletePermitsByActivitySpy.mock.invocationCallOrder[0]; - const createOrder = createPermitSpy.mock.invocationCallOrder[0]; - - expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - expect(createPermitSpy).toHaveBeenCalledTimes(1); - expect(createPermitSpy).toHaveBeenCalledWith( - expect.objectContaining({ - permitTypeId: 1, - activityId: '00000000', - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now - }) - ); - expect(deleteOrder).toBeLessThan(createOrder); - }); - - it('creates permits if they exist', async () => { - const now = new Date().toISOString(); - - const req = { - body: { - activityId: '00000000', - submissionId: '11111111', - appliedPermits: [ - { - permitTypeId: 1, - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now - }, - { - permitTypeId: 3, - trackingId: '456', - status: PermitStatus.APPLIED, - statusLastVerified: now - } - ], - investigatePermits: [ - { - permitTypeId: 12, - needed: PermitNeeded.UNDER_INVESTIGATION, - statusLastVerified: now - } - ] - }, - currentContext: CURRENT_CONTEXT - }; - const next = jest.fn(); + createActivitySpy.mockResolvedValue({ activityId: '00000000', initiativeId: Initiative.HOUSING, isDeleted: false }); + updateDraftSpy.mockResolvedValue({ submissionDraftId: '11111111', activityId: '00000000' } as SubmissionDraft); - updateSubmissionSpy.mockResolvedValue({ activityId: '00000000' } as Submission); - createPermitSpy.mockResolvedValue({} as Permit); - deletePermitsByActivitySpy.mockResolvedValue(0); // eslint-disable-next-line @typescript-eslint/no-explicit-any await submissionController.updateDraft(req as any, res as any, next); expect(createActivitySpy).toHaveBeenCalledTimes(0); - expect(updateSubmissionSpy).toHaveBeenCalledTimes(1); - expect(deletePermitsByActivitySpy).toHaveBeenCalledTimes(1); - expect(createPermitSpy).toHaveBeenCalledTimes(3); - expect(createPermitSpy).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - permitTypeId: 1, - activityId: '00000000', - trackingId: '123', - status: PermitStatus.APPLIED, - statusLastVerified: now - }) - ); - expect(createPermitSpy).toHaveBeenNthCalledWith( - 2, - expect.objectContaining({ - permitTypeId: 3, - activityId: '00000000', - trackingId: '456', - status: PermitStatus.APPLIED, - statusLastVerified: now - }) - ); - expect(createPermitSpy).toHaveBeenNthCalledWith( - 3, + expect(updateDraftSpy).toHaveBeenCalledTimes(1); + expect(updateDraftSpy).toHaveBeenCalledWith( expect.objectContaining({ - permitTypeId: 12, - activityId: '00000000', - needed: PermitNeeded.UNDER_INVESTIGATION, - statusLastVerified: now + submissionDraftId: '11111111' }) ); + expect(res.status).toHaveBeenCalledWith(200); + expect(res.json).toHaveBeenCalledWith({ submissionDraftId: '11111111', activityId: '00000000' }); }); }); diff --git a/frontend/src/components/form/FormAutosave.vue b/frontend/src/components/form/FormAutosave.vue index b3432387..efb24a19 100644 --- a/frontend/src/components/form/FormAutosave.vue +++ b/frontend/src/components/form/FormAutosave.vue @@ -32,14 +32,12 @@ onBeforeUnmount(() => { stopAutoSave(); }); -watch(values.value, (o, n) => { +watch(values.value, () => { if (timeoutId.value) { clearTimeout(timeoutId.value); } if (isDirty.value) { - console.log(o); - console.log(n); timeoutId.value = setTimeout(async () => { await callback(); timeoutId.value = null; diff --git a/frontend/src/components/housing/submission/SubmissionIntakeForm.vue b/frontend/src/components/housing/submission/SubmissionIntakeForm.vue index 7b218335..e530b37b 100644 --- a/frontend/src/components/housing/submission/SubmissionIntakeForm.vue +++ b/frontend/src/components/housing/submission/SubmissionIntakeForm.vue @@ -448,7 +448,7 @@ onBeforeMount(async () => { } else { if (submissionId && activityId) { response = (await submissionService.getSubmission(submissionId)).data; - permits = (await permitService.listPermits(activityId)).data; + permits = (await permitService.listPermits({ activityId: activityId })).data; documents = (await documentService.listDocuments(activityId)).data; submissionStore.setDocuments(documents); } diff --git a/frontend/src/views/housing/project/ProjectView.vue b/frontend/src/views/housing/project/ProjectView.vue index e33946f2..b5cc1227 100644 --- a/frontend/src/views/housing/project/ProjectView.vue +++ b/frontend/src/views/housing/project/ProjectView.vue @@ -157,7 +157,7 @@ async function handleEnquirySubmit(enquiryDescription: string = '') { }; try { - const response = await enquiryService.submitDraft(enquiryData); + const response = await enquiryService.createEnquiry(enquiryData); enquiryConfirmationId.value = response?.data?.activityId ? response.data.activityId : ''; } catch (e: any) { toast.error('Failed to submit enquiry', e); diff --git a/frontend/tests/unit/components/shasIntake/EnquiryIntakeForm.spec.ts b/frontend/tests/unit/components/shasIntake/EnquiryIntakeForm.spec.ts index 8fb9b172..4cb09dff 100644 --- a/frontend/tests/unit/components/shasIntake/EnquiryIntakeForm.spec.ts +++ b/frontend/tests/unit/components/shasIntake/EnquiryIntakeForm.spec.ts @@ -127,12 +127,12 @@ describe('EnquiryIntakeForm', () => { const wrapper = mount(EnquiryIntakeForm, wrapperSettings()); await flushPromises(); - const firstNameInput = wrapper.get('[name="contacts.0.firstName"]'); - const lastNameInput = wrapper.get('[name="contacts.0.lastName"]'); - const phoneInput = wrapper.get('[name="contacts.0.phoneNumber"]'); - const emailInput = wrapper.get('[name="contacts.0.email"]'); - const relationsInput = wrapper.get('[name="contacts.0.contactApplicantRelationship"]'); - const contactInput = wrapper.get('[name="contacts.0.contactPreference"]'); + const firstNameInput = wrapper.get('[name="contacts[0].firstName"]'); + const lastNameInput = wrapper.get('[name="contacts[0].lastName"]'); + const phoneInput = wrapper.get('[name="contacts[0].phoneNumber"]'); + const emailInput = wrapper.get('[name="contacts[0].email"]'); + const relationsInput = wrapper.get('[name="contacts[0].contactApplicantRelationship"]'); + const contactInput = wrapper.get('[name="contacts[0].contactPreference"]'); const relatedInput = wrapper.findAll('[name="basic.isRelated"]'); expect(firstNameInput.isVisible()).toBeTruthy(); @@ -291,7 +291,6 @@ describe('EnquiryIntakeForm', () => { formRef.setValues(modifiedFormValues); const result = await formRef?.validate(); - console.log(Object.keys(result.errors)); expect(Object.keys(result.errors).length).toBe(1); expect(result.errors[`${[IntakeFormCategory.CONTACTS]}[0].email`]).toBeTruthy(); }); diff --git a/frontend/tests/unit/service/enquiryService.spec.ts b/frontend/tests/unit/service/enquiryService.spec.ts index 4d9c2ccf..bf02062d 100644 --- a/frontend/tests/unit/service/enquiryService.spec.ts +++ b/frontend/tests/unit/service/enquiryService.spec.ts @@ -63,18 +63,11 @@ describe('enquiryService', () => { expect(getSpy).toHaveBeenCalledWith(`enquiry/${testEnquiry.enquiryId}`); }); - it('calls submitDraft with correct data', async () => { - await enquiryService.submitDraft(testEnquiry); + it('calls createEnquiry with correct data', async () => { + await enquiryService.createEnquiry(testEnquiry); expect(putSpy).toHaveBeenCalledTimes(1); - expect(putSpy).toHaveBeenCalledWith('enquiry/draft/submit', testEnquiry); - }); - - it('calls updateDraft with correct data', async () => { - await enquiryService.updateDraft(testEnquiry); - - expect(putSpy).toHaveBeenCalledTimes(1); - expect(putSpy).toHaveBeenCalledWith('enquiry/draft', testEnquiry); + expect(putSpy).toHaveBeenCalledWith('enquiry', testEnquiry); }); it('calls updateEnquiry with correct data', async () => {