computed-fix: добавил поле carryover к кварталам проектов #117
82
api/alembic/versions/0020_carryover_form3.py
Normal file
82
api/alembic/versions/0020_carryover_form3.py
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
|
||||||
|
revision = "0020"
|
||||||
|
down_revision = "0019"
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
_DOLLAR_TAG_RE = re.compile(r"\$\w+\$")
|
||||||
|
|
||||||
|
|
||||||
|
def _find_dollar_tag(line: str) -> str | None:
|
||||||
|
m = _DOLLAR_TAG_RE.search(line.strip())
|
||||||
|
return m.group(0) if m else None
|
||||||
|
|
||||||
|
|
||||||
|
def _split_statements(sql: str) -> list[str]:
|
||||||
|
statements: list[str] = []
|
||||||
|
current: list[str] = []
|
||||||
|
in_dollar = False
|
||||||
|
dollar_tag: str | None = None
|
||||||
|
|
||||||
|
for line in sql.split("\n"):
|
||||||
|
stripped = line.strip()
|
||||||
|
if stripped.startswith("--"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_dollar:
|
||||||
|
tag = _find_dollar_tag(stripped)
|
||||||
|
if tag and tag.endswith("$") and tag.startswith("$"):
|
||||||
|
dollar_tag = tag
|
||||||
|
in_dollar = True
|
||||||
|
current.append(line)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if in_dollar and dollar_tag and stripped.startswith(dollar_tag):
|
||||||
|
after = stripped[len(dollar_tag):].strip()
|
||||||
|
if after == ";" or after == "":
|
||||||
|
in_dollar = False
|
||||||
|
dollar_tag = None
|
||||||
|
if after == ";":
|
||||||
|
current.append(line)
|
||||||
|
statements.append("\n".join(current))
|
||||||
|
current = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
if not in_dollar and stripped.rstrip().endswith(";"):
|
||||||
|
current.append(line)
|
||||||
|
statements.append("\n".join(current))
|
||||||
|
current = []
|
||||||
|
continue
|
||||||
|
|
||||||
|
current.append(line)
|
||||||
|
|
||||||
|
remaining = "\n".join(current).strip()
|
||||||
|
if remaining:
|
||||||
|
statements.append(remaining)
|
||||||
|
|
||||||
|
return statements
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
ddl_path = os.path.join(os.path.dirname(__file__), "sql", "0020_carryover_form3.sql")
|
||||||
|
with open(ddl_path) as f:
|
||||||
|
content = f.read()
|
||||||
|
|
||||||
|
statements = _split_statements(content)
|
||||||
|
for stmt in statements:
|
||||||
|
stripped = stmt.strip().rstrip(";").strip()
|
||||||
|
if not stripped:
|
||||||
|
continue
|
||||||
|
if all(l.strip().startswith("--") or not l.strip() for l in stripped.split("\n")):
|
||||||
|
continue
|
||||||
|
op.execute(stripped)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
pass
|
||||||
353
api/alembic/versions/sql/0020_carryover_form3.sql
Normal file
353
api/alembic/versions/sql/0020_carryover_form3.sql
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
ALTER TABLE v3.rf_project_report_quarter ADD COLUMN carryover NUMERIC;
|
||||||
|
|
||||||
|
-- DROP FUNCTION v3.v_form3_report_sections(int4, _text);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION v3.v_form3_report_sections(p_report_id integer, p_sections text[] DEFAULT NULL::text[])
|
||||||
|
RETURNS TABLE(row_type character varying, depth integer, line_id integer, col_section_code character varying, col_item_id character varying, col_num_group_id character varying, col_name character varying, col_q1_adj_by_items numeric, col_q1_adj_increase numeric, col_q1_total_corr numeric, col_q1_m1 numeric, col_q1_m2 numeric, col_q1_m3 numeric, col_q1_quarter_actual numeric, col_q1_economy numeric, col_q2_carryover numeric, col_q2_adj_by_items numeric, col_q2_adj_increase numeric, col_q2_total_corr numeric, col_q2_m1 numeric, col_q2_m2 numeric, col_q2_m3 numeric, col_q2_quarter_actual numeric, col_q2_economy numeric, col_q3_carryover numeric, col_q3_adj_by_items numeric, col_q3_adj_increase numeric, col_q3_total_corr numeric, col_q3_m1 numeric, col_q3_m2 numeric, col_q3_m3 numeric, col_q3_quarter_actual numeric, col_q3_economy numeric, col_q4_carryover numeric, col_q4_adj_by_items numeric, col_q4_adj_increase numeric, col_q4_total_corr numeric, col_q4_m1 numeric, col_q4_m2 numeric, col_q4_m3 numeric, col_q4_spod numeric, col_q4_quarter_actual numeric, col_q4_economy numeric, col_year_total_corr numeric, col_year_total_actual numeric, col_year_economy numeric, col_q1_base_plan numeric, col_q2_base_plan numeric, col_q3_base_plan numeric, col_q4_base_plan numeric, col_year_base_plan numeric, col_q2_corrected_plan numeric, col_q3_corrected_plan numeric, col_q4_corrected_plan numeric, _sort_path integer[])
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
STABLE
|
||||||
|
AS $function$
|
||||||
|
DECLARE
|
||||||
|
s_q1 BOOLEAN := p_sections IS NULL OR 'q1' = ANY(p_sections);
|
||||||
|
s_q2 BOOLEAN := p_sections IS NULL OR 'q2' = ANY(p_sections);
|
||||||
|
s_q3 BOOLEAN := p_sections IS NULL OR 'q3' = ANY(p_sections);
|
||||||
|
s_q4 BOOLEAN := p_sections IS NULL OR 'q4' = ANY(p_sections);
|
||||||
|
s_year BOOLEAN := p_sections IS NULL OR 'year' = ANY(p_sections);
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (SELECT 1 FROM v3.rf_project_report WHERE id = p_report_id) THEN
|
||||||
|
RAISE EXCEPTION 'rf_project_report id=% не существует', p_report_id;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
RETURN QUERY
|
||||||
|
WITH
|
||||||
|
-- Дерево expense_item, ограниченное FORM_3 (через junction)
|
||||||
|
tw AS (
|
||||||
|
SELECT t.* FROM v3.mv_expense_item_tree t
|
||||||
|
JOIN v3.expense_item_form_type eft
|
||||||
|
ON eft.expense_item_id = t.id AND eft.form_type_code = 'FORM_3'
|
||||||
|
),
|
||||||
|
-- INPUT строки: одна на v3.rf_project_report_line
|
||||||
|
page AS (
|
||||||
|
SELECT l.id AS lid, l.expense_item_id AS eid,
|
||||||
|
t.parent_item_id AS sc, -- section-код родителя в дереве (1.01.1.)
|
||||||
|
ei.item_id AS ic,
|
||||||
|
ei.num_group_id AS ng, ei.name AS ename,
|
||||||
|
t.path AS tree_path
|
||||||
|
FROM v3.rf_project_report_line l
|
||||||
|
JOIN v3.expense_item ei ON ei.id = l.expense_item_id
|
||||||
|
JOIN tw t ON t.id = l.expense_item_id
|
||||||
|
WHERE l.rf_project_report_id = p_report_id
|
||||||
|
),
|
||||||
|
-- Поквартальные данные
|
||||||
|
jq AS (
|
||||||
|
SELECT q.rf_project_report_line_id AS lid, q.quarter,
|
||||||
|
q.adj_by_items, q.adj_increase,
|
||||||
|
q.actual_m1, q.actual_m2, q.actual_m3, q.actual_spod,
|
||||||
|
q.base_plan, q.corrected_plan, q.carryover
|
||||||
|
FROM v3.rf_project_report_quarter q
|
||||||
|
JOIN v3.rf_project_report_line l ON l.id = q.rf_project_report_line_id
|
||||||
|
WHERE l.rf_project_report_id = p_report_id
|
||||||
|
),
|
||||||
|
-- INPUT base: соединяем page + 4 квартала
|
||||||
|
input_base AS (
|
||||||
|
SELECT
|
||||||
|
pg.lid, pg.eid, pg.sc, pg.ic, pg.ng, pg.ename, pg.tree_path,
|
||||||
|
q1.adj_by_items AS q1_abi, q1.adj_increase AS q1_ai,
|
||||||
|
q1.actual_m1 AS q1_m1, q1.actual_m2 AS q1_m2, q1.actual_m3 AS q1_m3,
|
||||||
|
q2.carryover as q2_co,
|
||||||
|
q2.adj_by_items AS q2_abi, q2.adj_increase AS q2_ai,
|
||||||
|
q2.actual_m1 AS q2_m1, q2.actual_m2 AS q2_m2, q2.actual_m3 AS q2_m3,
|
||||||
|
q3.carryover as q3_co,
|
||||||
|
q3.adj_by_items AS q3_abi, q3.adj_increase AS q3_ai,
|
||||||
|
q3.actual_m1 AS q3_m1, q3.actual_m2 AS q3_m2, q3.actual_m3 AS q3_m3,
|
||||||
|
q4.carryover as q4_co,
|
||||||
|
q4.adj_by_items AS q4_abi, q4.adj_increase AS q4_ai,
|
||||||
|
q4.actual_m1 AS q4_m1, q4.actual_m2 AS q4_m2, q4.actual_m3 AS q4_m3,
|
||||||
|
q4.actual_spod AS q4_spod,
|
||||||
|
q1.base_plan AS q1_bp, q2.base_plan AS q2_bp,
|
||||||
|
q3.base_plan AS q3_bp, q4.base_plan AS q4_bp,
|
||||||
|
q2.corrected_plan AS q2_cp, q3.corrected_plan AS q3_cp, q4.corrected_plan AS q4_cp
|
||||||
|
FROM page pg
|
||||||
|
LEFT JOIN jq q1 ON q1.lid = pg.lid AND q1.quarter = 1
|
||||||
|
LEFT JOIN jq q2 ON q2.lid = pg.lid AND q2.quarter = 2
|
||||||
|
LEFT JOIN jq q3 ON q3.lid = pg.lid AND q3.quarter = 3
|
||||||
|
LEFT JOIN jq q4 ON q4.lid = pg.lid AND q4.quarter = 4
|
||||||
|
),
|
||||||
|
-- Computed totals per row
|
||||||
|
enriched AS (
|
||||||
|
SELECT b.*,
|
||||||
|
COALESCE(b.q1_abi,0) + COALESCE(b.q1_ai,0) AS q1_tc,
|
||||||
|
COALESCE(b.q1_m1,0) + COALESCE(b.q1_m2,0) + COALESCE(b.q1_m3,0) AS q1_qa,
|
||||||
|
COALESCE(b.q2_abi,0) + COALESCE(b.q2_ai,0) AS q2_tc,
|
||||||
|
COALESCE(b.q2_m1,0) + COALESCE(b.q2_m2,0) + COALESCE(b.q2_m3,0) AS q2_qa,
|
||||||
|
COALESCE(b.q3_abi,0) + COALESCE(b.q3_ai,0) AS q3_tc,
|
||||||
|
COALESCE(b.q3_m1,0) + COALESCE(b.q3_m2,0) + COALESCE(b.q3_m3,0) AS q3_qa,
|
||||||
|
COALESCE(b.q4_abi,0) + COALESCE(b.q4_ai,0) AS q4_tc,
|
||||||
|
COALESCE(b.q4_m1,0) + COALESCE(b.q4_m2,0) + COALESCE(b.q4_m3,0) + COALESCE(b.q4_spod,0) AS q4_qa
|
||||||
|
FROM input_base b
|
||||||
|
),
|
||||||
|
-- Per-line aggregates for tree rollup
|
||||||
|
agg_q AS (
|
||||||
|
SELECT l.expense_item_id AS eid, q.quarter,
|
||||||
|
SUM(q.adj_by_items) AS abi, SUM(q.adj_increase) AS ai,
|
||||||
|
SUM(q.actual_m1) AS m1, SUM(q.actual_m2) AS m2, SUM(q.actual_m3) AS m3,
|
||||||
|
SUM(q.actual_spod) AS spod,
|
||||||
|
SUM(q.base_plan) AS bp, SUM(q.corrected_plan) AS cp, SUM(q.carryover) as co
|
||||||
|
FROM v3.rf_project_report_line l
|
||||||
|
JOIN v3.rf_project_report_quarter q ON q.rf_project_report_line_id = l.id
|
||||||
|
WHERE l.rf_project_report_id = p_report_id
|
||||||
|
GROUP BY l.expense_item_id, q.quarter
|
||||||
|
),
|
||||||
|
tw_aq1 AS (
|
||||||
|
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||||||
|
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||||||
|
SUM(a.bp) AS bp
|
||||||
|
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 1
|
||||||
|
GROUP BY tw.id
|
||||||
|
),
|
||||||
|
tw_aq2 AS (
|
||||||
|
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||||||
|
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||||||
|
SUM(a.bp) AS bp, SUM(a.cp) AS cp, SUM(a.co) AS co
|
||||||
|
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 2
|
||||||
|
GROUP BY tw.id
|
||||||
|
),
|
||||||
|
tw_aq3 AS (
|
||||||
|
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||||||
|
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||||||
|
SUM(a.bp) AS bp, SUM(a.cp) AS cp, SUM(a.co) AS co
|
||||||
|
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 3
|
||||||
|
GROUP BY tw.id
|
||||||
|
),
|
||||||
|
tw_aq4 AS (
|
||||||
|
SELECT tw.id, SUM(a.abi) AS abi, SUM(a.ai) AS ai,
|
||||||
|
SUM(a.m1) AS m1, SUM(a.m2) AS m2, SUM(a.m3) AS m3,
|
||||||
|
SUM(a.spod) AS spod,
|
||||||
|
SUM(a.bp) AS bp, SUM(a.cp) AS cp, SUM(a.co) AS co
|
||||||
|
FROM tw LEFT JOIN agg_q a ON a.eid = ANY(tw.desc_ids) AND a.quarter = 4
|
||||||
|
GROUP BY tw.id
|
||||||
|
)
|
||||||
|
|
||||||
|
-- ═══ Часть A: INPUT строки ════════════════════════════════════════════════
|
||||||
|
SELECT * FROM (
|
||||||
|
SELECT
|
||||||
|
'INPUT'::VARCHAR AS row_type, 3 AS depth, e.lid::INT AS line_id,
|
||||||
|
e.sc, e.ic, e.ng, e.ename,
|
||||||
|
-- Q1
|
||||||
|
CASE WHEN s_q1 THEN e.q1_abi END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_ai END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_tc END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_m1 END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_m2 END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_m3 END,
|
||||||
|
CASE WHEN s_q1 THEN e.q1_qa END,
|
||||||
|
CASE WHEN s_q1 THEN ROUND(e.q1_tc - e.q1_qa, 1) END,
|
||||||
|
-- Q2 (carryover пока NULL — нет столбца в _quarter; задел на будущее)
|
||||||
|
CASE WHEN s_q2 THEN e.q2_co END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_abi END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_ai END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_tc END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_m1 END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_m2 END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_m3 END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_qa END,
|
||||||
|
CASE WHEN s_q2 THEN ROUND(e.q2_tc - e.q2_qa, 1) END,
|
||||||
|
-- Q3
|
||||||
|
CASE WHEN s_q3 THEN e.q3_co END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_abi END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_ai END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_tc END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_m1 END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_m2 END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_m3 END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_qa END,
|
||||||
|
CASE WHEN s_q3 THEN ROUND(e.q3_tc - e.q3_qa, 1) END,
|
||||||
|
-- Q4
|
||||||
|
CASE WHEN s_q4 THEN e.q4_co END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_abi END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_ai END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_tc END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_m1 END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_m2 END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_m3 END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_spod END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_qa END,
|
||||||
|
CASE WHEN s_q4 THEN ROUND(e.q4_tc - e.q4_qa, 1) END,
|
||||||
|
-- year
|
||||||
|
CASE WHEN s_year THEN e.q1_tc + e.q2_tc + e.q3_tc + e.q4_tc END,
|
||||||
|
CASE WHEN s_year THEN e.q1_qa + e.q2_qa + e.q3_qa + e.q4_qa END,
|
||||||
|
CASE WHEN s_year THEN ROUND((e.q1_tc + e.q2_tc + e.q3_tc + e.q4_tc) -
|
||||||
|
(e.q1_qa + e.q2_qa + e.q3_qa + e.q4_qa), 1) END,
|
||||||
|
-- план-блоки
|
||||||
|
CASE WHEN s_q1 THEN e.q1_bp END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_bp END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_bp END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_bp END,
|
||||||
|
CASE WHEN s_year THEN COALESCE(e.q1_bp,0)+COALESCE(e.q2_bp,0)+
|
||||||
|
COALESCE(e.q3_bp,0)+COALESCE(e.q4_bp,0) END,
|
||||||
|
CASE WHEN s_q2 THEN e.q2_cp END,
|
||||||
|
CASE WHEN s_q3 THEN e.q3_cp END,
|
||||||
|
CASE WHEN s_q4 THEN e.q4_cp END,
|
||||||
|
e.tree_path || ARRAY[e.lid::INT] AS _sort_path
|
||||||
|
FROM enriched e
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
-- ═══ Часть B: Иерархия (ROOT / GROUP / ITEM / SUB_ITEM) ═════════════════
|
||||||
|
SELECT
|
||||||
|
CASE t.depth WHEN 0 THEN 'ROOT' WHEN 1 THEN 'GROUP'
|
||||||
|
WHEN 2 THEN 'ITEM' WHEN 3 THEN 'SUB_ITEM' END::VARCHAR,
|
||||||
|
t.depth, NULL::INT,
|
||||||
|
CASE WHEN t.depth < 3 THEN t.item_id ELSE t.parent_item_id END,
|
||||||
|
CASE WHEN t.depth = 3 THEN t.item_id ELSE NULL END,
|
||||||
|
t.num_group_id, t.name,
|
||||||
|
-- Q1
|
||||||
|
CASE WHEN s_q1 THEN a1.abi END,
|
||||||
|
CASE WHEN s_q1 THEN a1.ai END,
|
||||||
|
CASE WHEN s_q1 THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0) END,
|
||||||
|
CASE WHEN s_q1 THEN a1.m1 END,
|
||||||
|
CASE WHEN s_q1 THEN a1.m2 END,
|
||||||
|
CASE WHEN s_q1 THEN a1.m3 END,
|
||||||
|
CASE WHEN s_q1 THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0) END,
|
||||||
|
CASE WHEN s_q1 THEN ROUND((COALESCE(a1.abi,0)+COALESCE(a1.ai,0)) -
|
||||||
|
(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)), 1) END,
|
||||||
|
-- Q2
|
||||||
|
CASE WHEN s_q2 THEN a2.co END,
|
||||||
|
CASE WHEN s_q2 THEN a2.abi END,
|
||||||
|
CASE WHEN s_q2 THEN a2.ai END,
|
||||||
|
CASE WHEN s_q2 THEN COALESCE(a2.abi,0)+COALESCE(a2.ai,0) END,
|
||||||
|
CASE WHEN s_q2 THEN a2.m1 END,
|
||||||
|
CASE WHEN s_q2 THEN a2.m2 END,
|
||||||
|
CASE WHEN s_q2 THEN a2.m3 END,
|
||||||
|
CASE WHEN s_q2 THEN COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0) END,
|
||||||
|
CASE WHEN s_q2 THEN ROUND((COALESCE(a2.abi,0)+COALESCE(a2.ai,0)) -
|
||||||
|
(COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)), 1) END,
|
||||||
|
-- Q3
|
||||||
|
CASE WHEN s_q3 THEN a3.co END,
|
||||||
|
CASE WHEN s_q3 THEN a3.abi END,
|
||||||
|
CASE WHEN s_q3 THEN a3.ai END,
|
||||||
|
CASE WHEN s_q3 THEN COALESCE(a3.abi,0)+COALESCE(a3.ai,0) END,
|
||||||
|
CASE WHEN s_q3 THEN a3.m1 END,
|
||||||
|
CASE WHEN s_q3 THEN a3.m2 END,
|
||||||
|
CASE WHEN s_q3 THEN a3.m3 END,
|
||||||
|
CASE WHEN s_q3 THEN COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0) END,
|
||||||
|
CASE WHEN s_q3 THEN ROUND((COALESCE(a3.abi,0)+COALESCE(a3.ai,0)) -
|
||||||
|
(COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)), 1) END,
|
||||||
|
-- Q4
|
||||||
|
CASE WHEN s_q4 THEN a4.co END,
|
||||||
|
CASE WHEN s_q4 THEN a4.abi END,
|
||||||
|
CASE WHEN s_q4 THEN a4.ai END,
|
||||||
|
CASE WHEN s_q4 THEN COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||||||
|
CASE WHEN s_q4 THEN a4.m1 END,
|
||||||
|
CASE WHEN s_q4 THEN a4.m2 END,
|
||||||
|
CASE WHEN s_q4 THEN a4.m3 END,
|
||||||
|
CASE WHEN s_q4 THEN a4.spod END,
|
||||||
|
CASE WHEN s_q4 THEN COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||||||
|
CASE WHEN s_q4 THEN ROUND((COALESCE(a4.abi,0)+COALESCE(a4.ai,0)) -
|
||||||
|
(COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)), 1) END,
|
||||||
|
-- year
|
||||||
|
CASE WHEN s_year THEN COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+
|
||||||
|
COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||||||
|
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+
|
||||||
|
COALESCE(a4.abi,0)+COALESCE(a4.ai,0) END,
|
||||||
|
CASE WHEN s_year THEN COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||||||
|
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||||||
|
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||||||
|
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0) END,
|
||||||
|
CASE WHEN s_year THEN
|
||||||
|
ROUND(
|
||||||
|
(COALESCE(a1.abi,0)+COALESCE(a1.ai,0)+COALESCE(a2.abi,0)+COALESCE(a2.ai,0)+
|
||||||
|
COALESCE(a3.abi,0)+COALESCE(a3.ai,0)+COALESCE(a4.abi,0)+COALESCE(a4.ai,0))
|
||||||
|
-
|
||||||
|
(COALESCE(a1.m1,0)+COALESCE(a1.m2,0)+COALESCE(a1.m3,0)+
|
||||||
|
COALESCE(a2.m1,0)+COALESCE(a2.m2,0)+COALESCE(a2.m3,0)+
|
||||||
|
COALESCE(a3.m1,0)+COALESCE(a3.m2,0)+COALESCE(a3.m3,0)+
|
||||||
|
COALESCE(a4.m1,0)+COALESCE(a4.m2,0)+COALESCE(a4.m3,0)+COALESCE(a4.spod,0)),
|
||||||
|
1)
|
||||||
|
END,
|
||||||
|
-- план-блоки (rollup)
|
||||||
|
CASE WHEN s_q1 THEN a1.bp END,
|
||||||
|
CASE WHEN s_q2 THEN a2.bp END,
|
||||||
|
CASE WHEN s_q3 THEN a3.bp END,
|
||||||
|
CASE WHEN s_q4 THEN a4.bp END,
|
||||||
|
CASE WHEN s_year THEN COALESCE(a1.bp,0)+COALESCE(a2.bp,0)+
|
||||||
|
COALESCE(a3.bp,0)+COALESCE(a4.bp,0) END,
|
||||||
|
CASE WHEN s_q2 THEN a2.cp END,
|
||||||
|
CASE WHEN s_q3 THEN a3.cp END,
|
||||||
|
CASE WHEN s_q4 THEN a4.cp END,
|
||||||
|
t.path
|
||||||
|
FROM tw t
|
||||||
|
LEFT JOIN tw_aq1 a1 ON a1.id = t.id
|
||||||
|
LEFT JOIN tw_aq2 a2 ON a2.id = t.id
|
||||||
|
LEFT JOIN tw_aq3 a3 ON a3.id = t.id
|
||||||
|
LEFT JOIN tw_aq4 a4 ON a4.id = t.id
|
||||||
|
) sub
|
||||||
|
ORDER BY sub._sort_path;
|
||||||
|
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-- DROP FUNCTION v3._apply_form3_cell(int4, text, jsonb);
|
||||||
|
|
||||||
|
CREATE OR REPLACE FUNCTION v3._apply_form3_cell(p_line_id integer, p_column text, p_value jsonb)
|
||||||
|
RETURNS void
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $function$
|
||||||
|
DECLARE
|
||||||
|
v_parts TEXT[];
|
||||||
|
v_scope TEXT;
|
||||||
|
v_field TEXT;
|
||||||
|
v_q SMALLINT;
|
||||||
|
v_target_col TEXT;
|
||||||
|
v_target_type TEXT;
|
||||||
|
v_sql TEXT;
|
||||||
|
BEGIN
|
||||||
|
v_parts := string_to_array(p_column, '.');
|
||||||
|
IF array_length(v_parts, 1) < 2 THEN
|
||||||
|
RAISE EXCEPTION 'bad_column_format: %, expected scope.field', p_column;
|
||||||
|
END IF;
|
||||||
|
v_scope := v_parts[1];
|
||||||
|
v_field := v_parts[2];
|
||||||
|
|
||||||
|
IF v_scope = 'totals' OR v_field IN ('total_corr','quarter_actual','economy') THEN
|
||||||
|
RAISE EXCEPTION 'computed_field: %', p_column;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF v_scope NOT IN ('q1','q2','q3','q4') THEN
|
||||||
|
RAISE EXCEPTION 'unknown_scope: % (FORM_3 supports only q1..q4)', v_scope;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
v_q := substring(v_scope FROM 2)::SMALLINT;
|
||||||
|
|
||||||
|
-- Маппинг JSONB-ключ → колонка rf_project_report_quarter
|
||||||
|
CASE v_field
|
||||||
|
WHEN 'adj_by_items' THEN v_target_col := 'adj_by_items'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'carryover' THEN v_target_col := 'carryover'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'adj_increase' THEN v_target_col := 'adj_increase'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'm1' THEN v_target_col := 'actual_m1'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'm2' THEN v_target_col := 'actual_m2'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'm3' THEN v_target_col := 'actual_m3'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'spod' THEN
|
||||||
|
IF v_q <> 4 THEN RAISE EXCEPTION 'spod is q4-only: %', p_column; END IF;
|
||||||
|
v_target_col := 'actual_spod'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'base_plan' THEN v_target_col := 'base_plan'; v_target_type := 'NUMERIC';
|
||||||
|
WHEN 'corrected_plan' THEN
|
||||||
|
IF v_q = 1 THEN RAISE EXCEPTION 'corrected_plan is q2-q4 only: %', p_column; END IF;
|
||||||
|
v_target_col := 'corrected_plan'; v_target_type := 'NUMERIC';
|
||||||
|
ELSE
|
||||||
|
RAISE EXCEPTION 'unknown_column: %', p_column;
|
||||||
|
END CASE;
|
||||||
|
|
||||||
|
v_sql := format(
|
||||||
|
'INSERT INTO v3.rf_project_report_quarter (rf_project_report_line_id, quarter, %1$I) '
|
||||||
|
|| 'VALUES ($1, $2, ($3 #>> ''{}'')::%2$s) '
|
||||||
|
|| 'ON CONFLICT (rf_project_report_line_id, quarter) DO UPDATE SET %1$I = EXCLUDED.%1$I',
|
||||||
|
v_target_col, v_target_type
|
||||||
|
);
|
||||||
|
EXECUTE v_sql USING p_line_id, v_q, p_value;
|
||||||
|
END;
|
||||||
|
$function$
|
||||||
|
;
|
||||||
@ -24,6 +24,7 @@ class RfProjectReportQuarter(Base):
|
|||||||
actual_m2: Mapped[float | None] = mapped_column(Numeric)
|
actual_m2: Mapped[float | None] = mapped_column(Numeric)
|
||||||
actual_m3: Mapped[float | None] = mapped_column(Numeric)
|
actual_m3: Mapped[float | None] = mapped_column(Numeric)
|
||||||
actual_spod: Mapped[float | None] = mapped_column(Numeric)
|
actual_spod: Mapped[float | None] = mapped_column(Numeric)
|
||||||
|
carryover: Mapped[float | None] = mapped_column(Numeric)
|
||||||
|
|
||||||
# line: Mapped[RfProjectReportLine] = relationship(
|
# line: Mapped[RfProjectReportLine] = relationship(
|
||||||
# "RfProjectReportLine", back_populates="quarters"
|
# "RfProjectReportLine", back_populates="quarters"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user