1
resposta

[Bug] Correção para reservas começando ou terminando em um final de semana

Olá.

Estava fazendo alguns testes na procedure criada nesta aula, que inclui a reserva de um aluguel baseado na data inicial e número de dias, excluindo finais de semana (finais de semana são adicionados à reserva, mas não são cobrados, então não entram na contagem de número de dias).

Acabei encontrando dois bugs por conta da lógica implementada no loop:

  • Se a reserva começar no sábado ou domingo, o primeiro dia é computado na contagem de número de dias (vContador está sendo iniciado como 1);
  • Se o último dia cair num sábado, a reserva não é atualizada para ir até segunda-feira (o loop já vai ter terminado sem verificar se o último dia é válido).

Segue abaixo uma possível correção do código, levando em conta essas condições. Outras partes do meu código estão um pouco diferentes das do instrutor, mas fazem a mesma coisa. O que eu fiz foi iniciar vContador em 0 e vDataFim como sendo um dia antes de vDataInicio (para que a contagem comece a partir do primeiro dia dentro do loop corrigido). Dentro do loop, primeiro calculo vDataFim e só depois verifico qual é o dia da semana em dayofweek. Assim, se o primeiro dia for um sábado ou domingo, ele não é computado na contagem de dias, bem como se o último dia for no sábado.

DROP PROCEDURE IF EXISTS nova_reserva;
DELIMITER //
CREATE PROCEDURE nova_reserva(
    vReserva VARCHAR(10),
    vClienteNome VARCHAR(255),
    vHospedagem VARCHAR(10),
    vDataInicio DATE, 
    vDias INTEGER, -- incluindo vDataInicio
    vPrecoUnitario DECIMAL(10,2) -- valor da diária
)
BEGIN
    -- novas regras: 
    -- 1. calcular a data final com base na inicial e número de dias
    -- 2. finais de semana NÃO contam para o cálculo dos dias e são
    -- incluídos de graça no total da reserva
    DECLARE vDataFim DATE;
    DECLARE vContador Integer;
    DECLARE vDiaSemana Integer;

    -- a partir do nome, vamos encontrar o id consultando a tabela clientes
    DECLARE vCliente VARCHAR(10);
    
    -- vamos verificar se há mais de um cliente com o mesmo nome
    DECLARE vQtdCliente INTEGER;
    
    -- variável para cálculo do preço total da reserva
    DECLARE vPrecoTotal DECIMAL(10,2);
    
    -- variável para exibir mensagem de erro
    DECLARE vMensagem VARCHAR(100);
    
    -- tratamento de erro para chave estrangeira (erro 1452)
    DECLARE EXIT HANDLER FOR 1452
    BEGIN
        SET vMensagem = 'Problema de chave estrangeira';
        -- use o select para exibir a mensagem como resultado da consulta
        SELECT vMensagem;
    END;
    
    -- verificando se há mais de um cliente com o mesmo nome
    -- podemos usar o resultado do SELECT como valor da variável
    SET vQtdCliente = (SELECT count(*) FROM clientes WHERE nome = vClienteNome);
    
    CASE 
    WHEN vQtdCliente > 1 THEN
        SET vMensagem = 'Não é possível fazer a inclusão, pois há mais de um cliente com o mesmo nome.';
        SELECT vMensagem;
    WHEN vQtdCliente = 0 THEN
        SET vMensagem = 'Não é possível fazer a inclusão, pois o cliente não existe.';
        SELECT vMensagem;
    WHEN vQtdCliente = 1 THEN
        -- cálculo dos dias, aplicando as novas regras
        SET vContador = 0;
        
        -- a data de início será incluída como sendo o primeiro dia
        -- das diárias. O loop faz esse acerto.
        SET vDataFim = vDataInicio - INTERVAL 1 DAY; 
        
        WHILE vContador < vDias
        DO
            -- independente do dia, adicionamos à data final
            SET vDataFim = vDataFim + INTERVAL 1 DAY;			
            SET vDiaSemana = dayofweek(vDataFim);
             -- 1 é domingo e 7 é sábado
            IF (vDiaSemana <> 1 AND vDiaSemana <> 7) THEN
                SET vContador = vContador + 1;
            END IF;
        END WHILE;        
        
        -- cálculo do valor total da reserva
        SET vPrecoTotal = vDias * vPrecoUnitario;
        
        -- ao invés de um SET, podemos usar o SELECT INTO para 
        -- atribuir o valor a uma variável
        SELECT cliente_id INTO vCliente FROM clientes WHERE nome = vClienteNome; 
        
        INSERT INTO reservas VALUES (
            vReserva, vCliente, vHospedagem, vDataInicio, vDataFim, vPrecoTotal
        );
        
        SET vMensagem = 'Aluguel incluído na base com sucesso!';
        SELECT vMensagem;
    END CASE;
END//
DELIMITER ;
1 resposta

Olá, Matheus! Como vai? :)

Realmente, você identificou dois bugs importantes na lógica do loop. A sua sugestão de solução foi muito bem pensada! Parabéns e obrigada por compartilhar com a comunidade. Tenho certeza de que será útil para outras pessoas! Parabéns!

Desejo muito sucesso! Continue firme nos estudos! ✨✨

Fico à disposição!