svod2-filter-fix: строки совсем без значений не попадают в свод 2 #195
15
api/alembic/versions/0049_summary2_related_records.py
Normal file
15
api/alembic/versions/0049_summary2_related_records.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from sql_migration import run_sql_file_migration
|
||||||
|
|
||||||
|
|
||||||
|
revision = "0049"
|
||||||
|
down_revision = "0048"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
run_sql_file_migration("0049_summary2_related_records.sql")
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
pass
|
||||||
842
api/alembic/versions/sql/0049_summary2_related_records.sql
Normal file
842
api/alembic/versions/sql/0049_summary2_related_records.sql
Normal file
@ -0,0 +1,842 @@
|
|||||||
|
-- Keep source lines with a related data record. For plan and quarter data,
|
||||||
|
-- require a nonzero number, a date, or text containing a non-whitespace character.
|
||||||
|
-- Other data sections qualify by record presence, even if their fields are empty.
|
||||||
|
-- DISTINCT prevents one-to-many joins from duplicating lines before pagination.
|
||||||
|
-- Technical synchronization links and audit history are not data sections.
|
||||||
|
CREATE OR REPLACE FUNCTION v3.v_svod2_go(
|
||||||
|
p_year integer,
|
||||||
|
p_org_unit_ids integer[] DEFAULT NULL,
|
||||||
|
p_offset integer DEFAULT NULL,
|
||||||
|
p_limit integer DEFAULT NULL,
|
||||||
|
p_sort_by varchar DEFAULT 'org_name',
|
||||||
|
p_sort_direction varchar DEFAULT 'asc'
|
||||||
|
)
|
||||||
|
RETURNS TABLE(
|
||||||
|
source_form varchar, source_id integer, source_org_unit_id integer,
|
||||||
|
org_name varchar, smeta_type varchar,
|
||||||
|
source_line_id integer, sort_order bigint, data jsonb
|
||||||
|
)
|
||||||
|
LANGUAGE sql STABLE SET search_path TO 'v3', 'pg_catalog'
|
||||||
|
AS $function$
|
||||||
|
WITH empty_sections AS MATERIALIZED (
|
||||||
|
SELECT
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.plan, '{}'::jsonb)) - 'line_id')
|
||||||
|
|| jsonb_build_object('plan_year', NULL) AS plan,
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.sequestration, '{}'::jsonb))
|
||||||
|
- 'id' - 'line_id' - 'actor') || jsonb_build_object('adj_year', NULL) AS sequestration,
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.reserve, '{}'::jsonb)) - 'line_id')
|
||||||
|
|| jsonb_build_object('amount_year', NULL) AS reserve,
|
||||||
|
jsonb_build_object(
|
||||||
|
'approved_q1', NULL, 'approved_q2', NULL,
|
||||||
|
'approved_q3', NULL, 'approved_q4', NULL,
|
||||||
|
'approved_year', NULL
|
||||||
|
) AS approved,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.contract_summary, '{}'::jsonb)) - 'line_id' AS contract_summary,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.allocation, '{}'::jsonb)) - 'line_id' AS allocation,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.collegial_approval, '{}'::jsonb)) - 'line_id' AS collegial,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.ckk, '{}'::jsonb)) - 'line_id' AS ckk,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.contract_detail, '{}'::jsonb)) - 'line_id' AS contract_detail,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.booking, '{}'::jsonb)) - 'id' - 'line_id' AS booking,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.budget_line_quarter, '{}'::jsonb))
|
||||||
|
- 'line_id' - 'quarter' AS quarter
|
||||||
|
), eligible_lines AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
bl.id AS line_id,
|
||||||
|
bf.form_type_code,
|
||||||
|
bf.id AS form_id,
|
||||||
|
bf.org_unit_id,
|
||||||
|
ou.title AS org_name,
|
||||||
|
ei.section_code,
|
||||||
|
ei.item_id,
|
||||||
|
CASE ei.sheet WHEN 'AHR' THEN 'АХР' WHEN 'CAP' THEN 'КВ'
|
||||||
|
WHEN 'OPER' THEN 'Операц' END::varchar AS smeta_type
|
||||||
|
FROM v3.budget_form bf
|
||||||
|
JOIN v3.budget_line bl ON bl.budget_form_id = bf.id
|
||||||
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
|
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||||||
|
LEFT JOIN v3.plan presence_plan ON presence_plan.line_id = bl.id
|
||||||
|
LEFT JOIN v3.reserve presence_reserve ON presence_reserve.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_summary presence_contract_summary ON presence_contract_summary.line_id = bl.id
|
||||||
|
LEFT JOIN v3.allocation presence_allocation ON presence_allocation.line_id = bl.id
|
||||||
|
LEFT JOIN v3.collegial_approval presence_collegial_approval ON presence_collegial_approval.line_id = bl.id
|
||||||
|
LEFT JOIN v3.ckk presence_ckk ON presence_ckk.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_detail presence_contract_detail ON presence_contract_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.sequestration presence_sequestration ON presence_sequestration.line_id = bl.id
|
||||||
|
LEFT JOIN v3.booking presence_booking ON presence_booking.line_id = bl.id
|
||||||
|
LEFT JOIN v3.budget_line_quarter presence_budget_line_quarter ON presence_budget_line_quarter.line_id = bl.id
|
||||||
|
LEFT JOIN v3.rent_detail presence_rent_detail ON presence_rent_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.security_detail presence_security_detail ON presence_security_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.utility_detail presence_utility_detail ON presence_utility_detail.line_id = bl.id
|
||||||
|
WHERE bf.year = p_year
|
||||||
|
AND bf.form_type_code IN ('FORM_1', 'FORM_4')
|
||||||
|
AND ei.sheet IN ('AHR', 'CAP', 'OPER')
|
||||||
|
AND (p_org_unit_ids IS NULL OR cardinality(p_org_unit_ids) = 0
|
||||||
|
OR bf.org_unit_id = ANY(p_org_unit_ids))
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
presence_plan.plan_q1 <> 0
|
||||||
|
OR presence_plan.plan_q2 <> 0
|
||||||
|
OR presence_plan.plan_q3 <> 0
|
||||||
|
OR presence_plan.plan_q4 <> 0
|
||||||
|
OR presence_plan.comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_reserve.line_id IS NOT NULL
|
||||||
|
OR presence_contract_summary.line_id IS NOT NULL
|
||||||
|
OR presence_allocation.line_id IS NOT NULL
|
||||||
|
OR presence_collegial_approval.line_id IS NOT NULL
|
||||||
|
OR presence_ckk.line_id IS NOT NULL
|
||||||
|
OR presence_contract_detail.line_id IS NOT NULL
|
||||||
|
OR presence_sequestration.line_id IS NOT NULL
|
||||||
|
OR presence_booking.line_id IS NOT NULL
|
||||||
|
OR (
|
||||||
|
presence_budget_line_quarter.adj_current <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_ssp <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_reserve <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.target_change <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_date IS NOT NULL
|
||||||
|
OR presence_budget_line_quarter.payment_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_ho <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_act ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.actual_m1 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m2 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m3 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_spod <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q2 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q3 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q4 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_economy <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_acts <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_procurement <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_economy_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_next_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.transfer_far_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.booking_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_eco_change <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_item_adj <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_increase <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_sequester <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_rent_detail.line_id IS NOT NULL
|
||||||
|
OR presence_security_detail.line_id IS NOT NULL
|
||||||
|
OR presence_utility_detail.line_id IS NOT NULL
|
||||||
|
)
|
||||||
|
), numbered_lines AS (
|
||||||
|
SELECT el.*,
|
||||||
|
row_number() OVER (
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.org_name END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.org_name END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.item_id END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.item_id END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.section_code END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.section_code END DESC NULLS LAST,
|
||||||
|
el.form_type_code, el.form_id, el.line_id
|
||||||
|
) AS sort_order
|
||||||
|
FROM eligible_lines el
|
||||||
|
), page AS MATERIALIZED (
|
||||||
|
SELECT * FROM numbered_lines
|
||||||
|
ORDER BY sort_order
|
||||||
|
LIMIT p_limit OFFSET COALESCE(p_offset, 0)
|
||||||
|
), booking_data AS (
|
||||||
|
SELECT b.line_id,
|
||||||
|
jsonb_agg(to_jsonb(b) - 'id' - 'line_id' ORDER BY b.quarter, b.source) AS booking
|
||||||
|
FROM page pg
|
||||||
|
JOIN v3.booking b ON b.line_id = pg.line_id
|
||||||
|
GROUP BY b.line_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
pg.form_type_code,
|
||||||
|
pg.form_id,
|
||||||
|
pg.org_unit_id,
|
||||||
|
pg.org_name,
|
||||||
|
pg.smeta_type,
|
||||||
|
pg.line_id,
|
||||||
|
pg.sort_order,
|
||||||
|
jsonb_build_object(
|
||||||
|
'line_id', bl.id,
|
||||||
|
'header', jsonb_build_object(
|
||||||
|
'section_code', ei.section_code, 'item_id', ei.item_id,
|
||||||
|
'num_group', ei.num_group_id, 'name', COALESCE(bl.name, ei.name),
|
||||||
|
'expense_item_id', ei.id, 'justification', bl.justification,
|
||||||
|
'internal_order', bl.internal_order, 'vsp_id', bl.vsp_id,
|
||||||
|
'project_id', bl.project_id, 'direction', bl.direction,
|
||||||
|
'smeta_direction', CASE bl.direction
|
||||||
|
WHEN 'Support' THEN 'Поддержка'
|
||||||
|
WHEN 'Development' THEN 'Развитие'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
'source_form', pg.form_type_code, 'source_id', pg.form_id,
|
||||||
|
'source_org_unit_id', pg.org_unit_id, 'org_name', pg.org_name,
|
||||||
|
'smeta_type', pg.smeta_type),
|
||||||
|
'plan', COALESCE(
|
||||||
|
(to_jsonb(pl) - 'line_id') || jsonb_build_object(
|
||||||
|
'plan_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0)
|
||||||
|
+ COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0)),
|
||||||
|
e.plan || jsonb_build_object('plan_year', NULL)),
|
||||||
|
'sequestration', jsonb_build_object(
|
||||||
|
'DFIP', COALESCE(
|
||||||
|
(to_jsonb(sd) - 'id' - 'line_id' - 'actor') || jsonb_build_object(
|
||||||
|
'adj_year', COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0)),
|
||||||
|
e.sequestration || jsonb_build_object('adj_year', NULL)),
|
||||||
|
'SSP_GO', COALESCE(
|
||||||
|
(to_jsonb(ss) - 'id' - 'line_id' - 'actor') || jsonb_build_object(
|
||||||
|
'adj_year', COALESCE(ss.adj_q1, 0) + COALESCE(ss.adj_q2, 0)
|
||||||
|
+ COALESCE(ss.adj_q3, 0) + COALESCE(ss.adj_q4, 0)),
|
||||||
|
e.sequestration || jsonb_build_object('adj_year', NULL))),
|
||||||
|
'reserve', COALESCE(
|
||||||
|
(to_jsonb(rv) - 'line_id') || jsonb_build_object(
|
||||||
|
'amount_year', COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0)
|
||||||
|
+ COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)),
|
||||||
|
e.reserve || jsonb_build_object('amount_year', NULL)),
|
||||||
|
'approved', CASE WHEN pl.line_id IS NULL AND sd.id IS NULL
|
||||||
|
AND rv.line_id IS NULL
|
||||||
|
THEN e.approved
|
||||||
|
ELSE jsonb_build_object(
|
||||||
|
'approved_q1', COALESCE(pl.plan_q1, 0) + COALESCE(sd.adj_q1, 0)
|
||||||
|
+ COALESCE(rv.amount_q1, 0),
|
||||||
|
'approved_q2', COALESCE(pl.plan_q2, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(rv.amount_q2, 0),
|
||||||
|
'approved_q3', COALESCE(pl.plan_q3, 0) + COALESCE(sd.adj_q3, 0)
|
||||||
|
+ COALESCE(rv.amount_q3, 0),
|
||||||
|
'approved_q4', COALESCE(pl.plan_q4, 0) + COALESCE(sd.adj_q4, 0)
|
||||||
|
+ COALESCE(rv.amount_q4, 0),
|
||||||
|
'approved_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0)
|
||||||
|
+ COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0)
|
||||||
|
+ COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0)
|
||||||
|
+ COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0)
|
||||||
|
+ COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)) END,
|
||||||
|
'contract_summary', COALESCE(to_jsonb(cs) - 'line_id', e.contract_summary),
|
||||||
|
'allocation', COALESCE(to_jsonb(al) - 'line_id', e.allocation),
|
||||||
|
'collegial', COALESCE(to_jsonb(ca) - 'line_id', e.collegial),
|
||||||
|
'ckk', COALESCE(to_jsonb(ck) - 'line_id', e.ckk),
|
||||||
|
'contract_detail', COALESCE(to_jsonb(cd) - 'line_id', e.contract_detail),
|
||||||
|
'booking', COALESCE(bk.booking, jsonb_build_array(e.booking)),
|
||||||
|
'q1', COALESCE(to_jsonb(q1) - 'line_id' - 'quarter', e.quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', CASE WHEN pg.form_type_code = 'FORM_1'
|
||||||
|
THEN CASE WHEN COALESCE(cd.expenses_q1, 0) > 0 THEN cd.expenses_q1 ELSE ck.expenses_q1 END
|
||||||
|
ELSE COALESCE(q1.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q1
|
||||||
|
WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1
|
||||||
|
ELSE cd.expenses_q1 END) END),
|
||||||
|
'q2', COALESCE(to_jsonb(q2) - 'line_id' - 'quarter', e.quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', CASE WHEN pg.form_type_code = 'FORM_1'
|
||||||
|
THEN CASE WHEN COALESCE(cd.expenses_q2, 0) > 0 THEN cd.expenses_q2 ELSE ck.expenses_q2 END
|
||||||
|
ELSE COALESCE(q2.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q2
|
||||||
|
WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2
|
||||||
|
ELSE cd.expenses_q2 END) END,
|
||||||
|
'plan_revision_eco_change', COALESCE(q2.target_change, q2.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q2.base_plan_correction, q2.plan_revision_item_adj)),
|
||||||
|
'q3', COALESCE(to_jsonb(q3) - 'line_id' - 'quarter', e.quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', CASE WHEN pg.form_type_code = 'FORM_1'
|
||||||
|
THEN CASE WHEN COALESCE(cd.expenses_q3, 0) > 0 THEN cd.expenses_q3 ELSE ck.expenses_q3 END
|
||||||
|
ELSE COALESCE(q3.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q3
|
||||||
|
WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3
|
||||||
|
ELSE cd.expenses_q3 END) END,
|
||||||
|
'plan_revision_eco_change', COALESCE(q3.target_change, q3.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q3.base_plan_correction, q3.plan_revision_item_adj)),
|
||||||
|
'q4', COALESCE(to_jsonb(q4) - 'line_id' - 'quarter', e.quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', CASE WHEN pg.form_type_code = 'FORM_1'
|
||||||
|
THEN CASE WHEN COALESCE(cd.expenses_q4, 0) > 0 THEN cd.expenses_q4 ELSE ck.expenses_q4 END
|
||||||
|
ELSE COALESCE(q4.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q4
|
||||||
|
WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4
|
||||||
|
ELSE cd.expenses_q4 END) END,
|
||||||
|
'plan_revision_eco_change', COALESCE(q4.target_change, q4.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q4.base_plan_correction, q4.plan_revision_item_adj))
|
||||||
|
) AS data
|
||||||
|
FROM page pg
|
||||||
|
JOIN v3.budget_line bl ON bl.id = pg.line_id
|
||||||
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
|
LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id
|
||||||
|
CROSS JOIN empty_sections e
|
||||||
|
LEFT JOIN v3.plan pl ON pl.line_id = bl.id
|
||||||
|
LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor = 'DFIP'
|
||||||
|
LEFT JOIN v3.sequestration ss ON ss.line_id = bl.id AND ss.actor = 'SSP_GO'
|
||||||
|
LEFT JOIN v3.reserve rv ON rv.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_summary cs ON cs.line_id = bl.id
|
||||||
|
LEFT JOIN v3.allocation al ON al.line_id = bl.id
|
||||||
|
LEFT JOIN v3.collegial_approval ca ON ca.line_id = bl.id
|
||||||
|
LEFT JOIN v3.ckk ck ON ck.line_id = bl.id
|
||||||
|
LEFT JOIN booking_data bk ON bk.line_id = bl.id
|
||||||
|
LEFT JOIN v3.budget_line_quarter q1 ON q1.line_id = bl.id AND q1.quarter = 1
|
||||||
|
LEFT JOIN v3.budget_line_quarter q2 ON q2.line_id = bl.id AND q2.quarter = 2
|
||||||
|
LEFT JOIN v3.budget_line_quarter q3 ON q3.line_id = bl.id AND q3.quarter = 3
|
||||||
|
LEFT JOIN v3.budget_line_quarter q4 ON q4.line_id = bl.id AND q4.quarter = 4
|
||||||
|
ORDER BY pg.sort_order;
|
||||||
|
$function$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION v3.v_svod2_rf(
|
||||||
|
p_year integer,
|
||||||
|
p_org_unit_ids integer[] DEFAULT NULL,
|
||||||
|
p_offset integer DEFAULT NULL,
|
||||||
|
p_limit integer DEFAULT NULL,
|
||||||
|
p_sort_by varchar DEFAULT 'org_name',
|
||||||
|
p_sort_direction varchar DEFAULT 'asc'
|
||||||
|
)
|
||||||
|
RETURNS TABLE(
|
||||||
|
source_form varchar, source_id integer, source_org_unit_id integer,
|
||||||
|
org_name varchar, smeta_type varchar,
|
||||||
|
source_line_id integer, sort_order bigint, data jsonb
|
||||||
|
)
|
||||||
|
LANGUAGE sql STABLE SET search_path TO 'v3', 'pg_catalog'
|
||||||
|
AS $function$
|
||||||
|
WITH empty_sections AS MATERIALIZED (
|
||||||
|
SELECT
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.plan, '{}'::jsonb)) - 'line_id')
|
||||||
|
|| jsonb_build_object('plan_year', NULL) AS plan,
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.sequestration, '{}'::jsonb))
|
||||||
|
- 'id' - 'line_id' - 'actor') || jsonb_build_object('adj_year', NULL) AS sequestration,
|
||||||
|
(to_jsonb(jsonb_populate_record(NULL::v3.reserve, '{}'::jsonb)) - 'line_id')
|
||||||
|
|| jsonb_build_object('amount_year', NULL) AS reserve,
|
||||||
|
jsonb_build_object(
|
||||||
|
'approved_q1', NULL, 'approved_q2', NULL,
|
||||||
|
'approved_q3', NULL, 'approved_q4', NULL,
|
||||||
|
'approved_year', NULL
|
||||||
|
) AS approved,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.contract_summary, '{}'::jsonb)) - 'line_id' AS contract_summary,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.allocation, '{}'::jsonb)) - 'line_id' AS allocation,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.collegial_approval, '{}'::jsonb)) - 'line_id' AS collegial,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.ckk, '{}'::jsonb)) - 'line_id' AS ckk,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.contract_detail, '{}'::jsonb)) - 'line_id' AS contract_detail,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.booking, '{}'::jsonb)) - 'id' - 'line_id' AS booking,
|
||||||
|
to_jsonb(jsonb_populate_record(NULL::v3.budget_line_quarter, '{}'::jsonb))
|
||||||
|
- 'line_id' - 'quarter' AS budget_quarter
|
||||||
|
), eligible_lines AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
'FORM_2'::varchar AS form_type_code,
|
||||||
|
bl.id AS line_id,
|
||||||
|
bf.id AS source_id,
|
||||||
|
bf.org_unit_id,
|
||||||
|
ou.title AS org_name,
|
||||||
|
ei.section_code,
|
||||||
|
ei.item_id,
|
||||||
|
CASE ei.sheet WHEN 'AHR' THEN 'АХР' WHEN 'CAP' THEN 'КВ'
|
||||||
|
WHEN 'OPER' THEN 'Операц' END::varchar AS smeta_type
|
||||||
|
FROM v3.budget_form bf
|
||||||
|
JOIN v3.budget_line bl ON bl.budget_form_id = bf.id
|
||||||
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
|
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||||||
|
LEFT JOIN v3.plan presence_plan ON presence_plan.line_id = bl.id
|
||||||
|
LEFT JOIN v3.reserve presence_reserve ON presence_reserve.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_summary presence_contract_summary ON presence_contract_summary.line_id = bl.id
|
||||||
|
LEFT JOIN v3.allocation presence_allocation ON presence_allocation.line_id = bl.id
|
||||||
|
LEFT JOIN v3.collegial_approval presence_collegial_approval ON presence_collegial_approval.line_id = bl.id
|
||||||
|
LEFT JOIN v3.ckk presence_ckk ON presence_ckk.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_detail presence_contract_detail ON presence_contract_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.sequestration presence_sequestration ON presence_sequestration.line_id = bl.id
|
||||||
|
LEFT JOIN v3.booking presence_booking ON presence_booking.line_id = bl.id
|
||||||
|
LEFT JOIN v3.budget_line_quarter presence_budget_line_quarter ON presence_budget_line_quarter.line_id = bl.id
|
||||||
|
LEFT JOIN v3.rent_detail presence_rent_detail ON presence_rent_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.security_detail presence_security_detail ON presence_security_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.utility_detail presence_utility_detail ON presence_utility_detail.line_id = bl.id
|
||||||
|
WHERE bf.year = p_year AND bf.form_type_code = 'FORM_2'
|
||||||
|
AND ei.sheet IN ('AHR', 'CAP', 'OPER')
|
||||||
|
AND (p_org_unit_ids IS NULL OR cardinality(p_org_unit_ids) = 0
|
||||||
|
OR bf.org_unit_id = ANY(p_org_unit_ids))
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
presence_plan.plan_q1 <> 0
|
||||||
|
OR presence_plan.plan_q2 <> 0
|
||||||
|
OR presence_plan.plan_q3 <> 0
|
||||||
|
OR presence_plan.plan_q4 <> 0
|
||||||
|
OR presence_plan.comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_reserve.line_id IS NOT NULL
|
||||||
|
OR presence_contract_summary.line_id IS NOT NULL
|
||||||
|
OR presence_allocation.line_id IS NOT NULL
|
||||||
|
OR presence_collegial_approval.line_id IS NOT NULL
|
||||||
|
OR presence_ckk.line_id IS NOT NULL
|
||||||
|
OR presence_contract_detail.line_id IS NOT NULL
|
||||||
|
OR presence_sequestration.line_id IS NOT NULL
|
||||||
|
OR presence_booking.line_id IS NOT NULL
|
||||||
|
OR (
|
||||||
|
presence_budget_line_quarter.adj_current <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_ssp <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_reserve <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.target_change <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_date IS NOT NULL
|
||||||
|
OR presence_budget_line_quarter.payment_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_ho <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_act ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.actual_m1 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m2 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m3 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_spod <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q2 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q3 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q4 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_economy <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_acts <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_procurement <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_economy_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_next_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.transfer_far_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.booking_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_eco_change <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_item_adj <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_increase <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_sequester <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_rent_detail.line_id IS NOT NULL
|
||||||
|
OR presence_security_detail.line_id IS NOT NULL
|
||||||
|
OR presence_utility_detail.line_id IS NOT NULL
|
||||||
|
)
|
||||||
|
), numbered_lines AS (
|
||||||
|
SELECT el.*,
|
||||||
|
row_number() OVER (
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.org_name END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.org_name END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.item_id END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.item_id END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'asc'
|
||||||
|
THEN el.section_code END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'desc'
|
||||||
|
THEN el.section_code END DESC NULLS LAST,
|
||||||
|
el.form_type_code, el.source_id, el.line_id
|
||||||
|
) AS sort_order
|
||||||
|
FROM eligible_lines el
|
||||||
|
), page AS MATERIALIZED (
|
||||||
|
SELECT * FROM numbered_lines
|
||||||
|
ORDER BY sort_order
|
||||||
|
LIMIT p_limit OFFSET COALESCE(p_offset, 0)
|
||||||
|
), booking_data AS (
|
||||||
|
SELECT b.line_id,
|
||||||
|
jsonb_agg(to_jsonb(b) - 'id' - 'line_id' ORDER BY b.quarter, b.source) AS booking
|
||||||
|
FROM page pg
|
||||||
|
JOIN v3.booking b ON b.line_id = pg.line_id
|
||||||
|
GROUP BY b.line_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
pg.form_type_code AS source_form, pg.source_id,
|
||||||
|
pg.org_unit_id AS source_org_unit_id, pg.org_name, pg.smeta_type,
|
||||||
|
pg.line_id AS source_line_id, pg.sort_order,
|
||||||
|
jsonb_build_object(
|
||||||
|
'line_id', bl.id,
|
||||||
|
'header', jsonb_build_object(
|
||||||
|
'section_code', ei.section_code, 'item_id', ei.item_id,
|
||||||
|
'num_group', ei.num_group_id, 'name', COALESCE(bl.name, ei.name),
|
||||||
|
'expense_item_id', ei.id, 'justification', bl.justification,
|
||||||
|
'internal_order', bl.internal_order, 'vsp_id', bl.vsp_id,
|
||||||
|
'project_id', bl.project_id, 'direction', bl.direction,
|
||||||
|
'smeta_direction', CASE bl.direction
|
||||||
|
WHEN 'Support' THEN 'Поддержка'
|
||||||
|
WHEN 'Development' THEN 'Развитие'
|
||||||
|
ELSE NULL
|
||||||
|
END,
|
||||||
|
'source_form', pg.form_type_code, 'source_id', pg.source_id,
|
||||||
|
'source_org_unit_id', pg.org_unit_id, 'org_name', pg.org_name,
|
||||||
|
'smeta_type', pg.smeta_type),
|
||||||
|
'plan', COALESCE(
|
||||||
|
(to_jsonb(pl) - 'line_id') || jsonb_build_object(
|
||||||
|
'plan_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0)
|
||||||
|
+ COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0)),
|
||||||
|
e.plan || jsonb_build_object('plan_year', NULL)),
|
||||||
|
'sequestration', jsonb_build_object(
|
||||||
|
'DFIP', COALESCE(
|
||||||
|
(to_jsonb(sd) - 'id' - 'line_id' - 'actor') || jsonb_build_object(
|
||||||
|
'adj_year', COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0)),
|
||||||
|
e.sequestration || jsonb_build_object('adj_year', NULL)),
|
||||||
|
'SSP_GO', COALESCE(
|
||||||
|
(to_jsonb(ss) - 'id' - 'line_id' - 'actor') || jsonb_build_object(
|
||||||
|
'adj_year', COALESCE(ss.adj_q1, 0) + COALESCE(ss.adj_q2, 0)
|
||||||
|
+ COALESCE(ss.adj_q3, 0) + COALESCE(ss.adj_q4, 0)),
|
||||||
|
e.sequestration || jsonb_build_object('adj_year', NULL))),
|
||||||
|
'reserve', COALESCE(
|
||||||
|
(to_jsonb(rv) - 'line_id') || jsonb_build_object(
|
||||||
|
'amount_year', COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0)
|
||||||
|
+ COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)),
|
||||||
|
e.reserve || jsonb_build_object('amount_year', NULL)),
|
||||||
|
'approved', CASE WHEN pl.line_id IS NULL AND sd.id IS NULL
|
||||||
|
AND ss.id IS NULL AND rv.line_id IS NULL
|
||||||
|
THEN e.approved
|
||||||
|
ELSE jsonb_build_object(
|
||||||
|
'approved_q1', COALESCE(pl.plan_q1, 0) + COALESCE(sd.adj_q1, 0)
|
||||||
|
+ COALESCE(ss.adj_q1, 0) + COALESCE(rv.amount_q1, 0),
|
||||||
|
'approved_q2', COALESCE(pl.plan_q2, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(ss.adj_q2, 0) + COALESCE(rv.amount_q2, 0),
|
||||||
|
'approved_q3', COALESCE(pl.plan_q3, 0) + COALESCE(sd.adj_q3, 0)
|
||||||
|
+ COALESCE(ss.adj_q3, 0) + COALESCE(rv.amount_q3, 0),
|
||||||
|
'approved_q4', COALESCE(pl.plan_q4, 0) + COALESCE(sd.adj_q4, 0)
|
||||||
|
+ COALESCE(ss.adj_q4, 0) + COALESCE(rv.amount_q4, 0),
|
||||||
|
'approved_year', COALESCE(pl.plan_q1, 0) + COALESCE(pl.plan_q2, 0)
|
||||||
|
+ COALESCE(pl.plan_q3, 0) + COALESCE(pl.plan_q4, 0)
|
||||||
|
+ COALESCE(sd.adj_q1, 0) + COALESCE(sd.adj_q2, 0)
|
||||||
|
+ COALESCE(sd.adj_q3, 0) + COALESCE(sd.adj_q4, 0)
|
||||||
|
+ COALESCE(ss.adj_q1, 0) + COALESCE(ss.adj_q2, 0)
|
||||||
|
+ COALESCE(ss.adj_q3, 0) + COALESCE(ss.adj_q4, 0)
|
||||||
|
+ COALESCE(rv.amount_q1, 0) + COALESCE(rv.amount_q2, 0)
|
||||||
|
+ COALESCE(rv.amount_q3, 0) + COALESCE(rv.amount_q4, 0)) END,
|
||||||
|
'contract_summary', COALESCE(to_jsonb(cs) - 'line_id', e.contract_summary),
|
||||||
|
'allocation', COALESCE(to_jsonb(al) - 'line_id', e.allocation),
|
||||||
|
'collegial', COALESCE(to_jsonb(ca) - 'line_id', e.collegial),
|
||||||
|
'ckk', COALESCE(to_jsonb(ck) - 'line_id', e.ckk),
|
||||||
|
'contract_detail', COALESCE(to_jsonb(cd) - 'line_id', e.contract_detail) || jsonb_build_object(
|
||||||
|
'expenses_q1', COALESCE(cd.expenses_q1, ck.expenses_q1),
|
||||||
|
'expenses_q2', COALESCE(cd.expenses_q2, ck.expenses_q2),
|
||||||
|
'expenses_q3', COALESCE(cd.expenses_q3, ck.expenses_q3),
|
||||||
|
'expenses_q4', COALESCE(cd.expenses_q4, ck.expenses_q4)),
|
||||||
|
'booking', COALESCE(bk.booking, jsonb_build_array(e.booking)),
|
||||||
|
'q1', COALESCE(to_jsonb(q1) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', COALESCE(q1.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q1
|
||||||
|
WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1
|
||||||
|
ELSE cd.expenses_q1 END)),
|
||||||
|
'q2', COALESCE(to_jsonb(q2) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', COALESCE(q2.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q2
|
||||||
|
WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2
|
||||||
|
ELSE cd.expenses_q2 END),
|
||||||
|
'plan_revision_eco_change', COALESCE(q2.target_change, q2.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q2.base_plan_correction, q2.plan_revision_item_adj)),
|
||||||
|
'q3', COALESCE(to_jsonb(q3) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', COALESCE(q3.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q3
|
||||||
|
WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3
|
||||||
|
ELSE cd.expenses_q3 END),
|
||||||
|
'plan_revision_eco_change', COALESCE(q3.target_change, q3.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q3.base_plan_correction, q3.plan_revision_item_adj)),
|
||||||
|
'q4', COALESCE(to_jsonb(q4) - 'line_id' - 'quarter', e.budget_quarter) || jsonb_build_object(
|
||||||
|
'booking_amount', COALESCE(q4.booking_amount,
|
||||||
|
CASE WHEN ei.sheet = 'AHR' THEN ck.expenses_q4
|
||||||
|
WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4
|
||||||
|
ELSE cd.expenses_q4 END),
|
||||||
|
'plan_revision_eco_change', COALESCE(q4.target_change, q4.plan_revision_eco_change),
|
||||||
|
'plan_revision_item_adj', COALESCE(q4.base_plan_correction, q4.plan_revision_item_adj))
|
||||||
|
) AS data
|
||||||
|
FROM page pg
|
||||||
|
JOIN v3.budget_line bl ON bl.id = pg.line_id
|
||||||
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
|
LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id
|
||||||
|
CROSS JOIN empty_sections e
|
||||||
|
LEFT JOIN v3.plan pl ON pl.line_id = bl.id
|
||||||
|
LEFT JOIN v3.sequestration sd ON sd.line_id = bl.id AND sd.actor = 'DFIP'
|
||||||
|
LEFT JOIN v3.sequestration ss ON ss.line_id = bl.id AND ss.actor = 'SSP_GO'
|
||||||
|
LEFT JOIN v3.reserve rv ON rv.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_summary cs ON cs.line_id = bl.id
|
||||||
|
LEFT JOIN v3.allocation al ON al.line_id = bl.id
|
||||||
|
LEFT JOIN v3.collegial_approval ca ON ca.line_id = bl.id
|
||||||
|
LEFT JOIN v3.ckk ck ON ck.line_id = bl.id
|
||||||
|
LEFT JOIN booking_data bk ON bk.line_id = bl.id
|
||||||
|
LEFT JOIN v3.budget_line_quarter q1 ON q1.line_id = bl.id AND q1.quarter = 1
|
||||||
|
LEFT JOIN v3.budget_line_quarter q2 ON q2.line_id = bl.id AND q2.quarter = 2
|
||||||
|
LEFT JOIN v3.budget_line_quarter q3 ON q3.line_id = bl.id AND q3.quarter = 3
|
||||||
|
LEFT JOIN v3.budget_line_quarter q4 ON q4.line_id = bl.id AND q4.quarter = 4
|
||||||
|
ORDER BY pg.sort_order;
|
||||||
|
$function$;
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION v3.v_svod2_go_export(
|
||||||
|
p_year integer,
|
||||||
|
p_org_unit_ids integer[] DEFAULT NULL,
|
||||||
|
p_sort_by varchar DEFAULT 'org_name',
|
||||||
|
p_sort_direction varchar DEFAULT 'asc'
|
||||||
|
)
|
||||||
|
RETURNS TABLE(sort_order bigint, row_values text[])
|
||||||
|
LANGUAGE sql
|
||||||
|
STABLE
|
||||||
|
SET search_path TO 'v3', 'pg_catalog'
|
||||||
|
AS $function$
|
||||||
|
WITH eligible_lines AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
bl.id AS line_id,
|
||||||
|
bf.form_type_code,
|
||||||
|
bf.id AS form_id,
|
||||||
|
bf.org_unit_id,
|
||||||
|
ou.title AS org_name,
|
||||||
|
ei.section_code,
|
||||||
|
ei.item_id,
|
||||||
|
ei.num_group_id,
|
||||||
|
COALESCE(bl.name, ei.name) AS name,
|
||||||
|
bl.justification,
|
||||||
|
CASE bl.direction
|
||||||
|
WHEN 'Support' THEN 'Поддержка'
|
||||||
|
WHEN 'Development' THEN 'Развитие'
|
||||||
|
END::varchar AS smeta_direction,
|
||||||
|
CASE ei.sheet
|
||||||
|
WHEN 'AHR' THEN 'АХР'
|
||||||
|
WHEN 'CAP' THEN 'КВ'
|
||||||
|
WHEN 'OPER' THEN 'Операц'
|
||||||
|
END::varchar AS smeta_type,
|
||||||
|
ei.sheet,
|
||||||
|
cd.counterparty,
|
||||||
|
cd.reference,
|
||||||
|
cd.subject,
|
||||||
|
cd.currency,
|
||||||
|
cd.ceiling_amount,
|
||||||
|
cd.expenses_q1,
|
||||||
|
cd.expenses_q2,
|
||||||
|
cd.expenses_q3,
|
||||||
|
cd.expenses_q4,
|
||||||
|
cd.vat_rate,
|
||||||
|
cd.deadline
|
||||||
|
FROM v3.budget_form bf
|
||||||
|
JOIN v3.budget_line bl ON bl.budget_form_id = bf.id
|
||||||
|
JOIN v3.expense_item ei ON ei.id = bl.expense_item_id
|
||||||
|
LEFT JOIN v3.contract_detail cd ON cd.line_id = bl.id
|
||||||
|
LEFT JOIN v3.org_unit ou ON ou.id = bf.org_unit_id
|
||||||
|
LEFT JOIN v3.plan presence_plan ON presence_plan.line_id = bl.id
|
||||||
|
LEFT JOIN v3.reserve presence_reserve ON presence_reserve.line_id = bl.id
|
||||||
|
LEFT JOIN v3.contract_summary presence_contract_summary ON presence_contract_summary.line_id = bl.id
|
||||||
|
LEFT JOIN v3.allocation presence_allocation ON presence_allocation.line_id = bl.id
|
||||||
|
LEFT JOIN v3.collegial_approval presence_collegial_approval ON presence_collegial_approval.line_id = bl.id
|
||||||
|
LEFT JOIN v3.ckk presence_ckk ON presence_ckk.line_id = bl.id
|
||||||
|
LEFT JOIN v3.sequestration presence_sequestration ON presence_sequestration.line_id = bl.id
|
||||||
|
LEFT JOIN v3.booking presence_booking ON presence_booking.line_id = bl.id
|
||||||
|
LEFT JOIN v3.budget_line_quarter presence_budget_line_quarter ON presence_budget_line_quarter.line_id = bl.id
|
||||||
|
LEFT JOIN v3.rent_detail presence_rent_detail ON presence_rent_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.security_detail presence_security_detail ON presence_security_detail.line_id = bl.id
|
||||||
|
LEFT JOIN v3.utility_detail presence_utility_detail ON presence_utility_detail.line_id = bl.id
|
||||||
|
WHERE bf.year = p_year
|
||||||
|
AND bf.form_type_code IN ('FORM_1', 'FORM_4')
|
||||||
|
AND ei.sheet IN ('AHR', 'CAP', 'OPER')
|
||||||
|
AND (
|
||||||
|
p_org_unit_ids IS NULL
|
||||||
|
OR cardinality(p_org_unit_ids) = 0
|
||||||
|
OR bf.org_unit_id = ANY(p_org_unit_ids)
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
(
|
||||||
|
presence_plan.plan_q1 <> 0
|
||||||
|
OR presence_plan.plan_q2 <> 0
|
||||||
|
OR presence_plan.plan_q3 <> 0
|
||||||
|
OR presence_plan.plan_q4 <> 0
|
||||||
|
OR presence_plan.comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_reserve.line_id IS NOT NULL
|
||||||
|
OR presence_contract_summary.line_id IS NOT NULL
|
||||||
|
OR presence_allocation.line_id IS NOT NULL
|
||||||
|
OR presence_collegial_approval.line_id IS NOT NULL
|
||||||
|
OR presence_ckk.line_id IS NOT NULL
|
||||||
|
OR cd.line_id IS NOT NULL
|
||||||
|
OR presence_sequestration.line_id IS NOT NULL
|
||||||
|
OR presence_booking.line_id IS NOT NULL
|
||||||
|
OR (
|
||||||
|
presence_budget_line_quarter.adj_current <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_ssp <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_reserve <> 0
|
||||||
|
OR presence_budget_line_quarter.adj_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.target_change <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction <> 0
|
||||||
|
OR presence_budget_line_quarter.base_plan_correction_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_date IS NOT NULL
|
||||||
|
OR presence_budget_line_quarter.payment_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_ho <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_amount_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.payment_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.payment_act ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.actual_m1 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m2 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_m3 <> 0
|
||||||
|
OR presence_budget_line_quarter.actual_spod <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q2 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q3 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_q4 <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_to_economy <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_acts <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_delay_procurement <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_economy_rf <> 0
|
||||||
|
OR presence_budget_line_quarter.transfer_next_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.transfer_far_comment ~ '[^[:space:]]'
|
||||||
|
OR presence_budget_line_quarter.booking_amount <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_eco_change <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_item_adj <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_increase <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_sequester <> 0
|
||||||
|
OR presence_budget_line_quarter.plan_revision_comment ~ '[^[:space:]]'
|
||||||
|
)
|
||||||
|
OR presence_rent_detail.line_id IS NOT NULL
|
||||||
|
OR presence_security_detail.line_id IS NOT NULL
|
||||||
|
OR presence_utility_detail.line_id IS NOT NULL
|
||||||
|
)
|
||||||
|
), page AS MATERIALIZED (
|
||||||
|
SELECT
|
||||||
|
el.*,
|
||||||
|
row_number() OVER (
|
||||||
|
ORDER BY
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc' THEN el.org_name END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc' THEN el.org_name END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'asc' THEN el.item_id END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'org_name' AND lower(p_sort_direction) = 'desc' THEN el.item_id END DESC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'asc' THEN el.section_code END ASC NULLS LAST,
|
||||||
|
CASE WHEN p_sort_by = 'section_code' AND lower(p_sort_direction) = 'desc' THEN el.section_code END DESC NULLS LAST,
|
||||||
|
el.form_type_code,
|
||||||
|
el.form_id,
|
||||||
|
el.line_id
|
||||||
|
) AS sort_order
|
||||||
|
FROM eligible_lines el
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
pg.sort_order,
|
||||||
|
ARRAY[
|
||||||
|
pg.smeta_type::text,
|
||||||
|
pg.smeta_direction::text,
|
||||||
|
pg.org_name::text,
|
||||||
|
pg.name::text,
|
||||||
|
pg.item_id::text,
|
||||||
|
pg.num_group_id::text,
|
||||||
|
pg.justification::text,
|
||||||
|
pl.plan_q1::text,
|
||||||
|
pl.plan_q2::text,
|
||||||
|
pl.plan_q3::text,
|
||||||
|
pl.plan_q4::text,
|
||||||
|
al.internal_order::text,
|
||||||
|
al.property_object::text,
|
||||||
|
sd.adj_q1::text,
|
||||||
|
sd.adj_q2::text,
|
||||||
|
sd.adj_q3::text,
|
||||||
|
sd.adj_q4::text,
|
||||||
|
rv.amount_q1::text,
|
||||||
|
rv.amount_q2::text,
|
||||||
|
rv.amount_q3::text,
|
||||||
|
rv.amount_q4::text,
|
||||||
|
(CASE WHEN pl.line_id IS NULL AND sd.id IS NULL AND rv.line_id IS NULL THEN NULL ELSE COALESCE(pl.plan_q1, 0) + COALESCE(sd.adj_q1, 0) + COALESCE(rv.amount_q1, 0) END)::text,
|
||||||
|
(CASE WHEN pl.line_id IS NULL AND sd.id IS NULL AND rv.line_id IS NULL THEN NULL ELSE COALESCE(pl.plan_q2, 0) + COALESCE(sd.adj_q2, 0) + COALESCE(rv.amount_q2, 0) END)::text,
|
||||||
|
(CASE WHEN pl.line_id IS NULL AND sd.id IS NULL AND rv.line_id IS NULL THEN NULL ELSE COALESCE(pl.plan_q3, 0) + COALESCE(sd.adj_q3, 0) + COALESCE(rv.amount_q3, 0) END)::text,
|
||||||
|
(CASE WHEN pl.line_id IS NULL AND sd.id IS NULL AND rv.line_id IS NULL THEN NULL ELSE COALESCE(pl.plan_q4, 0) + COALESCE(sd.adj_q4, 0) + COALESCE(rv.amount_q4, 0) END)::text,
|
||||||
|
pg.counterparty::text,
|
||||||
|
pg.reference::text,
|
||||||
|
pg.subject::text,
|
||||||
|
pg.currency::text,
|
||||||
|
pg.ceiling_amount::text,
|
||||||
|
pg.expenses_q1::text,
|
||||||
|
pg.expenses_q2::text,
|
||||||
|
pg.expenses_q3::text,
|
||||||
|
pg.expenses_q4::text,
|
||||||
|
pg.vat_rate::text,
|
||||||
|
pg.deadline::text,
|
||||||
|
q1.adj_current::text,
|
||||||
|
q1.adj_ssp::text,
|
||||||
|
q1.adj_rf::text,
|
||||||
|
q1.adj_reserve::text,
|
||||||
|
q1.payment_date::text,
|
||||||
|
q1.payment_amount::text,
|
||||||
|
q1.payment_amount_ho::text,
|
||||||
|
q1.payment_amount_rf::text,
|
||||||
|
q1.payment_act::text,
|
||||||
|
(CASE WHEN pg.form_type_code = 'FORM_1' THEN CASE WHEN COALESCE(cd.expenses_q1, 0) > 0 THEN cd.expenses_q1 ELSE ck.expenses_q1 END ELSE COALESCE(q1.booking_amount,
|
||||||
|
CASE WHEN pg.sheet = 'AHR' THEN ck.expenses_q1
|
||||||
|
WHEN ck.expenses_q1 > 0 THEN ck.expenses_q1
|
||||||
|
ELSE cd.expenses_q1 END) END)::text,
|
||||||
|
q1.actual_m1::text,
|
||||||
|
q1.actual_m2::text,
|
||||||
|
q1.actual_m3::text,
|
||||||
|
q1.transfer_to_q2::text,
|
||||||
|
q1.transfer_to_q3::text,
|
||||||
|
q1.transfer_to_q4::text,
|
||||||
|
q1.transfer_to_economy::text,
|
||||||
|
q2.adj_current::text,
|
||||||
|
q2.adj_ssp::text,
|
||||||
|
q2.adj_rf::text,
|
||||||
|
q2.adj_reserve::text,
|
||||||
|
q2.payment_date::text,
|
||||||
|
q2.payment_amount::text,
|
||||||
|
q2.payment_amount_ho::text,
|
||||||
|
q2.payment_amount_rf::text,
|
||||||
|
q2.payment_act::text,
|
||||||
|
(CASE WHEN pg.form_type_code = 'FORM_1' THEN CASE WHEN COALESCE(cd.expenses_q2, 0) > 0 THEN cd.expenses_q2 ELSE ck.expenses_q2 END ELSE COALESCE(q2.booking_amount,
|
||||||
|
CASE WHEN pg.sheet = 'AHR' THEN ck.expenses_q2
|
||||||
|
WHEN ck.expenses_q2 > 0 THEN ck.expenses_q2
|
||||||
|
ELSE cd.expenses_q2 END) END)::text,
|
||||||
|
q2.actual_m1::text,
|
||||||
|
q2.actual_m2::text,
|
||||||
|
q2.actual_m3::text,
|
||||||
|
COALESCE(q2.target_change, q2.plan_revision_eco_change)::text,
|
||||||
|
COALESCE(q2.base_plan_correction, q2.plan_revision_item_adj)::text,
|
||||||
|
q2.plan_revision_increase::text,
|
||||||
|
q2.plan_revision_sequester::text,
|
||||||
|
q2.transfer_to_q3::text,
|
||||||
|
q2.transfer_to_q4::text,
|
||||||
|
q2.transfer_to_economy::text,
|
||||||
|
q3.adj_current::text,
|
||||||
|
q3.adj_ssp::text,
|
||||||
|
q3.adj_rf::text,
|
||||||
|
q3.adj_reserve::text,
|
||||||
|
q3.payment_date::text,
|
||||||
|
q3.payment_amount::text,
|
||||||
|
q3.payment_amount_ho::text,
|
||||||
|
q3.payment_amount_rf::text,
|
||||||
|
q3.payment_act::text,
|
||||||
|
(CASE WHEN pg.form_type_code = 'FORM_1' THEN CASE WHEN COALESCE(cd.expenses_q3, 0) > 0 THEN cd.expenses_q3 ELSE ck.expenses_q3 END ELSE COALESCE(q3.booking_amount,
|
||||||
|
CASE WHEN pg.sheet = 'AHR' THEN ck.expenses_q3
|
||||||
|
WHEN ck.expenses_q3 > 0 THEN ck.expenses_q3
|
||||||
|
ELSE cd.expenses_q3 END) END)::text,
|
||||||
|
q3.actual_m1::text,
|
||||||
|
q3.actual_m2::text,
|
||||||
|
q3.actual_m3::text,
|
||||||
|
COALESCE(q3.target_change, q3.plan_revision_eco_change)::text,
|
||||||
|
COALESCE(q3.base_plan_correction, q3.plan_revision_item_adj)::text,
|
||||||
|
q3.plan_revision_increase::text,
|
||||||
|
q3.plan_revision_sequester::text,
|
||||||
|
q3.transfer_to_q4::text,
|
||||||
|
q3.transfer_to_economy::text,
|
||||||
|
q4.adj_current::text,
|
||||||
|
q4.adj_ssp::text,
|
||||||
|
q4.adj_rf::text,
|
||||||
|
q4.adj_reserve::text,
|
||||||
|
q4.payment_date::text,
|
||||||
|
q4.payment_amount::text,
|
||||||
|
q4.payment_amount_ho::text,
|
||||||
|
q4.payment_amount_rf::text,
|
||||||
|
q4.payment_act::text,
|
||||||
|
(CASE WHEN pg.form_type_code = 'FORM_1' THEN CASE WHEN COALESCE(cd.expenses_q4, 0) > 0 THEN cd.expenses_q4 ELSE ck.expenses_q4 END ELSE COALESCE(q4.booking_amount,
|
||||||
|
CASE WHEN pg.sheet = 'AHR' THEN ck.expenses_q4
|
||||||
|
WHEN ck.expenses_q4 > 0 THEN ck.expenses_q4
|
||||||
|
ELSE cd.expenses_q4 END) END)::text,
|
||||||
|
q4.actual_m1::text,
|
||||||
|
q4.actual_m2::text,
|
||||||
|
q4.actual_m3::text,
|
||||||
|
q4.actual_spod::text,
|
||||||
|
COALESCE(q4.target_change, q4.plan_revision_eco_change)::text,
|
||||||
|
COALESCE(q4.base_plan_correction, q4.plan_revision_item_adj)::text,
|
||||||
|
q4.plan_revision_increase::text,
|
||||||
|
q4.plan_revision_sequester::text
|
||||||
|
]::text[] AS row_values
|
||||||
|
FROM page pg
|
||||||
|
LEFT JOIN v3.contract_detail cd ON cd.line_id = pg.line_id
|
||||||
|
LEFT JOIN v3.ckk ck ON ck.line_id = pg.line_id
|
||||||
|
LEFT JOIN v3.plan pl ON pl.line_id = pg.line_id
|
||||||
|
LEFT JOIN v3.sequestration sd
|
||||||
|
ON sd.line_id = pg.line_id
|
||||||
|
AND sd.actor = 'DFIP'
|
||||||
|
LEFT JOIN v3.reserve rv ON rv.line_id = pg.line_id
|
||||||
|
LEFT JOIN v3.allocation al ON al.line_id = pg.line_id
|
||||||
|
LEFT JOIN v3.budget_line_quarter q1
|
||||||
|
ON q1.line_id = pg.line_id AND q1.quarter = 1
|
||||||
|
LEFT JOIN v3.budget_line_quarter q2
|
||||||
|
ON q2.line_id = pg.line_id AND q2.quarter = 2
|
||||||
|
LEFT JOIN v3.budget_line_quarter q3
|
||||||
|
ON q3.line_id = pg.line_id AND q3.quarter = 3
|
||||||
|
LEFT JOIN v3.budget_line_quarter q4
|
||||||
|
ON q4.line_id = pg.line_id AND q4.quarter = 4
|
||||||
|
ORDER BY pg.sort_order;
|
||||||
|
$function$;
|
||||||
148
api/tests/unit/test_summary2_related_records_sql.py
Normal file
148
api/tests/unit/test_summary2_related_records_sql.py
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
"""Exercise migration eligibility joins using SQLite, without an application DB."""
|
||||||
|
import ast
|
||||||
|
import re
|
||||||
|
import sqlite3
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
SQL_PATH = Path(__file__).resolve().parents[2] / "alembic/versions/sql/0049_summary2_related_records.sql"
|
||||||
|
TABLES = (
|
||||||
|
"plan", "reserve", "contract_summary", "allocation", "collegial_approval",
|
||||||
|
"ckk", "contract_detail", "sequestration", "booking", "budget_line_quarter",
|
||||||
|
"rent_detail", "security_detail", "utility_detail",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# Read column types without importing SQLAlchemy or application settings.
|
||||||
|
def data_columns(table):
|
||||||
|
path = SQL_PATH.parents[3] / f"src/db/models/{table}.py"
|
||||||
|
return {
|
||||||
|
node.target.id: node.value.args[0].id
|
||||||
|
for node in ast.walk(ast.parse(path.read_text()))
|
||||||
|
if isinstance(node, ast.AnnAssign)
|
||||||
|
and isinstance(node.target, ast.Name)
|
||||||
|
and node.target.id not in ("line_id", "quarter")
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Summary2RelatedRecordsSQLTest(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.db = sqlite3.connect(":memory:")
|
||||||
|
self.addCleanup(self.db.close)
|
||||||
|
self.db.create_function("has_text", 1, lambda value: bool(value and value.strip()))
|
||||||
|
self.db.execute("ATTACH DATABASE ':memory:' AS v3")
|
||||||
|
self.db.executescript("""
|
||||||
|
CREATE TABLE v3.budget_form(id, year, form_type_code, org_unit_id);
|
||||||
|
CREATE TABLE v3.budget_line(id, budget_form_id, expense_item_id,
|
||||||
|
name, justification, direction);
|
||||||
|
CREATE TABLE v3.expense_item(id, section_code, item_id, sheet,
|
||||||
|
num_group_id, name);
|
||||||
|
CREATE TABLE v3.org_unit(id, title);
|
||||||
|
INSERT INTO v3.budget_form VALUES (1, 2026, 'FORM_1', 1);
|
||||||
|
INSERT INTO v3.budget_line VALUES (1, 1, 1, NULL, NULL, NULL);
|
||||||
|
INSERT INTO v3.expense_item VALUES (1, '1', '1', 'AHR', '1', 'Item');
|
||||||
|
INSERT INTO v3.org_unit VALUES (1, 'Org');
|
||||||
|
""")
|
||||||
|
for table in TABLES:
|
||||||
|
columns = "line_id"
|
||||||
|
if table in ("plan", "budget_line_quarter"):
|
||||||
|
columns += "".join(f", {field} {'NUMERIC' if kind == 'Numeric' else 'TEXT'}"
|
||||||
|
for field, kind in data_columns(table).items())
|
||||||
|
if table == "budget_line_quarter":
|
||||||
|
columns += ", quarter"
|
||||||
|
if table == "contract_detail":
|
||||||
|
columns += ", counterparty, reference, subject, currency, ceiling_amount"
|
||||||
|
columns += ", expenses_q1, expenses_q2, expenses_q3, expenses_q4, vat_rate, deadline"
|
||||||
|
self.db.execute(f"CREATE TABLE v3.{table} ({columns})")
|
||||||
|
# Execute the eligibility SELECTs, adapting PostgreSQL casts, arrays and regex syntax.
|
||||||
|
self.queries = []
|
||||||
|
for part in SQL_PATH.read_text().split("eligible_lines AS (")[1:]:
|
||||||
|
query = part.split("), ", 1)[0].replace("::varchar", "")
|
||||||
|
query = re.sub(r"(presence_\w+\.\w+) ~ '\[\^\[:space:\]\]'",
|
||||||
|
r"has_text(\1)", query)
|
||||||
|
query = query.replace("p_year", ":year")
|
||||||
|
query = query.replace("cardinality(p_org_unit_ids)", "1")
|
||||||
|
query = query.replace("ANY(p_org_unit_ids)", ":org_id")
|
||||||
|
query = query.replace("p_org_unit_ids", ":org_id")
|
||||||
|
self.queries.append(query)
|
||||||
|
self.assertEqual(len(self.queries), 3)
|
||||||
|
|
||||||
|
def rows(self, index, year=2026, org_id=None):
|
||||||
|
return self.db.execute(self.queries[index], {"year": year, "org_id": org_id}).fetchall()
|
||||||
|
|
||||||
|
def test_each_related_table_alone_includes_line_even_with_empty_fields(self):
|
||||||
|
for index in range(3):
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?",
|
||||||
|
("FORM_2" if index == 1 else "FORM_1",))
|
||||||
|
self.assertEqual(self.rows(index), [])
|
||||||
|
for table in TABLES:
|
||||||
|
with self.subTest(query=index, table=table):
|
||||||
|
self.db.execute(f"INSERT INTO v3.{table}(line_id) VALUES (999)")
|
||||||
|
self.assertEqual(self.rows(index), [])
|
||||||
|
self.db.execute(f"INSERT INTO v3.{table}(line_id) VALUES (1)")
|
||||||
|
self.assertEqual(len(self.rows(index)), 0 if table in ("plan", "budget_line_quarter") else 1)
|
||||||
|
self.db.execute(f"DELETE FROM v3.{table}")
|
||||||
|
|
||||||
|
def test_multiple_related_records_do_not_duplicate_lines(self):
|
||||||
|
for table in ("booking", "budget_line_quarter", "rent_detail", "sequestration"):
|
||||||
|
self.db.executemany(f"INSERT INTO v3.{table}(line_id) VALUES (?)", [(1,)] * 4)
|
||||||
|
for index in range(3):
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?",
|
||||||
|
("FORM_2" if index == 1 else "FORM_4",))
|
||||||
|
self.assertEqual(len(self.rows(index)), 1)
|
||||||
|
|
||||||
|
def test_default_plan_and_quarters_do_not_qualify(self):
|
||||||
|
for table in ("plan", "budget_line_quarter"):
|
||||||
|
fields = data_columns(table)
|
||||||
|
names = ", ".join(fields)
|
||||||
|
values = [0 if kind == "Numeric" else " \t\n\r" if kind == "String" else None
|
||||||
|
for kind in fields.values()]
|
||||||
|
placeholders = ", ".join("?" for _ in fields)
|
||||||
|
self.db.execute(f"INSERT INTO v3.{table}(line_id, {names}) VALUES (1, {placeholders})", values)
|
||||||
|
# Service quarter numbers must not qualify as meaningful data.
|
||||||
|
self.db.execute("UPDATE v3.budget_line_quarter SET quarter = 1")
|
||||||
|
for quarter in (2, 3, 4):
|
||||||
|
self.db.execute("INSERT INTO v3.budget_line_quarter(line_id, quarter, actual_m1) VALUES (1, ?, 0)",
|
||||||
|
(quarter,))
|
||||||
|
for index in range(3):
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?",
|
||||||
|
("FORM_2" if index == 1 else "FORM_1",))
|
||||||
|
self.assertEqual(self.rows(index), [])
|
||||||
|
# Presence in another section still qualifies independently.
|
||||||
|
self.db.execute("INSERT INTO v3.reserve(line_id) VALUES (1)")
|
||||||
|
self.assertEqual(len(self.rows(index)), 1)
|
||||||
|
self.db.execute("DELETE FROM v3.reserve")
|
||||||
|
|
||||||
|
def test_each_meaningful_plan_or_quarter_field_qualifies(self):
|
||||||
|
for index in range(3):
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?",
|
||||||
|
("FORM_2" if index == 1 else "FORM_1",))
|
||||||
|
for table in ("plan", "budget_line_quarter"):
|
||||||
|
for field, kind in data_columns(table).items():
|
||||||
|
values = (12, -12) if kind == "Numeric" else ("2026-10-21",) if kind == "Date" else (" text ",)
|
||||||
|
for value in values:
|
||||||
|
with self.subTest(query=index, table=table, field=field, value=value):
|
||||||
|
self.db.execute(f"INSERT INTO v3.{table}(line_id, {field}) VALUES (1, ?)", (value,))
|
||||||
|
self.assertEqual(len(self.rows(index)), 1)
|
||||||
|
self.db.execute(f"DELETE FROM v3.{table}")
|
||||||
|
|
||||||
|
def test_source_year_org_and_sheet_filters_are_preserved(self):
|
||||||
|
self.db.execute("INSERT INTO v3.plan(line_id, plan_q1) VALUES (1, 1)")
|
||||||
|
for index in range(3):
|
||||||
|
for form in ("FORM_1", "FORM_2", "FORM_3", "FORM_4"):
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?", (form,))
|
||||||
|
expected = form == "FORM_2" if index == 1 else form in ("FORM_1", "FORM_4")
|
||||||
|
self.assertEqual(bool(self.rows(index)), expected)
|
||||||
|
self.db.execute("UPDATE v3.budget_form SET form_type_code = ?",
|
||||||
|
("FORM_2" if index == 1 else "FORM_1",))
|
||||||
|
self.assertEqual(self.rows(index, year=2025), [])
|
||||||
|
self.assertEqual(self.rows(index, org_id=2), [])
|
||||||
|
self.assertEqual(len(self.rows(index, org_id=1)), 1)
|
||||||
|
self.db.execute("UPDATE v3.expense_item SET sheet = 'OTHER'")
|
||||||
|
self.assertEqual(self.rows(index), [])
|
||||||
|
self.db.execute("UPDATE v3.expense_item SET sheet = 'AHR'")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
Loading…
x
Reference in New Issue
Block a user