diff --git a/app/Controller/Budget.php b/app/Controller/Budget.php new file mode 100644 index 000000000..01090550d --- /dev/null +++ b/app/Controller/Budget.php @@ -0,0 +1,111 @@ +getProject(); + + $this->response->html($this->projectLayout('budget/index', array( + 'total' => $this->budget->getTotal($project['id']), + 'project' => $project, + 'title' => t('Budget') + ))); + } + + /** + * Create budget lines + * + * @access public + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + if (empty($values)) { + $values['date'] = date('Y-m-d'); + } + + $this->response->html($this->projectLayout('budget/create', array( + 'lines' => $this->budget->getAll($project['id']), + 'values' => $values + array('project_id' => $project['id']), + 'errors' => $errors, + 'project' => $project, + 'title' => t('Budget') + ))); + } + + /** + * Validate and save a new budget + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + + $values = $this->request->getValues(); + list($valid, $errors) = $this->budget->validateCreation($values); + + if ($valid) { + + if ($this->budget->create($values['project_id'], $values['amount'], $values['comment'], $values['date'])) { + $this->session->flash(t('The budget line have been created successfully.')); + $this->response->redirect($this->helper->url('budget', 'create', array('project_id' => $project['id']))); + } + else { + $this->session->flashError(t('Unable to create the budget line.')); + } + } + + $this->create($values, $errors); + } + + /** + * Confirmation dialog before removing a budget + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + + $this->response->html($this->projectLayout('budget/remove', array( + 'project' => $project, + 'budget_id' => $this->request->getIntegerParam('budget_id'), + 'title' => t('Remove a budget line'), + ))); + } + + /** + * Remove a budget + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + + if ($this->budget->remove($this->request->getIntegerParam('budget_id'))) { + $this->session->flash(t('Budget line removed successfully.')); + } else { + $this->session->flashError(t('Unable to remove this budget line.')); + } + + $this->response->redirect($this->helper->url('budget', 'create', array('project_id' => $project['id']))); + } +} diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 56938f9d1..b52a78648 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -56,6 +56,7 @@ class Acl extends Base 'export' => array('tasks', 'subtasks', 'summary'), 'project' => array('edit', 'update', 'share', 'integration', 'users', 'alloweverybody', 'allow', 'setowner', 'revoke', 'duplicate', 'disable', 'enable'), 'swimlane' => '*', + 'budget' => '*', ); /** diff --git a/app/Model/Base.php b/app/Model/Base.php index f836231c5..8a90e286b 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -16,6 +16,7 @@ use Pimple\Container; * @property \Model\Action $action * @property \Model\Authentication $authentication * @property \Model\Board $board + * @property \Model\Budget $budget * @property \Model\Category $category * @property \Model\Comment $comment * @property \Model\CommentHistory $commentHistory diff --git a/app/Model/Budget.php b/app/Model/Budget.php new file mode 100644 index 000000000..03a90f7f9 --- /dev/null +++ b/app/Model/Budget.php @@ -0,0 +1,101 @@ +db->table(self::TABLE)->eq('project_id', $project_id)->desc('date')->findAll(); + } + + /** + * Get the current total of the budget + * + * @access public + * @param integer $project_id + * @return float + */ + public function getTotal($project_id) + { + $result = $this->db->table(self::TABLE)->columns('SUM(amount) as total')->eq('project_id', $project_id)->findOne(); + return isset($result['total']) ? (float) $result['total'] : 0; + } + + /** + * Add a new budget line in the database + * + * @access public + * @param integer $project_id + * @param float $amount + * @param string $comment + * @param string $date + * @return boolean|integer + */ + public function create($project_id, $amount, $comment, $date = '') + { + $values = array( + 'project_id' => $project_id, + 'amount' => $amount, + 'comment' => $comment, + 'date' => $date ?: date('Y-m-d'), + ); + + return $this->persist(self::TABLE, $values); + } + + /** + * Remove a specific budget line + * + * @access public + * @param integer $budget_id + * @return boolean + */ + public function remove($budget_id) + { + return $this->db->table(self::TABLE)->eq('id', $budget_id)->remove(); + } + + /** + * Validate creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $v = new Validator($values, array( + new Validators\Required('project_id', t('Field required')), + new Validators\Required('amount', t('Field required')), + )); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} \ No newline at end of file diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 44ca7fd4d..038687488 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,20 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 51; +const VERSION = 52; + +function version_52($pdo) +{ + $pdo->exec('CREATE TABLE budget_lines ( + `id` INT NOT NULL AUTO_INCREMENT, + `project_id` INT NOT NULL, + `amount` FLOAT NOT NULL, + `date` VARCHAR(10) NOT NULL, + `comment` TEXT, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8'); +} function version_51($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index fe01b1e9f..124aec765 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,19 @@ use PDO; use Core\Security; use Model\Link; -const VERSION = 32; +const VERSION = 33; + +function version_33($pdo) +{ + $pdo->exec('CREATE TABLE budget_lines ( + "id" SERIAL PRIMARY KEY, + "project_id" INTEGER NOT NULL, + "amount" REAL NOT NULL, + "date" VARCHAR(10) NOT NULL, + "comment" TEXT, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} function version_32($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index eaa5c7347..818ed78d6 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,19 @@ use Core\Security; use PDO; use Model\Link; -const VERSION = 50; +const VERSION = 51; + +function version_51($pdo) +{ + $pdo->exec('CREATE TABLE budget_lines ( + "id" INTEGER PRIMARY KEY, + "project_id" INTEGER NOT NULL, + "amount" REAL NOT NULL, + "date" TEXT NOT NULL, + "comment" TEXT, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} function version_50($pdo) { diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index a94bb745b..6f597a5c8 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -17,6 +17,7 @@ class ClassProvider implements ServiceProviderInterface 'Action', 'Authentication', 'Board', + 'Budget', 'Category', 'Color', 'Comment', diff --git a/app/Template/budget/create.php b/app/Template/budget/create.php new file mode 100644 index 000000000..0ff395c9e --- /dev/null +++ b/app/Template/budget/create.php @@ -0,0 +1,51 @@ +
| = t('Budget line') ?> | += t('Date') ?> | += t('Comment') ?> | += t('Action') ?> | +
|---|---|---|---|
| = n($line['amount']) ?> | += $this->e($line['date']) ?> | += $this->e($line['comment']) ?> | ++ = $this->a(t('Remove'), 'budget', 'confirm', array('project_id' => $project['id'], 'budget_id' => $line['id'])) ?> + | +
= t('Current budget: ') ?>= n($total) ?>
diff --git a/app/Template/budget/remove.php b/app/Template/budget/remove.php new file mode 100644 index 000000000..97f9c3dc8 --- /dev/null +++ b/app/Template/budget/remove.php @@ -0,0 +1,13 @@ += t('Do you really want to remove this budget line?') ?>
+ +