Author: Pedro Lucas Porcellis <porcellis@eletrotupi.com>
income: add income models, DAOs, and controllers
app/controllers/IncomesController.php | 73 ++++++++++++++++++++ app/daos/IncomeDAO.php | 102 +++++++++++++++++++++++++++++ app/models/Income.php | 93 ++++++++++++++++++++++++++ app/views/income_create.php | 52 ++++++++++++++ app/views/income_edit.php | 52 ++++++++++++++ app/views/incomes.php | 53 +++++++++++++++ app/views/layout.php | 1 config/routes.php | 25 +++++++ schema.sql | 13 +++
diff --git a/app/controllers/IncomesController.php b/app/controllers/IncomesController.php new file mode 100644 index 0000000000000000000000000000000000000000..37f44377b798f01accd887d44632f479790cf3c6 --- /dev/null +++ b/app/controllers/IncomesController.php @@ -0,0 +1,73 @@ +<?php + +require_once __DIR__ . '/../daos/IncomeDAO.php'; +require_once __DIR__ . '/../models/Income.php'; + +class IncomesController { + private $incomeDAO; + + public function __construct() { + $this->incomeDAO = new IncomeDAO(); + } + + public function index() { + $incomes = $this->incomeDAO->getIncomesByUser($_SESSION['user_id']); + + return Template::render('incomes', ['incomes' => $incomes]); + } + + public function create() { + return Template::render('income_create'); + } + + public function store() { + $data = $_POST; + + $income = new Income( + null, + $data['title'], + $data['amount'], + $data['income_type'], + $data['recurrence_period'], + $data['date'], + $_SESSION['user_id'], + ); + + $this->incomeDAO->create($income); + + header('Location: /incomes'); + exit; + } + + public function edit($id) { + $income = $this->incomeDAO->getIncomeById($id); + + return Template::render('income_edit', ['income' => $income]); + } + + public function update($id) { + $data = $_POST; + + $income = new Income( + $id, + $data['title'], + $data['amount'], + $data['income_type'], + $data['recurrence_period'], + $data['date'], + $_SESSION['user_id'], + ); + + $this->incomeDAO->update($income); + + header('Location: /incomes'); + exit; + } + + public function destroy($id) { + $this->incomeDAO->destroy($id, $_SESSION['user_id']); + + header('Location: /incomes'); + exit; + } +} diff --git a/app/daos/IncomeDAO.php b/app/daos/IncomeDAO.php new file mode 100644 index 0000000000000000000000000000000000000000..f564e36b4781c53a5673abb45ec868278213b3a1 --- /dev/null +++ b/app/daos/IncomeDAO.php @@ -0,0 +1,102 @@ +<?php + +require_once __DIR__ . '/../../config/database.php'; +require_once __DIR__ . '/../models/Income.php'; + +class IncomeDAO { + private $db; + + public function __construct() { + $this->db = getDatabaseConnection(); + } + + public function create($income) { + $sql = 'INSERT INTO incomes (user_id, title, amount, income_type, recurrence_period, date) + VALUES (:user_id, :title, :amount, :income_type, :recurrence_period, :date)'; + + $stmt = $this->db->prepare($sql); + $stmt->bindValue(':user_id', $income->getUserId()); + $stmt->bindValue(':title', $income->getTitle()); + $stmt->bindValue(':amount', $income->getAmount()); + $stmt->bindValue(':income_type', $income->getIncomeType()); + $stmt->bindValue(':recurrence_period', $income->getRecurrencePeriod()); + $stmt->bindValue(':date', $income->getDate()); + + return $stmt->execute(); + + $incomeId = $this->db->lastInsertId(); + $income->setId($incomeId); + + return $income; + } + + public function update($income) { + $sql = 'UPDATE incomes SET title = :title, amount = :amount, income_type = :income_type, + recurrence_period = :recurrence_period, date = :date WHERE id = :id AND user_id = :user_id'; + $stmt = $this->db->prepare($sql); + + $stmt->bindValue(':title', $income->getTitle()); + $stmt->bindValue(':amount', $income->getAmount()); + $stmt->bindValue(':income_type', $income->getIncomeType()); + $stmt->bindValue(':recurrence_period', $income->getRecurrencePeriod()); + $stmt->bindValue(':date', $income->getDate()); + $stmt->bindValue(':id', $income->getId()); + $stmt->bindValue(':user_id', $income->getUserId()); + + return $stmt->execute(); + } + + public function getIncomesByUser($user_id) { + $sql = 'SELECT * FROM incomes WHERE user_id = :user_id'; + $stmt = $this->db->prepare($sql); + + $stmt->bindParam(':user_id', $user_id); + $stmt->execute(); + + $incomes = []; + + while ($incomesData = $stmt->fetch(PDO::FETCH_ASSOC)) { + $incomes[] = new Income( + $incomesData['id'], + $incomesData['title'], + $incomesData['amount'], + $incomesData['income_type'], + $incomesData['recurrence_period'], + $incomesData['date'], + $incomesData['user_id'] + ); + } + + return $incomes; + } + + public function getIncomeById($id) { + $sql = 'SELECT * FROM incomes WHERE id = :id'; + $stmt = $this->db->prepare($sql); + + $stmt->bindParam(':id', $id); + $stmt->execute(); + + $incomeData = $stmt->fetch(PDO::FETCH_ASSOC); + + return new Income( + $incomeData['id'], + $incomeData['title'], + $incomeData['amount'], + $incomeData['income_type'], + $incomeData['recurrence_period'], + $incomeData['date'], + $incomeData['user_id'] + ); + } + + public function destroy($id, $user_id) { + $sql = 'DELETE FROM incomes WHERE id = :id AND user_id = :user_id'; + $stmt = $this->db->prepare($sql); + + $stmt->bindParam(':id', $id); + $stmt->bindParam(':user_id', $user_id); + + return $stmt->execute(); + } +} diff --git a/app/models/Income.php b/app/models/Income.php new file mode 100644 index 0000000000000000000000000000000000000000..ff5063ffe8e1543361349557b23de5549f6e0ce4 --- /dev/null +++ b/app/models/Income.php @@ -0,0 +1,93 @@ +<?php + +class Income extends Base { + const INCOME_TYPE_ONE_OFF = 'one_off'; + const INCOME_TYPE_REPEATABLE = 'repeatable'; + + const RECURRENCE_PERIOD_MONTHLY = 'monthly'; + const RECURRENCE_PERIOD_WEEKLY = 'weekly'; + const RECURRENCE_PERIOD_YEARLY = 'yearly'; + + private $title; + private $amount; + private $incomeType; + private $recurrencePeriod; + private $date; + private $userId; + + public function __construct($id, $title, $amount, $incomeType, $recurrencePeriod, $date, $userId) { + $this->id = $id; + $this->title = $title; + $this->amount = $amount; + $this->incomeType = $incomeType; + $this->recurrencePeriod = $recurrencePeriod; + $this->date = $date; + $this->userId = $userId; + } + + public function getTitle() { + return $this->title; + } + + public function getAmount() { + return $this->amount; + } + + public function getIncomeType() { + return $this->incomeType; + } + + public function getRecurrencePeriod() { + return $this->recurrencePeriod; + } + + public function getDate() { + return $this->date; + } + + public function getUserId() { + return $this->userId; + } + + public function setTitle($title) { + $this->title = $title; + } + + public function setAmount($amount) { + $this->amount = $amount; + } + + public function setIncomeType($incomeType) { + $this->incomeType = $incomeType; + } + + public function setRecurrencePeriod($recurrencePeriod) { + $this->recurrencePeriod = $recurrencePeriod; + } + + public function setDate($date) { + $this->date = $date; + } + + public function setUserId($userId) { + $this->userId = $userId; + } + + public function getIncomeTypeLabel() { + if ($this->incomeType === self::INCOME_TYPE_ONE_OFF) { + return 'Único'; + } else { + return 'Recorrente'; + } + } + + public function getRecurrencePeriodLabel() { + if ($this->recurrencePeriod === self::RECURRENCE_PERIOD_MONTHLY) { + return 'Mensal'; + } elseif ($this->recurrencePeriod === self::RECURRENCE_PERIOD_WEEKLY) { + return 'Semanal'; + } else { + return 'Anual'; + } + } +} diff --git a/app/views/income_create.php b/app/views/income_create.php new file mode 100644 index 0000000000000000000000000000000000000000..6725c1096c296525269541489a2a0f4b3182dc2a --- /dev/null +++ b/app/views/income_create.php @@ -0,0 +1,52 @@ +<div class="container mx-auto mt-10"> + <h1 class="text-2xl font-bold text-gray-700 mb-6">Adicionar Novo Rendimento</h1> + + <form action="/incomes/create" method="POST" class="bg-white p-6 rounded shadow-md"> + <div class="mb-4"> + <label for="title" class="block text-sm font-medium text-gray-700">Título</label> + <input type="text" id="title" name="title" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <div class="mb-4"> + <label for="amount" class="block text-sm font-medium text-gray-700">Valor</label> + <input type="number" id="amount" name="amount" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <div class="mb-4"> + <label for="date" class="block text-sm font-medium text-gray-700">Data</label> + <input type="date" id="date" name="date" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <label class="mt-5" for="income_type">Tipo de Rendimento</label> + <select name="income_type" id="income_type" required> + <option value="one_off" <?php echo (isset($income) && $income['income_type'] === 'one_off') ? 'selected' : ''; ?>>Único</option> + <option value="repeatable" <?php echo (isset($income) && $income['income_type'] === 'repeatable') ? 'selected' : ''; ?>>Recorrente</option> + </select> + + <div class="mt-5" id="recurrence_period_container" style="display: <?php echo (isset($income) && $income['income_type'] === 'repeatable') ? 'block' : 'none'; ?>;"> + <label for="recurrence_period">Período de Recorrência</label> + <select name="recurrence_period" id="recurrence_period"> + <option value="monthly" <?php echo (isset($income) && $income['recurrence_period'] === 'monthly') ? 'selected' : ''; ?>>Mensal</option> + <option value="weekly" <?php echo (isset($income) && $income['recurrence_period'] === 'weekly') ? 'selected' : ''; ?>>Semanal</option> + <option value="yearly" <?php echo (isset($income) && $income['recurrence_period'] === 'yearly') ? 'selected' : ''; ?>>Anual</option> + </select> + </div> + + <div class="mt-5"> + <button type="submit" class="bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700"> + Salvar Rendimento + </button> + </div> + </form> + + <script> + document.getElementById('income_type').addEventListener('change', function() { + const recurrencePeriodContainer = document.getElementById('recurrence_period_container'); + if (this.value === 'repeatable') { + recurrencePeriodContainer.style.display = 'block'; + } else { + recurrencePeriodContainer.style.display = 'none'; + } + }); + </script> +</div> diff --git a/app/views/income_edit.php b/app/views/income_edit.php new file mode 100644 index 0000000000000000000000000000000000000000..921512dea8bcba52f8f70d89ba9162d38380f0a6 --- /dev/null +++ b/app/views/income_edit.php @@ -0,0 +1,52 @@ +<div class="container mx-auto mt-10"> + <h1 class="text-2xl font-bold text-gray-700 mb-6">Editar Rendimento</h1> + + <form action="/incomes/edit/<?= $income->getId() ?>" method="POST" class="bg-white p-6 rounded shadow-md"> + <div class="mb-4"> + <label for="title" class="block text-sm font-medium text-gray-700">Título</label> + <input type="text" id="title" name="title" value="<?= $income->getTitle() ?>" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <div class="mb-4"> + <label for="amount" class="block text-sm font-medium text-gray-700">Valor</label> + <input type="number" id="amount" name="amount" value="<?= $income->getAmount() ?>" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <div class="mb-4"> + <label for="date" class="block text-sm font-medium text-gray-700">Data</label> + <input type="date" id="date" name="date" value="<?= $income->getDate() ?>" class="mt-1 block w-full border border-gray-300 rounded p-2" required /> + </div> + + <label class="mt-5" for="income_type">Tipo de Rendimento</label> + <select name="income_type" id="income_type" required> + <option value="one_off" <?php echo (isset($income) && $income->getIncomeType() === 'one_off') ? 'selected' : ''; ?>>Único</option> + <option value="repeatable" <?php echo (isset($income) && $income->getIncomeType() === 'repeatable') ? 'selected' : ''; ?>>Recorrente</option> + </select> + + <div class="mt-5" id="recurrence_period_container" style="display: <?php echo (isset($income) && $income->getIncomeType() === 'repeatable') ? 'block' : 'none'; ?>;"> + <label for="recurrence_period">Período de Recorrência</label> + <select name="recurrence_period" id="recurrence_period"> + <option value="monthly" <?php echo (isset($income) && $income->getRecurrencePeriod() === 'monthly') ? 'selected' : ''; ?>>Mensal</option> + <option value="weekly" <?php echo (isset($income) && $income->getRecurrencePeriod() === 'weekly') ? 'selected' : ''; ?>>Semanal</option> + <option value="yearly" <?php echo (isset($income) && $income->getRecurrencePeriod() === 'yearly') ? 'selected' : ''; ?>>Anual</option> + </select> + </div> + + <div class="mt-5"> + <button type="submit" class="bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700"> + Atualizar Rendimento + </button> + </div> + </form> + + <script> + document.getElementById('income_type').addEventListener('change', function() { + const recurrencePeriodContainer = document.getElementById('recurrence_period_container'); + if (this.value === 'repeatable') { + recurrencePeriodContainer.style.display = 'block'; + } else { + recurrencePeriodContainer.style.display = 'none'; + } + }); + </script> +</div> diff --git a/app/views/incomes.php b/app/views/incomes.php new file mode 100644 index 0000000000000000000000000000000000000000..57f78e3024830d8cf9c347e0db0217709a6385b0 --- /dev/null +++ b/app/views/incomes.php @@ -0,0 +1,53 @@ +<div class="container mx-auto mt-10"> + <div class="flex justify-between items-center mb-6"> + <h1 class="text-2xl font-bold text-gray-700">Rendimentos</h1> + <a href="/incomes/create" class="bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700"> + + Adicionar Novo Rendimento + </a> + </div> + + <?php if (empty($incomes)) : ?> + <div class="text-center py-10 bg-white rounded-lg shadow-md"> + <p class="text-xl text-gray-600">Nenhum rendimento encontrado.</p> + <p class="text-gray-500 mt-2">Crie um rendimento para melhor organizar as contas.</p> + <a href="/incomes/create" class="mt-4 inline-block bg-blue-600 text-white py-2 px-4 rounded hover:bg-blue-700"> + + Adicionar Novo Rendimento + </a> + </div> + <?php else : ?> + <div class="bg-white shadow-md rounded-lg overflow-hidden"> + <table class="min-w-full table-auto"> + <thead> + <tr class="bg-gray-200"> + <th class="py-3 px-6 text-left">Título</th> + <th class="py-3 px-6 text-left">Valor</th> + <th class="py-3 px-6 text-left">Tipo</th> + <th class="py-3 px-6 text-left">Recorrencia</th> + <th class="py-3 px-6 text-left">Data</th> + <th class="py-3 px-6 text-right">Ações</th> + </tr> + </thead> + <tbody> + <?php foreach ($incomes as $income) : ?> + <tr class="border-b"> + <td class="py-3 px-6"><?= htmlspecialchars($income->getTitle()) ?></td> + <td class="py-3 px-6">R$ <?= number_format($income->getAmount(), 2, ',', '.') ?></td> + <td class="py-3 px-6"><?= htmlspecialchars($income->getIncomeTypeLabel()) ?></td> + <td class="py-3 px-6"><?= htmlspecialchars($income->getRecurrencePeriodLabel()) ?></td> + <td class="py-3 px-6"><?= date('d/m/Y', strtotime($income->getDate())) ?></td> + <td class="py-3 px-6 flex justify-end space-x-2"> + <a href="/incomes/edit/<?= $income->getId() ?>" class="text-yellow-600 hover:text-yellow-700"> + Editar + </a> + + <a href="/incomes/delete/<?= $income->getId() ?>" class="text-red-600 hover:text-red-700"> + Apagar + </a> + </td> + </tr> + <?php endforeach; ?> + </tbody> + </table> + </div> + <?php endif; ?> +</div> diff --git a/app/views/layout.php b/app/views/layout.php index 6db2a2ea2eeaef7a6fec31ab366b7a9109ca8f27..1788340999037f2ef0f5e5a5672ac804c4c14291 100644 --- a/app/views/layout.php +++ b/app/views/layout.php @@ -17,6 +17,7 @@