diff --git a/app/Http/Controllers/Json/AutoCompleteController.php b/app/Http/Controllers/Json/AutoCompleteController.php
index 820d4ed711..4a6a204d78 100644
--- a/app/Http/Controllers/Json/AutoCompleteController.php
+++ b/app/Http/Controllers/Json/AutoCompleteController.php
@@ -27,6 +27,7 @@ use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
+use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
@@ -35,6 +36,7 @@ use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
use FireflyIII\Repositories\Category\CategoryRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
+use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
@@ -342,6 +344,34 @@ class AutoCompleteController extends Controller
return response()->json($return);
}
+ /**
+ * An auto-complete specifically for expense accounts, used when mass updating mostly.
+ *
+ * @param Request $request
+ *
+ * @return JsonResponse
+ */
+ public function objectGroups(Request $request): JsonResponse
+ {
+ $search = $request->get('search');
+
+ /** @var ObjectGroupRepositoryInterface $repository */
+ $repository = app(ObjectGroupRepositoryInterface::class);
+
+ $return = [];
+ $result = $repository->search((string) $search);
+
+ /** @var ObjectGroup $account */
+ foreach ($result as $objectGroup) {
+ $return[] = [
+ 'id' => $objectGroup->id,
+ 'title' => $objectGroup->title,
+ ];
+ }
+
+ return response()->json($return);
+ }
+
/**
* @return JsonResponse
* @codeCoverageIgnore
diff --git a/app/Http/Requests/PiggyBankFormRequest.php b/app/Http/Requests/PiggyBankFormRequest.php
index d1eabc9754..afe336b96b 100644
--- a/app/Http/Requests/PiggyBankFormRequest.php
+++ b/app/Http/Requests/PiggyBankFormRequest.php
@@ -54,6 +54,7 @@ class PiggyBankFormRequest extends Request
'targetamount' => $this->string('targetamount'),
'targetdate' => $this->date('targetdate'),
'notes' => $this->nlString('notes'),
+ 'object_group' => $this->string('object_group'),
];
}
diff --git a/app/Models/ObjectGroup.php b/app/Models/ObjectGroup.php
index b88f396b5d..7e6e03cb26 100644
--- a/app/Models/ObjectGroup.php
+++ b/app/Models/ObjectGroup.php
@@ -10,14 +10,27 @@ use Illuminate\Database\Eloquent\Model;
* Class ObjectGroup
*
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\PiggyBank[] $piggyBanks
- * @property-read int|null $piggy_banks_count
+ * @property-read int|null $piggy_banks_count
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup query()
* @mixin \Eloquent
+ * @property int $id
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property string|null $deleted_at
+ * @property string $title
+ * @property int $order
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereDeletedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereOrder($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereTitle($value)
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\Models\ObjectGroup whereUpdatedAt($value)
*/
class ObjectGroup extends Model
{
+ protected $fillable = ['title', 'order'];
/**
* @return \Illuminate\Database\Eloquent\Relations\MorphToMany
*/
diff --git a/app/Models/PiggyBank.php b/app/Models/PiggyBank.php
index 03cfc64384..8417638efc 100644
--- a/app/Models/PiggyBank.php
+++ b/app/Models/PiggyBank.php
@@ -78,9 +78,9 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property-read int|null $notes_count
* @property-read int|null $piggy_bank_events_count
* @property-read int|null $piggy_bank_repetitions_count
- * @property bool $encrypted
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\ObjectGroup[] $objectGroups
* @property-read int|null $object_groups_count
+ * @property bool $encrypted
*/
class PiggyBank extends Model
{
diff --git a/app/Providers/FireflyServiceProvider.php b/app/Providers/FireflyServiceProvider.php
index e489379798..66a427222a 100644
--- a/app/Providers/FireflyServiceProvider.php
+++ b/app/Providers/FireflyServiceProvider.php
@@ -37,6 +37,8 @@ use FireflyIII\Helpers\Report\PopupReport;
use FireflyIII\Helpers\Report\PopupReportInterface;
use FireflyIII\Helpers\Report\ReportHelper;
use FireflyIII\Helpers\Report\ReportHelperInterface;
+use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepository;
+use FireflyIII\Repositories\ObjectGroup\ObjectGroupRepositoryInterface;
use FireflyIII\Repositories\Telemetry\TelemetryRepository;
use FireflyIII\Repositories\Telemetry\TelemetryRepositoryInterface;
use FireflyIII\Repositories\TransactionType\TransactionTypeRepository;
@@ -172,6 +174,7 @@ class FireflyServiceProvider extends ServiceProvider
// other generators
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(TransactionTypeRepositoryInterface::class, TransactionTypeRepository::class);
+ $this->app->bind(ObjectGroupRepositoryInterface::class,ObjectGroupRepository::class);
$this->app->bind(AttachmentHelperInterface::class, AttachmentHelper::class);
// more generators:
diff --git a/app/Repositories/ObjectGroup/CreatesObjectGroups.php b/app/Repositories/ObjectGroup/CreatesObjectGroups.php
new file mode 100644
index 0000000000..24f01ed050
--- /dev/null
+++ b/app/Repositories/ObjectGroup/CreatesObjectGroups.php
@@ -0,0 +1,61 @@
+first();
+ }
+
+ /**
+ * @param string $title
+ *
+ * @return ObjectGroup|null
+ */
+ protected function findOrCreateObjectGroup(string $title): ?ObjectGroup
+ {
+ $group = null;
+ $maxOrder = $this->getObjectGroupMaxOrder();
+ if (!$this->hasObjectGroup($title)) {
+ return ObjectGroup::create(
+ [
+ 'title' => $title,
+ 'order' => $maxOrder + 1,
+ ]
+ );
+ }
+
+ return $this->findObjectGroup($title);
+ }
+
+ /**
+ * @return int
+ */
+ protected function getObjectGroupMaxOrder(): int
+ {
+ return ObjectGroup::max('order');
+ }
+
+ /**
+ * @param string $title
+ *
+ * @return bool
+ */
+ protected function hasObjectGroup(string $title): bool
+ {
+ return 1 === ObjectGroup::where('title', $title)->count();
+ }
+}
diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepository.php b/app/Repositories/ObjectGroup/ObjectGroupRepository.php
new file mode 100644
index 0000000000..dd33a81ca7
--- /dev/null
+++ b/app/Repositories/ObjectGroup/ObjectGroupRepository.php
@@ -0,0 +1,44 @@
+get();
+ }
+
+ /**
+ * @param string $query
+ *
+ * @return Collection
+ */
+ public function search(string $query): Collection
+ {
+ $dbQuery = ObjectGroup::orderBy('order');
+ if ('' !== $query) {
+ // split query on spaces just in case:
+ $parts = explode(' ', $query);
+ foreach ($parts as $part) {
+ $search = sprintf('%%%s%%', $part);
+ $dbQuery->where('title', 'LIKE', $search);
+ }
+
+ }
+
+ return $dbQuery->get(['object_groups.*']);
+ }
+}
diff --git a/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php
new file mode 100644
index 0000000000..110c35d359
--- /dev/null
+++ b/app/Repositories/ObjectGroup/ObjectGroupRepositoryInterface.php
@@ -0,0 +1,25 @@
+save();
}
+ $objectGroupTitle = $data['object_group'] ?? '';
+ if ('' !== $objectGroupTitle) {
+ $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
+ if (null !== $objectGroup) {
+ $piggyBank->objectGroups()->sync([$objectGroup->id]);
+ $piggyBank->save();
+ }
+ }
+
return $piggyBank;
}
@@ -313,6 +323,15 @@ trait ModifiesPiggyBanks
$repetition->save();
}
+ $objectGroupTitle = $data['object_group'] ?? '';
+ if ('' !== $objectGroupTitle) {
+ $objectGroup = $this->findOrCreateObjectGroup($objectGroupTitle);
+ if (null !== $objectGroup) {
+ $piggyBank->objectGroups()->sync([$objectGroup->id]);
+ $piggyBank->save();
+ }
+ }
+
return $piggyBank;
}
diff --git a/app/Repositories/PiggyBank/PiggyBankRepository.php b/app/Repositories/PiggyBank/PiggyBankRepository.php
index 3be16b5c9e..ae93017271 100644
--- a/app/Repositories/PiggyBank/PiggyBankRepository.php
+++ b/app/Repositories/PiggyBank/PiggyBankRepository.php
@@ -265,7 +265,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
*/
public function getPiggyBanks(): Collection
{
- return $this->user->piggyBanks()->with(['account'])->orderBy('order', 'ASC')->get();
+ return $this->user->piggyBanks()->with(['account', 'objectGroups'])->orderBy('order', 'ASC')->get();
}
diff --git a/app/Transformers/PiggyBankTransformer.php b/app/Transformers/PiggyBankTransformer.php
index 15aa4f6fe6..97d9c38dc2 100644
--- a/app/Transformers/PiggyBankTransformer.php
+++ b/app/Transformers/PiggyBankTransformer.php
@@ -25,6 +25,7 @@ namespace FireflyIII\Transformers;
use FireflyIII\Models\Account;
+use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
@@ -83,6 +84,17 @@ class PiggyBankTransformer extends AbstractTransformer
$notes = $this->piggyRepos->getNoteText($piggyBank);
$notes = '' === $notes ? null : $notes;
+ $objectGroupId = null;
+ $objectGroupOrder = 0;
+ $objectGroupTitle = null;
+ /** @var ObjectGroup $objectGroup */
+ $objectGroup = $piggyBank->objectGroups->first();
+ if (null !== $objectGroup) {
+ $objectGroupId = (int) $objectGroup->id;
+ $objectGroupOrder = (int) $objectGroup->order;
+ $objectGroupTitle = $objectGroup->title;
+ }
+
// get currently saved amount:
$currentAmountStr = $this->piggyRepos->getCurrentAmount($piggyBank);
$currentAmount = round($currentAmountStr, $currency->decimal_places);
@@ -114,9 +126,12 @@ class PiggyBankTransformer extends AbstractTransformer
'save_per_month' => round($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places),
'start_date' => $startDate,
'target_date' => $targetDate,
- 'order' => (int)$piggyBank->order,
+ 'order' => (int) $piggyBank->order,
'active' => true,
'notes' => $notes,
+ 'object_group_id' => $objectGroupId,
+ 'object_group_order' => $objectGroupOrder,
+ 'object_group_title' => $objectGroupTitle,
'links' => [
[
'rel' => 'self',
diff --git a/app/User.php b/app/User.php
index 4a2f39b2b7..ee262ecccf 100644
--- a/app/User.php
+++ b/app/User.php
@@ -139,6 +139,8 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property bool $blocked
* @property string|null $blocked_code
* @property-read \Illuminate\Database\Eloquent\Collection|\FireflyIII\Models\Role[] $roles
+ * @property string|null $provider
+ * @method static \Illuminate\Database\Eloquent\Builder|\FireflyIII\User whereProvider($value)
*/
class User extends Authenticatable
{
diff --git a/database/migrations/2020_06_07_063612_changes_for_v530.php b/database/migrations/2020_06_07_063612_changes_for_v530.php
new file mode 100644
index 0000000000..fc7d256109
--- /dev/null
+++ b/database/migrations/2020_06_07_063612_changes_for_v530.php
@@ -0,0 +1,52 @@
+increments('id');
+ $table->timestamps();
+ $table->softDeletes();
+ $table->string('title', 255);
+ $table->mediumInteger('order', false, true)->default(0);
+ }
+ );
+ }
+
+ if (!Schema::hasTable('object_groupables')) {
+ Schema::create(
+ 'object_groupables', static function (Blueprint $table) {
+ $table->integer('object_group_id');
+ $table->integer('object_groupable_id', false, true);
+ $table->string('object_groupable_type', 255);
+ }
+ );
+ }
+ }
+}
diff --git a/public/v1/js/ff/object-groups/create-edit.js b/public/v1/js/ff/object-groups/create-edit.js
new file mode 100644
index 0000000000..eaff3b35d3
--- /dev/null
+++ b/public/v1/js/ff/object-groups/create-edit.js
@@ -0,0 +1,52 @@
+/*
+ * edit.js
+ * Copyright (c) 2019 james@firefly-iii.org
+ *
+ * This file is part of Firefly III (https://github.com/firefly-iii).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see
- {{ piggy.name }} - {% if piggy.attachments.count > 0 %} - - {% endif %} - | -- {{ formatAmountBySymbol(piggy.current_amount,piggy.currency_symbol,piggy.currency_decimal_places) }} - | - - - - - - - - - + {% for objectGroupOrder, objectGroup in piggyBanks %} +
{{ objectGroup.object_group_title }} | |
+ {{ piggy.name }} + {% if piggy.attachments.count > 0 %} + + {% endif %} + + | ++ {{ formatAmountBySymbol(piggy.current_amount,piggy.currency_symbol,piggy.currency_decimal_places) }} + | + + + + + + + + + +