Management of links and first form for link storing #616

This commit is contained in:
James Cole 2017-08-21 07:13:03 +02:00
parent c5cdd748fc
commit 35ab4a5ff4
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
25 changed files with 948 additions and 31 deletions

View File

@ -14,6 +14,11 @@ namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Requests\LinkTypeFormRequest;
use FireflyIII\Models\LinkType;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use Illuminate\Http\Request;
use Preferences;
use View;
/**
@ -41,14 +46,166 @@ class LinkController extends Controller
}
/**
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index()
public function create()
{
$subTitle = trans('firefly.create_new_link_type');
$subTitleIcon = 'fa-link';
// put previous url in session if not redirect from store (not "create another").
if (session('link_types.create.fromStore') !== true) {
$this->rememberPreviousUri('link_types.create.uri');
}
return view('admin.link.create', compact('subTitle', 'subTitleIcon'));
}
/**
* @param LinkType $linkType
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function delete(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
{
if (!$linkType->editable) {
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
return redirect(route('admin.links.index'));
}
$subTitle = trans('firefly.delete_link_type', ['name' => $linkType->name]);
$otherTypes = $repository->get();
$count = $repository->countJournals($linkType);
$moveTo = [];
$moveTo[0] = trans('firefly.do_not_save_connection');
/** @var LinkType $otherType */
foreach ($otherTypes as $otherType) {
if ($otherType->id !== $linkType->id) {
$moveTo[$otherType->id] = sprintf('%s (%s / %s)', $otherType->name, $otherType->inward, $otherType->outward);
}
}
// put previous url in session
$this->rememberPreviousUri('link_types.delete.uri');
return view('admin.link.delete', compact('linkType', 'subTitle', 'moveTo', 'count'));
}
/**
* @param Request $request
* @param LinkTypeRepositoryInterface $repository
* @param LinkType $linkType
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function destroy(Request $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
{
$name = $linkType->name;
$moveTo = $repository->find(intval($request->get('move_link_type_before_delete')));
$repository->destroy($linkType, $moveTo);
$request->session()->flash('success', strval(trans('firefly.deleted_link_type', ['name' => $name])));
Preferences::mark();
return redirect($this->getPreviousUri('link_types.delete.uri'));
}
/**
* @param Request $request
* @param LinkType $linkType
*
* @return \Illuminate\Contracts\View\Factory|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\View\View
*/
public function edit(Request $request, LinkType $linkType)
{
if (!$linkType->editable) {
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
return redirect(route('admin.links.index'));
}
$subTitle = trans('firefly.edit_link_type', ['name' => $linkType->name]);
$subTitleIcon = 'fa-link';
// put previous url in session if not redirect from store (not "return_to_edit").
if (session('link_types.edit.fromUpdate') !== true) {
$this->rememberPreviousUri('link_types.edit.uri');
}
$request->session()->forget('link_types.edit.fromUpdate');
return view('admin.link.edit', compact('subTitle', 'subTitleIcon', 'linkType'));
}
/**
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
*/
public function index(LinkTypeRepositoryInterface $repository)
{
$subTitle = trans('firefly.journal_link_configuration');
$subTitleIcon = 'fa-link';
$linkTypes = $repository->get();
$linkTypes->each(
function (LinkType $linkType) use ($repository) {
$linkType->journalCount = $repository->countJournals($linkType);
}
);
return view('admin.link.index', compact('subTitle', 'subTitleIcon'));
return view('admin.link.index', compact('subTitle', 'subTitleIcon', 'linkTypes'));
}
/**
* @param LinkTypeFormRequest $request
* @param LinkTypeRepositoryInterface $repository
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function store(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository)
{
$data = [
'name' => $request->string('name'),
'inward' => $request->string('inward'),
'outward' => $request->string('outward'),
];
$linkType = $repository->store($data);
$request->session()->flash('success', strval(trans('firefly.stored_new_link_type', ['name' => $linkType->name])));
if (intval($request->get('create_another')) === 1) {
// set value so create routine will not overwrite URL:
$request->session()->put('link_types.create.fromStore', true);
return redirect(route('link_types.create', [$request->input('what')]))->withInput();
}
// redirect to previous URL.
return redirect($this->getPreviousUri('link_types.create.uri'));
}
public function update(LinkTypeFormRequest $request, LinkTypeRepositoryInterface $repository, LinkType $linkType)
{
if (!$linkType->editable) {
$request->session()->flash('error', strval(trans('firefly.cannot_edit_link_type', ['name' => $linkType->name])));
return redirect(route('admin.links.index'));
}
$data = [
'name' => $request->string('name'),
'inward' => $request->string('inward'),
'outward' => $request->string('outward'),
];
$repository->update($linkType, $data);
$request->session()->flash('success', strval(trans('firefly.updated_link_type', ['name' => $linkType->name])));
Preferences::mark();
if (intval($request->get('return_to_edit')) === 1) {
// set value so edit routine will not overwrite URL:
$request->session()->put('link_types.edit.fromUpdate', true);
return redirect(route('admin.links.edit', [$linkType->id]))->withInput(['return_to_edit' => 1]);
}
// redirect to previous URL.
return redirect($this->getPreviousUri('link_types.edit.uri'));
}
}

View File

@ -12,10 +12,13 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Json;
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Response;
/**
@ -74,6 +77,39 @@ class AutoCompleteController extends Controller
return Response::json($return);
}
/**
* @param JournalCollectorInterface $collector
*
* @return \Illuminate\Http\JsonResponse|mixed
*/
public function journalsWithId(JournalCollectorInterface $collector, TransactionJournal $except)
{
$cache = new CacheProperties;
$cache->addProperty('recent-journals-id');
if ($cache->has()) {
return $cache->get(); // @codeCoverageIgnore
}
$collector->setLimit(400)->setPage(1);
$set = $collector->getJournals()->pluck('description', 'journal_id')->toArray();
$return = [];
foreach ($set as $id => $description) {
$id = intval($id);
if ($id !== $except->id) {
$return[] = [
'id' => $id,
'name' => $id . ': ' . $description,
];
}
}
$cache->store($return);
return Response::json($return);
}
/**
* @param AccountRepositoryInterface $repository
*

View File

@ -0,0 +1,25 @@
<?php
/**
* LinkController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Transaction;
use Illuminate\Http\Request;
class LinkController
{
public function store(Request $request) {
var_dump($request->all());
}
}

View File

@ -20,6 +20,7 @@ use FireflyIII\Helpers\Filter\InternalTransferFilter;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Repositories\Journal\JournalTaskerInterface;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use FireflyIII\Support\CacheProperties;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
@ -155,18 +156,18 @@ class TransactionController extends Controller
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|View
*/
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker)
public function show(TransactionJournal $journal, JournalTaskerInterface $tasker, LinkTypeRepositoryInterface $linkTypeRepository)
{
if ($this->isOpeningBalance($journal)) {
return $this->redirectToAccount($journal);
}
$linkTypes = $linkTypeRepository->get();
$events = $tasker->getPiggyBankEvents($journal);
$transactions = $tasker->getTransactionsOverview($journal);
$what = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
$subTitle = trans('firefly.' . $what) . ' "' . e($journal->description) . '"';
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions'));
return view('transactions.show', compact('journal', 'events', 'subTitle', 'what', 'transactions', 'linkTypes'));
}

View File

@ -0,0 +1,57 @@
<?php
/**
* LinkTypeFormRequest.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Requests;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
/**
* Class BillFormRequest
*
*
* @package FireflyIII\Http\Requests
*/
class LinkTypeFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged and admins
return auth()->check() && auth()->user()->hasRole('owner');
}
/**
* @return array
*/
public function rules()
{
/** @var LinkTypeRepositoryInterface $repository */
$repository = app(LinkTypeRepositoryInterface::class);
$nameRule = 'required|min:1|unique:link_types,name';
$idRule = '';
if (!is_null($repository->find($this->integer('id'))->id)) {
$idRule = 'exists:link_types,id';
$nameRule = 'required|min:1';
}
$rules = [
'id' => $idRule,
'name' => $nameRule,
'inward' => 'required|min:1|different:outward',
'outward' => 'required|min:1|different:inward',
];
return $rules;
}
}

View File

@ -85,7 +85,7 @@ class Request extends FormRequest
*
* @return string
*/
protected function string(string $field): string
public function string(string $field): string
{
$string = $this->get($field) ?? '';
$search = [

View File

@ -21,6 +21,7 @@ use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
use FireflyIII\Models\Category;
use FireflyIII\Models\ImportJob;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\Rule;
use FireflyIII\Models\RuleGroup;
@ -159,6 +160,26 @@ Breadcrumbs::register(
}
);
Breadcrumbs::register(
'admin.links.create', function (BreadCrumbGenerator $breadcrumbs) {
$breadcrumbs->parent('admin.links.index');
$breadcrumbs->push(trans('firefly.create_new_link_type'), route('admin.links.create'));
}
);
Breadcrumbs::register(
'admin.links.edit', function (BreadCrumbGenerator $breadcrumbs, LinkType $linkType) {
$breadcrumbs->parent('admin.links.index');
$breadcrumbs->push(trans('firefly.edit_link_type', ['name' => $linkType->name]), route('admin.links.edit', [$linkType->id]));
}
);
Breadcrumbs::register(
'admin.links.delete', function (BreadCrumbGenerator $breadcrumbs, LinkType $linkType) {
$breadcrumbs->parent('admin.links.index');
$breadcrumbs->push(trans('firefly.delete_link_type', ['name' => $linkType->name]), route('admin.links.delete', [$linkType->id]));
}
);
/**
* ATTACHMENTS

View File

@ -16,6 +16,7 @@ namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* @property int $journalCount
* Class LinkType
*
* @package FireflyIII\Models
@ -35,4 +36,8 @@ class LinkType extends Model
'editable' => 'boolean',
];
public function transactionJournalLinks() {
return $this->hasMany(TransactionJournalLink::class);
}
}

View File

@ -372,6 +372,21 @@ class TransactionJournal extends Model
return $entry;
}
/**
* @return HasMany
*/
public function sourceJournalLinks(): HasMany
{
return $this->hasMany(TransactionJournalLink::class, 'source_id');
}
/**
* @return HasMany
*/
public function destinationJournalLinks(): HasMany
{
return $this->hasMany(TransactionJournalLink::class, 'destination_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
*/

View File

@ -0,0 +1,53 @@
<?php
/**
* TransactionJournalLink.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class TransactionJournalLink
*
* @package FireflyIII\Models
*/
class TransactionJournalLink extends Model
{
protected $table = 'journal_links';
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function linkType()
{
return $this->belongsTo(LinkType::class);
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function source()
{
return $this->belongsTo(TransactionJournal::class,'source_id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
*/
public function destination()
{
return $this->belongsTo(TransactionJournal::class,'destination_id');
}
}

View File

@ -0,0 +1,63 @@
<?php
/**
* AdminServiceProvider.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Providers;
use FireflyIII\Repositories\LinkType\LinkTypeRepository;
use FireflyIII\Repositories\LinkType\LinkTypeRepositoryInterface;
use Illuminate\Foundation\Application;
use Illuminate\Support\ServiceProvider;
class AdminServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
}
/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->linkType();
}
/**
*
*/
private function linkType()
{
$this->app->bind(
LinkTypeRepositoryInterface::class,
function (Application $app) {
/** @var LinkTypeRepository $repository */
$repository = app(LinkTypeRepository::class);
if ($app->auth->check()) {
$repository->setUser(auth()->user());
}
return $repository;
}
);
}
}

View File

@ -0,0 +1,119 @@
<?php
/**
* LinkTypeRepository.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\LinkType;
use FireflyIII\Models\LinkType;
use FireflyIII\Models\TransactionJournalLink;
use FireflyIII\User;
use Illuminate\Support\Collection;
/**
* Class LinkTypeRepository
*
* @package FireflyIII\Repositories\LinkType
*/
class LinkTypeRepository implements LinkTypeRepositoryInterface
{
/** @var User */
private $user;
/**
* @param LinkType $linkType
*
* @return int
*/
public function countJournals(LinkType $linkType): int
{
return $linkType->transactionJournalLinks()->count() * 2;
}
/**
* @param LinkType $linkType
* @param LinkType $moveTo
*
* @return bool
*/
public function destroy(LinkType $linkType, LinkType $moveTo): bool
{
if (!is_null($moveTo->id)) {
TransactionJournalLink::where('link_type_id', $linkType->id)->update(['link_type_id' => $moveTo->id]);
}
$linkType->delete();
return true;
}
/**
* @param int $id
*
* @return LinkType
*/
public function find(int $id): LinkType
{
$linkType = LinkType::find($id);
if (is_null($linkType)) {
return new LinkType;
}
return $linkType;
}
/**
* @return Collection
*/
public function get(): Collection
{
return LinkType::orderBy('name', 'ASC')->get();
}
/**
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
/**
* @param array $data
*
* @return LinkType
*/
public function store(array $data): LinkType
{
$linkType = new LinkType;
$linkType->name = $data['name'];
$linkType->inward = $data['inward'];
$linkType->outward = $data['outward'];
$linkType->editable = true;
$linkType->save();
return $linkType;
}
/**
* @param LinkType $linkType
* @param array $data
*
* @return LinkType
*/
public function update(LinkType $linkType, array $data): LinkType
{
$linkType->name = $data['name'];
$linkType->inward = $data['inward'];
$linkType->outward = $data['outward'];
$linkType->save();
return $linkType;
}
}

View File

@ -0,0 +1,67 @@
<?php
/**
* LinkTypeRepositoryInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Repositories\LinkType;
use FireflyIII\Models\LinkType;
use Illuminate\Support\Collection;
/**
* Interface LinkTypeRepositoryInterface
*
* @package FireflyIII\Repositories\LinkType
*/
interface LinkTypeRepositoryInterface
{
/**
* @param int $id
*
* @return LinkType
*/
public function find(int $id): LinkType;
/**
* @return Collection
*/
public function get(): Collection;
/**
* @param LinkType $linkType
*
* @return int
*/
public function countJournals(LinkType $linkType): int;
/**
* @param LinkType $linkType
* @param LinkType $moveTo
*
* @return bool
*/
public function destroy(LinkType $linkType, LinkType $moveTo): bool;
/**
* @param array $data
*
* @return LinkType
*/
public function store(array $data): LinkType;
/**
* @param LinkType $linkType
* @param array $data
*
* @return LinkType
*/
public function update(LinkType $linkType, array $data): LinkType;
}

View File

@ -88,6 +88,7 @@ return [
FireflyIII\Providers\RuleGroupServiceProvider::class,
FireflyIII\Providers\SearchServiceProvider::class,
FireflyIII\Providers\TagServiceProvider::class,
FireflyIII\Providers\AdminServiceProvider::class,
],

View File

@ -16,8 +16,9 @@ class ChangesForV470 extends Migration
*/
public function down()
{
Schema::dropIfExists('journal_links');
Schema::dropIfExists('link_types');
Schema::dropIfExists('journal_types');
}
/**
@ -38,6 +39,9 @@ class ChangesForV470 extends Migration
$table->string('outward');
$table->string('inward');
$table->boolean('editable');
$table->unique(['name']);
$table->unique(['outward','inward']);
}
);
}
@ -46,11 +50,20 @@ class ChangesForV470 extends Migration
Schema::create(
'journal_links', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->integer('link_type_id', false, true);
$table->integer('source_id', false, true);
$table->integer('destination_id', false, true);
$table->text('comment');
$table->integer('sequence', false, true);
$table->foreign('link_type_id')->references('id')->on('link_types')->onDelete('cascade');
$table->foreign('source_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->foreign('destination_id')->references('id')->on('transaction_journals')->onDelete('cascade');
$table->unique(['link_type_id','source_id','destination_id']);
}
);
}

View File

@ -0,0 +1,39 @@
/*
* show.js
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
/** global: autoCompleteUri */
$(function () {
"use strict";
$.getJSON(autoCompleteUri).done(function (data) {
var $input = $("#link_other");
$input.typeahead({
source: data,
autoSelect: true
});
$input.change(function() {
var current = $input.typeahead("getActive");
if (current) {
// Some item from your model is active!
if (current.name.toLowerCase() === $input.val().toLowerCase()) {
// This means the exact match is found. Use toLowerCase() if you want case insensitive match.
$('input[name="link_journal_id"]').val(current.id);
} else {
$('input[name="link_journal_id"]').val(0);
}
} else {
$('input[name="link_journal_id"]').val(0);
}
});
});
});

View File

@ -937,6 +937,26 @@ return [
'no_block_code' => 'No reason for block or user not blocked',
// links
'journal_link_configuration' => 'Transaction links configuration',
'create_new_link_type' => 'Create new link type',
'store_new_link_type' => 'Store new link type',
'update_link_type' => 'Update link type',
'edit_link_type' => 'Edit link type ":name"',
'updated_link_type' => 'Updated link type ":name"',
'delete_link_type' => 'Delete link type ":name"',
'deleted_link_type' => 'Deleted link type ":name"',
'stored_new_link_type' => 'Store new link type ":name"',
'cannot_edit_link_type' => 'Cannot edit link type ":name"',
'link_type_help_name' => 'Ie. "Duplicates"',
'link_type_help_inward' => 'Ie. "duplicates"',
'link_type_help_outward' => 'Ie. "is duplicated by"',
'save_connections_by_moving' => 'Save the link between these transaction(s) by moving them to another link type:',
'do_not_save_connection' => '(do not save connection)',
'link_transaction' => 'Link transaction',
'link_to_other_transaction' => 'Link this transaction to another transaction',
'select_transaction_to_link' => 'Select a transaction to link this transaction to',
'this_transaction' => 'This transaction',
'transaction' => 'Transaction',
'comments' => 'Comments',
// split a transaction:

View File

@ -135,6 +135,7 @@ return [
'delete_attachment' => 'Delete attachment ":name"',
'delete_rule' => 'Delete rule ":title"',
'delete_rule_group' => 'Delete rule group ":title"',
'delete_link_type' => 'Delete link type ":name"',
'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
@ -147,11 +148,13 @@ return [
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?',
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
'linkType_areYouSure' => 'Are you sure you want to delete the link type ":name" (":inward" / ":outward")?',
'permDeleteWarning' => 'Deleting stuff from Firely is permanent and cannot be undone.',
'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.',
'delete_all_permanently' => 'Delete selected permanently',
'update_all_journals' => 'Update these transactions',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
'also_delete_connections' => 'The only transaction linked with this link type will lose this connection.|All :count transactions linked with this link type will lose their connection.',
'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will spared deletion.',
@ -189,4 +192,7 @@ return [
'payment_date' => 'Payment date',
'invoice_date' => 'Invoice date',
'internal_reference' => 'Internal reference',
'inward' => 'Inward description',
'outward' => 'Outward description',
];

View File

@ -78,14 +78,17 @@ return [
'source_account' => 'Source account',
'destination_account' => 'Destination account',
'accounts_count' => 'Number of accounts',
'journals_count' => 'Number of transactions',
'attachments_count' => 'Number of attachments',
'bills_count' => 'Number of bills',
'categories_count' => 'Number of categories',
'export_jobs_count' => 'Number of export jobs',
'import_jobs_count' => 'Number of import jobs',
'budget_count' => 'Number of budgets',
'rule_and_groups_count' => 'Number of rules and rule groups',
'tags_count' => 'Number of tags',
'accounts_count' => 'Number of accounts',
'journals_count' => 'Number of transactions',
'attachments_count' => 'Number of attachments',
'bills_count' => 'Number of bills',
'categories_count' => 'Number of categories',
'export_jobs_count' => 'Number of export jobs',
'import_jobs_count' => 'Number of import jobs',
'budget_count' => 'Number of budgets',
'rule_and_groups_count' => 'Number of rules and rule groups',
'tags_count' => 'Number of tags',
'inward' => 'Inward description',
'outward' => 'Outward description',
'number_of_transactions' => 'Number of transactions',
];

View File

@ -0,0 +1,44 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('admin.links.store') }}" accept-charset="UTF-8" class="form-horizontal">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'mandatoryFields'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('name', null, {helpText: trans('firefly.link_type_help_name')}) }}
{{ ExpandedForm.text('inward', null, {helpText: trans('firefly.link_type_help_inward')}) }}
{{ ExpandedForm.text('outward', null, {helpText: trans('firefly.link_type_help_outward')}) }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<!-- panel for options -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'options'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.optionsList('create','link_type') }}
</div>
<div class="box-footer">
<button type="submit" class="btn pull-right btn-success">{{ 'store_new_link_type'|_ }}</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,53 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, linkType) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('admin.links.destroy',linkType.id) }}" accept-charset="UTF-8" class="form-horizontal" id="destroy">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-lg-offset-3 col-md-12 col-sm-12">
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">{{ trans('form.delete_link_type', {'name': linkType.name}) }}</h3>
</div>
<div class="box-body">
<p class="text-danger">
{{ trans('form.permDeleteWarning') }}
</p>
<p>
{{ trans('form.linkType_areYouSure', {'name': linkType.name,'inward': linkType.inward,'outward': linkType.outward}) }}
</p>
{% if count > 0 %}
<p>
{{ Lang.choice('form.also_delete_connections', count, {count: count}) }}
</p>
{% endif %}
{% if count > 0 %}
<p class="text-success">
{{ 'save_connections_by_moving'|_ }}
</p>
<p>
{{ Form.select('move_link_type_before_delete', moveTo, null, {class: 'form-control'}) }}
</p>
{% else %}
<input type="hidden" name="move_link_type_before_delete" value="0"/>
{% endif %}
</div>
<div class="box-footer">
<input type="submit" name="submit" value="{{ trans('form.deletePermanently') }}" class="btn pull-right btn-danger"/>
<a href="{{ URL.previous() }}" class="btn-default btn">{{ trans('form.cancel') }}</a>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@ -0,0 +1,48 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName, linkType) }}
{% endblock %}
{% block content %}
{{ Form.model(linkType, {'class' : 'form-horizontal','id' : 'update','url' : route('admin.links.update', linkType.id) } ) }}
<input type="hidden" name="id" value="{{ linkType.id }}"/>
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'mandatoryFields'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.text('name', null, {helpText: trans('firefly.link_type_help_name')}) }}
{{ ExpandedForm.text('inward', null, {helpText: trans('firefly.link_type_help_inward')}) }}
{{ ExpandedForm.text('outward', null, {helpText: trans('firefly.link_type_help_outward')}) }}
</div>
</div>
</div>
<div class="col-lg-6 col-md-6 col-sm-12 col-xs-12">
<!-- panel for options -->
<div class="box">
<div class="box-header with-border">
<h3 class="box-title">{{ 'options'|_ }}</h3>
</div>
<div class="box-body">
{{ ExpandedForm.optionsList('update','link_type') }}
</div>
<div class="box-footer">
<button type="submit" class="btn pull-right btn-success">
{{ ('update_link_type')|_ }}
</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@ -10,8 +10,47 @@
<div class="box-header with-border">
<h3 class="box-title">{{ 'journal_link_configuration'|_ }}</h3>
</div>
<div class="box-body">
<div class="box-body no-padding">
<table class="table table-hover sortable">
<thead>
<tr>
<th class="hidden-sm hidden-xs" data-defaultsort="disabled">&nbsp;</th>
<th data-defaultsign="az">{{ trans('list.name') }}</th>
<th class="hidden-sm hidden-xs" data-defaultsign="az">{{ trans('list.inward') }}</th>
<th class="hidden-sm hidden-xs" data-defaultsign="az">{{ trans('list.outward') }}</th>
<th data-defaultsign="_19">{{ trans('list.number_of_transactions') }}</th>
</tr>
</thead>
<tbody>
{% for linkType in linkTypes %}
<tr>
<td>
{% if linkType.editable %}
<div class="btn-group btn-group-xs">
<a class="btn btn-default btn-xs" href="{{ route('admin.links.edit',linkType.id) }}"><i class="fa fa-fw fa-pencil"></i></a>
<a class="btn btn-danger btn-xs" href="{{ route('admin.links.delete',linkType.id) }}"><i class="fa fa-fw fa-trash-o"></i></a>
</div>
{% endif %}
</td>
<td>
<strong>{{ linkType.name }}</strong>
</td>
<td>
{{ linkType.inward }}
</td>
<td>
{{ linkType.outward }}
</td>
<td data-value="{{ linkType.journalCount }}">
{{ linkType.journalCount }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="box-footer">
<a href="{{ route('admin.links.create') }}" class="btn btn-success">{{ 'create_new_link_type'|_ }}</a>
</div>
</div>
</div>

View File

@ -383,30 +383,37 @@
{# link journal modal:#}
<div class="modal fade" tabindex="-1" role="dialog" id="linkJournalModal">
<form action="#" method="post" class="form-horizontal">
<form action="{{ route('transactions.link.store', [journal.id]) }}" method="post" class="form-horizontal">
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title">Link another transaction</h4>
<h4 class="modal-title">{{ 'link_to_other_transaction'|_ }}</h4>
</div>
<div class="modal-body" id="helpBody">
<p>Select a transaction to link this transaction to</p>
<p>{{ 'select_transaction_to_link'|_ }}</p>
<div class="form-group">
<label for="link_type" class="col-sm-2 control-label">This transaction</label>
<label for="link_type" class="col-sm-2 control-label">{{ 'this_transaction'|_ }}</label>
<div class="col-sm-10">
<select class="form-control" name="link_type"><option>a</option><option>a</option><option>a</option></select>
<select id="link_type" class="form-control" name="link_type">
{% for linkType in linkTypes %}
<option label="{{ linkType.inward }}" value="{{ linkType.id }}_inward">{{ linkType.inward }}</option>
<option label="{{ linkType.outward }}" value="{{ linkType.id }}_inward">{{ linkType.outward }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="form-group">
<label for="link_type" class="col-sm-2 control-label">Transaction</label>
<label for="link_other" class="col-sm-2 control-label">{{ 'transaction'|_ }}</label>
<div class="col-sm-10">
<input type="text" value="" class="form-control">
<input type="text" name="link_other" autocomplete="off" id="link_other" value="" class="form-control">
<input type="hidden" name="link_journal_id" value="0">
</div>
</div>
<div class="form-group">
<label for="comments" class="col-sm-2 control-label">Comments</label>
<label for="comments" class="col-sm-2 control-label">{{ 'comments'|_ }}</label>
<div class="col-sm-10">
<textarea class="form-control"></textarea>
<textarea id="comments" name="comments" class="form-control"></textarea>
</div>
</div>
@ -414,7 +421,7 @@
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">{{ 'do something'|_ }}</button>
<button type="submit" class="btn btn-primary">{{ 'submit'|_ }}</button>
</div>
</div>
</div>
@ -422,3 +429,10 @@
</div>
{% endblock %}
{% block scripts %}
<script type="text/javascript">
var autoCompleteUri = "{{ route('json.journals-with-id',[journal.id]) }}";
</script>
<script type="text/javascript" src="js/lib/bootstrap3-typeahead.min.js"></script>
<script type="text/javascript" src="js/ff/transactions/show.js"></script>
{% endblock %}

View File

@ -443,6 +443,7 @@ Route::group(
Route::get('box/bills-unpaid', ['uses' => 'JsonController@boxBillsUnpaid', 'as' => 'box.unpaid']);
Route::get('box/bills-paid', ['uses' => 'JsonController@boxBillsPaid', 'as' => 'box.paid']);
Route::get('transaction-journals/all', ['uses' => 'JsonController@allTransactionJournals', 'as' => 'all-transaction-journals']);
Route::get('transaction-journals/with-id/{tj}', ['uses' => 'Json\AutoCompleteController@journalsWithId', 'as' => 'journals-with-id']);
Route::get('transaction-journals/{what}', ['uses' => 'JsonController@transactionJournals', 'as' => 'transaction-journals']);
Route::get('transaction-types', ['uses' => 'JsonController@transactionTypes', 'as' => 'transaction-types']);
Route::get('trigger', ['uses' => 'JsonController@trigger', 'as' => 'trigger']);
@ -724,7 +725,7 @@ Route::group(
);
/**
* Convert Controller
* Transaction Convert Controller
*/
Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'Transaction', 'prefix' => 'transactions/convert', 'as' => 'transactions.convert.'], function () {
@ -733,6 +734,15 @@ Route::group(
}
);
/**
* Transaction Link Controller
*/
Route::group(
['middleware' => 'user-full-auth', 'namespace' => 'Transaction', 'prefix' => 'transactions/link', 'as' => 'transactions.link.'], function () {
Route::post('store/{tj}', ['uses' => 'LinkController@store', 'as' => 'store']);
}
);
/**
* Report Popup Controller
*/
@ -760,6 +770,14 @@ Route::group(
// journal links manager
Route::get('links', ['uses' => 'LinkController@index', 'as' => 'links.index']);
Route::get('links/create', ['uses' => 'LinkController@create', 'as' => 'links.create']);
Route::get('links/edit/{linkType}', ['uses' => 'LinkController@edit', 'as' => 'links.edit']);
Route::get('links/delete/{linkType}', ['uses' => 'LinkController@delete', 'as' => 'links.delete']);
Route::post('links/store', ['uses' => 'LinkController@store', 'as' => 'links.store']);
Route::post('links/update/{linkType}', ['uses' => 'LinkController@update', 'as' => 'links.update']);
Route::post('links/destroy/{linkType}', ['uses' => 'LinkController@destroy', 'as' => 'links.destroy']);
// FF configuration:
Route::get('configuration', ['uses' => 'ConfigurationController@index', 'as' => 'configuration.index']);