diff --git a/schema/deploy/functions/handle_milestone_form_change_commit.sql b/schema/deploy/functions/handle_milestone_form_change_commit.sql index f8d62430f5..8c7fb67ca2 100644 --- a/schema/deploy/functions/handle_milestone_form_change_commit.sql +++ b/schema/deploy/functions/handle_milestone_form_change_commit.sql @@ -130,7 +130,7 @@ begin 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; + update cif.payment set archived_at = now() where reporting_requirement_id = fc.form_data_record_id and archived_at is null; end if; diff --git a/schema/deploy/functions/handle_milestone_form_change_commit@1.14.0.sql b/schema/deploy/functions/handle_milestone_form_change_commit@1.14.0.sql new file mode 100644 index 0000000000..f8d62430f5 --- /dev/null +++ b/schema/deploy/functions/handle_milestone_form_change_commit@1.14.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 and archived_at is null; + -- 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 and archived_at is null; + 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 ac5437c673..f8d62430f5 100644 --- a/schema/revert/functions/handle_milestone_form_change_commit.sql +++ b/schema/revert/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/revert/functions/handle_milestone_form_change_commit@1.14.0.sql b/schema/revert/functions/handle_milestone_form_change_commit@1.14.0.sql new file mode 100644 index 0000000000..ac5437c673 --- /dev/null +++ b/schema/revert/functions/handle_milestone_form_change_commit@1.14.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/sqitch.plan b/schema/sqitch.plan index 9e563bc5a5..6265303236 100644 --- a/schema/sqitch.plan +++ b/schema/sqitch.plan @@ -361,3 +361,4 @@ computed_columns/form_change_holdback_amount_to_date [computed_columns/form_chan computed_columns/form_change_total_project_value [computed_columns/form_change_total_project_value@1.13.0] 2023-07-27T22:19:30Z Gurjeet Matharu # Modified cif.form_change_total_project_value function to return null if any input values are missing computed_columns/form_change_actual_performance_milestone_amount [computed_columns/form_change_actual_performance_milestone_amount@1.13.0] 2023-09-07T20:12:19Z Dylan Leard # Migration: actual amount should calculate based on maximum_performance_milestone_amount @1.14.0 2023-09-26T22:35:17Z Josh Larouche # release v1.14.0 +functions/handle_milestone_form_change_commit [functions/handle_milestone_form_change_commit@1.14.0] 2023-09-29T20:17:51Z Sepehr Sobhani # Update commit handler to fix the issue with archiving milestones diff --git a/schema/verify/functions/handle_milestone_form_change_commit@1.14.0.sql b/schema/verify/functions/handle_milestone_form_change_commit@1.14.0.sql new file mode 100644 index 0000000000..e9111d0a77 --- /dev/null +++ b/schema/verify/functions/handle_milestone_form_change_commit@1.14.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;