"""read-only flattened reporting views for Power BI Revision ID: 0002 Revises: 0001 Create Date: 2026-07-13 The powerbi_ro MySQL user (created by db/init/01-powerbi-user.sh) has SELECT on exactly these three views and nothing else. """ from alembic import op revision = "0002" down_revision = "0001" branch_labels = None depends_on = None VW_NCR_FULL = """ CREATE OR REPLACE VIEW vw_ncr_full AS SELECT n.id AS ncr_id, n.ncr_number, n.ncr_year, n.ncr_seq, n.created_at, n.job_number, d.name AS department, dc.name AS deviation_category, req.display_name AS requester, req.email AS requester_email, da.display_name AS disposition_authority, n.stage, n.stage_entered_at, DATEDIFF(UTC_TIMESTAMP(), n.stage_entered_at) AS days_in_stage, n.deviation_detail, n.qc_authority, n.work_order, n.disposition_notes, n.secondary_review_needed, (SELECT GROUP_CONCAT(u2.display_name ORDER BY u2.display_name SEPARATOR '; ') FROM ncr_secondary_assignees sa2 JOIN users u2 ON u2.id = sa2.user_id WHERE sa2.ncr_id = n.id) AS secondary_authorities, n.operations_complete, n.operations_completed_at, opu.display_name AS operations_completed_by, n.qc_approval, n.inspection_notes, n.qc_closed, n.qc_closed_at, qcu.display_name AS qc_closed_by, n.labor_cost, n.material_cost, n.service_cost, n.other_cost, COALESCE(n.labor_cost, 0) + COALESCE(n.material_cost, 0) + COALESCE(n.service_cost, 0) + COALESCE(n.other_cost, 0) AS total_cost, n.costing_completed_at, n.closed_at, clu.display_name AS closed_by, ji.part_id, ji.part_description, ji.customer_name, ji.work_order_status, (SELECT COUNT(*) FROM attachments a WHERE a.ncr_id = n.id) AS attachment_count FROM ncrs n JOIN departments d ON d.id = n.department_id JOIN deviation_categories dc ON dc.id = n.deviation_category_id JOIN users req ON req.id = n.requester_id JOIN users da ON da.id = n.disposition_authority_id LEFT JOIN users opu ON opu.id = n.operations_completed_by_id LEFT JOIN users qcu ON qcu.id = n.qc_closed_by_id LEFT JOIN users clu ON clu.id = n.closed_by_id LEFT JOIN job_info ji ON ji.ncr_id = n.id """ VW_NCR_STAGE_HISTORY = """ CREATE OR REPLACE VIEW vw_ncr_stage_history AS SELECT t.id AS transition_id, t.ncr_id, n.ncr_number, n.job_number, t.from_stage, t.to_stage, t.action, t.acted_at, u.display_name AS acted_by, u.email AS acted_by_email, t.note FROM stage_transitions t JOIN ncrs n ON n.id = t.ncr_id JOIN users u ON u.id = t.acted_by_id """ VW_NCR_COSTS = """ CREATE OR REPLACE VIEW vw_ncr_costs AS SELECT n.id AS ncr_id, n.ncr_number, n.ncr_year, n.job_number, d.name AS department, dc.name AS deviation_category, n.created_at, n.closed_at, n.stage, n.labor_cost, n.material_cost, n.service_cost, n.other_cost, COALESCE(n.labor_cost, 0) + COALESCE(n.material_cost, 0) + COALESCE(n.service_cost, 0) + COALESCE(n.other_cost, 0) AS total_cost FROM ncrs n JOIN departments d ON d.id = n.department_id JOIN deviation_categories dc ON dc.id = n.deviation_category_id WHERE n.costing_completed_at IS NOT NULL """ def upgrade() -> None: op.execute(VW_NCR_FULL) op.execute(VW_NCR_STAGE_HISTORY) op.execute(VW_NCR_COSTS) def downgrade() -> None: op.execute("DROP VIEW IF EXISTS vw_ncr_costs") op.execute("DROP VIEW IF EXISTS vw_ncr_stage_history") op.execute("DROP VIEW IF EXISTS vw_ncr_full")