diff --git a/schema/deploy/functions/handle_milestone_form_change_commit.sql b/schema/deploy/functions/handle_milestone_form_change_commit.sql index ac5437c673..f8d62430f5 100644 --- a/schema/deploy/functions/handle_milestone_form_change_commit.sql +++ b/schema/deploy/functions/handle_milestone_form_change_commit.sql @@ -107,7 +107,7 @@ begin gross_amount = adjusted_or_calculated_gross_amount, net_amount = adjusted_or_calculated_net_amount, date_sent_to_csnr = (fc.new_form_data->>'dateSentToCsnr')::timestamptz - where reporting_requirement_id = fc.form_data_record_id; + where reporting_requirement_id = fc.form_data_record_id and archived_at is null; -- otherwise we create a new payment record else insert into cif.payment( @@ -123,7 +123,7 @@ begin ); end if; else - update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id and archived_at is null; end if; elsif fc.operation = 'archive' then diff --git a/schema/deploy/functions/handle_milestone_form_change_commit@1.12.0.sql b/schema/deploy/functions/handle_milestone_form_change_commit@1.12.0.sql new file mode 100644 index 0000000000..ac5437c673 --- /dev/null +++ b/schema/deploy/functions/handle_milestone_form_change_commit@1.12.0.sql @@ -0,0 +1,149 @@ +-- Deploy cif:functions/handle_milestone_form_change_commit to pg +-- requires: tables/form_change + +begin; + +create or replace function cif_private.handle_milestone_form_change_commit(fc cif.form_change) + returns int as $$ +declare + reporting_requirement_record_id int; + report_is_eligible_for_expenses boolean; + adjusted_or_calculated_gross_amount numeric; + adjusted_or_calculated_net_amount numeric; +begin + + -- If there is no change in the form data, return the form_change record and do not touch the associated table. + if (fc.new_form_data = '{}') then + return fc.form_data_record_id; -- can be null if creating with empty form data...problem? + end if; + + report_is_eligible_for_expenses := fc.new_form_data->>'reportType' in (select name from cif.report_type where has_expenses=true); + adjusted_or_calculated_gross_amount := coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric); + adjusted_or_calculated_net_amount := coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric); + + if (fc.change_status = 'committed') then + raise exception 'Cannot commit form_change. It has already been committed.'; + end if; + + reporting_requirement_record_id := fc.form_data_record_id; + + if fc.operation = 'create' then + + insert into cif.reporting_requirement( + project_id, + report_type, + report_due_date, + submitted_date, + comments, + reporting_requirement_index, + description + ) values ( + (select form_data_record_id from cif.form_change pfc where form_data_table_name = 'project' and pfc.project_revision_id = fc.project_revision_id), + (fc.new_form_data->>'reportType'), + (fc.new_form_data->>'reportDueDate')::timestamptz, + (fc.new_form_data->>'submittedDate')::timestamptz, + (fc.new_form_data->>'comments'), + (fc.new_form_data->>'reportingRequirementIndex')::int, + (fc.new_form_data->>'description') + ) returning id into reporting_requirement_record_id; + + insert into cif.milestone_report( + reporting_requirement_id, + substantial_completion_date, + certified_by, + certifier_professional_designation, + maximum_amount, + total_eligible_expenses + ) values ( + reporting_requirement_record_id, + (fc.new_form_data->>'substantialCompletionDate')::timestamptz, + (fc.new_form_data->>'certifiedBy'), + (fc.new_form_data->>'certifierProfessionalDesignation'), + (fc.new_form_data->>'maximumAmount')::numeric, + (fc.new_form_data->>'totalEligibleExpenses')::numeric + ); + + -- Only create a payment if the report type is eligible for expenses + if report_is_eligible_for_expenses then + insert into cif.payment( + reporting_requirement_id, + gross_amount, + net_amount, + date_sent_to_csnr + ) values ( + reporting_requirement_record_id, + adjusted_or_calculated_gross_amount, + adjusted_or_calculated_net_amount, + (fc.new_form_data->>'dateSentToCsnr')::timestamptz + ); + end if; + + update cif.form_change set form_data_record_id = reporting_requirement_record_id where id = fc.id; + + elsif fc.operation = 'update' then + + update cif.reporting_requirement rr set + report_type = (fc.new_form_data->>'reportType'), + report_due_date = (fc.new_form_data->>'reportDueDate')::timestamptz, + submitted_date = (fc.new_form_data->>'submittedDate')::timestamptz, + comments = (fc.new_form_data->>'comments'), + reporting_requirement_index = (fc.new_form_data->>'reportingRequirementIndex')::int, + description = (fc.new_form_data->>'description') + where rr.id = fc.form_data_record_id; + + update cif.milestone_report mr set + substantial_completion_date = (fc.new_form_data->>'substantialCompletionDate')::timestamptz, + certified_by = (fc.new_form_data->>'certifiedBy'), + certifier_professional_designation = (fc.new_form_data->>'certifierProfessionalDesignation'), + maximum_amount = (fc.new_form_data->>'maximumAmount')::numeric, + total_eligible_expenses = (fc.new_form_data->>'totalEligibleExpenses')::numeric + where mr.reporting_requirement_id = fc.form_data_record_id; + + if report_is_eligible_for_expenses then + -- This part is covering the case where the user changes the report type from a non-expense report type to an expense report type (or vice versa) + -- if there's a payment record with the same reporting_requirement_id, we update it + if exists(select 1 from cif.payment where reporting_requirement_id = fc.form_data_record_id and archived_at is null) then + update cif.payment set + gross_amount = adjusted_or_calculated_gross_amount, + net_amount = adjusted_or_calculated_net_amount, + date_sent_to_csnr = (fc.new_form_data->>'dateSentToCsnr')::timestamptz + where reporting_requirement_id = fc.form_data_record_id; + -- otherwise we create a new payment record + else + insert into cif.payment( + reporting_requirement_id, + gross_amount, + net_amount, + date_sent_to_csnr + ) values ( + reporting_requirement_record_id, + adjusted_or_calculated_gross_amount, + adjusted_or_calculated_net_amount, + (fc.new_form_data->>'dateSentToCsnr')::timestamptz + ); + end if; + else + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + end if; + + elsif fc.operation = 'archive' then + + update cif.reporting_requirement set archived_at = now() where id = fc.form_data_record_id; + update cif.milestone_report set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + + end if; + + return reporting_requirement_record_id; +end; +$$ language plpgsql volatile; + +grant execute on function cif_private.handle_milestone_form_change_commit to cif_internal, cif_external, cif_admin; + +comment on function cif_private.handle_milestone_form_change_commit + is $$ + The custom function used to parse milestone form_change data into table data. + The data within the single form_change record is parsed into the reporting_requirement, milestone_report and payment tables. + $$; + +commit; diff --git a/schema/revert/functions/handle_milestone_form_change_commit.sql b/schema/revert/functions/handle_milestone_form_change_commit.sql index a594ad0b5e..ac5437c673 100644 --- a/schema/revert/functions/handle_milestone_form_change_commit.sql +++ b/schema/revert/functions/handle_milestone_form_change_commit.sql @@ -7,6 +7,9 @@ create or replace function cif_private.handle_milestone_form_change_commit(fc ci returns int as $$ declare reporting_requirement_record_id int; + report_is_eligible_for_expenses boolean; + adjusted_or_calculated_gross_amount numeric; + adjusted_or_calculated_net_amount numeric; begin -- If there is no change in the form data, return the form_change record and do not touch the associated table. @@ -14,6 +17,10 @@ begin return fc.form_data_record_id; -- can be null if creating with empty form data...problem? end if; + report_is_eligible_for_expenses := fc.new_form_data->>'reportType' in (select name from cif.report_type where has_expenses=true); + adjusted_or_calculated_gross_amount := coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric); + adjusted_or_calculated_net_amount := coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric); + if (fc.change_status = 'committed') then raise exception 'Cannot commit form_change. It has already been committed.'; end if; @@ -56,17 +63,20 @@ begin (fc.new_form_data->>'totalEligibleExpenses')::numeric ); - insert into cif.payment( - reporting_requirement_id, - gross_amount, - net_amount, - date_sent_to_csnr - ) values ( - reporting_requirement_record_id, - coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric), - coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric), - (fc.new_form_data->>'dateSentToCsnr')::timestamptz - ); + -- Only create a payment if the report type is eligible for expenses + if report_is_eligible_for_expenses then + insert into cif.payment( + reporting_requirement_id, + gross_amount, + net_amount, + date_sent_to_csnr + ) values ( + reporting_requirement_record_id, + adjusted_or_calculated_gross_amount, + adjusted_or_calculated_net_amount, + (fc.new_form_data->>'dateSentToCsnr')::timestamptz + ); + end if; update cif.form_change set form_data_record_id = reporting_requirement_record_id where id = fc.id; @@ -89,12 +99,32 @@ begin total_eligible_expenses = (fc.new_form_data->>'totalEligibleExpenses')::numeric where mr.reporting_requirement_id = fc.form_data_record_id; - - update cif.payment py set - gross_amount = coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric), - net_amount = coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric), - date_sent_to_csnr = (fc.new_form_data->>'dateSentToCsnr')::timestamptz - where py.reporting_requirement_id = fc.form_data_record_id; + if report_is_eligible_for_expenses then + -- This part is covering the case where the user changes the report type from a non-expense report type to an expense report type (or vice versa) + -- if there's a payment record with the same reporting_requirement_id, we update it + if exists(select 1 from cif.payment where reporting_requirement_id = fc.form_data_record_id and archived_at is null) then + update cif.payment set + gross_amount = adjusted_or_calculated_gross_amount, + net_amount = adjusted_or_calculated_net_amount, + date_sent_to_csnr = (fc.new_form_data->>'dateSentToCsnr')::timestamptz + where reporting_requirement_id = fc.form_data_record_id; + -- otherwise we create a new payment record + else + insert into cif.payment( + reporting_requirement_id, + gross_amount, + net_amount, + date_sent_to_csnr + ) values ( + reporting_requirement_record_id, + adjusted_or_calculated_gross_amount, + adjusted_or_calculated_net_amount, + (fc.new_form_data->>'dateSentToCsnr')::timestamptz + ); + end if; + else + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + end if; elsif fc.operation = 'archive' then diff --git a/schema/revert/functions/handle_milestone_form_change_commit@1.12.0.sql b/schema/revert/functions/handle_milestone_form_change_commit@1.12.0.sql new file mode 100644 index 0000000000..a594ad0b5e --- /dev/null +++ b/schema/revert/functions/handle_milestone_form_change_commit@1.12.0.sql @@ -0,0 +1,119 @@ +-- Deploy cif:functions/handle_milestone_form_change_commit to pg +-- requires: tables/form_change + +begin; + +create or replace function cif_private.handle_milestone_form_change_commit(fc cif.form_change) + returns int as $$ +declare + reporting_requirement_record_id int; +begin + + -- If there is no change in the form data, return the form_change record and do not touch the associated table. + if (fc.new_form_data = '{}') then + return fc.form_data_record_id; -- can be null if creating with empty form data...problem? + end if; + + if (fc.change_status = 'committed') then + raise exception 'Cannot commit form_change. It has already been committed.'; + end if; + + reporting_requirement_record_id := fc.form_data_record_id; + + if fc.operation = 'create' then + + insert into cif.reporting_requirement( + project_id, + report_type, + report_due_date, + submitted_date, + comments, + reporting_requirement_index, + description + ) values ( + (select form_data_record_id from cif.form_change pfc where form_data_table_name = 'project' and pfc.project_revision_id = fc.project_revision_id), + (fc.new_form_data->>'reportType'), + (fc.new_form_data->>'reportDueDate')::timestamptz, + (fc.new_form_data->>'submittedDate')::timestamptz, + (fc.new_form_data->>'comments'), + (fc.new_form_data->>'reportingRequirementIndex')::int, + (fc.new_form_data->>'description') + ) returning id into reporting_requirement_record_id; + + insert into cif.milestone_report( + reporting_requirement_id, + substantial_completion_date, + certified_by, + certifier_professional_designation, + maximum_amount, + total_eligible_expenses + ) values ( + reporting_requirement_record_id, + (fc.new_form_data->>'substantialCompletionDate')::timestamptz, + (fc.new_form_data->>'certifiedBy'), + (fc.new_form_data->>'certifierProfessionalDesignation'), + (fc.new_form_data->>'maximumAmount')::numeric, + (fc.new_form_data->>'totalEligibleExpenses')::numeric + ); + + insert into cif.payment( + reporting_requirement_id, + gross_amount, + net_amount, + date_sent_to_csnr + ) values ( + reporting_requirement_record_id, + coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric), + coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric), + (fc.new_form_data->>'dateSentToCsnr')::timestamptz + ); + + update cif.form_change set form_data_record_id = reporting_requirement_record_id where id = fc.id; + + elsif fc.operation = 'update' then + + update cif.reporting_requirement rr set + report_type = (fc.new_form_data->>'reportType'), + report_due_date = (fc.new_form_data->>'reportDueDate')::timestamptz, + submitted_date = (fc.new_form_data->>'submittedDate')::timestamptz, + comments = (fc.new_form_data->>'comments'), + reporting_requirement_index = (fc.new_form_data->>'reportingRequirementIndex')::int, + description = (fc.new_form_data->>'description') + where rr.id = fc.form_data_record_id; + + update cif.milestone_report mr set + substantial_completion_date = (fc.new_form_data->>'substantialCompletionDate')::timestamptz, + certified_by = (fc.new_form_data->>'certifiedBy'), + certifier_professional_designation = (fc.new_form_data->>'certifierProfessionalDesignation'), + maximum_amount = (fc.new_form_data->>'maximumAmount')::numeric, + total_eligible_expenses = (fc.new_form_data->>'totalEligibleExpenses')::numeric + where mr.reporting_requirement_id = fc.form_data_record_id; + + + update cif.payment py set + gross_amount = coalesce((fc.new_form_data->>'adjustedGrossAmount')::numeric, (fc.new_form_data->>'calculatedGrossAmount')::numeric), + net_amount = coalesce((fc.new_form_data->>'adjustedNetAmount')::numeric, (fc.new_form_data->>'calculatedNetAmount')::numeric), + date_sent_to_csnr = (fc.new_form_data->>'dateSentToCsnr')::timestamptz + where py.reporting_requirement_id = fc.form_data_record_id; + + elsif fc.operation = 'archive' then + + update cif.reporting_requirement set archived_at = now() where id = fc.form_data_record_id; + update cif.milestone_report set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id; + + end if; + + return reporting_requirement_record_id; +end; +$$ language plpgsql volatile; + +grant execute on function cif_private.handle_milestone_form_change_commit to cif_internal, cif_external, cif_admin; + +comment on function cif_private.handle_milestone_form_change_commit + is $$ + The custom function used to parse milestone form_change data into table data. + The data within the single form_change record is parsed into the reporting_requirement, milestone_report and payment tables. + $$; + +commit; diff --git a/schema/sqitch.plan b/schema/sqitch.plan index 7bb72d6f4f..8a70fb7e88 100644 --- a/schema/sqitch.plan +++ b/schema/sqitch.plan @@ -353,3 +353,4 @@ functions/migration_rebuild_project_summary_report_history 2023-07-28T18:00:34Z migrations/009_rebuild_project_summary_history 2023-08-04T22:08:03Z Brianna Cerkiewicz # Migration to call fuction that rebuilds project summary report history computed_columns/project_milestone_status 2023-08-08T16:07:16Z Brianna Cerkiewicz # Create computed column to calculate a project's milestone status (late, complete, not due, etc.) @1.12.0 2023-08-15T23:37:54Z Sepehr Sobhani # release v1.12.0 +functions/handle_milestone_form_change_commit [functions/handle_milestone_form_change_commit@1.12.0] 2023-08-31T18:47:59Z Sepehr Sobhani # Update commit handler to fix the issue with updating archived records diff --git a/schema/test/unit/functions/handle_milestone_form_change_commit_test.sql b/schema/test/unit/functions/handle_milestone_form_change_commit_test.sql index 13535fa571..6a8add49e3 100644 --- a/schema/test/unit/functions/handle_milestone_form_change_commit_test.sql +++ b/schema/test/unit/functions/handle_milestone_form_change_commit_test.sql @@ -1,6 +1,6 @@ begin; -select plan(17); +select plan(19); /** SETUP **/ truncate table cif.form_change, @@ -34,9 +34,9 @@ insert into cif.project_revision(id, change_status, project_id) values (1, 'pending', 1); -select cif.create_form_change('create', 'milestone', 'cif', 'milestone', '{}', 7, null, 'staged', '[]'); -select cif.create_form_change('update', 'milestone', 'cif', 'milestone', '{"description": "value"}', null, null, 'committed', '[]'); -select cif.create_form_change('create', 'milestone', 'cif', 'milestone', +select cif.create_form_change('create', 'milestone', 'cif', 'reporting_requirement', '{}', 7, null, 'staged', '[]'); +select cif.create_form_change('update', 'milestone', 'cif', 'reporting_requirement', '{"description": "value"}', null, null, 'committed', '[]'); +select cif.create_form_change('create', 'milestone', 'cif', 'reporting_requirement', '{ "reportType": "General Milestone", "reportDueDate": "2021-08-31 14:24:46.318423-07", @@ -55,7 +55,7 @@ select cif.create_form_change('create', 'milestone', 'cif', 'milestone', "dateSentToCsnr": "2021-08-29 14:24:46.318423-07" }', null, 1, 'staged', '[]'); -select cif.create_form_change('create', 'milestone', 'cif', 'milestone', +select cif.create_form_change('create', 'milestone', 'cif', 'reporting_requirement', '{ "reportType": "Reporting Milestone", "reportDueDate": "2021-10-31 14:24:46.318423-07", @@ -221,7 +221,7 @@ insert into cif.project_revision(id, change_status, project_id) overriding system value values (2, 'pending', 1); -select cif.create_form_change('update', 'milestone', 'cif', 'milestone', +select cif.create_form_change('update', 'milestone', 'cif', 'reporting_requirement', '{ "reportType": "General Milestone", "reportDueDate": "2021-10-31 14:24:46.318423-07", @@ -305,7 +305,7 @@ insert into cif.project_revision(id, change_status, project_id) overriding system value values (3, 'pending', 1); -select cif.create_form_change('update', 'milestone', 'cif', 'milestone', +select cif.create_form_change('update', 'milestone', 'cif', 'reporting_requirement', '{ "reportType": "Reporting Milestone", "reportDueDate": "2021-10-31 14:24:46.318423-07", @@ -382,7 +382,7 @@ insert into cif.project_revision(id, change_status, project_id) overriding system value values (4, 'pending', 1); -select cif.create_form_change('update', 'milestone', 'cif', 'milestone', +select cif.create_form_change('update', 'milestone', 'cif', 'reporting_requirement', '{ "reportType": "Advanced Milestone", "reportDueDate": "2021-10-31 14:24:46.318423-07", @@ -420,8 +420,8 @@ select results_eq( $$, 'The values were correctly updated in reporting_requirement table on update with a expense report type' ); --- -- milestone_report table --- -- Test 16 +-- milestone_report table +-- Test 16 select results_eq( $$ select reporting_requirement_id, @@ -442,8 +442,8 @@ select results_eq( $$, 'The values were correctly updated in the milestone_report table on update with a expense report type' ); --- -- payment table --- -- Test 17 +-- payment table +-- Test 17 select results_eq( $$ select reporting_requirement_id, gross_amount, net_amount, date_sent_to_csnr, archived_at from cif.payment where reporting_requirement_id = 1 and id = 2; @@ -460,6 +460,41 @@ select results_eq( 'Correctly add a new payment record when updating a non-expense report type milestone with a expense report type' ); +-- Test 18 +select lives_ok( + $$ + select cif_private.handle_milestone_form_change_commit((select row(form_change.*)::cif.form_change from cif.form_change where id = 8)); + $$, + 'Throws no -Deleted records cannot be modified- error and update the correct payment record that is not archived when updating a milestone with a expense report type' +); + +-- Testing updating a non-expense report type milestone with a expense report type +update cif.project_revision set change_status = 'committed' where id = 4; +insert into cif.project_revision(id, change_status, project_id) + overriding system value + values (5, 'pending', 1); + +select cif.create_form_change('update', 'milestone', 'cif', 'reporting_requirement', + '{ + "reportType": "Reporting Milestone", + "reportDueDate": "2021-10-31 14:24:46.318423-07", + "submittedDate": "2021-09-30 14:24:46.318423-07", + "comments": "reporting milestone comments", + "reportingRequirementIndex": 1, + "description": "desc", + "substantialCompletionDate": "2021-09-29 14:24:46.318423-07", + "certifiedBy": "Reporting Jon", + "certifierProfessionalDesignation": "Reporting Eng" + }', 1, 5, 'staged', '[]'); + +-- Test 19 +select lives_ok( + $$ + select cif_private.handle_milestone_form_change_commit((select row(form_change.*)::cif.form_change from cif.form_change where id = 9)); + $$, + 'Throws no -Deleted records cannot be modified- error and update the correct payment record that is not archived when updating a milestone with a non-expense report type' +); + select finish(); rollback; diff --git a/schema/verify/functions/handle_milestone_form_change_commit@1.12.0.sql b/schema/verify/functions/handle_milestone_form_change_commit@1.12.0.sql new file mode 100644 index 0000000000..e9111d0a77 --- /dev/null +++ b/schema/verify/functions/handle_milestone_form_change_commit@1.12.0.sql @@ -0,0 +1,7 @@ +-- Verify cif:functions/handle_milestone_form_change_commit on pg + +begin; + +select pg_get_functiondef('cif_private.handle_milestone_form_change_commit(cif.form_change)'::regprocedure); + +rollback;