Merge branch 'release/3.2.5'

This commit is contained in:
James Cole
2015-01-31 06:35:37 +01:00
155 changed files with 2966 additions and 1812 deletions

View File

@@ -4,7 +4,12 @@ php:
- 5.5
- 5.6
addons:
code_climate:
repo_token: 26489f9e854fcdf7e7660ba29c1455694685465b1f90329a79f7d2bf448acb61
install:
- rm composer.lock
- composer install
script:
@@ -13,4 +18,7 @@ script:
- php vendor/bin/codecept run --coverage --coverage-xml
after_script:
- cp -v tests/_output/coverage.xml build/logs/clover.xml
- php vendor/bin/coveralls
- vendor/bin/test-reporter --stdout > codeclimate.json
- "curl -X POST -d @codeclimate.json -H 'Content-Type: application/json' -H 'User-Agent: Code Climate (PHP Test Reporter v0.1.1)' https://codeclimate.com/test_reports"

View File

@@ -1,10 +1,12 @@
Firefly III
Firefly III (v3.2.5)
===========
[![Build Status](https://travis-ci.org/JC5/firefly-iii.svg?branch=develop)](https://travis-ci.org/JC5/firefly-iii)
[![Project Status](http://stillmaintained.com/JC5/firefly-iii.png?a=b)](http://stillmaintained.com/JC5/firefly-iii)
[![Coverage Status](https://coveralls.io/repos/JC5/firefly-iii/badge.png?branch=master)](https://coveralls.io/r/JC5/firefly-iii?branch=master)
[![SensioLabsInsight](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102/mini.png)](https://insight.sensiolabs.com/projects/d44c7012-5f50-41ad-add8-8445330e4102)
[![Code Climate](https://codeclimate.com/github/JC5/firefly-iii/badges/gpa.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Test Coverage](https://codeclimate.com/github/JC5/firefly-iii/badges/coverage.svg)](https://codeclimate.com/github/JC5/firefly-iii)
[![Latest Stable Version](https://poser.pugx.org/grumpydictator/firefly-iii/v/stable.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)
[![Total Downloads](https://poser.pugx.org/grumpydictator/firefly-iii/downloads.svg)](https://packagist.org/packages/grumpydictator/firefly-iii)

View File

@@ -0,0 +1,607 @@
# ************************************************************
# Sequel Pro SQL dump
# Version 4096
#
# http://www.sequelpro.com/
# http://code.google.com/p/sequel-pro/
#
# Host: 127.0.0.1 (MySQL 5.6.19-0ubuntu0.14.04.1)
# Database: homestead
# Generation Time: 2015-01-31 05:33:30 +0000
# ************************************************************
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
# Dump of table account_meta
# ------------------------------------------------------------
DROP TABLE IF EXISTS `account_meta`;
CREATE TABLE `account_meta` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`account_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`data` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_meta_account_id_name_unique` (`account_id`,`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table account_types
# ------------------------------------------------------------
DROP TABLE IF EXISTS `account_types`;
CREATE TABLE `account_types` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`type` varchar(30) COLLATE utf8_unicode_ci NOT NULL,
`editable` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `account_types_type_unique` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `account_types` WRITE;
/*!40000 ALTER TABLE `account_types` DISABLE KEYS */;
INSERT INTO `account_types` (`id`, `created_at`, `updated_at`, `type`, `editable`)
VALUES
(1,'2015-01-31 05:33:21','2015-01-31 05:33:21','Default account',1),
(2,'2015-01-31 05:33:21','2015-01-31 05:33:21','Cash account',0),
(3,'2015-01-31 05:33:21','2015-01-31 05:33:21','Asset account',1),
(4,'2015-01-31 05:33:21','2015-01-31 05:33:21','Expense account',1),
(5,'2015-01-31 05:33:21','2015-01-31 05:33:21','Revenue account',1),
(6,'2015-01-31 05:33:21','2015-01-31 05:33:21','Initial balance account',0),
(7,'2015-01-31 05:33:21','2015-01-31 05:33:21','Beneficiary account',1),
(8,'2015-01-31 05:33:21','2015-01-31 05:33:21','Import account',0);
/*!40000 ALTER TABLE `account_types` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table accounts
# ------------------------------------------------------------
DROP TABLE IF EXISTS `accounts`;
CREATE TABLE `accounts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`account_type_id` int(10) unsigned NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`active` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `accounts_user_id_account_type_id_name_unique` (`user_id`,`account_type_id`,`name`),
KEY `accounts_account_type_id_foreign` (`account_type_id`),
CONSTRAINT `accounts_account_type_id_foreign` FOREIGN KEY (`account_type_id`) REFERENCES `account_types` (`id`) ON DELETE CASCADE,
CONSTRAINT `accounts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table bills
# ------------------------------------------------------------
DROP TABLE IF EXISTS `bills`;
CREATE TABLE `bills` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`match` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`amount_min` decimal(10,2) NOT NULL,
`amount_max` decimal(10,2) NOT NULL,
`date` date NOT NULL,
`active` tinyint(1) NOT NULL,
`automatch` tinyint(1) NOT NULL,
`repeat_freq` enum('daily','weekly','monthly','quarterly','half-year','yearly') COLLATE utf8_unicode_ci NOT NULL,
`skip` smallint(5) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uid_name_unique` (`user_id`,`name`),
CONSTRAINT `bills_uid_for` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budget_limits
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budget_limits`;
CREATE TABLE `budget_limits` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`budget_id` int(10) unsigned DEFAULT NULL,
`startdate` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
`repeats` tinyint(1) NOT NULL,
`repeat_freq` enum('daily','weekly','monthly','quarterly','half-year','yearly') COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `unique_bl_combi` (`budget_id`,`startdate`,`repeat_freq`),
CONSTRAINT `bid_foreign` FOREIGN KEY (`budget_id`) REFERENCES `budgets` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budget_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budget_transaction_journal`;
CREATE TABLE `budget_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`budget_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `budid_tjid_unique` (`budget_id`,`transaction_journal_id`),
KEY `budget_transaction_journal_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `budget_transaction_journal_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `budget_transaction_journal_budget_id_foreign` FOREIGN KEY (`budget_id`) REFERENCES `budgets` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table budgets
# ------------------------------------------------------------
DROP TABLE IF EXISTS `budgets`;
CREATE TABLE `budgets` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `budgets_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `budgets_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table categories
# ------------------------------------------------------------
DROP TABLE IF EXISTS `categories`;
CREATE TABLE `categories` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `categories_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `categories_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table category_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `category_transaction_journal`;
CREATE TABLE `category_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`category_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `catid_tjid_unique` (`category_id`,`transaction_journal_id`),
KEY `category_transaction_journal_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `category_transaction_journal_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `category_transaction_journal_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table components
# ------------------------------------------------------------
DROP TABLE IF EXISTS `components`;
CREATE TABLE `components` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`name` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`class` varchar(20) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `components_user_id_class_name_unique` (`user_id`,`class`,`name`),
CONSTRAINT `components_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table limit_repetitions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `limit_repetitions`;
CREATE TABLE `limit_repetitions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`budget_limit_id` int(10) unsigned NOT NULL,
`startdate` date NOT NULL,
`enddate` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `limit_repetitions_limit_id_startdate_enddate_unique` (`budget_limit_id`,`startdate`,`enddate`),
CONSTRAINT `limit_repetitions_limit_id_foreign` FOREIGN KEY (`budget_limit_id`) REFERENCES `budget_limits` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table migrations
# ------------------------------------------------------------
DROP TABLE IF EXISTS `migrations`;
CREATE TABLE `migrations` (
`migration` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`batch` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `migrations` WRITE;
/*!40000 ALTER TABLE `migrations` DISABLE KEYS */;
INSERT INTO `migrations` (`migration`, `batch`)
VALUES
('2014_06_27_163032_create_users_table',1),
('2014_06_27_163145_create_account_types_table',1),
('2014_06_27_163259_create_accounts_table',1),
('2014_06_27_163817_create_components_table',1),
('2014_06_27_163818_create_piggybanks_table',1),
('2014_06_27_164042_create_transaction_currencies_table',1),
('2014_06_27_164512_create_transaction_types_table',1),
('2014_06_27_164619_create_recurring_transactions_table',1),
('2014_06_27_164620_create_transaction_journals_table',1),
('2014_06_27_164836_create_transactions_table',1),
('2014_06_27_165344_create_component_transaction_table',1),
('2014_07_05_171326_create_component_transaction_journal_table',1),
('2014_07_06_123842_create_preferences_table',1),
('2014_07_09_204843_create_session_table',1),
('2014_07_17_183717_create_limits_table',1),
('2014_07_19_055011_create_limit_repeat_table',1),
('2014_08_06_044416_create_component_recurring_transaction_table',1),
('2014_08_12_173919_create_piggybank_repetitions_table',1),
('2014_08_18_100330_create_piggybank_events_table',1),
('2014_08_23_113221_create_reminders_table',1),
('2014_11_10_172053_create_account_meta_table',1),
('2014_11_29_135749_create_transaction_groups_table',1),
('2014_11_29_140217_create_transaction_group_transaction_journal_table',1),
('2014_12_13_190730_changes_for_v321',1),
('2014_12_24_191544_changes_for_v322',1),
('2015_01_18_082406_changes_for_v325',1);
/*!40000 ALTER TABLE `migrations` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table piggy_bank_events
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_bank_events`;
CREATE TABLE `piggy_bank_events` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`piggy_bank_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned DEFAULT NULL,
`date` date NOT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
KEY `piggybank_events_piggybank_id_foreign` (`piggy_bank_id`),
KEY `piggybank_events_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `piggybank_events_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE SET NULL,
CONSTRAINT `piggybank_events_piggybank_id_foreign` FOREIGN KEY (`piggy_bank_id`) REFERENCES `piggy_banks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table piggy_bank_repetitions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_bank_repetitions`;
CREATE TABLE `piggy_bank_repetitions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`piggy_bank_id` int(10) unsigned NOT NULL,
`startdate` date DEFAULT NULL,
`targetdate` date DEFAULT NULL,
`currentamount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `piggybank_repetitions_piggybank_id_startdate_targetdate_unique` (`piggy_bank_id`,`startdate`,`targetdate`),
CONSTRAINT `piggybank_repetitions_piggybank_id_foreign` FOREIGN KEY (`piggy_bank_id`) REFERENCES `piggy_banks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table piggy_banks
# ------------------------------------------------------------
DROP TABLE IF EXISTS `piggy_banks`;
CREATE TABLE `piggy_banks` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`account_id` int(10) unsigned NOT NULL,
`name` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`targetamount` decimal(10,2) NOT NULL,
`startdate` date DEFAULT NULL,
`targetdate` date DEFAULT NULL,
`repeats` tinyint(1) NOT NULL,
`rep_length` enum('day','week','quarter','month','year') COLLATE utf8_unicode_ci DEFAULT NULL,
`rep_every` smallint(5) unsigned NOT NULL,
`rep_times` smallint(5) unsigned DEFAULT NULL,
`reminder` enum('day','week','quarter','month','year') COLLATE utf8_unicode_ci DEFAULT NULL,
`reminder_skip` smallint(5) unsigned NOT NULL,
`remind_me` tinyint(1) NOT NULL,
`order` int(10) unsigned NOT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `piggybanks_account_id_name_unique` (`account_id`,`name`),
CONSTRAINT `piggybanks_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table preferences
# ------------------------------------------------------------
DROP TABLE IF EXISTS `preferences`;
CREATE TABLE `preferences` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`data` text COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `preferences_user_id_name_unique` (`user_id`,`name`),
CONSTRAINT `preferences_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table reminders
# ------------------------------------------------------------
DROP TABLE IF EXISTS `reminders`;
CREATE TABLE `reminders` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`user_id` int(10) unsigned NOT NULL,
`startdate` date NOT NULL,
`enddate` date DEFAULT NULL,
`active` tinyint(1) NOT NULL,
`notnow` tinyint(1) NOT NULL DEFAULT '0',
`remindersable_id` int(10) unsigned DEFAULT NULL,
`remindersable_type` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `reminders_user_id_foreign` (`user_id`),
CONSTRAINT `reminders_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table sessions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `sessions`;
CREATE TABLE `sessions` (
`id` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
`payload` text COLLATE utf8_unicode_ci NOT NULL,
`last_activity` int(11) NOT NULL,
UNIQUE KEY `sessions_id_unique` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_currencies
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_currencies`;
CREATE TABLE `transaction_currencies` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`code` varchar(3) COLLATE utf8_unicode_ci NOT NULL,
`name` varchar(48) COLLATE utf8_unicode_ci DEFAULT NULL,
`symbol` varchar(8) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `transaction_currencies_code_unique` (`code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `transaction_currencies` WRITE;
/*!40000 ALTER TABLE `transaction_currencies` DISABLE KEYS */;
INSERT INTO `transaction_currencies` (`id`, `created_at`, `updated_at`, `deleted_at`, `code`, `name`, `symbol`)
VALUES
(1,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'EUR','Euro','€'),
(2,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'USD','US Dollar','$'),
(3,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'HUF','Hungarian forint','Ft');
/*!40000 ALTER TABLE `transaction_currencies` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table transaction_group_transaction_journal
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_group_transaction_journal`;
CREATE TABLE `transaction_group_transaction_journal` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`transaction_group_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `tt_joined` (`transaction_group_id`,`transaction_journal_id`),
KEY `tr_trj_id` (`transaction_journal_id`),
CONSTRAINT `tr_trj_id` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE,
CONSTRAINT `tr_grp_id` FOREIGN KEY (`transaction_group_id`) REFERENCES `transaction_groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_groups
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_groups`;
CREATE TABLE `transaction_groups` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`relation` enum('balance') COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
KEY `transaction_groups_user_id_foreign` (`user_id`),
CONSTRAINT `transaction_groups_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_journals
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_journals`;
CREATE TABLE `transaction_journals` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`user_id` int(10) unsigned NOT NULL,
`transaction_type_id` int(10) unsigned NOT NULL,
`bill_id` int(10) unsigned DEFAULT NULL,
`transaction_currency_id` int(10) unsigned NOT NULL,
`description` varchar(1024) COLLATE utf8_unicode_ci DEFAULT NULL,
`completed` tinyint(1) NOT NULL,
`date` date NOT NULL,
`encrypted` tinyint(1) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
KEY `transaction_journals_user_id_foreign` (`user_id`),
KEY `transaction_journals_transaction_type_id_foreign` (`transaction_type_id`),
KEY `transaction_journals_transaction_currency_id_foreign` (`transaction_currency_id`),
KEY `bill_id_foreign` (`bill_id`),
CONSTRAINT `bill_id_foreign` FOREIGN KEY (`bill_id`) REFERENCES `bills` (`id`) ON DELETE SET NULL,
CONSTRAINT `transaction_journals_transaction_currency_id_foreign` FOREIGN KEY (`transaction_currency_id`) REFERENCES `transaction_currencies` (`id`) ON DELETE CASCADE,
CONSTRAINT `transaction_journals_transaction_type_id_foreign` FOREIGN KEY (`transaction_type_id`) REFERENCES `transaction_types` (`id`) ON DELETE CASCADE,
CONSTRAINT `transaction_journals_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table transaction_types
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transaction_types`;
CREATE TABLE `transaction_types` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`type` varchar(50) COLLATE utf8_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `transaction_types_type_unique` (`type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
LOCK TABLES `transaction_types` WRITE;
/*!40000 ALTER TABLE `transaction_types` DISABLE KEYS */;
INSERT INTO `transaction_types` (`id`, `created_at`, `updated_at`, `deleted_at`, `type`)
VALUES
(1,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'Withdrawal'),
(2,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'Deposit'),
(3,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'Transfer'),
(4,'2015-01-31 05:33:21','2015-01-31 05:33:21',NULL,'Opening balance');
/*!40000 ALTER TABLE `transaction_types` ENABLE KEYS */;
UNLOCK TABLES;
# Dump of table transactions
# ------------------------------------------------------------
DROP TABLE IF EXISTS `transactions`;
CREATE TABLE `transactions` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`deleted_at` timestamp NULL DEFAULT NULL,
`account_id` int(10) unsigned NOT NULL,
`transaction_journal_id` int(10) unsigned NOT NULL,
`description` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`amount` decimal(10,2) NOT NULL,
PRIMARY KEY (`id`),
KEY `transactions_account_id_foreign` (`account_id`),
KEY `transactions_transaction_journal_id_foreign` (`transaction_journal_id`),
CONSTRAINT `transactions_account_id_foreign` FOREIGN KEY (`account_id`) REFERENCES `accounts` (`id`) ON DELETE CASCADE,
CONSTRAINT `transactions_transaction_journal_id_foreign` FOREIGN KEY (`transaction_journal_id`) REFERENCES `transaction_journals` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
# Dump of table users
# ------------------------------------------------------------
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
`email` varchar(100) COLLATE utf8_unicode_ci NOT NULL,
`password` varchar(60) COLLATE utf8_unicode_ci NOT NULL,
`reset` varchar(32) COLLATE utf8_unicode_ci DEFAULT NULL,
`remember_token` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_email_unique` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

View File

@@ -5,8 +5,7 @@ use FireflyIII\Exception\FireflyException;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("NPathComplexity")
*
* Class BillController
*
*/
@@ -120,9 +119,9 @@ class BillController extends BaseController
*/
public function show(Bill $bill)
{
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$journals = $bill->transactionjournals()->withRelevantData()->orderBy('date', 'DESC')->get();
$bill->nextExpectedMatch = $this->_repository->nextExpectedMatch($bill);
$hideBill = true;
$hideBill = true;
return View::make('bills.show', compact('journals', 'hideBill', 'bill'))->with(
@@ -136,7 +135,7 @@ class BillController extends BaseController
*/
public function store()
{
$data = Input::all();
$data = Input::except(['_token', 'post_submit_action']);
$data['user_id'] = Auth::user()->id;
@@ -149,17 +148,19 @@ class BillController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store bill: ' . $messages['errors']->first());
return Redirect::route('bills.create')->withInput();
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if (Input::get('post_submit_action') == 'validate_only') {
return Redirect::route('bills.create')->withInput();
}
// store
$this->_repository->store($data);
Session::flash('success', 'Bill "' . e($data['name']) . '" stored.');
if ($data['post_submit_action'] == 'store') {
if (Input::get('post_submit_action') == 'store') {
return Redirect::route('bills.index');
}
@@ -176,8 +177,8 @@ class BillController extends BaseController
public function update(Bill $bill)
{
$data = Input::except('_token');
$data['active'] = isset($data['active']) ? 1 : 0;
$data['automatch'] = isset($data['automatch']) ? 1 : 0;
$data['active'] = intval(Input::get('active'));
$data['automatch'] = intval(Input::get('automatch'));
$data['user_id'] = Auth::user()->id;
// always validate:
@@ -189,10 +190,12 @@ class BillController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update bill: ' . $messages['errors']->first());
return Redirect::route('bills.edit', $bill->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('bills.edit', $bill->id)->withInput();
}

View File

@@ -8,9 +8,6 @@ use FireflyIII\Shared\Preferences\PreferencesInterface as Pref;
* Class BudgetController
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*/
class BudgetController extends BaseController
@@ -45,7 +42,7 @@ class BudgetController extends BaseController
$date = Session::get('start', Carbon::now()->startOfMonth());
$limitRepetition = $this->_repository->updateLimitAmount($budget, $date, $amount);
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition->id]);
return Response::json(['name' => $budget->name, 'repetition' => $limitRepetition ? $limitRepetition->id : 0]);
}
@@ -148,6 +145,8 @@ class BudgetController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param Budget $budget
* @param LimitRepetition $repetition
*
@@ -184,10 +183,11 @@ class BudgetController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not validate budget: ' . $messages['errors']->first());
return Redirect::route('budgets.create')->withInput();
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('budgets.create')->withInput();
}
@@ -222,10 +222,11 @@ class BudgetController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update budget: ' . $messages['errors']->first());
return Redirect::route('budgets.edit', $budget->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('budgets.edit', $budget->id)->withInput();
}

View File

@@ -6,7 +6,6 @@ use FireflyIII\Exception\FireflyException;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CategoryController
*/
@@ -106,6 +105,7 @@ class CategoryController extends BaseController
}
/**
*
* @return $this
* @throws FireflyException
*/
@@ -123,10 +123,11 @@ class CategoryController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store category: ' . $messages['errors']->first());
return Redirect::route('categories.create')->withInput();
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('categories.create')->withInput();
}
@@ -141,6 +142,7 @@ class CategoryController extends BaseController
}
/**
*
* @param Category $category
*
* @return $this
@@ -160,10 +162,11 @@ class CategoryController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update category: ' . $messages['errors']->first());
return Redirect::route('categories.edit', $category->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('categories.edit', $category->id)->withInput();
}

View File

@@ -4,7 +4,6 @@ use FireflyIII\Database\TransactionCurrency\TransactionCurrency as Repository;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* Class CurrencyController
*/
@@ -123,6 +122,8 @@ class CurrencyController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return $this|\Illuminate\Http\RedirectResponse
*/
public function store()
@@ -174,10 +175,11 @@ class CurrencyController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update currency: ' . $messages['errors']->first());
return Redirect::route('currency.edit', $currency->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('currency.edit', $currency->id)->withInput();
}

View File

@@ -6,10 +6,6 @@ use Grumpydictator\Gchart\GChart as GChart;
/**
* Class GoogleChartController
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("MethodLength") // There is one with 45 lines and im gonna move it.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*/
class GoogleChartController extends BaseController
{
@@ -46,6 +42,7 @@ class GoogleChartController extends BaseController
{
$this->_chart->addColumn('Day of month', 'date');
$this->_chart->addColumn('Balance for ' . $account->name, 'number');
$this->_chart->addCertainty(1);
$start = $this->_start;
$end = $this->_end;
@@ -65,7 +62,7 @@ class GoogleChartController extends BaseController
$current = clone $start;
while ($end >= $current) {
$this->_chart->addRow(clone $current, Steam::balance($account, $current));
$this->_chart->addRow(clone $current, Steam::balance($account, $current), false);
$current->addDay();
}
@@ -76,7 +73,7 @@ class GoogleChartController extends BaseController
}
/**
* This method renders the b
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*/
public function allAccountsBalanceChart()
{
@@ -88,19 +85,25 @@ class GoogleChartController extends BaseController
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$accounts = count($pref->data) > 0 ? $acct->getByIds($pref->data) : $acct->getAssetAccounts();
$accounts = count($pref->data) > 0 ? $acct->getByIds($pref->data) : $acct->getAccountsByType(['Default account', 'Asset account']);
$index = 1;
/** @var Account $account */
foreach ($accounts as $account) {
$this->_chart->addColumn('Balance for ' . $account->name, 'number');
$this->_chart->addCertainty($index);
$index++;
}
$current = clone $this->_start;
$current->subDay();
$today = Carbon::now();
while ($this->_end >= $current) {
$row = [clone $current];
$row = [clone $current];
$certain = $current < $today;
foreach ($accounts as $account) {
$row[] = Steam::balance($account, $current);
$row[] = $certain;
}
$this->_chart->addRowArray($row);
$current->addDay();
@@ -112,6 +115,49 @@ class GoogleChartController extends BaseController
}
/**
* @param int $year
*
* @return $this|\Illuminate\Http\JsonResponse
*/
public function allBudgetsAndSpending($year)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \FireflyIII\Database\Budget\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget\Budget');
$budgets = $budgetRepository->get();
$budgets->sortBy('name');
$this->_chart->addColumn('Month', 'date');
foreach ($budgets as $budget) {
$this->_chart->addColumn($budget->name, 'number');
}
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$row = [clone $start];
foreach ($budgets as $budget) {
$spent = $budgetRepository->spentInMonth($budget, $start);
//$repetition = $budgetRepository->repetitionOnStartingOnDate($budget, $start);
$row[] = $spent;
}
$this->_chart->addRowArray($row);
$start->addMonth();
}
$this->_chart->generate();
return Response::json($this->_chart->getData());
}
/**
* @return \Illuminate\Http\JsonResponse
*/
@@ -121,8 +167,6 @@ class GoogleChartController extends BaseController
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
Log::debug('Now in allBudgetsHomeChart()');
/** @var \FireflyIII\Database\Budget\Budget $bdt */
$bdt = App::make('FireflyIII\Database\Budget\Budget');
$budgets = $bdt->get();
@@ -130,18 +174,13 @@ class GoogleChartController extends BaseController
/** @var Budget $budget */
foreach ($budgets as $budget) {
Log::debug('Now working budget #' . $budget->id . ', ' . $budget->name);
/** @var \LimitRepetition $repetition */
$repetition = $bdt->repetitionOnStartingOnDate($budget, $this->_start);
if (is_null($repetition)) {
\Log::debug('Budget #' . $budget->id . ' has no repetition on ' . $this->_start->format('Y-m-d'));
// use the session start and end for our search query
if (is_null($repetition)) { // use the session start and end for our search query
$searchStart = $this->_start;
$searchEnd = $this->_end;
$limit = 0; // the limit is zero:
} else {
\Log::debug('Budget #' . $budget->id . ' has a repetition on ' . $this->_start->format('Y-m-d') . '!');
// use the limit's start and end for our search query
$searchStart = $repetition->startdate;
$searchEnd = $repetition->enddate;
@@ -149,7 +188,6 @@ class GoogleChartController extends BaseController
}
$expenses = floatval($budget->transactionjournals()->before($searchEnd)->after($searchStart)->lessThan(0)->sum('amount')) * -1;
\Log::debug('Expenses in budget ' . $budget->name . ' before ' . $searchEnd->format('Y-m-d') . ' and after ' . $searchStart . ' are: ' . $expenses);
if ($expenses > 0) {
$this->_chart->addRow($budget->name, $limit, $expenses);
}
@@ -289,47 +327,56 @@ class GoogleChartController extends BaseController
/**
*
* @param Budget $budget
* @param $year
* @param Budget $budget
*
* @return \Illuminate\Http\JsonResponse
*/
public function budgetsAndSpending(Budget $budget, $year)
public function budgetsAndSpending(Budget $budget, $year = 0)
{
try {
new Carbon('01-01-' . $year);
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid year.');
}
/** @var \FireflyIII\Database\Budget\Budget $budgetRepository */
$budgetRepository = App::make('FireflyIII\Database\Budget\Budget');
$this->_chart->addColumn('Month', 'date');
$this->_chart->addColumn('Budgeted', 'number');
$this->_chart->addColumn('Spent', 'number');
if ($year == 0) {
// grab the first budgetlimit ever:
$firstLimit = $budget->budgetlimits()->orderBy('startdate', 'ASC')->first();
if ($firstLimit) {
$start = new Carbon($firstLimit->startdate);
} else {
$start = Carbon::now()->startOfYear();
}
// grab the last budget limit ever:
$lastLimit = $budget->budgetlimits()->orderBy('startdate', 'DESC')->first();
if ($lastLimit) {
$end = new Carbon($lastLimit->startdate);
} else {
$end = Carbon::now()->endOfYear();
}
} else {
$start = Carbon::createFromDate(intval($year), 1, 1);
$end = clone $start;
$end->endOfYear();
}
$start = new Carbon('01-01-' . $year);
$end = clone $start;
$end->endOfYear();
while ($start <= $end) {
$spent = $budgetRepository->spentInMonth($budget, $start);
$repetition = $budgetRepository->repetitionOnStartingOnDate($budget, $start);
if ($repetition) {
$budgeted = floatval($repetition->amount);
\Log::debug('Found a repetition on ' . $start->format('Y-m-d'). ' for budget ' . $budget->name.'!');
\Log::debug('Found a repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name . '!');
} else {
\Log::debug('No repetition on ' . $start->format('Y-m-d'). ' for budget ' . $budget->name);
\Log::debug('No repetition on ' . $start->format('Y-m-d') . ' for budget ' . $budget->name);
$budgeted = null;
}
$this->_chart->addRow(clone $start, $budgeted, $spent);
$start->addMonth();
}
$this->_chart->generate();
return Response::json($this->_chart->getData());

View File

@@ -1,9 +1,10 @@
<?php
/**
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* Class HelpController
*
*/
class HelpController extends BaseController
{
@@ -14,41 +15,70 @@ class HelpController extends BaseController
*/
public function show($route)
{
$helpText = '<p>There is no help for this route!</p>';
$helpTitle = 'Help';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => 'Help',
];
if (!Route::has($route)) {
\Log::error('No such route: ' . $route);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
if (Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text')) {
$helpText = Cache::get('help.' . $route . '.text');
$helpTitle = Cache::get('help.' . $route . '.title');
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
return Response::json($content);
}
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
\Log::debug('URL is: ' . $uri);
if ($this->_inCache($route)) {
$content = [
'text' => Cache::get('help.' . $route . '.text'),
'title' => Cache::get('help.' . $route . '.title'),
];
return Response::json($content);
}
$content = $this->_getFromGithub($route);
Cache::put('help.' . $route . '.text', $content['text'], 10080); // a week.
Cache::put('help.' . $route . '.title', $content['title'], 10080);
return Response::json($content);
}
/**
* @param $route
*
* @return bool
*/
protected function _inCache($route)
{
return Cache::has('help.' . $route . '.title') && Cache::has('help.' . $route . '.text');
}
/**
* @param $route
*
* @return array
*/
protected function _getFromGithub($route)
{
$uri = 'https://raw.githubusercontent.com/JC5/firefly-iii-help/master/' . e($route) . '.md';
$content = [
'text' => '<p>There is no help for this route!</p>',
'title' => $route,
];
try {
$helpText = file_get_contents($uri);
$content['text'] = file_get_contents($uri);
} catch (ErrorException $e) {
\Log::error(trim($e->getMessage()));
}
\Log::debug('Found help for ' . $route);
\Log::debug('Help text length for route ' . $route . ' is ' . strlen($helpText));
\Log::debug('Help text IS: "' . $helpText . '".');
if (strlen(trim($helpText)) == 0) {
$helpText = '<p>There is no help for this route.</p>';
if (strlen(trim($content['text'])) == 0) {
$content['text'] = '<p>There is no help for this route.</p>';
}
$content['text'] = \Michelf\Markdown::defaultTransform($content['text']);
$helpText = \Michelf\Markdown::defaultTransform($helpText);
$helpTitle = $route;
return $content;
Cache::put('help.' . $route . '.text', $helpText, 10080); // a week.
Cache::put('help.' . $route . '.title', $helpTitle, 10080);
return Response::json(['title' => $helpTitle, 'text' => $helpText]);
}
}
}

View File

@@ -33,7 +33,7 @@ class HomeController extends BaseController
/** @var \FireflyIII\Shared\Preferences\PreferencesInterface $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\PreferencesInterface');
$count = $acct->countAssetAccounts();
$count = $acct->countAccountsByType(['Default account', 'Asset account']);
$start = Session::get('start', Carbon::now()->startOfMonth());
$end = Session::get('end', Carbon::now()->endOfMonth());
@@ -42,7 +42,7 @@ class HomeController extends BaseController
// get the preference for the home accounts to show:
$frontPage = $preferences->get('frontPageAccounts', []);
if ($frontPage->data == []) {
$accounts = $acct->getAssetAccounts();
$accounts = $acct->getAccountsByType(['Default account', 'Asset account']);
} else {
$accounts = $acct->getByIds($frontPage->data);
}
@@ -77,8 +77,11 @@ class HomeController extends BaseController
$preferences->set('viewRange', $range);
Session::forget('range');
}
return Redirect::intended('/');
if (isset($_SERVER['HTTP_REFERER']) && (!strpos($_SERVER['HTTP_REFERER'], Config::get('app.url')) === false)) {
return Redirect::back();
} else {
return Redirect::intended();
}
}
/**
@@ -88,7 +91,14 @@ class HomeController extends BaseController
{
Navigation::next();
return Redirect::intended('/');
if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], Config::get('app.url')) === 0) {
Log::debug('Redirect back');
return Redirect::back();
} else {
Log::debug('Redirect intended');
return Redirect::intended();
}
}
/**
@@ -98,6 +108,12 @@ class HomeController extends BaseController
{
Navigation::prev();
return Redirect::intended('/');
if (isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], Config::get('app.url')) === 0) {
Log::debug('Redirect back');
return Redirect::back();
} else {
Log::debug('Redirect intended');
return Redirect::intended();
}
}
}

View File

@@ -36,7 +36,7 @@ class JsonController extends BaseController
{
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getExpenseAccounts();
$list = $accounts->getAccountsByType(['Expense account', 'Beneficiary account']);
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;
@@ -53,7 +53,7 @@ class JsonController extends BaseController
{
/** @var \FireflyIII\Database\Account\Account $accounts */
$accounts = App::make('FireflyIII\Database\Account\Account');
$list = $accounts->getRevenueAccounts();
$list = $accounts->getAccountsByType(['Revenue account']);
$return = [];
foreach ($list as $entry) {
$return[] = $entry->name;

View File

@@ -8,9 +8,6 @@ use Illuminate\Support\Collection;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
*
* Class PiggyBankController
@@ -62,7 +59,7 @@ class PiggyBankController extends BaseController
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$accounts = FFForm::makeSelectList($acct->getAccountsByType(['Default account', 'Asset account']));
$subTitle = 'Create new piggy bank';
$subTitleIcon = 'fa-plus';
@@ -96,6 +93,8 @@ class PiggyBankController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param PiggyBank $piggyBank
*
* @return $this
@@ -107,7 +106,7 @@ class PiggyBankController extends BaseController
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$accounts = FFForm::makeSelectList($acct->getAccountsByType(['Default account', 'Asset account']));
$subTitle = 'Edit piggy bank "' . e($piggyBank->name) . '"';
$subTitleIcon = 'fa-pencil';
@@ -291,11 +290,12 @@ class PiggyBankController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggy_banks.create')->withInput();
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('piggy_banks.create')->withInput();
}
@@ -335,10 +335,11 @@ class PiggyBankController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update piggy bank: ' . $messages['errors']->first());
return Redirect::route('piggy_banks.edit', $piggyBank->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('piggy_banks.edit', $piggyBank->id)->withInput();
}

View File

@@ -3,7 +3,6 @@
/**
* Class PreferencesController
*
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
*/
class PreferencesController extends BaseController
@@ -29,7 +28,7 @@ class PreferencesController extends BaseController
/** @var \FireflyIII\Shared\Preferences\Preferences $preferences */
$preferences = App::make('FireflyIII\Shared\Preferences\Preferences');
$accounts = $acct->getAssetAccounts();
$accounts = $acct->getAccountsByType(['Default account', 'Asset account']);
$viewRange = $preferences->get('viewRange', '1M');
$viewRangeValue = $viewRange->data;
$frontPage = $preferences->get('frontPageAccounts', []);

View File

@@ -1,8 +1,7 @@
<?php
/**
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* Class ProfileController
*/
class ProfileController extends BaseController
@@ -39,19 +38,9 @@ class ProfileController extends BaseController
return View::make('profile.change-password');
}
if (strlen(Input::get('new1')) == 0 || strlen(Input::get('new2')) == 0) {
Session::flash('error', 'Do fill in a password!');
return View::make('profile.change-password');
}
if (Input::get('new1') == Input::get('old')) {
Session::flash('error', 'The idea is to change your password.');
return View::make('profile.change-password');
}
if (Input::get('new1') !== Input::get('new2')) {
Session::flash('error', 'New passwords do not match!');
$result = $this->_validatePassword(Input::get('old'), Input::get('new1'), Input::get('new2'));
if (!($result === true)) {
Session::flash('error', $result);
return View::make('profile.change-password');
}
@@ -66,4 +55,31 @@ class ProfileController extends BaseController
return Redirect::route('profile');
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param string $old
* @param string $new1
* @param string $new2
*
* @return string|bool
*/
protected function _validatePassword($old, $new1, $new2)
{
if (strlen($new1) == 0 || strlen($new2) == 0) {
return 'Do fill in a password!';
}
if ($new1 == $old) {
return 'The idea is to change your password.';
}
if ($new1 !== $new2) {
return 'New passwords do not match!';
}
return true;
}
}

View File

@@ -3,6 +3,8 @@ use FireflyIII\Helper\Related\RelatedInterface;
use Illuminate\Support\Collection;
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* Class RelatedController
*/
class RelatedController extends BaseController
@@ -10,6 +12,9 @@ class RelatedController extends BaseController
protected $_repository;
/**
* @param RelatedInterface $repository
*/
public function __construct(RelatedInterface $repository)
{
$this->_repository = $repository;
@@ -17,6 +22,8 @@ class RelatedController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $journal
*
* @return \Illuminate\Http\JsonResponse
@@ -91,6 +98,8 @@ class RelatedController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param TransactionJournal $parentJournal
* @param TransactionJournal $childJournal
*

View File

@@ -6,8 +6,6 @@ use FireflyIII\Exception\FireflyException;
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
*
* Class RepeatedExpenseController
*/
@@ -34,7 +32,7 @@ class RepeatedExpenseController extends BaseController
/** @var \FireflyIII\Database\Account\Account $acct */
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$accounts = FFForm::makeSelectList($acct->getAccountsByType(['Default account', 'Asset account']));
return View::make('repeatedExpense.create', compact('accounts', 'periods'))->with('subTitle', 'Create new repeated expense')->with(
'subTitleIcon', 'fa-plus'
@@ -79,7 +77,7 @@ class RepeatedExpenseController extends BaseController
$acct = App::make('FireflyIII\Database\Account\Account');
$periods = Config::get('firefly.piggy_bank_periods');
$accounts = FFForm::makeSelectList($acct->getAssetAccounts());
$accounts = FFForm::makeSelectList($acct->getAccountsByType(['Default account', 'Asset account']));
$subTitle = 'Edit repeated expense "' . e($repeatedExpense->name) . '"';
$subTitleIcon = 'fa-pencil';
@@ -137,7 +135,7 @@ class RepeatedExpenseController extends BaseController
}
/**
*
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*/
public function store()
{
@@ -152,7 +150,6 @@ class RepeatedExpenseController extends BaseController
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['order'] = 0;
// always validate:
$messages = $this->_repository->validate($data);
Session::flash('warnings', $messages['warnings']);
@@ -160,11 +157,12 @@ class RepeatedExpenseController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store repeated expense: ' . $messages['errors']->first());
return Redirect::route('repeated.create')->withInput();
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('repeated.create')->withInput();
}
@@ -180,6 +178,8 @@ class RepeatedExpenseController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param PiggyBank $repeatedExpense
*
* @return $this
@@ -196,7 +196,6 @@ class RepeatedExpenseController extends BaseController
$data['remind_me'] = isset($data['remind_me']) ? 1 : 0;
$data['user_id'] = Auth::user()->id;
// always validate:
$messages = $this->_repository->validate($data);
Session::flash('warnings', $messages['warnings']);
@@ -204,10 +203,11 @@ class RepeatedExpenseController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update repeated expense: ' . $messages['errors']->first());
return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput();
}
// return to update screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('repeated.edit', $repeatedExpense->id)->withInput();
}

View File

@@ -48,13 +48,15 @@ class ReportController extends BaseController
} catch (Exception $e) {
return View::make('error')->with('message', 'Invalid date');
}
$date = new Carbon($year . '-' . $month . '-01');
$dayEarly = clone $date;
$dayEarly = $dayEarly->subDay();
$accounts = $this->_repository->getAccountListBudgetOverview($date);
$budgets = $this->_repository->getBudgetsForMonth($date);
$date = new Carbon($year . '-' . $month . '-01');
$dayEarly = clone $date;
$subTitle = 'Budget report for ' . $date->format('F Y');
$subTitleIcon = 'fa-calendar';
$dayEarly = $dayEarly->subDay();
$accounts = $this->_repository->getAccountListBudgetOverview($date);
$budgets = $this->_repository->getBudgetsForMonth($date);
return View::make('reports.budget', compact('date', 'accounts', 'budgets', 'dayEarly'));
return View::make('reports.budget', compact('subTitle', 'subTitleIcon', 'date', 'accounts', 'budgets', 'dayEarly'));
}
@@ -126,7 +128,8 @@ class ReportController extends BaseController
$groupedExpenses = $this->_repository->expensesGroupedByAccount($date, $end, 15);
return View::make(
'reports.year', compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
'reports.year',
compact('date', 'groupedIncomes', 'groupedExpenses', 'year', 'balances', 'title', 'subTitle', 'subTitleIcon', 'mainTitleIcon')
);
}

View File

@@ -9,10 +9,6 @@ use Illuminate\Support\Collection;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("CyclomaticComplexity") // It's all 5. So ok.
* @SuppressWarnings("CouplingBetweenObjects") // There's only so much I can remove.
* @SuppressWarnings("TooManyMethods") // I'm also fine with this.
* @SuppressWarnings("ExcessiveClassComplexity")
*
* Class TransactionController
*
@@ -166,6 +162,8 @@ class TransactionController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's 7. More than 5 but alright.
*
* @param $what
*
* @return $this
@@ -198,7 +196,6 @@ class TransactionController extends BaseController
}
/**
* @param TransactionJournal $journal
*
@@ -235,6 +232,8 @@ class TransactionController extends BaseController
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param $what
*
* @return $this|\Illuminate\Http\RedirectResponse
@@ -243,35 +242,31 @@ class TransactionController extends BaseController
public function store($what)
{
$data = Input::except('_token');
$transactionType = $this->_repository->getJournalType($what);
$transactionCurrency = $this->_repository->getJournalCurrency('EUR');
$transactionCurrency = $this->_repository->getJournalCurrencyById(intval($data['amount_currency_id']));
$data['transaction_type_id'] = $transactionType->id;
$data['transaction_currency_id'] = $transactionCurrency->id;
$data['completed'] = 0;
$data['what'] = $what;
$data['currency'] = 'EUR';
// always validate:
$messages = $this->_repository->validate($data);
$messages = $this->_repository->validate($data);
Session::flash('warnings', $messages['warnings']);
Session::flash('successes', $messages['successes']);
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not store transaction: ' . $messages['errors']->first());
}
// return to create screen:
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
return Redirect::route('transactions.create', $data['what'])->withInput();
}
// store
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('transactions.create', $data['what'])->withInput();
}
$journal = $this->_repository->store($data);
Event::fire('transactionJournal.store', [$journal, Input::get('piggy_bank_id')]); // new and used.
/*
* Also trigger on both transactions.
*/
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
Event::fire('transaction.store', [$transaction]);
@@ -295,10 +290,9 @@ class TransactionController extends BaseController
public function update(TransactionJournal $journal)
{
$data = Input::except('_token');
$data['currency'] = 'EUR';
$data['what'] = strtolower($journal->transactionType->type);
$data['transaction_type_id'] = $journal->transaction_type_id;
$data['transaction_currency_id'] = $journal->transaction_currency_id;
$data['transaction_currency_id'] = intval($data['amount_currency_id']);
$data['completed'] = 1;
$messages = $this->_repository->validate($data);
@@ -307,8 +301,10 @@ class TransactionController extends BaseController
Session::flash('errors', $messages['errors']);
if ($messages['errors']->count() > 0) {
Session::flash('error', 'Could not update transaction: ' . $messages['errors']->first());
return Redirect::route('transactions.edit', $journal->id)->withInput();
}
if ($data['post_submit_action'] == 'validate_only' || $messages['errors']->count() > 0) {
if ($data['post_submit_action'] == 'validate_only') {
return Redirect::route('transactions.edit', $journal->id)->withInput();
}
$this->_repository->update($journal, $data);

View File

@@ -77,7 +77,12 @@ class UserController extends BaseController
$user = $repository->register(Input::all());
if ($user) {
$email->sendVerificationMail($user);
$result = $email->sendVerificationMail($user);
if ($result === false && Config::get('mail.pretend') === false) {
$user->delete();
return View::make('error')->with('message', 'The email message could not be send. See the log files.');
}
return View::make('user.verification-pending');
}
@@ -121,6 +126,11 @@ class UserController extends BaseController
*/
public function register()
{
if ((Config::get('mail.from.address') == '@gmail.com' || Config::get('mail.from.address') == '')
&& Config::get('mail.pretend') === false
) {
return View::make('error')->with('message', 'Configuration error in <code>app/config/' . App::environment() . '/mail.php</code>');
}
return View::make('user.register');
}

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateUsersTable
*/
class CreateUsersTable extends Migration

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateAccountTypesTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateAccountsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateComponentsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreatePiggybanksTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateTransactionCurrenciesTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateTransactionTypesTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateRecurringTransactionsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateTransactionJournalsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateTransactionsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateComponentTransactionTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateComponentTransactionJournalTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreatePreferencesTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateSessionTable
*
*/

View File

@@ -4,6 +4,9 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)\
*
*
* Class CreateLimitsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateLimitRepeatTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateComponentRecurringTransactionTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreatePiggyInstance
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreatePiggybankEventsTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateRemindersTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateAccountMetaTable
*
*/

View File

@@ -4,6 +4,8 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
*
* Class CreateTransactionGroupsTable
*
*/

View File

@@ -5,6 +5,9 @@ use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
/**
* @SuppressWarnings(PHPMD.ShortMethodName) // method names are mandated by laravel.
* @SuppressWarnings("TooManyMethods") // I'm fine with this
*
* Down:
* 1. Create new Components based on Budgets.
* 2. Create new Components based on Categories

View File

@@ -4,6 +4,10 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
* @SuppressWarnings("MethodLength") // I don't mind this in case of migrations.
*
* Class ChangesForV322
*/
class ChangesForV322 extends Migration

View File

@@ -0,0 +1,71 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\QueryException;
use Illuminate\Database\Schema\Blueprint;
/**
* @SuppressWarnings(PHPMD.ShortMethodName)
* @SuppressWarnings("MethodLength") // I don't mind this in case of migrations.
*
* Class ChangesForV325
*/
class ChangesForV325 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
// delete an old index:
try {
Schema::table(
'budget_limits', function (Blueprint $table) {
//$table->dropIndex('unique_ci_combo');
$table->dropUnique('unique_ci_combi');
}
);
} catch (QueryException $e) {
// don't care.
} catch (PDOException $e) {
// don't care.
} catch (\Exception $e) {
// don't care either.
}
// allow journal descriptions to be encrypted.
Schema::table(
'transaction_journals', function (Blueprint $table) {
$table->boolean('encrypted')->default(0);
}
);
try {
DB::update('ALTER TABLE `transaction_journals` MODIFY `description` VARCHAR(1024)');
} catch (QueryException $e) {
// don't care.
} catch (PDOException $e) {
// don't care.
} catch (\Exception $e) {
// don't care either.
}
}
}

View File

@@ -10,31 +10,14 @@ class AccountTypeSeeder extends Seeder
{
DB::table('account_types')->delete();
AccountType::create(
['type' => 'Default account', 'editable' => true]
);
AccountType::create(
['type' => 'Cash account', 'editable' => false]
);
AccountType::create(
['type' => 'Asset account', 'editable' => true]
);
AccountType::create(
['type' => 'Expense account', 'editable' => true]
);
AccountType::create(
['type' => 'Revenue account', 'editable' => true]
);
AccountType::create(
['type' => 'Initial balance account', 'editable' => false]
);
AccountType::create(
['type' => 'Beneficiary account', 'editable' => true]
);
AccountType::create(
['type' => 'Import account', 'editable' => false]
);
AccountType::create(['type' => 'Default account', 'editable' => true]);
AccountType::create(['type' => 'Cash account', 'editable' => false]);
AccountType::create(['type' => 'Asset account', 'editable' => true]);
AccountType::create(['type' => 'Expense account', 'editable' => true]);
AccountType::create(['type' => 'Revenue account', 'editable' => true]);
AccountType::create(['type' => 'Initial balance account', 'editable' => false]);
AccountType::create(['type' => 'Beneficiary account', 'editable' => true]);
AccountType::create(['type' => 'Import account', 'editable' => false]);
}

View File

@@ -18,8 +18,10 @@ class DatabaseSeeder extends Seeder
$this->call('AccountTypeSeeder');
$this->call('TransactionCurrencySeeder');
$this->call('TransactionTypeSeeder');
$this->call('DefaultUserSeeder');
$this->call('TestContentSeeder');
if (App::environment() == 'testing') {
$this->call('TestDataSeeder');
}
}
}

View File

@@ -1,22 +0,0 @@
<?php
/**
* Class DefaultUserSeeder
*/
class DefaultUserSeeder extends Seeder
{
public function run()
{
DB::table('users')->delete();
if (App::environment() == 'testing' || App::environment() == 'homestead') {
User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'acceptance@example.com', 'password' => 'acceptance', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null]);
User::create(['email' => 'reset@example.com', 'password' => 'functional', 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
}
}
}

View File

@@ -1,14 +1,15 @@
<?php
use Carbon\Carbon;
/**
*
* @SuppressWarnings("CamelCase") // I'm fine with this.
* @SuppressWarnings("TooManyMethods") // I'm fine with this
* @SuppressWarnings("CouplingBetweenObjects") // I'm fine with this
* @SuppressWarnings("MethodLength") // I'm fine with this
*
* Class TestContentSeeder
* Class TestDataSeeder
*/
class TestContentSeeder extends Seeder
class TestDataSeeder extends Seeder
{
/** @var string */
public $eom;
@@ -72,87 +73,123 @@ class TestContentSeeder extends Seeder
*/
public function run()
{
if (App::environment() == 'testing' || App::environment() == 'homestead') {
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
// create initial accounts and various other stuff:
$this->createAssetAccounts($user);
$this->createBudgets($user);
$this->createCategories($user);
$this->createPiggyBanks($user);
$this->createReminders($user);
$this->createRecurringTransactions($user);
$this->createBills($user);
$this->createExpenseAccounts($user);
$this->createRevenueAccounts($user);
// get some objects from the database:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
$landLord = Account::whereName('Land lord')->orderBy('id', 'DESC')->first();
$utilities = Account::whereName('Utilities company')->orderBy('id', 'DESC')->first();
$television = Account::whereName('TV company')->orderBy('id', 'DESC')->first();
$phone = Account::whereName('Phone agency')->orderBy('id', 'DESC')->first();
$employer = Account::whereName('Employer')->orderBy('id', 'DESC')->first();
User::create(['email' => 'reset@example.com', 'password' => 'functional', 'reset' => 'okokokokokokokokokokokokokokokok', 'remember_token' => null]);
User::create(['email' => 'functional@example.com', 'password' => 'functional', 'reset' => null, 'remember_token' => null]);
$bills = Budget::whereName('Bills')->orderBy('id', 'DESC')->first();
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
$user = User::create(['email' => 'thegrumpydictator@gmail.com', 'password' => 'james', 'reset' => null, 'remember_token' => null]);
Log::debug('Created users.');
// create initial accounts and various other stuff:
$this->createAssetAccounts($user);
Log::debug('Created asset accounts.');
$this->createBudgets($user);
Log::debug('Created budgets.');
$this->createCategories($user);
Log::debug('Created categories.');
$this->createPiggyBanks($user);
Log::debug('Created piggy banks.');
$this->createReminders($user);
Log::debug('Created reminders.');
$this->createRecurringTransactions($user);
Log::debug('Created recurring transactions.');
$this->createBills($user);
Log::debug('Created bills.');
$this->createExpenseAccounts($user);
Log::debug('Created expense accounts.');
$this->createRevenueAccounts($user);
Log::debug('Created revenue accounts.');
$house = Category::whereName('House')->orderBy('id', 'DESC')->first();
// get some objects from the database:
$checking = Account::whereName('Checking account')->orderBy('id', 'DESC')->first();
Log::debug('Found checking: ' . json_encode($checking));
$savings = Account::whereName('Savings account')->orderBy('id', 'DESC')->first();
Log::debug('Found savings: ' . json_encode($savings));
$landLord = Account::whereName('Land lord')->orderBy('id', 'DESC')->first();
Log::debug('Found landlord: ' . json_encode($landLord));
$utilities = Account::whereName('Utilities company')->orderBy('id', 'DESC')->first();
Log::debug('Found utilities: ' . json_encode($utilities));
$television = Account::whereName('TV company')->orderBy('id', 'DESC')->first();
Log::debug('Found tv company: ' . json_encode($television));
$phone = Account::whereName('Phone agency')->orderBy('id', 'DESC')->first();
Log::debug('Found phone company: ' . json_encode($phone));
$employer = Account::whereName('Employer')->orderBy('id', 'DESC')->first();
Log::debug('Found employer: ' . json_encode($employer));
$withdrawal = TransactionType::whereType('Withdrawal')->first();
$deposit = TransactionType::whereType('Deposit')->first();
$transfer = TransactionType::whereType('Transfer')->first();
$bills = Budget::whereName('Bills')->orderBy('id', 'DESC')->first();
Log::debug('Found bills budget: ' . json_encode($bills));
$groceries = Budget::whereName('Groceries')->orderBy('id', 'DESC')->first();
Log::debug('Found groceries budget: ' . json_encode($groceries));
$euro = TransactionCurrency::whereCode('EUR')->first();
$rentBill = Bill::where('name', 'Rent')->first();
$house = Category::whereName('House')->orderBy('id', 'DESC')->first();
Log::debug('Found house category: ' . json_encode($checking));
$current = clone $this->_yearAgoStartOfMonth;
while ($current <= $this->_startOfMonth) {
$cur = $current->format('Y-m-d');
$formatted = $current->format('F Y');
$withdrawal = TransactionType::whereType('Withdrawal')->first();
Log::debug('Found withdrawal: ' . json_encode($withdrawal));
$deposit = TransactionType::whereType('Deposit')->first();
Log::debug('Found deposit: ' . json_encode($deposit));
$transfer = TransactionType::whereType('Transfer')->first();
Log::debug('Found transfer: ' . json_encode($transfer));
// create expenses for rent, utilities, TV, phone on the 1st of the month.
$this->createTransaction($checking, $landLord, 800, $withdrawal, 'Rent for ' . $formatted, $cur, $euro, $bills, $house, $rentBill);
$this->createTransaction($checking, $utilities, 150, $withdrawal, 'Utilities for ' . $formatted, $cur, $euro, $bills, $house);
$this->createTransaction($checking, $television, 50, $withdrawal, 'TV for ' . $formatted, $cur, $euro, $bills, $house);
$this->createTransaction($checking, $phone, 50, $withdrawal, 'Phone bill for ' . $formatted, $cur, $euro, $bills, $house);
$euro = TransactionCurrency::whereCode('EUR')->first();
Log::debug('Found euro: ' . json_encode($euro));
// two transactions. One without a budget, one without a category.
$this->createTransaction($checking, $phone, 10, $withdrawal, 'Extra charges on phone bill for ' . $formatted, $cur, $euro, null, $house);
$this->createTransaction($checking, $television, 5, $withdrawal, 'Extra charges on TV bill for ' . $formatted, $cur, $euro, $bills, null);
// income from job:
$this->createTransaction($employer, $checking, rand(3500, 4000), $deposit, 'Salary for ' . $formatted, $cur, $euro);
$this->createTransaction($checking, $savings, 2000, $transfer, 'Salary to savings account in ' . $formatted, $cur, $euro);
$this->createGroceries($current);
$this->createBigExpense(clone $current);
echo 'Created test-content for ' . $current->format('F Y') . "\n";
$current->addMonth();
}
$rentBill = Bill::where('name', 'Rent')->first();
Log::debug('Found bill "rent": ' . json_encode($rentBill));
// piggy bank event
// add money to this piggy bank
// create a piggy bank event to match:
$piggyBank = PiggyBank::whereName('New camera')->orderBy('id', 'DESC')->first();
$intoPiggy = $this->createTransaction($checking, $savings, 100, $transfer, 'Money for piggy', $this->yaeom, $euro, $groceries, $house);
PiggyBankEvent::create(
[
'piggy_bank_id' => $piggyBank->id,
'transaction_journal_id' => $intoPiggy->id,
'date' => $this->yaeom,
'amount' => 100
]
);
$current = clone $this->_yearAgoStartOfMonth;
while ($current <= $this->_startOfMonth) {
$cur = $current->format('Y-m-d');
$formatted = $current->format('F Y');
Log::debug('Now at: ' . $cur);
// create expenses for rent, utilities, TV, phone on the 1st of the month.
$this->createTransaction($checking, $landLord, 800, $withdrawal, 'Rent for ' . $formatted, $cur, $euro, $bills, $house, $rentBill);
Log::debug('Created rent.');
$this->createTransaction($checking, $utilities, 150, $withdrawal, 'Utilities for ' . $formatted, $cur, $euro, $bills, $house);
Log::debug('Created utilities.');
$this->createTransaction($checking, $television, 50, $withdrawal, 'TV for ' . $formatted, $cur, $euro, $bills, $house);
Log::debug('Created TV.');
$this->createTransaction($checking, $phone, 50, $withdrawal, 'Phone bill for ' . $formatted, $cur, $euro, $bills, $house);
Log::debug('Created phone bill.');
// two transactions. One without a budget, one without a category.
$this->createTransaction($checking, $phone, 10, $withdrawal, 'Extra charges on phone bill for ' . $formatted, $cur, $euro, null, $house);
Log::debug('Created extra charges no budget.');
$this->createTransaction($checking, $television, 5, $withdrawal, 'Extra charges on TV bill for ' . $formatted, $cur, $euro, $bills, null);
Log::debug('Created extra charges no category.');
// income from job:
$this->createTransaction($employer, $checking, rand(3500, 4000), $deposit, 'Salary for ' . $formatted, $cur, $euro);
Log::debug('Created income.');
$this->createTransaction($checking, $savings, 2000, $transfer, 'Salary to savings account in ' . $formatted, $cur, $euro);
Log::debug('Created savings.');
$this->createGroceries($current);
Log::debug('Created groceries range.');
$this->createBigExpense(clone $current);
Log::debug('Created big expense.');
echo 'Created test-content for ' . $current->format('F Y') . "\n";
$current->addMonth();
}
// piggy bank event
// add money to this piggy bank
// create a piggy bank event to match:
$piggyBank = PiggyBank::whereName('New camera')->orderBy('id', 'DESC')->first();
$intoPiggy = $this->createTransaction($checking, $savings, 100, $transfer, 'Money for piggy', $this->yaeom, $euro, $groceries, $house);
PiggyBankEvent::create(
[
'piggy_bank_id' => $piggyBank->id,
'transaction_journal_id' => $intoPiggy->id,
'date' => $this->yaeom,
'amount' => 100
]
);
}
/**
@@ -181,6 +218,9 @@ class TestContentSeeder extends Seeder
}
/**
* @SuppressWarnings(PHPMD.ShortVariable)
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
*
* @param Account $from
* @param Account $to
* @param $amount
@@ -202,15 +242,24 @@ class TestContentSeeder extends Seeder
$user = User::whereEmail('thegrumpydictator@gmail.com')->first();
$billID = is_null($bill) ? null : $bill->id;
Log::debug('String length of encrypted description ("'.$description.'") is: ' . strlen(Crypt::encrypt($description)));
/** @var TransactionJournal $journal */
$journal = TransactionJournal::create(
[
'user_id' => $user->id, 'transaction_type_id' => $type->id, 'transaction_currency_id' => $currency->id, 'bill_id' => $billID,
'description' => $description, 'completed' => 1, 'date' => $date
'user_id' => $user->id,
'transaction_type_id' => $type->id,
'transaction_currency_id' => $currency->id,
'bill_id' => $billID,
'description' => $description,
'completed' => 1,
'date' => $date
]
);
//Log::debug('Journal valid: ' . Steam::boolString($journal->isValid()));
//Log::debug('Journal errors: ' . json_encode($journal->getErrors()));
//Log::debug('Journal created: ' . json_encode($journal));
Transaction::create(['account_id' => $from->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount * -1]);
Transaction::create(['account_id' => $to->id, 'transaction_journal_id' => $journal->id, 'amount' => $amount]);
@@ -411,16 +460,8 @@ class TestContentSeeder extends Seeder
{
// bill
Bill::create(
[
'user_id' => $user->id, 'name' => 'Rent', 'match' => 'rent,landlord',
'amount_min' => 700,
'amount_max' => 900,
'date' => $this->som,
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
]
['user_id' => $user->id, 'name' => 'Rent', 'match' => 'rent,landlord', 'amount_min' => 700, 'amount_max' => 900, 'date' => $this->som,
'active' => 1, 'automatch' => 1, 'repeat_freq' => 'monthly', 'skip' => 0,]
);
// bill
@@ -429,13 +470,10 @@ class TestContentSeeder extends Seeder
'user_id' => $user->id,
'name' => 'Gas licht',
'match' => 'no,match',
'amount_min' => 500,
'amount_max' => 700,
'amount_min' => 500, 'amount_max' => 700,
'date' => $this->som,
'active' => 1,
'automatch' => 1,
'repeat_freq' => 'monthly',
'skip' => 0,
'active' => 1, 'automatch' => 1,
'repeat_freq' => 'monthly', 'skip' => 0,
]
);
@@ -557,5 +595,8 @@ class TestContentSeeder extends Seeder
);
$group->transactionjournals()->save($one);
$group->transactionjournals()->save($two);
$group->save();
}
}
}

View File

@@ -41,47 +41,6 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
return $this->getUser()->accounts()->accountTypeIn($types)->count();
}
/**
* @return int
*/
public function countAssetAccounts()
{
return $this->countAccountsByType(['Default account', 'Asset account']);
}
/**
* @return int
*/
public function countExpenseAccounts()
{
return $this->countAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts()
{
return $this->countAccountsByType(['Revenue account']);
}
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account)
{
/** @var \FireflyIII\Database\AccountType\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType\AccountType');
$accountType = $acctType->findByWhat('initial');
return $this->getUser()->accounts()->where('account_type_id', $accountType->id)->where('name', 'LIKE', $account->name . '%')->first();
}
/**
* @param array $types
*
@@ -93,7 +52,7 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
* Basic query:
*/
$query = $this->getUser()->accounts()->accountTypeIn($types)->withMeta()->orderBy('name', 'ASC');;
$set = $query->get(['accounts.*']);
$set = $query->get(['accounts.*', 'account_meta.data as accountRole']);
$set->each(
function (\Account $account) {
@@ -101,63 +60,13 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
* Get last activity date.
*/
$account->lastActivityDate = $this->getLastActivity($account);
$account->accountRole = \Config::get('firefly.accountRoles.' . json_decode($account->accountRole));
}
);
return $set;
}
/**
* Get all asset accounts. Optional JSON based parameters.
*
* @param array $metaFilter
*
* @return Collection
*/
public function getAssetAccounts($metaFilter = [])
{
$list = $this->getAccountsByType(['Default account', 'Asset account']);
$list->each(
function (\Account $account) {
// get accountRole:
/** @var \AccountMeta $entry */
$accountRole = $account->accountmeta()->whereName('accountRole')->first();
if (!$accountRole) {
$accountRole = new \AccountMeta;
$accountRole->account_id = $account->id;
$accountRole->name = 'accountRole';
$accountRole->data = 'defaultExpense';
$accountRole->save();
}
$account->accountRole = $accountRole->data;
}
);
return $list;
}
/**
* @return Collection
*/
public function getExpenseAccounts()
{
return $this->getAccountsByType(['Expense account', 'Beneficiary account']);
}
/**
* Get all revenue accounts.
*
* @return Collection
*/
public function getRevenueAccounts()
{
return $this->getAccountsByType(['Revenue account']);
}
/**
* @param \Account $account
*
@@ -180,51 +89,32 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
{
$opposingData = ['name' => $account->name . ' Initial Balance', 'active' => 0, 'what' => 'initial'];
$opposingAccount = $this->store($opposingData);
/*
* Create a journal from opposing to account or vice versa.
*/
$balance = floatval($data['openingbalance']);
$date = new Carbon($data['openingbalancedate']);
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $tj */
$tj = \App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$balance = floatval($data['openingBalance']);
$date = new Carbon($data['openingBalanceDate']);
/** @var \FireflyIII\Database\TransactionJournal\TransactionJournal $journals */
$journals = \App::make('FireflyIII\Database\TransactionJournal\TransactionJournal');
$fromAccount = $opposingAccount;
$toAccount = $account;
if ($balance < 0) {
// first transaction draws money from the new account to the opposing
$from = $account;
$to = $opposingAccount;
} else {
// first transaction puts money into account
$from = $opposingAccount;
$to = $account;
$fromAccount = $account;
$toAccount = $opposingAccount;
}
// data for transaction journal:
$balance = $balance < 0 ? $balance * -1 : $balance;
// find the account type:
/** @var \FireflyIII\Database\TransactionType\TransactionType $typeRepository */
$typeRepository = \App::make('FireflyIII\Database\TransactionType\TransactionType');
$type = $typeRepository->findByWhat('opening');
$currency = $journals->getJournalCurrencyById(intval($data['balance_currency_id']));
//$currency = \Amount::getDefaultCurrency();
// find the currency.
$currency = \Amount::getDefaultCurrency();
$opening = ['transaction_type_id' => $type->id, 'transaction_currency_id' => $currency->id, 'amount' => $balance, 'from' => $fromAccount,
'completed' => 0, 'what' => 'opening', 'to' => $toAccount, 'date' => $date,
'description' => 'Opening balance for new account ' . $account->name,];
$opening = [
'transaction_type_id' => $type->id,
'transaction_currency_id' => $currency->id,
'amount' => $balance,
'from' => $from,
'completed' => 0,
'currency' => 'EUR',
'what' => 'opening',
'to' => $to,
'date' => $date,
'description' => 'Opening balance for new account ' . $account->name,];
$validation = $tj->validate($opening);
$validation = $journals->validate($opening);
if ($validation['errors']->count() == 0) {
$tj->store($opening);
$journals->store($opening);
return true;
} else {
@@ -233,45 +123,68 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
\App::abort(500);
}
return false;
}
/**
* @param \Account $account
*
* @return int
*/
public function getLastActivity(\Account $account)
{
$lastActivityKey = 'account.' . $account->id . '.lastActivityDate';
if (\Cache::has($lastActivityKey)) {
return \Cache::get($lastActivityKey);
}
$transaction = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'DESC')->first();
if ($transaction) {
$date = $transaction->transactionJournal->date;
} else {
$date = 0;
}
\Cache::forever($lastActivityKey, $date);
return $date;
}
/**
* @SuppressWarnings(PHPMD.ExcessiveMethodLength) // cannot make it shorter because of query.
* @param Eloquent $model
*
* @return bool
*/
public function destroy(Eloquent $model)
{
// delete piggy banks
// delete journals:
$journals = \TransactionJournal::whereIn(
$journals = \TransactionJournal::whereIn(
'id', function (QueryBuilder $query) use ($model) {
$query->select('transaction_journal_id')
->from('transactions')->whereIn(
'account_id', function (QueryBuilder $query) use ($model) {
$query
->select('id')
->from('accounts')
->where(
function (QueryBuilder $q) use ($model) {
$q->where('id', $model->id);
$q->orWhere(
function (QueryBuilder $q) use ($model) {
$q->where('accounts.name', 'LIKE', '%' . $model->name . '%');
$q->where('accounts.account_type_id', 3);
$q->where('accounts.active', 0);
}
);
}
)->where('accounts.user_id', $this->getUser()->id);
}
)->get();
->from('transactions')
->whereIn(
'account_id', function (QueryBuilder $query) use ($model) {
$query
->select('accounts.id')
->from('accounts')
->leftJoin('account_types', 'account_types.id', '=', 'accounts.account_type_id')
->where(
function (QueryBuilder $q) use ($model) {
$q->where('accounts.id', $model->id);
$q->orWhere(
function (QueryBuilder $q) use ($model) {
$q->where('accounts.name', 'LIKE', '%' . $model->name . '%');
$q->where('account_types.type', 'Initial balance account');
$q->where('accounts.active', 0);
}
);
}
)->where('accounts.user_id', $this->getUser()->id);
}
)->get();
}
)->get();
/*
* Get all transactions.
*/
$transactions = [];
/** @var \TransactionJournal $journal */
foreach ($journals as $journal) {
@@ -281,25 +194,26 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
}
$journal->delete();
}
// also delete transactions.
if (count($transactions) > 0) {
\Transaction::whereIn('id', $transactions)->delete();
}
/*
* Trigger deletion:
*/
\Event::fire('account.destroy', [$model]);
// delete accounts:
// get account type:
/** @var \FireflyIII\Database\AccountType\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType\AccountType');
$accountType = $acctType->findByWhat('initial');
//$q->where('account_types.type', '');
\Account::where(
function (EloquentBuilder $q) use ($model) {
function (EloquentBuilder $q) use ($model, $accountType) {
$q->where('id', $model->id);
$q->orWhere(
function (EloquentBuilder $q) use ($model) {
function (EloquentBuilder $q) use ($model, $accountType) {
$q->where('accounts.name', 'LIKE', '%' . $model->name . '%');
$q->where('accounts.account_type_id', 3);
$q->where('accounts.account_type_id', $accountType->id);
$q->where('accounts.active', 0);
}
);
@@ -318,9 +232,6 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
public function store(array $data)
{
/*
* Find account type.
*/
/** @var \FireflyIII\Database\AccountType\AccountType $acctType */
$acctType = \App::make('FireflyIII\Database\AccountType\AccountType');
@@ -330,7 +241,6 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
$data['account_type_id'] = $accountType->id;
$data['active'] = isset($data['active']) && $data['active'] === '1' ? 1 : 0;
$data = array_except($data, ['_token', 'what']);
$account = new \Account($data);
if (!$account->isValid()) {
@@ -339,7 +249,7 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
\App::abort(500);
}
$account->save();
if (isset($data['openingbalance']) && floatval($data['openingbalance']) != 0) {
if (isset($data['openingBalance']) && floatval($data['openingBalance']) != 0) {
$this->storeInitialBalance($account, $data);
}
@@ -379,15 +289,15 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
$model->save();
if (isset($data['openingbalance']) && isset($data['openingbalancedate']) && strlen($data['openingbalancedate']) > 0) {
if (isset($data['openingBalance']) && isset($data['openingBalanceDate']) && strlen($data['openingBalanceDate']) > 0) {
/** @noinspection PhpParamsInspection */
$openingBalance = $this->openingBalanceTransaction($model);
if (is_null($openingBalance)) {
$this->storeInitialBalance($model, $data);
} else {
$openingBalance->date = new Carbon($data['openingbalancedate']);
$openingBalance->date = new Carbon($data['openingBalanceDate']);
$openingBalance->save();
$amount = floatval($data['openingbalance']);
$amount = floatval($data['openingBalance']);
/** @var \Transaction $transaction */
foreach ($openingBalance->transactions as $transaction) {
if ($transaction->account_id == $model->id) {
@@ -453,14 +363,14 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
* Opening balance and opening balance date.
*/
if (isset($model['what']) && $model['what'] == 'asset') {
if (isset($model['openingbalance']) && strlen($model['openingbalance']) > 0 && !is_numeric($model['openingbalance'])) {
$errors->add('openingbalance', 'This is not a number.');
if (isset($model['openingBalance']) && strlen($model['openingBalance']) > 0 && !is_numeric($model['openingBalance'])) {
$errors->add('openingBalance', 'This is not a number.');
}
if (isset($model['openingbalancedate']) && strlen($model['openingbalancedate']) > 0) {
if (isset($model['openingBalanceDate']) && strlen($model['openingBalanceDate']) > 0) {
try {
new Carbon($model['openingbalancedate']);
new Carbon($model['openingBalanceDate']);
} catch (\Exception $e) {
$errors->add('openingbalancedate', 'This date is invalid.');
$errors->add('openingBalanceDate', 'This date is invalid.');
}
}
}
@@ -469,11 +379,11 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
if (!$errors->has('name')) {
$successes->add('name', 'OK');
}
if (!$errors->has('openingbalance')) {
$successes->add('openingbalance', 'OK');
if (!$errors->has('openingBalance')) {
$successes->add('openingBalance', 'OK');
}
if (!$errors->has('openingbalancedate')) {
$successes->add('openingbalancedate', 'OK');
if (!$errors->has('openingBalanceDate')) {
$successes->add('openingBalanceDate', 'OK');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
@@ -492,6 +402,9 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @codeCoverageIgnore
*
* @param $what
*
* @throws NotImplementedException
@@ -507,6 +420,7 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function get()
{
@@ -531,14 +445,14 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
*/
public function firstExpenseAccountOrCreate($name)
{
/** @var \FireflyIII\Database\AccountType\AccountType $accountTypeRepository */
$accountTypeRepository = \App::make('FireflyIII\Database\AccountType\AccountType');
/** @var \FireflyIII\Database\AccountType\AccountType $typeRepository */
$typeRepository = \App::make('FireflyIII\Database\AccountType\AccountType');
$accountType = $accountTypeRepository->findByWhat('expense');
$accountType = $typeRepository->findByWhat('expense');
// if name is "", find cash account:
if (strlen($name) == 0) {
$cashAccountType = $accountTypeRepository->findByWhat('cash');
$cashAccountType = $typeRepository->findByWhat('cash');
// find or create cash account:
return \Account::firstOrCreate(
@@ -560,10 +474,20 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
*/
public function firstRevenueAccountOrCreate($name)
{
/** @var \FireflyIII\Database\AccountType\AccountType $accountTypeRepository */
$accountTypeRepository = \App::make('FireflyIII\Database\AccountType\AccountType');
/** @var \FireflyIII\Database\AccountType\AccountType $typeRepository */
$typeRepository = \App::make('FireflyIII\Database\AccountType\AccountType');
$accountType = $accountTypeRepository->findByWhat('revenue');
$accountType = $typeRepository->findByWhat('revenue');
// if name is "", find cash account:
if (strlen($name) == 0) {
$cashAccountType = $typeRepository->findByWhat('cash');
// find or create cash account:
return \Account::firstOrCreate(
['name' => 'Cash account', 'account_type_id' => $cashAccountType->id, 'active' => 0, 'user_id' => $this->getUser()->id,]
);
}
$data = ['user_id' => $this->getUser()->id, 'account_type_id' => $accountType->id, 'name' => $name, 'active' => 1];
@@ -571,57 +495,6 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
}
/**
* @param \Account $account
* @param int $limit
*
* @return \Illuminate\Pagination\Paginator
*/
public function getAllTransactionJournals(\Account $account, $limit = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $this->getUser()->transactionJournals()->withRelevantData()->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = $this->getUser()->transactionJournals()->leftJoin('transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id')
->orderBy('date', 'DESC')->where('transactions.account_id', $account->id)->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* @param \Account $account
*
* @return int
*/
public function getLastActivity(\Account $account)
{
$lastActivityKey = 'account.' . $account->id . '.lastActivityDate';
if (\Cache::has($lastActivityKey)) {
return \Cache::get($lastActivityKey);
}
$transaction = $account->transactions()
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->orderBy('transaction_journals.date', 'DESC')->first();
if ($transaction) {
$date = $transaction->transactionJournal->date;
} else {
$date = 0;
}
\Cache::forever($lastActivityKey, $date);
return $date;
}
/**
* @param \Account $account
* @param int $limit
@@ -656,24 +529,4 @@ class Account implements CUDInterface, CommonDatabaseCallsInterface, AccountInte
}
/**
* @param \Account $account
* @param Carbon $start
* @param Carbon $end
*
* @return \Illuminate\Pagination\Paginator
*/
public function getTransactionJournalsInRange(\Account $account, Carbon $start, Carbon $end)
{
$set = $this->getUser()->transactionJournals()->transactionTypes(['Withdrawal'])->withRelevantData()->leftJoin(
'transactions', 'transactions.transaction_journal_id', '=', 'transaction_journals.id'
)->where('transactions.account_id', $account->id)->before($end)->after($start)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
return $set;
}
}

View File

@@ -21,34 +21,6 @@ interface AccountInterface
*/
public function countAccountsByType(array $types);
/**
* Counts the number of total asset accounts. Useful for DataTables.
*
* @return int
*/
public function countAssetAccounts();
/**
* Counts the number of total expense accounts. Useful for DataTables.
*
* @return int
*/
public function countExpenseAccounts();
/**
* Counts the number of total revenue accounts. Useful for DataTables.
*
* @return int
*/
public function countRevenueAccounts();
/**
* @param \Account $account
*
* @return \Account|null
*/
public function findInitialBalanceAccount(\Account $account);
/**
* Get all accounts of the selected types. Is also capable of handling DataTables' parameters.
*
@@ -58,24 +30,6 @@ interface AccountInterface
*/
public function getAccountsByType(array $types);
/**
* Get all asset accounts. The parameters are optional and are provided by the DataTables plugin.
*
* @return Collection
*/
public function getAssetAccounts();
/**
* @return Collection
*/
public function getExpenseAccounts();
/**
* Get all revenue accounts.
*
* @return Collection
*/
public function getRevenueAccounts();
/**
* @param \Account $account

View File

@@ -19,9 +19,11 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
/**
* @param Eloquent $model
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function destroy(Eloquent $model)
{
@@ -30,9 +32,11 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
/**
* @param array $data
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function store(array $data)
{
@@ -42,9 +46,11 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
/**
* @param Eloquent $model
* @param array $data
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function update(Eloquent $model, array $data)
{
@@ -52,6 +58,8 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
@@ -59,6 +67,7 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
*
* @return array
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function validate(array $model)
{
@@ -66,12 +75,15 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function find($objectId)
{
@@ -88,34 +100,27 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
*/
public function findByWhat($what)
{
switch ($what) {
case 'expense':
return \AccountType::whereType('Expense account')->first();
break;
case 'asset':
return \AccountType::whereType('Asset account')->first();
break;
case 'revenue':
return \AccountType::whereType('Revenue account')->first();
break;
case 'cash':
return \AccountType::whereType('Cash account')->first();
break;
case 'initial':
return \AccountType::whereType('Initial balance account')->first();
break;
default:
throw new FireflyException('Cannot find account type described as "' . e($what) . '".');
break;
$typeMap = [
'expense' => 'Expense account',
'asset' => 'Asset account',
'revenue' => 'Revenue account',
'cash' => 'Cash account',
'initial' => 'Initial balance account',
];
if (isset($typeMap[$what])) {
return \AccountType::whereType($typeMap[$what])->first();
}
throw new FireflyException('Cannot find account type described as "' . e($what) . '".');
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns all objects.
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function get()
{
@@ -123,10 +128,13 @@ class AccountType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{

View File

@@ -114,7 +114,7 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
$warnings = new MessageBag;
$successes = new MessageBag;
$errors = new MessageBag;
if (isset($model['amount_min']) && isset($model['amount_max']) && floatval($model['amount_min']) > floatval($model['amount_max'])) {
if (floatval($model['amount_min']) > floatval($model['amount_max'])) {
$errors->add('amount_max', 'Maximum amount can not be less than minimum amount.');
$errors->add('amount_min', 'Minimum amount can not be more than maximum amount.');
}
@@ -133,12 +133,15 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function find($objectId)
{
@@ -146,12 +149,15 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -169,39 +175,18 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{
throw new NotImplementedException;
}
/**
* Returns all objects.
*
* @return Collection
*/
public function getActive()
{
return $this->getUser()->bills()->where('active', 1)->get();
}
/**
* @param \Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return \TransactionJournal|null
*/
public function getJournalForBillInRange(\Bill $bill, Carbon $start, Carbon $end)
{
return $this->getUser()->transactionjournals()->where('bill_id', $bill->id)->after($start)->before($end)->first();
}
/**
* @param \Bill $bill
*
@@ -218,15 +203,14 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param \Bill $bill
*
* @return Carbon|null
*/
public function nextExpectedMatch(\Bill $bill)
{
/*
* The date Firefly tries to find. If this stays null, it's "unknown".
*/
$finalDate = null;
if ($bill->active == 0) {
return $finalDate;
@@ -238,10 +222,6 @@ class Bill implements CUDInterface, CommonDatabaseCallsInterface, BillInterface
*/
$today = \DateKit::addPeriod(new Carbon, $bill->repeat_freq, 0);
/*
* FF3 loops from the $start of the bill, and to make sure
* $skip works, it adds one (for modulo).
*/
$skip = $bill->skip + 1;
$start = \DateKit::startOfPeriod(new Carbon, $bill->repeat_freq);
/*

View File

@@ -11,18 +11,6 @@ use Carbon\Carbon;
*/
interface BillInterface
{
/**
* @param \Bill $bill
* @param Carbon $start
* @param Carbon $end
*
* @return null|\TransactionJournal
* @internal param Carbon $current
* @internal param Carbon $currentEnd
*
*/
public function getJournalForBillInRange(\Bill $bill, Carbon $start, Carbon $end);
/**
* @param \Bill $bill
*

View File

@@ -8,9 +8,9 @@ use FireflyIII\Database\SwitchUser;
use FireflyIII\Exception\FireflyException;
use FireflyIII\Exception\NotImplementedException;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Query\Builder as QueryBuilder;
use Illuminate\Support\Collection;
use Illuminate\Support\MessageBag;
use Illuminate\Database\Query\Builder as QueryBuilder;
/**
* Class Budget
@@ -97,6 +97,71 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function expenseNoBudget(Carbon $start, Carbon $end)
{
// Add expenses that have no budget:
return $this->getUser()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
}
)
->before($end)
->after($start)
->lessThan(0)
->transactionTypes(['Withdrawal'])
->get();
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsNoBudget(Carbon $start, Carbon $end)
{
$set = $this->getUser()
->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
return $set;
}
/**
* This method includes the time because otherwise, SQLite does not understand it.
*
* @param \Budget $budget
* @param Carbon $date
*
* @return \LimitRepetition|null
*/
public function repetitionOnStartingOnDate(\Budget $budget, Carbon $date)
{
return \LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
}
/**
* Returns an object with id $id.
*
@@ -110,12 +175,15 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -135,10 +203,13 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{
@@ -190,116 +261,6 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
return $budget->limitrepetitions()->where('limit_repetitions.startdate', $date)->first(['limit_repetitions.*']);
}
/**
* @param \Budget $budget
* @param int $limit
*
* @return \Illuminate\Pagination\Paginator
*/
public function getTransactionJournals(\Budget $budget, $limit = 50)
{
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $budget->transactionJournals()->withRelevantData()->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(['transaction_journals.*']);
$count = $budget->transactionJournals()->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* @param \Budget $budget
* @param \LimitRepetition $repetition
* @param int $limit
*
* @return \Illuminate\Pagination\Paginator
*/
public function getTransactionJournalsInRepetition(\Budget $budget, \LimitRepetition $repetition, $limit = 50)
{
$start = $repetition->startdate;
$end = $repetition->enddate;
$offset = intval(\Input::get('page')) > 0 ? intval(\Input::get('page')) * $limit : 0;
$set = $budget->transactionJournals()->withRelevantData()->before($end)->after($start)->take($limit)->offset($offset)->orderBy('date', 'DESC')->get(
['transaction_journals.*']
);
$count = $budget->transactionJournals()->before($end)->after($start)->count();
$items = [];
foreach ($set as $entry) {
$items[] = $entry;
}
return \Paginator::make($items, $count, $limit);
}
/**
* This method includes the time because otherwise, SQLite does not understand it.
*
* @param \Budget $budget
* @param Carbon $date
*
* @return \LimitRepetition|null
*/
public function repetitionOnStartingOnDate(\Budget $budget, Carbon $date)
{
return \LimitRepetition::
leftJoin('budget_limits', 'limit_repetitions.budget_limit_id', '=', 'budget_limits.id')
->where('limit_repetitions.startdate', $date->format('Y-m-d 00:00:00'))
->where('budget_limits.budget_id', $budget->id)
->first(['limit_repetitions.*']);
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function expenseNoBudget(Carbon $start, Carbon $end)
{
// Add expenses that have no budget:
return $this->getUser()
->transactionjournals()
->whereNotIn(
'transaction_journals.id', function (QueryBuilder $query) use ($start, $end) {
$query
->select('transaction_journals.id')
->from('transaction_journals')
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->where('transaction_journals.date', '>=', $start->format('Y-m-d 00:00:00'))
->where('transaction_journals.date', '<=', $end->format('Y-m-d 00:00:00'));
}
)
->before($end)
->after($start)
->lessThan(0)
->transactionTypes(['Withdrawal'])
->get();
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function journalsNoBudget(Carbon $start, Carbon $end)
{
$set = $this->getUser()
->transactionjournals()
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'transaction_journals.id')
->whereNull('budget_transaction_journal.id')
->before($end)
->after($start)
->orderBy('transaction_journals.date')
->get(['transaction_journals.*']);
return $set;
}
/**
* @param \Budget $budget
* @param Carbon $date
@@ -316,20 +277,6 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
return $sum;
}
/**
* @param \Budget $budget
* @param Carbon $start
* @param Carbon $end
*
* @return float
*/
public function spentInPeriod(\Budget $budget, Carbon $start, Carbon $end)
{
$sum = floatval($budget->transactionjournals()->before($end)->after($start)->lessThan(0)->sum('amount')) * -1;
return $sum;
}
/**
* This method updates the amount (envelope) for the given date and budget. This results in a (new) limit (aka an envelope)
* for that budget. Returned to the user is the new limit repetition.
@@ -343,7 +290,7 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
*/
public function updateLimitAmount(\Budget $budget, Carbon $date, $amount)
{
/** @var \Limit $limit */
/** @var \BudgetLimit $limit */
$limit = $this->limitOnStartingOnDate($budget, $date);
if (!$limit) {
// create one!
@@ -381,7 +328,7 @@ class Budget implements CUDInterface, CommonDatabaseCallsInterface, BudgetInterf
* @param \Budget $budget
* @param Carbon $date
*
* @return \Limit
* @return \BudgetLimit
*/
public function limitOnStartingOnDate(\Budget $budget, Carbon $date)
{

View File

@@ -99,12 +99,15 @@ class Category implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function find($objectId)
{
@@ -112,12 +115,15 @@ class Category implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -125,6 +131,8 @@ class Category implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns all objects.
*
* @return Collection
@@ -135,10 +143,13 @@ class Category implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{
@@ -195,19 +206,6 @@ class Category implements CUDInterface, CommonDatabaseCallsInterface
return $set;
}
/**
* @param \Category $category
* @param Carbon $date
*
* @return null
* @throws NotImplementedException
* @internal param \Category $budget
*/
public function repetitionOnStartingOnDate(\Category $category, Carbon $date)
{
throw new NotImplementedException;
}
/**
* @param \Category $category
* @param Carbon $date

View File

@@ -21,7 +21,6 @@ class PiggyBank extends PiggyBankShared implements CUDInterface, CommonDatabaseC
*
* @return mixed
* @throws FireflyException
* @throws NotImplementedException
*/
public function findRepetitionByDate(\PiggyBank $piggyBank, Carbon $date)
{
@@ -30,13 +29,10 @@ class PiggyBank extends PiggyBankShared implements CUDInterface, CommonDatabaseC
if ($reps->count() == 1) {
return $reps->first();
}
if ($reps->count() == 0) {
throw new FireflyException('Should always find a piggy bank repetition.');
}
// should filter the one we need:
$repetitions = $reps->filter(
function (\PiggyBankRepetition $rep) use ($date) {
if ($date >= $rep->startdate && $date <= $rep->targetdate) {
if ($date->between($rep->startdate, $rep->targetdate)) {
return $rep;
}

View File

@@ -58,12 +58,15 @@ class PiggyBankShared
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -75,14 +78,11 @@ class PiggyBankShared
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{
return \PiggyBank::
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')->whereIn('piggy_banks.id', [$ids])->where(
'accounts.user_id', $this->getUser()->id
)
->first(['piggy_banks.*']);
throw new NotImplementedException;
}
/**
@@ -123,6 +123,8 @@ class PiggyBankShared
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param Eloquent $model
* @param array $data
*

View File

@@ -17,6 +17,8 @@ class RepeatedExpense extends PiggyBankShared implements CUDInterface, CommonDat
{
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* Based on the piggy bank, the reminder-setting and
* other variables this method tries to divide the piggy bank into equal parts. Each is
* accommodated by a reminder (if everything goes to plan).

View File

@@ -22,9 +22,11 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
/**
* @param Eloquent $model
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function destroy(Eloquent $model)
{
@@ -59,11 +61,14 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param Eloquent $model
* @param array $data
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function update(Eloquent $model, array $data)
{
@@ -92,12 +97,15 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function find($objectId)
{
@@ -105,12 +113,15 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -122,6 +133,7 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function get()
{
@@ -129,10 +141,13 @@ class Transaction implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{

View File

@@ -89,23 +89,27 @@ class TransactionCurrency implements TransactionCurrencyInterface, CommonDatabas
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
* @throws NotImplementedException
*
* @return \Eloquent
*/
public function find($objectId)
{
throw new NotImplementedException;
return \TransactionCurrency::find($objectId);
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
* @throws NotImplementedException
* @codeCoverageIgnore
*
* @return \AccountType|null
*/
@@ -125,8 +129,11 @@ class TransactionCurrency implements TransactionCurrencyInterface, CommonDatabas
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $objectIds
* @throws NotImplementedException
* @codeCoverageIgnore
*
* @return Collection
*/

View File

@@ -63,12 +63,13 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
*/
public function store(array $data)
{
$currency = $this->getJournalCurrency($data['currency']);
$journal = new \TransactionJournal(
$journal = new \TransactionJournal(
[
'transaction_type_id' => $data['transaction_type_id'],
'transaction_currency_id' => $currency->id, 'user_id' => $this->getUser()->id,
'description' => $data['description'], 'date' => $data['date'], 'completed' => 0]
'transaction_currency_id' => $data['transaction_currency_id'],
'user_id' => $this->getUser()->id,
'description' => $data['description'],
'date' => $data['date'], 'completed' => 0]
);
$journal->save();
@@ -102,7 +103,7 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
public function update(Eloquent $model, array $data)
{
$journalType = $this->getJournalType($data['what']);
$currency = $this->getJournalCurrency($data['currency']);
$currency = $this->getJournalCurrencyById($data['transaction_currency_id']);
$model->description = $data['description'];
$model->date = $data['date'];
@@ -117,9 +118,6 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
$this->storeBudget($data, $model);
$this->storeCategory($data, $model);
/*
* Now we can update the transactions related to this journal.
*/
$amount = floatval($data['amount']);
/** @var \Transaction $transaction */
foreach ($model->transactions()->get() as $transaction) {
@@ -164,109 +162,29 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
if (!isset($model['what'])) {
$errors->add('description', 'Internal error: need to know type of transaction!');
}
/*
* Amount
*/
if (isset($model['amount']) && floatval($model['amount']) < 0.01) {
$errors->add('amount', 'Amount must be > 0.01');
} else {
if (!isset($model['amount'])) {
$errors->add('amount', 'Amount must be set!');
} else {
$successes->add('amount', 'OK');
if (strlen($model['description']) == 0) {
$errors->add('description', 'The description field is required.');
}
$errors = $errors->merge($this->_validateAmount($model));
$errors = $errors->merge($this->_validateBudget($model));
$errors = $errors->merge($this->_validateAccount($model));
$list = ['date', 'description', 'amount', 'budget_id', 'from', 'to', 'account_from_id', 'account_to_id', 'category', 'account_id', 'expense_account',
'revenue_account'];
foreach ($list as $entry) {
if (!$errors->has($entry)) {
$successes->add($entry, 'OK');
}
}
/*
* Budget
*/
if (isset($model['budget_id']) && !ctype_digit($model['budget_id'])) {
$errors->add('budget_id', 'Invalid budget');
} else {
$successes->add('budget_id', 'OK');
}
$successes->add('category', 'OK');
/*
* Many checks to catch invalid or not-existing accounts.
*/
switch (true) {
// this combination is often seen in withdrawals.
case (isset($model['account_id']) && isset($model['expense_account'])):
if (intval($model['account_id']) < 1) {
$errors->add('account_id', 'Invalid account.');
} else {
$successes->add('account_id', 'OK');
}
$successes->add('expense_account', 'OK');
break;
case (isset($model['account_id']) && isset($model['revenue_account'])):
if (intval($model['account_id']) < 1) {
$errors->add('account_id', 'Invalid account.');
} else {
$successes->add('account_id', 'OK');
}
$successes->add('revenue_account', 'OK');
break;
case (isset($model['account_from_id']) && isset($model['account_to_id'])):
if (intval($model['account_from_id']) < 1 || intval($model['account_from_id']) < 1) {
$errors->add('account_from_id', 'Invalid account selected.');
$errors->add('account_to_id', 'Invalid account selected.');
} else {
if (intval($model['account_from_id']) == intval($model['account_to_id'])) {
$errors->add('account_to_id', 'Cannot be the same as "from" account.');
$errors->add('account_from_id', 'Cannot be the same as "to" account.');
} else {
$successes->add('account_from_id', 'OK');
$successes->add('account_to_id', 'OK');
}
}
break;
case (isset($model['to']) && isset($model['from'])):
if (is_object($model['to']) && is_object($model['from'])) {
$successes->add('from', 'OK');
$successes->add('to', 'OK');
}
break;
default:
throw new FireflyException('Cannot validate accounts for transaction journal.');
break;
}
/*
* Add "OK"
*/
if (!$errors->has('description')) {
$successes->add('description', 'OK');
}
if (!$errors->has('date')) {
$successes->add('date', 'OK');
}
return ['errors' => $errors, 'warnings' => $warnings, 'successes' => $successes];
}
/**
* @param $currency
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @return null|\TransactionCurrency
*/
public function getJournalCurrency($currency)
{
/** @var \FireflyIII\Database\TransactionCurrency\TransactionCurrency $currencyRepository */
$currencyRepository = \App::make('FireflyIII\Database\TransactionCurrency\TransactionCurrency');
return $currencyRepository->findByCode($currency);
}
/**
* @param array $data
*
* @return array
@@ -377,25 +295,133 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
return $typeRepository->findByWhat($type);
}
/**
* @param int $currencyId
*
* @return null|\TransactionCurrency
*/
public function getJournalCurrencyById($currencyId)
{
/** @var \FireflyIII\Database\TransactionCurrency\TransactionCurrency $currencyRepository */
$currencyRepository = \App::make('FireflyIII\Database\TransactionCurrency\TransactionCurrency');
return $currencyRepository->find($currencyId);
}
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* @param array $model
*
* @return MessageBag
*/
protected function _validateAmount(array $model)
{
$errors = new MessageBag;
if (isset($model['amount']) && floatval($model['amount']) < 0.01) {
$errors->add('amount', 'Amount must be > 0.01');
} else {
if (!isset($model['amount'])) {
$errors->add('amount', 'Amount must be set!');
}
}
return $errors;
}
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* @param array $model
*
* @return MessageBag
*/
protected function _validateBudget(array $model)
{
/*
* Budget (is not in rules)
*/
$errors = new MessageBag;
if (isset($model['budget_id']) && !ctype_digit($model['budget_id'])) {
$errors->add('budget_id', 'Invalid budget');
}
return $errors;
}
/**
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* @param array $model
*
* @return MessageBag
* @throws FireflyException
*/
protected function _validateAccount(array $model)
{
$errors = new MessageBag;
switch (true) {
// this combination is often seen in withdrawals.
case (isset($model['account_id']) && isset($model['expense_account'])):
if (intval($model['account_id']) < 1) {
$errors->add('account_id', 'Invalid account.');
}
break;
// often seen in deposits
case (isset($model['account_id']) && isset($model['revenue_account'])):
if (intval($model['account_id']) < 1) {
$errors->add('account_id', 'Invalid account.');
}
break;
// often seen in transfers
case (isset($model['account_from_id']) && isset($model['account_to_id'])):
if (intval($model['account_from_id']) < 1 || intval($model['account_from_id']) < 1) {
$errors->add('account_from_id', 'Invalid account selected.');
$errors->add('account_to_id', 'Invalid account selected.');
} else {
if (intval($model['account_from_id']) == intval($model['account_to_id'])) {
$errors->add('account_to_id', 'Cannot be the same as "from" account.');
$errors->add('account_from_id', 'Cannot be the same as "to" account.');
}
}
break;
case (isset($model['from']) && isset($model['to'])):
break;
default:
throw new FireflyException('Cannot validate accounts for transaction journal.');
break;
}
return $errors;
}
/**
* Returns an object with id $id.
*
* @param int $objectId
*
* @codeCoverageIgnore
* @throws NotImplementedException
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return \Eloquent
*/
public function find($objectId)
{
return $this->getUser()->transactionjournals()->find($objectId);
throw new NotImplementedException;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Finds an account type using one of the "$what"'s: expense, asset, revenue, opening, etc.
*
* @param $what
*
* @return \AccountType|null
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function findByWhat($what)
{
@@ -406,11 +432,11 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
* Returns all objects.
*
* @return Collection
* @codeCoverageIgnore
*/
public function get()
{
return $this->getUser()->transactionjournals()->with(['TransactionType', 'transactions', 'transactions.account', 'transactions.account.accountType'])
->get();
throw new NotImplementedException;
}
/**
@@ -444,17 +470,6 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
return $this->getUser()->transactionjournals()->orderBy('date', 'ASC')->first();
}
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getInDateRange(Carbon $start, Carbon $end)
{
return $this->getuser()->transactionjournals()->withRelevantData()->before($end)->after($start)->get();
}
/**
* @param Carbon $date
*
@@ -539,6 +554,19 @@ class TransactionJournal implements TransactionJournalInterface, CUDInterface, C
return $query;
}
/**
* @param $currency
*
* @return null|\TransactionCurrency
*/
public function getJournalCurrency($currency)
{
/** @var \FireflyIII\Database\TransactionCurrency\TransactionCurrency $currencyRepository */
$currencyRepository = \App::make('FireflyIII\Database\TransactionCurrency\TransactionCurrency');
return $currencyRepository->findByCode($currency);
}
/**
* @param int $limit
*

View File

@@ -19,14 +19,6 @@ interface TransactionJournalInterface
*/
public function first();
/**
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function getInDateRange(Carbon $start, Carbon $end);
/**
* @param Carbon $date
*

View File

@@ -20,10 +20,13 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param Eloquent $model
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function destroy(Eloquent $model)
{
@@ -31,10 +34,13 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $data
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function store(array $data)
{
@@ -42,11 +48,14 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param Eloquent $model
* @param array $data
*
* @return bool
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function update(Eloquent $model, array $data)
{
@@ -54,6 +63,8 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Validates an array. Returns an array containing MessageBags
* errors/warnings/successes.
*
@@ -61,6 +72,7 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
*
* @return array
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function validate(array $model)
{
@@ -68,12 +80,15 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns an object with id $id.
*
* @param int $objectId
*
* @return \Eloquent
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function find($objectId)
{
@@ -96,17 +111,21 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
'withdrawal' => 'Withdrawal',
'deposit' => 'Deposit',
];
if(!isset($translation[$what])) {
if (!isset($translation[$what])) {
throw new FireflyException('Cannot find transaction type described as "' . e($what) . '".');
}
return \TransactionType::whereType($translation[$what])->first();
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* Returns all objects.
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function get()
{
@@ -114,10 +133,13 @@ class TransactionType implements CUDInterface, CommonDatabaseCallsInterface
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $ids
*
* @return Collection
* @throws NotImplementedException
* @codeCoverageIgnore
*/
public function getByIds(array $ids)
{

View File

@@ -34,6 +34,8 @@ class PiggyBank
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param \TransactionJournal $journal
*
* @throws \FireflyIII\Exception\FireflyException
@@ -109,6 +111,8 @@ class PiggyBank
*/
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param \TransactionJournal $journal
* @param int $piggyBankId
*/
@@ -176,6 +180,8 @@ class PiggyBank
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's 6. More than 5 but alright.
*
* Validates the presence of repetitions for all repeated expenses!
*/
public function validateRepeatedExpenses()
@@ -185,32 +191,24 @@ class PiggyBank
}
/** @var \FireflyIII\Database\PiggyBank\RepeatedExpense $repository */
$repository = \App::make('FireflyIII\Database\PiggyBank\RepeatedExpense');
$list = $repository->get();
$today = Carbon::now();
/** @var \PiggyBank $entry */
foreach ($list as $entry) {
$start = $entry->startdate;
$target = $entry->targetdate;
// find a repetition on this date:
$count = $entry->piggyBankrepetitions()->starts($start)->targets($target)->count();
$count = $entry->piggyBankrepetitions()->starts($entry->startdate)->targets($entry->targetdate)->count();
if ($count == 0) {
$repetition = new \PiggyBankRepetition;
$repetition->piggyBank()->associate($entry);
$repetition->startdate = $start;
$repetition->targetdate = $target;
$repetition->startdate = $entry->startdate;
$repetition->targetdate = $entry->targetdate;
$repetition->currentamount = 0;
$repetition->save();
}
// then continue and do something in the current relevant time frame.
$currentTarget = clone $target;
$currentTarget = clone $entry->startdate;
$currentStart = null;
while ($currentTarget < $today) {
$currentStart = \DateKit::subtractPeriod($currentTarget, $entry->rep_length, 0);
$currentTarget = \DateKit::addPeriod($currentTarget, $entry->rep_length, 0);
// create if not exists:
$count = $entry->piggyBankRepetitions()->starts($currentStart)->targets($currentTarget)->count();
if ($count == 0) {
$repetition = new \PiggyBankRepetition;

View File

@@ -32,6 +32,7 @@ class FF3ServiceProvider extends ServiceProvider
/**
* Return the services bla bla.
*
* @CodeCoverageIgnore
* @return array
*/
public function provides()

View File

@@ -24,169 +24,15 @@ class Form
*/
public static function ffAmount($name, $value = null, array $options = [])
{
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$options['step'] = 'any';
$options['min'] = '0.01';
return self::ffInput('amount', $name, $value, $options);
}
/**
* @param $type
* @param $name
* @param null $value
* @param array $options
* @param array $list
*
* @return string
* @throws FireflyException
*/
public static function ffInput($type, $name, $value = null, array $options = [], $list = [])
{
/*
* add some defaults to this method:
*/
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$label = self::label($name, $options);
/*
* Make label and placeholder look nice.
*/
$options['placeholder'] = ucfirst($name);
/*
* Get pre filled value:
*/
if (\Session::has('preFilled')) {
$preFilled = \Session::get('preFilled');
$value = isset($preFilled[$name]) && is_null($value) ? $preFilled[$name] : $value;
}
/*
* Get the value.
*/
if (!is_null(\Input::old($name))) {
/*
* Old value overrules $value.
*/
$value = \Input::old($name);
}
/*
* Get errors, warnings and successes from session:
*/
/** @var MessageBag $errors */
$errors = \Session::get('errors');
/** @var MessageBag $warnings */
$warnings = \Session::get('warnings');
/** @var MessageBag $successes */
$successes = \Session::get('successes');
/*
* If errors, add some more classes.
*/
switch (true) {
case (!is_null($errors) && $errors->has($name)):
$classes = 'form-group has-error has-feedback';
break;
case (!is_null($warnings) && $warnings->has($name)):
$classes = 'form-group has-warning has-feedback';
break;
case (!is_null($successes) && $successes->has($name)):
$classes = 'form-group has-success has-feedback';
break;
default:
$classes = 'form-group';
break;
}
/*
* Add some HTML.
*/
$html = '<div class="' . $classes . '">';
$html .= '<label for="' . $options['id'] . '" class="col-sm-4 control-label">' . $label . '</label>';
$html .= '<div class="col-sm-8">';
/*
* Switch input type:
*/
unset($options['label']);
switch ($type) {
case 'text':
$html .= \Form::input('text', $name, $value, $options);
break;
case 'amount':
$html .= '<div class="input-group"><div class="input-group-addon">' . \Amount::getCurrencySymbol() . '</div>';
$html .= \Form::input('number', $name, $value, $options);
$html .= '</div>';
break;
case 'number':
$html .= \Form::input('number', $name, $value, $options);
break;
case 'checkbox':
$checked = $options['checked'];
unset($options['placeholder'], $options['autocomplete'], $options['class']);
$html .= '<div class="checkbox"><label>';
$html .= \Form::checkbox($name, $value, $checked, $options);
$html .= '</label></div>';
break;
case 'date':
$html .= \Form::input('date', $name, $value, $options);
break;
case 'select':
$html .= \Form::select($name, $list, $value, $options);
break;
default:
throw new FireflyException('Cannot handle type "' . $type . '" in FFFormBuilder.');
break;
}
/*
* If errors, respond to them:
*/
if (!is_null($errors)) {
if ($errors->has($name)) {
$html .= '<span class="glyphicon glyphicon-remove form-control-feedback"></span>';
$html .= '<p class="text-danger">' . e($errors->first($name)) . '</p>';
}
}
unset($errors);
/*
* If warnings, respond to them:
*/
if (!is_null($warnings)) {
if ($warnings->has($name)) {
$html .= '<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>';
$html .= '<p class="text-warning">' . e($warnings->first($name)) . '</p>';
}
}
unset($warnings);
/*
* If successes, respond to them:
*/
if (!is_null($successes)) {
if ($successes->has($name)) {
$html .= '<span class="glyphicon glyphicon-ok form-control-feedback"></span>';
$html .= '<p class="text-success">' . e($successes->first($name)) . '</p>';
}
}
unset($successes);
$html .= '</div>';
$html .= '</div>';
$defaultCurrency = isset($options['currency']) ? $options['currency'] : \Amount::getDefaultCurrency();
$currencies = \TransactionCurrency::orderBy('code','ASC')->get();
$html = \View::make('form.amount', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
@@ -204,12 +50,81 @@ class Form
return $options['label'];
}
$labels = ['amount_min' => 'Amount (min)', 'amount_max' => 'Amount (max)', 'match' => 'Matches on', 'repeat_freq' => 'Repetition',
'account_from_id' => 'Account from', 'account_to_id' => 'Account to', 'account_id' => 'Asset account'];
'account_from_id' => 'Account from', 'account_to_id' => 'Account to', 'account_id' => 'Asset account', 'budget_id' => 'Budget'
, 'piggy_bank_id' => 'Piggy bank'];
return isset($labels[$name]) ? $labels[$name] : str_replace('_', ' ', ucfirst($name));
}
/**
* @param $name
* @param $label
* @param array $options
*
* @return array
*/
public static function expandOptionArray($name, $label, array $options)
{
$options['class'] = 'form-control';
$options['id'] = 'ffInput_' . $name;
$options['autocomplete'] = 'off';
$options['placeholder'] = ucfirst($label);
return $options;
}
/**
* @param $name
*
* @return string
*/
public static function getHolderClasses($name)
{
/*
* Get errors, warnings and successes from session:
*/
/** @var MessageBag $errors */
$errors = \Session::get('errors');
/** @var MessageBag $successes */
$successes = \Session::get('successes');
switch (true) {
case (!is_null($errors) && $errors->has($name)):
$classes = 'form-group has-error has-feedback';
break;
case (!is_null($successes) && $successes->has($name)):
$classes = 'form-group has-success has-feedback';
break;
default:
$classes = 'form-group';
break;
}
return $classes;
}
/**
* @param $name
* @param $value
*
* @return mixed
*/
public static function fillFieldValue($name, $value)
{
if (\Session::has('preFilled')) {
$preFilled = \Session::get('preFilled');
$value = isset($preFilled[$name]) && is_null($value) ? $preFilled[$name] : $value;
}
if (!is_null(\Input::old($name))) {
$value = \Input::old($name);
}
return $value;
}
/**
* @param $name
* @param null $value
@@ -220,10 +135,15 @@ class Form
*/
public static function ffBalance($name, $value = null, array $options = [])
{
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$options['step'] = 'any';
return self::ffInput('amount', $name, $value, $options);
$defaultCurrency = isset($options['currency']) ? $options['currency'] : \Amount::getDefaultCurrency();
$currencies = \TransactionCurrency::orderBy('code','ASC')->get();
$html = \View::make('form.balance', compact('defaultCurrency', 'currencies', 'classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
@@ -238,8 +158,16 @@ class Form
public static function ffCheckbox($name, $value = 1, $checked = null, $options = [])
{
$options['checked'] = $checked === true ? true : null;
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
return self::ffInput('checkbox', $name, $value, $options);
unset($options['placeholder'], $options['autocomplete'], $options['class']);
$html = \View::make('form.checkbox', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
@@ -252,7 +180,13 @@ class Form
*/
public static function ffDate($name, $value = null, array $options = [])
{
return self::ffInput('date', $name, $value, $options);
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$html = \View::make('form.date', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
/**
@@ -265,9 +199,14 @@ class Form
*/
public static function ffInteger($name, $value = null, array $options = [])
{
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$options['step'] = '1';
$html = \View::make('form.integer', compact('classes', 'name', 'label', 'value', 'options'))->render();
return self::ffInput('number', $name, $value, $options);
return $html;
}
@@ -284,57 +223,9 @@ class Form
{
$previousValue = \Input::old('post_submit_action');
$previousValue = is_null($previousValue) ? 'store' : $previousValue;
/*
* Store.
*/
switch ($type) {
case 'create':
$store = '<div class="form-group"><label for="' . $name . '_store" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'store', $previousValue == 'store', ['id' => $name . '_store']);
$store .= 'Store ' . $name . '</label></div></div></div>';
break;
case 'update':
$store = '<div class="form-group"><label for="' . $name . 'update" class="col-sm-4 control-label">Store</label>';
$store .= '<div class="col-sm-8"><div class="radio"><label>';
$store .= \Form::radio('post_submit_action', 'update', $previousValue == 'update' || $previousValue == 'store', ['id' => $name . '_update']);
$store .= 'Update ' . $name . '</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store) ' . $type);
break;
}
$html = \View::make('form.options', compact('type', 'name', 'previousValue'))->render();
/*
* validate is always the same:
*/
$validate = '<div class="form-group"><label for="' . $name . 'validate_only" class="col-sm-4 control-label">Validate only';
$validate .= '</label><div class="col-sm-8"><div class="radio"><label>';
$validate .= \Form::radio('post_submit_action', 'validate_only', $previousValue == 'validate_only', ['id' => $name . '_validate_only']);
$validate .= 'Only validate, do not save</label></div></div></div>';
/*
* Store & return:
*/
switch ($type) {
case 'create':
$return = '<div class="form-group"><label for="' . $name . 'return_to_form" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'create_another', $previousValue == 'create_another', ['id' => $name . '_create_another']);
$return .= 'After storing, return here to create another one.</label></div></div></div>';
break;
case 'update':
$return = '<div class="form-group"><label for="' . $name . 'return_to_edit" class="col-sm-4 control-label">';
$return .= 'Return here</label><div class="col-sm-8"><div class="radio"><label>';
$return .= \Form::radio('post_submit_action', 'return_to_edit', $previousValue == 'return_to_edit', ['id' => $name . '_return_to_edit']);
$return .= 'After updating, return here.</label></div></div></div>';
break;
default:
throw new FireflyException('Cannot create ffOptionsList for option (store+return) ' . $type);
break;
}
return $store . $validate . $return;
return $html;
}
/**
@@ -344,11 +235,16 @@ class Form
* @param array $options
*
* @return string
* @throws FireflyException
*/
public static function ffSelect($name, array $list = [], $selected = null, array $options = [])
{
return self::ffInput('select', $name, $selected, $options, $list);
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$selected = self::fillFieldValue($name, $selected);
$html = \View::make('form.select', compact('classes', 'name', 'label', 'selected', 'options', 'list'))->render();
return $html;
}
/**
@@ -361,9 +257,14 @@ class Form
*/
public static function ffTags($name, $value = null, array $options = [])
{
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$options['data-role'] = 'tagsinput';
$html = \View::make('form.tags', compact('classes', 'name', 'label', 'value', 'options'))->render();
return self::ffInput('text', $name, $value, $options);
return $html;
}
/**
@@ -376,7 +277,13 @@ class Form
*/
public static function ffText($name, $value = null, array $options = [])
{
return self::ffInput('text', $name, $value, $options);
$label = self::label($name, $options);
$options = self::expandOptionArray($name, $label, $options);
$classes = self::getHolderClasses($name);
$value = self::fillFieldValue($name, $value);
$html = \View::make('form.text', compact('classes', 'name', 'label', 'value', 'options'))->render();
return $html;
}
}

View File

@@ -56,14 +56,36 @@ class Related implements RelatedInterface
}
$exclude = array_unique($exclude);
$query = $this->getUser()->transactionjournals()
->withRelevantData()
->before($end)
->after($start)
->whereNotIn('id', $exclude)
->where('description', 'LIKE', '%' . $query . '%')
->get();
/** @var Collection $collection */
$collection = $this->getUser()->transactionjournals()
->withRelevantData()
->before($end)
->where('encrypted', 0)
->after($start)
->whereNotIn('id', $exclude)
->where('description', 'LIKE', '%' . $query . '%')
->get();
return $query;
// manually search encrypted entries:
/** @var Collection $encryptedCollection */
$encryptedCollection = $this->getUser()->transactionjournals()
->withRelevantData()
->before($end)
->where('encrypted', 1)
->after($start)
->whereNotIn('id', $exclude)
->get();
$encrypted = $encryptedCollection->filter(
function (\TransactionJournal $journal) use ($query) {
$strPos = strpos($journal->description, $query);
if ($strPos !== false) {
return $journal;
}
}
);
$collected = $collection->merge($encrypted);
return $collected;
}
}

View File

@@ -12,15 +12,6 @@ use Illuminate\Support\Collection;
class Helper implements HelperInterface
{
/**
* @param $what
*
* @return int
*/
public function getTransactionTypeIdByWhat($what) {
}
/**
*
* Get the account_id, which is the asset account that paid for the transaction.
@@ -49,7 +40,7 @@ class Helper implements HelperInterface
/** @var \FireflyIII\Database\Account\Account $accountRepository */
$accountRepository = \App::make('FireflyIII\Database\Account\Account');
return $accountRepository->getAssetAccounts();
return $accountRepository->getAccountsByType(['Default account', 'Asset account']);
}
/**
@@ -90,4 +81,5 @@ class Helper implements HelperInterface
}
}

View File

@@ -22,13 +22,6 @@ interface HelperInterface
*/
public function getAssetAccount($what, Collection $transactions);
/**
* @param $what
*
* @return int
*/
public function getTransactionTypeIdByWhat($what);
/**
* @return Collection
*/

View File

@@ -7,12 +7,12 @@ use FireflyIII\Database\Account\Account as AccountRepository;
use FireflyIII\Database\SwitchUser;
use FireflyIII\Database\TransactionJournal\TransactionJournal as JournalRepository;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Support\Collection;
/**
* Class Report
* @SuppressWarnings("CamelCase") // I'm fine with this.
*
* Class Report
*
* @package FireflyIII\Report
*/
@@ -45,6 +45,8 @@ class Report implements ReportInterface
}
/**
* This methods fails to take in account transfers FROM shared accounts.
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
@@ -115,7 +117,7 @@ class Report implements ReportInterface
$accounts = [];
/** @var \Account $account */
foreach ($list as $account) {
$id = intval($account->id);
$id = intval($account->id);
/** @noinspection PhpParamsInspection */
$accounts[$id] = [
'name' => $account->name,
@@ -205,18 +207,18 @@ class Report implements ReportInterface
$set = $this->_queries->journalsByExpenseAccount($start, $end);
$expenses = $this->_helper->makeArray($set);
$alt = $this->_queries->sharedExpenses($start, $end);
$transfers = $this->_helper->makeArray($alt);
$expenses[-1] = [
'amount' => 0,
'name' => 'Transfers to shared',
'spent' => 0
];
foreach ($transfers as $transfer) {
$expenses[-1]['amount'] += $transfer['amount'];
}
// $alt = $this->_queries->sharedExpenses($start, $end);
// $transfers = $this->_helper->makeArray($alt);
//
// $expenses[-1] = [
// 'amount' => 0,
// 'name' => 'Transfers to shared',
// 'spent' => 0
// ];
//
// foreach ($transfers as $transfer) {
// $expenses[-1]['amount'] += $transfer['amount'];
// }
$expenses = $this->_helper->sortArray($expenses);
$limited = $this->_helper->limitArray($expenses, $limit);
@@ -226,6 +228,10 @@ class Report implements ReportInterface
}
/**
* This method gets all incomes (journals) in a list.
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param Carbon $date
* @param bool $shared
*
@@ -239,46 +245,48 @@ class Report implements ReportInterface
$end->endOfMonth();
$userId = $this->_accounts->getUser()->id;
return $this->_queries->incomeByPeriod($start, $end);
$list = \TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->transactionTypes(['Deposit'])
->where('transaction_journals.user_id', $userId)
->where('transactions.amount', '>', 0)
->where('transaction_journals.user_id', \Auth::user()->id)
->where('account_meta.data', '!=', '"sharedExpense"')
->orderBy('date', 'ASC')
->before($end)->after($start)->get(['transaction_journals.*']);
// $list = \TransactionJournal::leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
// ->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
// ->leftJoin(
// 'account_meta', function (JoinClause $join) {
// $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
// }
// )
// ->transactionTypes(['Deposit'])
// ->where('transaction_journals.user_id', $userId)
// ->where('transactions.amount', '>', 0)
// ->where('transaction_journals.user_id', \Auth::user()->id)
// ->where('account_meta.data', '!=', '"sharedExpense"')
// ->orderBy('date', 'ASC')
// ->before($end)->after($start)->get(['transaction_journals.*']);
//
// // incoming from a shared account: it's profit (income):
// $transfers = \TransactionJournal::withRelevantData()
// ->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
// ->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
// ->leftJoin(
// 'account_meta', function (JoinClause $join) {
// $join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
// }
// )
// ->transactionTypes(['Transfer'])
// ->where('transaction_journals.user_id', $userId)
// ->where('transactions.amount', '<', 0)
// ->where('account_meta.data', '=', '"sharedExpense"')
// ->orderBy('date', 'ASC')
// ->before($end)->after($start)->get(['transaction_journals.*']);
//
// $list = $list->merge($transfers);
// $list->sort(
// function (\TransactionJournal $journal) {
// return $journal->date->format('U');
// }
// );
//
// return $list;
// incoming from a shared account: it's profit (income):
$transfers = \TransactionJournal::withRelevantData()
->leftJoin('transactions', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
->leftJoin('accounts', 'transactions.account_id', '=', 'accounts.id')
->leftJoin(
'account_meta', function (JoinClause $join) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', 'accountRole');
}
)
->transactionTypes(['Transfer'])
->where('transaction_journals.user_id', $userId)
->where('transactions.amount', '<', 0)
->where('account_meta.data', '=', '"sharedExpense"')
->orderBy('date', 'ASC')
->before($end)->after($start)->get(['transaction_journals.*']);
$list = $list->merge($transfers);
$list->sort(
function (\TransactionJournal $journal) {
return $journal->date->format('U');
}
);
return $list;
}
/**
@@ -295,21 +303,21 @@ class Report implements ReportInterface
\PiggyBank::
leftJoin('accounts', 'accounts.id', '=', 'piggy_banks.account_id')
->where('accounts.user_id', \Auth::user()->id)
->where('repeats', 0)
->where(
function (Builder $query) use ($start, $end) {
$query->whereNull('piggy_banks.deleted_at');
$query->orWhere(
function (Builder $query) use ($start, $end) {
$query->whereNotNull('piggy_banks.deleted_at');
$query->where('piggy_banks.deleted_at', '>=', $start->format('Y-m-d 00:00:00'));
$query->where('piggy_banks.deleted_at', '<=', $end->format('Y-m-d 00:00:00'));
}
);
}
)
->get(['piggy_banks.*']);
->where('accounts.user_id', \Auth::user()->id)
->where('repeats', 0)
->where(
function (Builder $query) use ($start, $end) {
$query->whereNull('piggy_banks.deleted_at');
$query->orWhere(
function (Builder $query) use ($start, $end) {
$query->whereNotNull('piggy_banks.deleted_at');
$query->where('piggy_banks.deleted_at', '>=', $start->format('Y-m-d 00:00:00'));
$query->where('piggy_banks.deleted_at', '<=', $end->format('Y-m-d 00:00:00'));
}
);
}
)
->get(['piggy_banks.*']);
}
@@ -353,6 +361,7 @@ class Report implements ReportInterface
}
/**
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
@@ -361,8 +370,7 @@ class Report implements ReportInterface
*/
public function revenueGroupedByAccount(Carbon $start, Carbon $end, $limit = 15)
{
return $this->_queries->journalsByRevenueAccount($start, $end);
return $this->_queries->journalsByRevenueAccount($start, $end, $limit);
}
@@ -387,7 +395,7 @@ class Report implements ReportInterface
$sharedAccounts[] = $account->id;
}
$accounts = $this->_accounts->getAssetAccounts()->filter(
$accounts = $this->_accounts->getAccountsByType(['Default account', 'Asset account'])->filter(
function (\Account $account) use ($sharedAccounts) {
if (!in_array($account->id, $sharedAccounts)) {
return $account;

View File

@@ -68,16 +68,13 @@ class ReportQuery implements ReportQueryInterface
}
)
->leftJoin('budget_transaction_journal', 'budget_transaction_journal.transaction_journal_id', '=', 'otherJournals.id')
->before($end)
->after($start)
->before($end)->after($start)
->where('transaction_types.type', 'Withdrawal')
->where('transaction_journals.user_id', \Auth::user()->id)
->whereNull('budget_transaction_journal.budget_id')
->whereNull('transaction_journals.deleted_at')
->whereNull('budget_transaction_journal.budget_id')->whereNull('transaction_journals.deleted_at')
->whereNull('otherJournals.deleted_at')
->where('transactions.account_id', $account->id)
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')
->groupBy('transaction_journals.id')
->whereNotNull('transaction_group_transaction_journal.transaction_group_id')->groupBy('transaction_journals.id')
->get(
[
'transaction_journals.id as transferId',
@@ -202,6 +199,70 @@ class ReportQuery implements ReportQueryInterface
}
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end)
{
return \TransactionJournal::
leftJoin(
'transactions as t_from', function (JoinClause $join) {
$join->on('t_from.transaction_journal_id', '=', 'transaction_journals.id')->where('t_from.amount', '<', 0);
}
)
->leftJoin('accounts as ac_from', 't_from.account_id', '=', 'ac_from.id')
->leftJoin(
'account_meta as acm_from', function (JoinClause $join) {
$join->on('ac_from.id', '=', 'acm_from.account_id')->where('acm_from.name', '=', 'accountRole');
}
)
->leftJoin(
'transactions as t_to', function (JoinClause $join) {
$join->on('t_to.transaction_journal_id', '=', 'transaction_journals.id')->where('t_to.amount', '>', 0);
}
)
->leftJoin('accounts as ac_to', 't_to.account_id', '=', 'ac_to.id')
->leftJoin(
'account_meta as acm_to', function (JoinClause $join) {
$join->on('ac_to.id', '=', 'acm_to.account_id')->where('acm_to.name', '=', 'accountRole');
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', \Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('transaction_journals.date')
->get(
['transaction_journals.id',
'transaction_journals.description',
'transaction_types.type',
't_to.amount', 'transaction_journals.date', 't_from.account_id as account_id',
'ac_from.name as name']
);
}
/**
* Gets a list of expenses grouped by the budget they were filed under.
*
@@ -278,6 +339,8 @@ class ReportQuery implements ReportQueryInterface
* Gets a list of expense accounts and the expenses therein, grouped by that expense account.
* This result excludes transfers to shared accounts which are expenses, technically.
*
* So now it will include them!
*
* @param Carbon $start
* @param Carbon $end
*
@@ -309,8 +372,25 @@ class ReportQuery implements ReportQueryInterface
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Withdrawal')
->where('acm_from.data', '!=', '"sharedExpense"')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Withdrawal');
$q->where('acm_from.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_to.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)
->after($start)
->where('transaction_journals.user_id', \Auth::user()->id)
@@ -324,10 +404,11 @@ class ReportQuery implements ReportQueryInterface
*
* @param Carbon $start
* @param Carbon $end
* @param int $limit
*
* @return Collection
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end)
public function journalsByRevenueAccount(Carbon $start, Carbon $end, $limit = 15)
{
return \TransactionJournal::
leftJoin(
@@ -353,8 +434,22 @@ class ReportQuery implements ReportQueryInterface
}
)
->leftJoin('transaction_types', 'transaction_types.id', '=', 'transaction_journals.transaction_type_id')
->where('transaction_types.type', 'Deposit')
->where('acm_to.data', '!=', '"sharedExpense"')
->where(
function ($query) {
$query->where(
function ($q) {
$q->where('transaction_types.type', 'Deposit');
$q->where('acm_to.data', '!=', '"sharedExpense"');
}
);
$query->orWhere(
function ($q) {
$q->where('transaction_types.type', 'Transfer');
$q->where('acm_from.data', '=', '"sharedExpense"');
}
);
}
)
->before($end)->after($start)
->where('transaction_journals.user_id', \Auth::user()->id)
->groupBy('t_from.account_id')->orderBy('amount')

View File

@@ -117,6 +117,18 @@ interface ReportQueryInterface
*/
public function journalsByRevenueAccount(Carbon $start, Carbon $end);
/**
* This method returns all "income" journals in a certain period, which are both transfers from a shared account
* and "ordinary" deposits. The query used is almost equal to ReportQueryInterface::journalsByRevenueAccount but it does
* not group and returns different fields.
*
* @param Carbon $start
* @param Carbon $end
*
* @return Collection
*/
public function incomeByPeriod(Carbon $start, Carbon $end);
/**
* With an equally misleading name, this query returns are transfers to shared accounts. These are considered
* expenses.

View File

@@ -80,6 +80,8 @@ class Search
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param array $words
*
* @return Collection

View File

@@ -3,6 +3,8 @@ namespace FireflyIII\Shared\Mail;
use Swift_RfcComplianceException;
use Illuminate\Mail\Message;
use Swift_TransportException;
/**
* Class Registration
*
@@ -57,7 +59,16 @@ class Registration implements RegistrationInterface
}
);
} catch (Swift_RfcComplianceException $e) {
\Log::error($e->getMessage());
return false;
} catch(Swift_TransportException $e) {
\Log::error($e->getMessage());
return false;
} catch(\Exception $e) {
\Log::error($e->getMessage());
return false;
}
return true;
}
@@ -84,7 +95,16 @@ class Registration implements RegistrationInterface
}
);
} catch (Swift_RfcComplianceException $e) {
\Log::error($e->getMessage());
return false;
} catch(Swift_TransportException $e) {
\Log::error($e->getMessage());
return false;
} catch(\Exception $e) {
\Log::error($e->getMessage());
return false;
}
return true;
}
}

View File

@@ -13,7 +13,7 @@ use FireflyIII\Exception\FireflyException;
class Date
{
/**
* @param Carbon $theDate
* @param Carbon $theDate
* @param $repeatFreq
* @param $skip
*
@@ -25,41 +25,37 @@ class Date
$date = clone $theDate;
$add = ($skip + 1);
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do addPeriod for $repeat_freq "' . $repeatFreq . '"');
break;
case 'daily':
$date->addDays($add);
break;
case 'week':
case 'weekly':
$date->addWeeks($add);
break;
case 'month':
case 'monthly':
$date->addMonths($add);
break;
case 'quarter':
case 'quarterly':
$months = $add * 3;
$date->addMonths($months);
break;
case 'half-year':
$months = $add * 6;
$date->addMonths($months);
break;
case 'year':
case 'yearly':
$date->addYears($add);
break;
$functionMap = [
'daily' => 'addDays',
'weekly' => 'addWeeks',
'week' => 'addWeeks',
'month' => 'addMonths',
'monthly' => 'addMonths',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYears',
'yearly' => 'addYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (!isset($functionMap[$repeatFreq])) {
throw new FireflyException('Cannot do addPeriod for $repeat_freq "' . $repeatFreq . '"');
}
if (isset($modifierMap[$repeatFreq])) {
$add = $add * $modifierMap[$repeatFreq];
}
$function = $functionMap[$repeatFreq];
$date->$function($add);
return $date;
}
/**
* @param Carbon $theCurrentEnd
* @param Carbon $theCurrentEnd
* @param $repeatFreq
*
* @return Carbon
@@ -68,78 +64,81 @@ class Date
public function endOfPeriod(Carbon $theCurrentEnd, $repeatFreq)
{
$currentEnd = clone $theCurrentEnd;
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->addDay();
break;
case 'week':
case 'weekly':
$currentEnd->addWeek()->subDay();
break;
case 'month':
case 'monthly':
$currentEnd->addMonth()->subDay();
break;
case 'quarter':
case 'quarterly':
$currentEnd->addMonths(3)->subDay();
break;
case 'half-year':
$currentEnd->addMonths(6)->subDay();
break;
case 'year':
case 'yearly':
$currentEnd->addYear()->subDay();
break;
$functionMap = [
'daily' => 'addDay',
'week' => 'addWeek',
'weekly' => 'addWeek',
'month' => 'addMonth',
'monthly' => 'addMonth',
'quarter' => 'addMonths',
'quarterly' => 'addMonths',
'half-year' => 'addMonths',
'year' => 'addYear',
'yearly' => 'addYear',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
$subDay = ['week', 'weekly', 'month', 'monthly', 'quarter', 'quarterly', 'half-year', 'year', 'yearly'];
if (!isset($functionMap[$repeatFreq])) {
throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq);
}
$function = $functionMap[$repeatFreq];
if (isset($modifierMap[$repeatFreq])) {
$currentEnd->$function($modifierMap[$repeatFreq]);
} else {
$currentEnd->$function();
}
if (in_array($repeatFreq, $subDay)) {
$currentEnd->subDay();
}
return $currentEnd;
}
/**
* @param Carbon $theCurrentEnd
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param Carbon $theCurrentEnd
* @param $repeatFreq
* @param Carbon $maxDate
* @param Carbon $maxDate
*
* @return Carbon
* @throws FireflyException
*/
public function endOfX(Carbon $theCurrentEnd, $repeatFreq, Carbon $maxDate)
{
$functionMap = [
'daily' => 'endOfDay',
'week' => 'endOfWeek',
'weekly' => 'endOfWeek',
'month' => 'endOfMonth',
'monthly' => 'endOfMonth',
'quarter' => 'lastOfQuarter',
'quarterly' => 'lastOfQuarter',
'year' => 'endOfYear',
'yearly' => 'endOfYear',
];
$specials = ['mont', 'monthly'];
$currentEnd = clone $theCurrentEnd;
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do endOfPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$currentEnd->endOfDay();
break;
case 'week':
case 'weekly':
$currentEnd->endOfWeek();
break;
case 'month':
case 'monthly':
$currentEnd->endOfMonth();
break;
case 'quarter':
case 'quarterly':
$currentEnd->lastOfQuarter();
break;
case 'half-year':
$month = intval($theCurrentEnd->format('m'));
$currentEnd->endOfYear();
if ($month <= 6) {
$currentEnd->subMonths(6);
}
break;
case 'year':
case 'yearly':
$currentEnd->endOfYear();
break;
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$currentEnd->$function();
}
if (isset($specials[$repeatFreq])) {
$month = intval($theCurrentEnd->format('m'));
$currentEnd->endOfYear();
if ($month <= 6) {
$currentEnd->subMonths(6);
}
}
if ($currentEnd > $maxDate) {
return clone $maxDate;
@@ -149,7 +148,7 @@ class Date
}
/**
* @param Carbon $date
* @param Carbon $date
* @param $repeatFrequency
*
* @return string
@@ -157,33 +156,25 @@ class Date
*/
public function periodShow(Carbon $date, $repeatFrequency)
{
switch ($repeatFrequency) {
default:
throw new FireflyException('No date formats for frequency "' . $repeatFrequency . '"!');
break;
case 'daily':
return $date->format('j F Y');
break;
case 'week':
case 'weekly':
return $date->format('\W\e\e\k W, Y');
break;
case 'quarter':
return $date->format('F Y');
break;
case 'monthly':
case 'month':
return $date->format('F Y');
break;
case 'year':
case 'yearly':
return $date->format('Y');
break;
$formatMap = [
'daily' => 'j F Y',
'week' => '\W\e\e\k W, Y',
'weekly' => '\W\e\e\k W, Y',
'quarter' => 'F Y',
'month' => 'F Y',
'monthly' => 'F Y',
'year' => 'Y',
'yearly' => 'Y',
];
if (isset($formatMap[$repeatFrequency])) {
return $date->format($formatMap[$repeatFrequency]);
}
throw new FireflyException('No date formats for frequency "' . $repeatFrequency . '"!');
}
/**
* @param Carbon $theDate
* @param Carbon $theDate
* @param $repeatFreq
*
* @return Carbon
@@ -192,43 +183,38 @@ class Date
public function startOfPeriod(Carbon $theDate, $repeatFreq)
{
$date = clone $theDate;
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'daily':
$date->startOfDay();
break;
case 'week':
case 'weekly':
$date->startOfWeek();
break;
case 'month':
case 'monthly':
$date->startOfMonth();
break;
case 'quarter':
case 'quarterly':
$date->firstOfQuarter();
break;
case 'half-year':
$month = intval($date->format('m'));
$date->startOfYear();
if ($month >= 7) {
$date->addMonths(6);
}
break;
case 'year':
case 'yearly':
$date->startOfYear();
break;
}
return $date;
$functionMap = [
'daily' => 'startOfDay',
'week' => 'startOfWeek',
'weekly' => 'startOfWeek',
'month' => 'startOfMonth',
'monthly' => 'startOfMonth',
'quarter' => 'firstOfQuarter',
'quartly' => 'firstOfQuarter',
'year' => 'startOfYear',
'yearly' => 'startOfYear',
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function();
return $date;
}
if ($repeatFreq == 'half-year') {
$month = intval($date->format('m'));
$date->startOfYear();
if ($month >= 7) {
$date->addMonths(6);
}
return $date;
}
throw new FireflyException('Cannot do startOfPeriod for $repeat_freq ' . $repeatFreq);
}
/**
* @param Carbon $theDate
* @param Carbon $theDate
* @param $repeatFreq
* @param int $subtract
*
@@ -238,38 +224,35 @@ class Date
public function subtractPeriod(Carbon $theDate, $repeatFreq, $subtract = 1)
{
$date = clone $theDate;
switch ($repeatFreq) {
default:
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
break;
case 'day':
case 'daily':
$date->subDays($subtract);
break;
case 'week':
case 'weekly':
$date->subWeeks($subtract);
break;
case 'month':
case 'monthly':
$date->subMonths($subtract);
break;
case 'quarter':
case 'quarterly':
$months = $subtract * 3;
$date->subMonths($months);
break;
case 'half-year':
$months = $subtract * 6;
$date->subMonths($months);
break;
case 'year':
case 'yearly':
$date->subYears($subtract);
break;
$functionMap = [
'daily' => 'subDays',
'week' => 'subWeeks',
'weekly' => 'subWeeks',
'month' => 'subMonths',
'monthly' => 'subMonths',
'year' => 'subYears',
'yearly' => 'subYears',
];
$modifierMap = [
'quarter' => 3,
'quarterly' => 3,
'half-year' => 6,
];
if (isset($functionMap[$repeatFreq])) {
$function = $functionMap[$repeatFreq];
$date->$function($subtract);
return $date;
}
if (isset($modifierMap[$repeatFreq])) {
$subtract = $subtract * $modifierMap[$repeatFreq];
$date->subMonths($subtract);
return $date;
}
return $date;
throw new FireflyException('Cannot do subtractPeriod for $repeat_freq ' . $repeatFreq);
}
}

View File

@@ -69,36 +69,29 @@ class Filter
*/
protected function updateStartDate($range, Carbon $start)
{
switch ($range) {
default:
throw new FireflyException('updateStartDate cannot handle $range ' . $range);
break;
case '1D':
$start->startOfDay();
break;
case '1W':
$start->startOfWeek();
break;
case '1M':
$start->startOfMonth();
break;
case '3M':
$start->firstOfQuarter();
break;
case '6M':
if (intval($start->format('m')) >= 7) {
$start->startOfYear()->addMonths(6);
} else {
$start->startOfYear();
}
break;
case '1Y':
$start->startOfYear();
break;
$functionMap = [
'1D' => 'startOfDay',
'1W' => 'startOfWeek',
'1M' => 'startOfMonth',
'3M' => 'firstOfQuarter',
'1Y' => 'startOfYear',
];
if (isset($functionMap[$range])) {
$function = $functionMap[$range];
$start->$function();
return $start;
}
if ($range == '6M') {
if (intval($start->format('m')) >= 7) {
$start->startOfYear()->addMonths(6);
} else {
$start->startOfYear();
}
return $start;
return $start;
}
throw new FireflyException('updateStartDate cannot handle $range ' . $range);
}
/**
@@ -110,40 +103,36 @@ class Filter
*/
protected function updateEndDate($range, Carbon $start)
{
$end = clone $start;
switch ($range) {
default:
throw new FireflyException('updateEndDate cannot handle $range ' . $range);
break;
case '1D':
$end->endOfDay();
break;
case '1W':
$end->endOfWeek();
break;
case '1M':
$end->endOfMonth();
break;
case '3M':
$end->lastOfQuarter();
break;
case '6M':
if (intval($start->format('m')) >= 7) {
$end->endOfYear();
} else {
$end->startOfYear()->addMonths(6);
}
break;
case '1Y':
$end->endOfYear();
break;
$functionMap = [
'1D' => 'endOfDay',
'1W' => 'endOfWeek',
'1M' => 'endOfMonth',
'3M' => 'lastOfQuarter',
'1Y' => 'endOfYear',
];
$end = clone $start;
if (isset($functionMap[$range])) {
$function = $functionMap[$range];
$end->$function();
return $end;
}
if ($range == '6M') {
if (intval($start->format('m')) >= 7) {
$end->endOfYear();
} else {
$end->startOfYear()->addMonths(6);
}
return $end;
return $end;
}
throw new FireflyException('updateEndDate cannot handle $range ' . $range);
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param $range
* @param Carbon $date
*
@@ -152,37 +141,28 @@ class Filter
*/
protected function periodName($range, Carbon $date)
{
switch ($range) {
default:
throw new FireflyException('No _periodName() for range "' . $range . '"');
break;
case '1D':
return $date->format('jS F Y');
break;
case '1W':
return 'week ' . $date->format('W, Y');
break;
case '1M':
return $date->format('F Y');
break;
case '3M':
$month = intval($date->format('m'));
return 'Q' . ceil(($month / 12) * 4) . ' ' . $date->format('Y');
break;
case '6M':
$month = intval($date->format('m'));
$half = ceil(($month / 12) * 2);
$halfName = $half == 1 ? 'first' : 'second';
return $halfName . ' half of ' . $date->format('d-m-Y');
break;
case '1Y':
return $date->format('Y');
break;
$formatMap = [
'1D' => 'jS F Y',
'1W' => '\w\e\ek W, Y',
'1M' => 'F Y',
'1Y' => 'Y',
];
if (isset($formatMap[$range])) {
return $date->format($formatMap[$range]);
}
if ($range == '3M') {
$month = intval($date->format('m'));
return 'Q' . ceil(($month / 12) * 4) . ' ' . $date->format('Y');
}
if ($range == '6M') {
$month = intval($date->format('m'));
$half = ceil(($month / 12) * 2);
$halfName = $half == 1 ? 'first' : 'second';
return $halfName . ' half of ' . $date->format('d-m-Y');
}
throw new FireflyException('No _periodName() for range "' . $range . '"');
}
/**
@@ -194,37 +174,35 @@ class Filter
*/
public function previous($range, Carbon $date)
{
switch ($range) {
default:
throw new FireflyException('Cannot do _previous() on ' . $range);
break;
case '1D':
$date->startOfDay()->subDay();
break;
case '1W':
$date->startOfWeek()->subWeek();
break;
case '1M':
$date->startOfMonth()->subMonth();
break;
case '3M':
$date->firstOfQuarter()->subMonths(3)->firstOfQuarter();
break;
case '6M':
$month = intval($date->format('m'));
if ($month <= 6) {
$date->startOfYear()->subMonths(6);
} else {
$date->startOfYear();
}
break;
case '1Y':
$date->startOfYear()->subYear();
break;
$functionMap = [
'1D' => 'Day',
'1W' => 'Week',
'1M' => 'Month',
'1Y' => 'Year'
];
if (isset($functionMap[$range])) {
$startFunction = 'startOf' . $functionMap[$range];
$subFunction = 'sub' . $functionMap[$range];
$date->$startFunction()->$subFunction();
return $date;
}
if ($range == '3M') {
$date->firstOfQuarter()->subMonths(3)->firstOfQuarter();
return $date;
return $date;
}
if ($range == '6M') {
$month = intval($date->format('m'));
$date->startOfYear();
if ($month <= 6) {
$date->subMonths(6);
}
return $date;
}
throw new FireflyException('Cannot do _previous() on ' . $range);
}
/**

View File

@@ -12,6 +12,8 @@ use Illuminate\Support\Collection;
class Form
{
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* Takes any collection and tries to make a sensible select list compatible array of it.
*
* @param Collection $set
@@ -32,7 +34,7 @@ class Form
$title = null;
foreach ($fields as $field) {
if (is_null($title) && isset($entry->$field)) {
if (isset($entry->$field)) {
$title = $entry->$field;
}
}

View File

@@ -15,6 +15,8 @@ class Reminders
{
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*
* @param \Reminder $reminder
*
* @return int
@@ -62,6 +64,9 @@ class Reminders
return $reminders;
}
/**
* @SuppressWarnings("CyclomaticComplexity") // It's exactly 5. So I don't mind.
*/
public function updateReminders()
{
/** @var Collection $set */

View File

@@ -35,6 +35,7 @@ class Steam
}
/**
* @codeCoverageIgnore
* @param $boolean
*
* @return string
@@ -61,12 +62,18 @@ class Steam
{
$pct = $repetition->currentamount / $piggyBank->targetamount * 100;
if ($pct > 100) {
// @codeCoverageIgnoreStart
return 100;
// @codeCoverageIgnoreEnd
} else {
return floor($pct);
}
}
/**
* @codeCoverageIgnore
* @throws \Exception
*/
public function removeEmptyBudgetLimits()
{
$user = \Auth::user();

View File

@@ -11,6 +11,8 @@ use Illuminate\Validation\Validator;
class FireflyValidator extends Validator
{
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param $attribute
* @param $value
* @param $parameters

View File

@@ -1,10 +1,10 @@
<?php
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Watson\Validating\ValidatingTrait;
use \Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\Eloquent\SoftDeletingTrait;
use Illuminate\Database\Query\JoinClause;
use Watson\Validating\ValidatingTrait;
/**
* Class Account
@@ -18,15 +18,15 @@ class Account extends Eloquent
* @var array
*/
public static $rules
= [
= [
'name' => ['required', 'between:1,100'],
'user_id' => 'required|exists:users,id',
'account_type_id' => 'required|exists:account_types,id',
'active' => 'required|boolean'
];
protected $dates = ['deleted_at', 'created_at', 'updated_at'];
protected $fillable = ['name', 'user_id', 'account_type_id', 'active'];
protected $dates = ['deleted_at', 'created_at', 'updated_at'];
protected $fillable = ['name', 'user_id', 'account_type_id', 'active'];
/**
* Account type.
@@ -67,7 +67,7 @@ class Account extends Eloquent
/**
*
* @param EloquentBuilder $query
* @param array $types
* @param array $types
*/
public function scopeAccountTypeIn(EloquentBuilder $query, array $types)
{
@@ -82,9 +82,13 @@ class Account extends Eloquent
*
* @param EloquentBuilder $query
*/
public function scopeWithMeta(EloquentBuilder $query)
public function scopeWithMeta(EloquentBuilder $query, $field = 'accountRole')
{
$query->with(['accountmeta']);
$query->leftJoin(
'account_meta', function (JoinClause $join) use ($field) {
$join->on('account_meta.account_id', '=', 'accounts.id')->where('account_meta.name', '=', $field);
}
);
}
/**

View File

@@ -1,5 +1,4 @@
<?php
use Carbon\Carbon;
use Watson\Validating\ValidatingTrait;
use \Illuminate\Database\Eloquent\Model as Eloquent;
/**

View File

@@ -1,8 +1,5 @@
<?php
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Model as Eloquent;
use Illuminate\Database\QueryException;
use Watson\Validating\ValidatingTrait;
/**

View File

@@ -1,6 +1,5 @@
<?php
use FireflyIII\Exception\FireflyException;
use Watson\Validating\ValidatingTrait;
use \Illuminate\Database\Eloquent\Model as Eloquent;
/**

View File

@@ -20,7 +20,7 @@ class TransactionJournal extends Eloquent
protected $rules
= ['transaction_type_id' => 'required|exists:transaction_types,id',
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
'description' => 'required|between:1,255',
'description' => 'required|between:1,1024',
'date' => 'required|date',
'completed' => 'required|between:0,1'];
@@ -82,6 +82,15 @@ class TransactionJournal extends Eloquent
return ['created_at', 'updated_at', 'date'];
}
public function getDescriptionAttribute($value)
{
if ($this->encrypted) {
return Crypt::decrypt($value);
}
return $value;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
@@ -198,6 +207,12 @@ class TransactionJournal extends Eloquent
);
}
public function setDescriptionAttribute($value)
{
$this->attributes['description'] = Crypt::encrypt($value);
$this->attributes['encrypted'] = true;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/

View File

@@ -204,7 +204,7 @@ Route::group(
Route::get('/categories/edit/{category}', ['uses' => 'CategoryController@edit', 'as' => 'categories.edit']);
Route::get('/categories/delete/{category}', ['uses' => 'CategoryController@delete', 'as' => 'categories.delete']);
Route::get('/categories/show/{category}', ['uses' => 'CategoryController@show', 'as' => 'categories.show']);
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noBudget']);
Route::get('/categories/list/noCategory', ['uses' => 'CategoryController@noCategory', 'as' => 'categories.noCategory']);
// currency controller
Route::get('/currency', ['uses' => 'CurrencyController@index', 'as' => 'currency.index']);
@@ -222,11 +222,13 @@ Route::group(
Route::get('/chart/reports/income-expenses/{year}', ['uses' => 'GoogleChartController@yearInExp']);
Route::get('/chart/reports/income-expenses-sum/{year}', ['uses' => 'GoogleChartController@yearInExpSum']);
Route::get('/chart/bills/{bill}', ['uses' => 'GoogleChartController@billOverview']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/piggy_history/{piggyBank}', ['uses' => 'GoogleChartController@piggyBankHistory']);
// google chart for components (categories + budgets combined)
Route::get('/chart/budget/{budget}/spending/{year}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budget/{budget}/spending/{year?}', ['uses' => 'GoogleChartController@budgetsAndSpending']);
Route::get('/chart/budgets/spending/{year?}', ['uses' => 'GoogleChartController@allBudgetsAndSpending']);
Route::get('/chart/budget/{budget}/{limitrepetition}', ['uses' => 'GoogleChartController@budgetLimitSpending']);
Route::get('/chart/category/{category}/spending/{year}', ['uses' => 'GoogleChartController@categoriesAndSpending']);
// help controller

View File

@@ -9,7 +9,9 @@ use League\FactoryMuffin\Facade as f;
class TestCase extends Illuminate\Foundation\Testing\TestCase
{
/**
* Creates the application.
* @SuppressWarnings(PHPMD.UnusedLocalVariable)
*
* Creates the application..
*
* @return \Symfony\Component\HttpKernel\HttpKernelInterface
*/

View File

@@ -15,8 +15,12 @@ League\FactoryMuffin\Facade::define(
return $set[rand(0, count($set) - 1)];
},
'rep_every' => function() {return rand(0,3);},
'rep_times' => function() {return rand(0,3);},
'rep_every' => function () {
return rand(0, 3);
},
'rep_times' => function () {
return rand(0, 3);
},
'reminder' => function () {
$set = ['day', 'week', 'quarter', 'month', 'year'];

View File

@@ -5,8 +5,8 @@ League\FactoryMuffin\Facade::define(
'account_id' => 'factory|Account',
'transaction_journal_id' => 'factory|TransactionJournal',
'description' => 'sentence',
'amount' => function() {
return round(rand(100,10000) / 100,2);
'amount' => function () {
return round(rand(100, 10000) / 100, 2);
}
]
);

View File

@@ -29,9 +29,9 @@
</div>
<div class="panel-body">
@if($what == 'asset')
{{Form::ffBalance('openingbalance')}}
{{Form::ffDate('openingbalancedate', date('Y-m-d'))}}
@endif
{{Form::ffBalance('openingBalance')}}
{{Form::ffDate('openingBalanceDate', date('Y-m-d'))}}
@endif
{{Form::ffCheckbox('active','1',true)}}
{{Form::ffSelect('account_role',Config::get('firefly.accountRoles'))}}
</div>

View File

@@ -26,7 +26,7 @@
<div class="panel-body">
{{Form::ffCheckbox('active','1')}}
@if($account->accounttype->type == 'Default account' || $account->accounttype->type == 'Asset account')
{{Form::ffBalance('openingBalance')}}
{{Form::ffBalance('openingBalance',null, ['currency' => $openingBalance->transactionCurrency])}}
{{Form::ffDate('openingBalanceDate')}}
{{Form::ffSelect('account_role',Config::get('firefly.accountRoles'))}}
@endif

View File

@@ -6,6 +6,21 @@
<div class="panel panel-default">
<div class="panel-heading">
<i class="fa fa-fw {{$subTitleIcon}} fa-fw"></i> {{{$account->name}}}
<!-- ACTIONS MENU -->
<div class="pull-right">
<div class="btn-group">
<button type="button" class="btn btn-default btn-xs dropdown-toggle" data-toggle="dropdown">
Actions
<span class="caret"></span>
</button>
<ul class="dropdown-menu pull-right" role="menu">
<li><a href="{{route('accounts.edit',$account->id)}}"><i class="fa fa-pencil fa-fw"></i> Edit</a></li>
<li><a href="{{route('accounts.delete',$account->id)}}"><i class="fa fa-trash fa-fw"></i> Delete</a></li>
</ul>
</div>
</div>
</div>
<div class="panel-body">
<div id="overview-chart"></div>

View File

@@ -8,7 +8,7 @@
Overview
</div>
<div class="panel-body">
<div id="componentOverview"></div>
<div id="budgetOverview"></div>
</div>
</div>
@@ -71,7 +71,8 @@
@stop
@section('scripts')
<script type="text/javascript">
var componentID = {{$budget->id}};
var budgetID = {{$budget->id}};
var currencyCode = '{{Amount::getCurrencyCode()}}';
@if(!is_null($repetition))
var repetitionID = {{$repetition->id}};
var year = {{$repetition->startdate->format('Y')}};

View File

@@ -43,25 +43,11 @@
<li>Set the default currency display;</li>
<li>Set the default currency for new transactions;</li>
<li>Add, modify and remove supported currencies.</li>
<li>Display the actual currency of a transaction</li>
<li>Update a transaction's currency.</li>
</ul>
</div>
</div>
<div class="panel panel-red">
<div class="panel-heading">
Not supported yet
</div>
<div class="panel-body">
<ul>
<li>Display the actual currency of a transaction<br />
<small>See the help-page.</small></li>
<li>
Update a transaction's currency.
</li>
</ul>
</div>
</div>
</div>
</div>
@stop

View File

@@ -0,0 +1,20 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
<div class="input-group">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle amountCurrencyDropdown" data-toggle="dropdown" aria-expanded="false">
<span id="amountCurrentSymbol">{{$defaultCurrency->symbol}}</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
@foreach($currencies as $currency)
<li><a href="#" class="currencySelect" data-id="{{{$currency->id}}}" data-field="amount" data-currency="{{{$currency->code}}}" data-symbol="{{{$currency->symbol}}}">{{{$currency->name}}}</a></li>
@endforeach
</ul>
</div>
{{Form::input('number', $name, $value, $options)}}
{{Form::input('hidden','amount_currency_id',$defaultCurrency->id)}}
@include('form.feedback')
</div>
</div>
</div>

View File

@@ -0,0 +1,20 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
<div class="input-group">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle balanceCurrencyDropdown" data-toggle="dropdown" aria-expanded="false">
<span id="balanceCurrentSymbol">{{$defaultCurrency->symbol}}</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
@foreach($currencies as $currency)
<li><a href="#" class="currencySelect" data-id="{{{$currency->id}}}" data-field="balance" data-currency="{{{$currency->code}}}" data-symbol="{{{$currency->symbol}}}">{{{$currency->name}}}</a></li>
@endforeach
</ul>
</div>
{{Form::input('number', $name, $value, $options)}}
{{Form::input('hidden','balance_currency_id',$defaultCurrency->id)}}
@include('form.feedback')
</div>
</div>
</div>

View File

@@ -0,0 +1,11 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
<div class="checkbox">
<label>
{{Form::checkbox($name, $value, $options['checked'], $options)}}
</label>
</div>
@include('form.feedback')
</div>
</div>

View File

@@ -0,0 +1,7 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
{{Form::input('date', $name, $value, $options)}}
@include('form.feedback')
</div>
</div>

View File

@@ -0,0 +1,12 @@
@if(Session::has('errors') && Session::get('errors')->has($name))
<span class="glyphicon glyphicon-remove form-control-feedback"></span>
<p class="text-danger">{{{Session::get('errors')->first($name)}}}</p>
@endif
@if(Session::has('warnings') && Session::get('warnings')->has($name))
<span class="glyphicon glyphicon-warning-sign form-control-feedback"></span>
<p class="text-warning">{{{Session::get('warnings')->first($name)}}}</p>
@endif
@if(Session::has('successes') && Session::get('successes')->has($name))
<span class="glyphicon glyphicon-ok form-control-feedback"></span>
<p class="text-success">{{{Session::get('successes')->first($name)}}}</p>
@endif

View File

@@ -0,0 +1,9 @@
<div class="{{{$classes}}}">
<label for="{{{$options['id']}}}" class="col-sm-4 control-label">{{{$label}}}</label>
<div class="col-sm-8">
<div class="input-group">
{{Form::input('number', $name, $value, $options)}}
@include('form.feedback')
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More