mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-09-25 14:58:40 +00:00
Code for #3052
This commit is contained in:
@@ -25,7 +25,9 @@ namespace FireflyIII\Http\Controllers\Transaction;
|
|||||||
|
|
||||||
|
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Services\Internal\Update\GroupCloneService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class CreateController
|
* Class CreateController
|
||||||
@@ -34,6 +36,7 @@ class CreateController extends Controller
|
|||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* CreateController constructor.
|
* CreateController constructor.
|
||||||
|
*
|
||||||
* @codeCoverageIgnore
|
* @codeCoverageIgnore
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
@@ -55,6 +58,21 @@ class CreateController extends Controller
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionGroup $group
|
||||||
|
*
|
||||||
|
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
|
||||||
|
*/
|
||||||
|
public function cloneGroup(TransactionGroup $group)
|
||||||
|
{
|
||||||
|
|
||||||
|
/** @var GroupCloneService $service */
|
||||||
|
$service = app(GroupCloneService::class);
|
||||||
|
$newGroup = $service->cloneGroup($group);
|
||||||
|
|
||||||
|
return redirect(route('transactions.show', [$newGroup->id]));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new transaction group.
|
* Create a new transaction group.
|
||||||
*
|
*
|
||||||
|
146
app/Services/Internal/Update/GroupCloneService.php
Normal file
146
app/Services/Internal/Update/GroupCloneService.php
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GroupCloneService.php
|
||||||
|
* Copyright (c) 2019 thegrumpydictator@gmail.com
|
||||||
|
*
|
||||||
|
* 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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Services\Internal\Update;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Models\Budget;
|
||||||
|
use FireflyIII\Models\Category;
|
||||||
|
use FireflyIII\Models\Note;
|
||||||
|
use FireflyIII\Models\Tag;
|
||||||
|
use FireflyIII\Models\Transaction;
|
||||||
|
use FireflyIII\Models\TransactionGroup;
|
||||||
|
use FireflyIII\Models\TransactionJournal;
|
||||||
|
use FireflyIII\Models\TransactionJournalMeta;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class GroupCloneService
|
||||||
|
* TODO test.
|
||||||
|
*/
|
||||||
|
class GroupCloneService
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param TransactionGroup $group
|
||||||
|
*
|
||||||
|
* @return TransactionGroup
|
||||||
|
*/
|
||||||
|
public function cloneGroup(TransactionGroup $group): TransactionGroup
|
||||||
|
{
|
||||||
|
$newGroup = $group->replicate();
|
||||||
|
$newGroup->save();
|
||||||
|
foreach ($group->transactionJournals as $journal) {
|
||||||
|
$this->cloneJournal($journal, $newGroup, (int)$group->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return $newGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournal $journal
|
||||||
|
* @param TransactionGroup $newGroup
|
||||||
|
* @param int $originalGroup
|
||||||
|
*/
|
||||||
|
private function cloneJournal(TransactionJournal $journal, TransactionGroup $newGroup, int $originalGroup): void
|
||||||
|
{
|
||||||
|
$newJournal = $journal->replicate();
|
||||||
|
$newJournal->transaction_group_id = $newGroup->id;
|
||||||
|
$newJournal->date = Carbon::now();
|
||||||
|
$newJournal->save();
|
||||||
|
|
||||||
|
foreach ($journal->transactions as $transaction) {
|
||||||
|
$this->cloneTransaction($transaction, $newJournal);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone notes
|
||||||
|
/** @var Note $note */
|
||||||
|
foreach ($journal->notes as $note) {
|
||||||
|
$this->cloneNote($note, $newJournal, $originalGroup);
|
||||||
|
}
|
||||||
|
// clone location (not yet available)
|
||||||
|
|
||||||
|
// clone meta
|
||||||
|
/** @var TransactionJournalMeta $meta */
|
||||||
|
foreach ($journal->transactionJournalMeta as $meta) {
|
||||||
|
$this->cloneMeta($meta, $newJournal);
|
||||||
|
}
|
||||||
|
// clone category
|
||||||
|
/** @var Category $category */
|
||||||
|
foreach ($journal->categories as $category) {
|
||||||
|
$newJournal->categories()->save($category);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone budget
|
||||||
|
/** @var Budget $budget */
|
||||||
|
foreach ($journal->budgets as $budget) {
|
||||||
|
$newJournal->budgets()->save($budget);
|
||||||
|
}
|
||||||
|
// clone links (ongoing).
|
||||||
|
|
||||||
|
// clone tags
|
||||||
|
/** @var Tag $tag */
|
||||||
|
foreach ($journal->tags as $tag) {
|
||||||
|
$newJournal->tags()->save($tag);
|
||||||
|
}
|
||||||
|
// add note saying "cloned".
|
||||||
|
|
||||||
|
// add relation.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param TransactionJournalMeta $meta
|
||||||
|
* @param TransactionJournal $newJournal
|
||||||
|
*/
|
||||||
|
private function cloneMeta(TransactionJournalMeta $meta, TransactionJournal $newJournal): void
|
||||||
|
{
|
||||||
|
$newMeta = $meta->replicate();
|
||||||
|
$newMeta->transaction_journal_id = $newJournal->id;
|
||||||
|
if ('recurrence_id' !== $newMeta->name) {
|
||||||
|
$newMeta->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function cloneNote(Note $note, TransactionJournal $newJournal, int $oldGroupId): void
|
||||||
|
{
|
||||||
|
$newNote = $note->replicate();
|
||||||
|
$newNote->text .= sprintf(
|
||||||
|
"\n\n%s", trans('firefly.clones_journal_x', ['description' => $newJournal->description, 'id' => $oldGroupId])
|
||||||
|
);
|
||||||
|
$newNote->noteable_id = $newJournal->id;
|
||||||
|
$newNote->save();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Transaction $transaction
|
||||||
|
* @param TransactionJournal $newJournal
|
||||||
|
*/
|
||||||
|
private function cloneTransaction(Transaction $transaction, TransactionJournal $newJournal): void
|
||||||
|
{
|
||||||
|
$newTransaction = $transaction->replicate();
|
||||||
|
$newTransaction->transaction_journal_id = $newJournal->id;
|
||||||
|
$newTransaction->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
6
public/v1/js/ff/transactions/show.js
vendored
6
public/v1/js/ff/transactions/show.js
vendored
@@ -27,8 +27,14 @@ $(function () {
|
|||||||
makeAutoComplete();
|
makeAutoComplete();
|
||||||
})
|
})
|
||||||
$('[data-toggle="tooltip"]').tooltip();
|
$('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
|
$('#clone_button').click(cloneNewFunction);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function cloneNewFunction() {
|
||||||
|
return confirm(newCloneInstructions);
|
||||||
|
}
|
||||||
|
|
||||||
function getLinkModal(e) {
|
function getLinkModal(e) {
|
||||||
var button = $(e.currentTarget);
|
var button = $(e.currentTarget);
|
||||||
var journalId = parseInt(button.data('journal'));
|
var journalId = parseInt(button.data('journal'));
|
||||||
|
@@ -58,7 +58,8 @@ return [
|
|||||||
'no_rules_for_bill' => 'This bill has no rules associated to it.',
|
'no_rules_for_bill' => 'This bill has no rules associated to it.',
|
||||||
'go_to_asset_accounts' => 'View your asset accounts',
|
'go_to_asset_accounts' => 'View your asset accounts',
|
||||||
'go_to_budgets' => 'Go to your budgets',
|
'go_to_budgets' => 'Go to your budgets',
|
||||||
'clone_instructions' => 'To clone a transaction, search for the "store as new" checkbox in the edit screen',
|
'new_clone_instructions' => 'This button will automatically clone the transaction and set the date to today. Are you sure?',
|
||||||
|
'clones_journal_x' => 'This transaction is a clone of ":description" (#:id)',
|
||||||
'go_to_categories' => 'Go to your categories',
|
'go_to_categories' => 'Go to your categories',
|
||||||
'go_to_bills' => 'Go to your bills',
|
'go_to_bills' => 'Go to your bills',
|
||||||
'go_to_expense_accounts' => 'See your expense accounts',
|
'go_to_expense_accounts' => 'See your expense accounts',
|
||||||
|
@@ -61,9 +61,8 @@
|
|||||||
|
|
||||||
|
|
||||||
{% if groupArray.transactions[0].type != 'opening balance' and groupArray.transactions[0].type != 'reconciliation' %}
|
{% if groupArray.transactions[0].type != 'opening balance' and groupArray.transactions[0].type != 'reconciliation' %}
|
||||||
<!--CLONE-->
|
<!-- since 5.1.0 -->
|
||||||
<a href="#" class="btn btn-default" style="white-space: nowrap;" onclick="return false;" data-toggle="tooltip" data-placement="top" title="{{ 'clone_instructions' |_|escape}}">Clone</a>
|
<a href="{{ route('transactions.clone', [transactionGroup.id]) }}" id="clone_button" class="btn btn-default"><i class="fa fa-copy"></i> {{ 'clone'|_ }}</a>
|
||||||
{#<a href="{{ route('transactions.clone', [transactionGroup.id]) }}" class="btn btn-default"><i class="fa fa-copy"></i> {{ 'clone'|_ }}</a>#}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{#
|
{#
|
||||||
<a href="{{ route('transactions.delete', [transactionGroup.id]) }}" class="btn btn-danger"><i class="fa fa-trash"></i> {{ 'delete'|_ }}</a>
|
<a href="{{ route('transactions.delete', [transactionGroup.id]) }}" class="btn btn-danger"><i class="fa fa-trash"></i> {{ 'delete'|_ }}</a>
|
||||||
@@ -415,6 +414,7 @@
|
|||||||
var modalDialogURI = '{{ route('transactions.link.modal', ['%JOURNAL%']) }}';
|
var modalDialogURI = '{{ route('transactions.link.modal', ['%JOURNAL%']) }}';
|
||||||
var acURI = '{{ route('json.autocomplete.all-journals-with-id') }}';
|
var acURI = '{{ route('json.autocomplete.all-journals-with-id') }}';
|
||||||
var groupURI = '{{ route('transactions.show',['%GROUP%']) }}';
|
var groupURI = '{{ route('transactions.show',['%GROUP%']) }}';
|
||||||
|
var newCloneInstructions = '{{ 'new_clone_instructions'|_|escape('js') }}';
|
||||||
</script>
|
</script>
|
||||||
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/lib/typeahead/typeahead.bundle.min.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
<script type="text/javascript" src="v1/js/ff/transactions/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
<script type="text/javascript" src="v1/js/ff/transactions/show.js?v={{ FF_VERSION }}" nonce="{{ JS_NONCE }}"></script>
|
||||||
|
@@ -963,6 +963,9 @@ Route::group(
|
|||||||
Route::get('create/{objectType}', ['uses' => 'Transaction\CreateController@create', 'as' => 'create']);
|
Route::get('create/{objectType}', ['uses' => 'Transaction\CreateController@create', 'as' => 'create']);
|
||||||
Route::post('store', ['uses' => 'Transaction\CreateController@store', 'as' => 'store']);
|
Route::post('store', ['uses' => 'Transaction\CreateController@store', 'as' => 'store']);
|
||||||
|
|
||||||
|
// clone group
|
||||||
|
Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CreateController@cloneGroup', 'as' => 'clone']);
|
||||||
|
|
||||||
// edit group
|
// edit group
|
||||||
Route::get('edit/{transactionGroup}', ['uses' => 'Transaction\EditController@edit', 'as' => 'edit']);
|
Route::get('edit/{transactionGroup}', ['uses' => 'Transaction\EditController@edit', 'as' => 'edit']);
|
||||||
Route::post('update', ['uses' => 'Transaction\EditController@update', 'as' => 'update']);
|
Route::post('update', ['uses' => 'Transaction\EditController@update', 'as' => 'update']);
|
||||||
@@ -971,17 +974,6 @@ Route::group(
|
|||||||
Route::get('delete/{transactionGroup}', ['uses' => 'Transaction\DeleteController@delete', 'as' => 'delete']);
|
Route::get('delete/{transactionGroup}', ['uses' => 'Transaction\DeleteController@delete', 'as' => 'delete']);
|
||||||
Route::post('destroy/{transactionGroup}', ['uses' => 'Transaction\DeleteController@destroy', 'as' => 'destroy']);
|
Route::post('destroy/{transactionGroup}', ['uses' => 'Transaction\DeleteController@destroy', 'as' => 'destroy']);
|
||||||
|
|
||||||
// clone group:
|
|
||||||
//Route::get('clone/{transactionGroup}', ['uses' => 'Transaction\CloneController@clone', 'as' => 'clone']);
|
|
||||||
|
|
||||||
//Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
|
|
||||||
//Route::get('debug/{tj}', ['uses' => 'Transaction\SingleController@debugShow', 'as' => 'debug']);
|
|
||||||
|
|
||||||
//Route::post('reorder', ['uses' => 'TransactionController@reorder', 'as' => 'reorder']);
|
|
||||||
//Route::post('reconcile', ['uses' => 'TransactionController@reconcile', 'as' => 'reconcile']);
|
|
||||||
// TODO end of improvement.
|
|
||||||
|
|
||||||
|
|
||||||
Route::get('show/{transactionGroup}', ['uses' => 'Transaction\ShowController@show', 'as' => 'show']);
|
Route::get('show/{transactionGroup}', ['uses' => 'Transaction\ShowController@show', 'as' => 'show']);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
Reference in New Issue
Block a user