DELIMITER $$ -- =================================================================================== -- PROCEDURE 1: Matricular um novo aluno e gerar sua primeira mensalidade. -- Versão refinada com mais parâmetros e lógica de vencimento aprimorada. -- =================================================================================== DROP PROCEDURE IF EXISTS sp_matricular_aluno$$ CREATE PROCEDURE sp_matricular_aluno( IN p_nome_completo VARCHAR(255), IN p_cpf VARCHAR(11), IN p_email VARCHAR(100), IN p_telefone VARCHAR(20), IN p_data_nascimento DATE, -- Parâmetro adicionado para o cadastro completo IN p_valor_mensal DECIMAL(10,2), IN p_dia_vencimento INT -- Dia do mês para o vencimento (ex: 10) ) BEGIN DECLARE v_id_aluno INT; DECLARE v_data_vencimento DATE; -- Se algo der errado, desfaz todas as alterações. DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Falha na matrícula. Nenhuma alteração foi aplicada.'; END; START TRANSACTION; -- 1. Cria o aluno INSERT INTO alunos (nome_completo, cpf, email, telefone, data_nascimento, ativo) VALUES (p_nome_completo, p_cpf, p_email, p_telefone, p_data_nascimento, TRUE); SET v_id_aluno = LAST_INSERT_ID(); -- 2. Calcula a data do primeiro vencimento de forma inteligente SET v_data_vencimento = STR_TO_DATE(CONCAT(YEAR(CURDATE()), '-', MONTH(CURDATE()), '-', p_dia_vencimento), '%Y-%m-%d'); -- Se o dia do vencimento já passou neste mês, a primeira cobrança é para o mês seguinte. IF v_data_vencimento < CURDATE() THEN SET v_data_vencimento = v_data_vencimento + INTERVAL 1 MONTH; END IF; -- 3. Gera a primeira mensalidade INSERT INTO mensalidades (id_aluno, mes_referencia, valor_devido, data_vencimento, status) VALUES ( v_id_aluno, DATE_FORMAT(v_data_vencimento, '%Y-%m-01'), -- O mês de referência é o mesmo do vencimento p_valor_mensal, v_data_vencimento, 'Pendente' ); COMMIT; END$$ -- =================================================================================== -- PROCEDURE 2: Registrar um pagamento (funciona em conjunto com o trigger). -- =================================================================================== DROP PROCEDURE IF EXISTS sp_registrar_pagamento$$ CREATE PROCEDURE sp_registrar_pagamento( IN p_id_mensalidade INT, IN p_valor_pago DECIMAL(10,2), IN p_forma_pgto VARCHAR(50), IN p_obs TEXT ) BEGIN -- Rollback automático se der erro. DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Falha ao registrar pagamento. Nenhuma alteração foi aplicada.'; END; START TRANSACTION; -- 1. Insere o pagamento. INSERT INTO Pagamentos (id_mensalidade, valor_pago, data_pagamento, forma_pagamento, observacao) VALUES (p_id_mensalidade, p_valor_pago, CURDATE(), p_forma_pgto, p_obs); -- 2. O trigger 'tg_after_insert_pagamento' fará o resto do trabalho. COMMIT; END$$ DELIMITER ; DELIMITER $$ -- =================================================================================== -- PROCEDURE: sp_estornar_pagamento -- Objetivo: Desfaz um registro de pagamento e recalcula o status da mensalidade -- associada de forma segura e atômica (ACID). -- =================================================================================== DROP PROCEDURE IF EXISTS sp_estornar_pagamento$$ CREATE PROCEDURE sp_estornar_pagamento( IN p_id_pagamento INT ) BEGIN -- Variáveis para armazenar dados intermediários DECLARE v_id_mensalidade INT; DECLARE v_total_pago DECIMAL(10,2); DECLARE v_data_vencimento DATE; -- Handler para garantir que, se qualquer erro ocorrer, a transação -- seja desfeita (ROLLBACK) e uma mensagem clara seja retornada. DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Falha ao estornar pagamento. Nenhuma alteração foi aplicada.'; END; -- Inicia a transação para garantir que todas as operações sejam um bloco único. START TRANSACTION; -- 1. Encontra a mensalidade associada ao pagamento e bloqueia a linha -- com 'FOR UPDATE' para evitar que outra transação a modifique ao mesmo tempo. SELECT id_mensalidade INTO v_id_mensalidade FROM Pagamentos WHERE id_pagamento = p_id_pagamento FOR UPDATE; -- 2. Valida se o pagamento realmente existe. IF v_id_mensalidade IS NULL THEN -- Força um erro para ser capturado pelo EXIT HANDLER. SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'Pagamento não encontrado.'; END IF; -- 3. Exclui o registro do pagamento da tabela. DELETE FROM Pagamentos WHERE id_pagamento = p_id_pagamento; -- 4. Recalcula o valor total que ainda resta pago para a mensalidade. SELECT IFNULL(SUM(valor_pago), 0) INTO v_total_pago FROM Pagamentos WHERE id_mensalidade = v_id_mensalidade; -- 5. Busca a data de vencimento para definir o status corretamente. SELECT data_vencimento INTO v_data_vencimento FROM Mensalidades WHERE id_mensalidade = v_id_mensalidade; -- 6. Atualiza o status da mensalidade com base na nova situação financeira. UPDATE Mensalidades SET status = CASE -- Se ainda resta algum valor pago, é parcial. WHEN v_total_pago > 0 THEN 'Pago Parcialmente' -- Se não resta nada pago E a data já passou, está vencido. WHEN v_data_vencimento < CURDATE() THEN 'Vencido' -- Se não, volta a ser pendente. ELSE 'Pendente' END WHERE id_mensalidade = v_id_mensalidade; -- Se todos os passos foram executados com sucesso, confirma as alterações. COMMIT; END$$ DELIMITER ;