computed-fix: добавил поле carryover к кварталам проектов #117

Merged
tsygankoviva merged 1 commits from computed-fix into test 2026-08-21 17:18:13 +03:00
3 changed files with 436 additions and 0 deletions

View 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

View 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$
;

View File

@ -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"