diff --git a/app/BulkActions/Banking/Transfers.php b/app/BulkActions/Banking/Transfers.php
index e8b9a106a..23ad1f2a1 100644
--- a/app/BulkActions/Banking/Transfers.php
+++ b/app/BulkActions/Banking/Transfers.php
@@ -5,6 +5,7 @@ namespace App\BulkActions\Banking;
use App\Abstracts\BulkAction;
use App\Jobs\Banking\DeleteTransfer;
use App\Models\Banking\Transfer;
+use App\Exports\Banking\Transfers as Export;
class Transfers extends BulkAction
{
@@ -16,6 +17,11 @@ class Transfers extends BulkAction
'message' => 'bulk_actions.message.delete',
'permission' => 'delete-banking-transfers',
],
+ 'export' => [
+ 'name' => 'general.export',
+ 'message' => 'bulk_actions.message.export',
+ 'type' => 'download',
+ ],
];
public function destroy($request)
@@ -30,4 +36,11 @@ class Transfers extends BulkAction
}
}
}
+
+ public function export($request)
+ {
+ $selected = $this->getSelectedInput($request);
+
+ return \Excel::download(new Export($selected), \Str::filename(trans_choice('general.transfers', 2)) . '.xlsx');
+ }
}
diff --git a/app/Exports/Banking/Transfers.php b/app/Exports/Banking/Transfers.php
new file mode 100644
index 000000000..70c250fdb
--- /dev/null
+++ b/app/Exports/Banking/Transfers.php
@@ -0,0 +1,60 @@
+usingSearchString(request('search'));
+
+ if (!empty($this->ids)) {
+ $model->whereIn('id', (array) $this->ids);
+ }
+
+ return $model->cursor();
+ }
+
+ public function map($model): array
+ {
+ $model->transferred_at = Date::parse($model->expense_transaction->paid_at)->format('Y-m-d');
+ $model->amount = $model->expense_transaction->amount;
+ $model->from_account_name = $model->expense_transaction->account->name;
+ $model->from_currency_code = $model->expense_transaction->currency_code;
+ $model->from_currency_rate = $model->expense_transaction->currency_rate;
+ $model->to_account_name = $model->income_transaction->account->name;
+ $model->to_currency_code = $model->income_transaction->currency_code;
+ $model->to_currency_rate = $model->income_transaction->currency_rate;
+ $model->description = $model->income_transaction->description;
+ $model->payment_method = $model->income_transaction->payment_method;
+ $model->reference = $model->income_transaction->reference;
+
+ return parent::map($model);
+ }
+
+ public function fields(): array
+ {
+ return [
+ 'transferred_at',
+ 'amount',
+ 'from_currency_code',
+ 'from_currency_rate',
+ 'from_account_name',
+ 'to_currency_code',
+ 'to_currency_rate',
+ 'to_account_name',
+ 'description',
+ 'payment_method',
+ 'reference',
+ ];
+ }
+}
diff --git a/app/Http/Controllers/Banking/Transfers.php b/app/Http/Controllers/Banking/Transfers.php
index 58cc85b32..64e7b7a71 100644
--- a/app/Http/Controllers/Banking/Transfers.php
+++ b/app/Http/Controllers/Banking/Transfers.php
@@ -4,6 +4,9 @@ namespace App\Http\Controllers\Banking;
use App\Abstracts\Http\Controller;
use App\Http\Requests\Banking\Transfer as Request;
+use App\Exports\Banking\Transfers as Export;
+use App\Http\Requests\Common\Import as ImportRequest;
+use App\Imports\Banking\Transfers as Import;
use App\Jobs\Banking\CreateTransfer;
use App\Jobs\Banking\UpdateTransfer;
use App\Jobs\Banking\DeleteTransfer;
@@ -218,4 +221,32 @@ class Transfers extends Controller
return response()->json($response);
}
+
+ /**
+ * Import the specified resource.
+ *
+ * @param ImportRequest $request
+ *
+ * @return Response
+ */
+ public function import(ImportRequest $request)
+ {
+ \Excel::import(new Import(), $request->file('import'));
+
+ $message = trans('messages.success.imported', ['type' => trans_choice('general.transfers', 2)]);
+
+ flash($message)->success();
+
+ return redirect()->route('transfers.index');
+ }
+
+ /**
+ * Export the specified resource.
+ *
+ * @return Response
+ */
+ public function export()
+ {
+ return \Excel::download(new Export(), \Str::filename(trans_choice('general.transfers', 2)) . '.xlsx');
+ }
}
diff --git a/app/Imports/Banking/Transfers.php b/app/Imports/Banking/Transfers.php
new file mode 100644
index 000000000..f298076f2
--- /dev/null
+++ b/app/Imports/Banking/Transfers.php
@@ -0,0 +1,118 @@
+getFromAccountId($row);
+ $row['to_account_id'] = $this->getToAccountId($row);
+ $row['expense_transaction_id'] = $this->getExpenseTransactionId($row);
+ $row['income_transaction_id'] = $this->getIncomeTransactionId($row);
+
+ return $row;
+ }
+
+ public function rules(): array
+ {
+ return [
+ 'from_account_id' => 'required|integer',
+ 'from_currency_code' => 'required|string|currency',
+ 'from_currency_rate' => 'required',
+ 'to_account_id' => 'required|integer',
+ 'to_currency_code' => 'required|string|currency',
+ 'to_currency_rate' => 'required',
+ 'amount' => 'required|amount',
+ 'transferred_at' => 'required|date_format:Y-m-d',
+ 'payment_method' => 'required|string',
+ ];
+ }
+
+ private function getExpenseTransactionId($row)
+ {
+ $expense_transaction = Transaction::create([
+ 'company_id' => session('company_id'),
+ 'type' => 'expense',
+ 'account_id' => $row['from_account_id'],
+ 'paid_at' => $row['transferred_at'],
+ 'currency_code' => $row['from_currency_code'],
+ 'currency_rate' => $row['from_currency_rate'],
+ 'amount' => $row['amount'],
+ 'contact_id' => 0,
+ 'description' => $row['description'],
+ 'category_id' => Category::transfer(), // Transfer Category ID
+ 'payment_method' => $row['payment_method'],
+ 'reference' => $row['reference'],
+ ]);
+
+ return $expense_transaction->id;
+ }
+
+ private function getIncomeTransactionId($row)
+ {
+ $amount = $row['amount'];
+ // Convert amount if not same currency
+ if ($row['from_currency_code'] !== $row['to_currency_code']) {
+ $amount = $this->convertBetween(
+ $amount,
+ $row['from_currency_code'],
+ $row['from_currency_rate'],
+ $row['to_currency_code'],
+ $row['to_currency_rate']
+ );
+ }
+
+ $income_transaction = Transaction::create([
+ 'company_id' => session('company_id'),
+ 'type' => 'income',
+ 'account_id' => $row['to_account_id'],
+ 'paid_at' => $row['transferred_at'],
+ 'currency_code' => $row['to_currency_code'],
+ 'currency_rate' => $row['to_currency_rate'],
+ 'amount' => $amount,
+ 'contact_id' => 0,
+ 'description' => $row['description'],
+ 'category_id' => Category::transfer(), // Transfer Category ID
+ 'payment_method' => $row['payment_method'],
+ 'reference' => $row['reference'],
+ ]);
+
+ return $income_transaction->id;
+ }
+
+ private function getFromAccountId($row)
+ {
+ $row['account_id'] = $row['from_account_id'] ?? null;
+ $row['account_name'] = $row['from_account_name'] ?? null;
+ $row['account_number'] = $row['from_account_number'] ?? null;
+ $row['currency_code'] = $row['from_currency_code'] ?? null;
+
+ return $this->getAccountId($row);
+ }
+
+ private function getToAccountId($row)
+ {
+ $row['account_id'] = $row['to_account_id'] ?? null;
+ $row['account_name'] = $row['to_account_name'] ?? null;
+ $row['account_number'] = $row['to_account_number'] ?? null;
+ $row['currency_code'] = $row['to_currency_code'] ?? null;
+
+ return $this->getAccountId($row);
+ }
+}
diff --git a/public/files/import/transfers.xlsx b/public/files/import/transfers.xlsx
new file mode 100644
index 000000000..2ed56ab8f
Binary files /dev/null and b/public/files/import/transfers.xlsx differ
diff --git a/resources/views/banking/transfers/index.blade.php b/resources/views/banking/transfers/index.blade.php
index 85a461b28..12619eda1 100644
--- a/resources/views/banking/transfers/index.blade.php
+++ b/resources/views/banking/transfers/index.blade.php
@@ -2,11 +2,13 @@
@section('title', trans_choice('general.transfers', 2))
-@permission('create-banking-transfers')
- @section('new_button')
- {{ trans('general.add_new') }}
- @endsection
-@endpermission
+@section('new_button')
+ @permission('create-banking-transfers')
+
+ @endpermission
+
+
+@endsection
@section('content')
@if ($transfers->count())
diff --git a/routes/admin.php b/routes/admin.php
index 37b221147..ee2ea519c 100644
--- a/routes/admin.php
+++ b/routes/admin.php
@@ -134,6 +134,8 @@ Route::group(['prefix' => 'banking'], function () {
Route::get('transactions/export', 'Banking\Transactions@export')->name('transactions.export');
Route::resource('transactions', 'Banking\Transactions');
+ Route::post('transfers/import', 'Banking\Transfers@import')->name('transfers.import');
+ Route::get('transfers/export', 'Banking\Transfers@export')->name('transfers.export');
Route::resource('transfers', 'Banking\Transfers', ['middleware' => ['date.format', 'money']]);
Route::post('reconciliations/calculate', 'Banking\Reconciliations@calculate')->middleware(['money']);
diff --git a/tests/Feature/Banking/TransfersTest.php b/tests/Feature/Banking/TransfersTest.php
index ba3b31d00..5b8f17ff6 100644
--- a/tests/Feature/Banking/TransfersTest.php
+++ b/tests/Feature/Banking/TransfersTest.php
@@ -2,9 +2,13 @@
namespace Tests\Feature\Banking;
-use App\Models\Banking\Account;
-use Tests\Feature\FeatureTestCase;
+use App\Exports\Banking\Transfers as Export;
use App\Jobs\Banking\CreateTransfer;
+use App\Models\Banking\Account;
+use App\Models\Banking\Transfer;
+use Illuminate\Http\UploadedFile;
+use Illuminate\Support\Facades\File;
+use Tests\Feature\FeatureTestCase;
class TransfersTest extends FeatureTestCase
{
@@ -69,6 +73,69 @@ class TransfersTest extends FeatureTestCase
$this->assertFlashLevel('success');
}
+ public function testItShouldExportTransfers()
+ {
+ $count = 5;
+ factory(Transfer::class, $count)->create();
+
+ \Excel::fake();
+
+ $this->loginAs()
+ ->get(route('transfers.export'))
+ ->assertStatus(200);
+
+ \Excel::assertDownloaded(
+ \Str::filename(trans_choice('general.transfers', 2)) . '.xlsx',
+ function (Export $export) use ($count) {
+ // Assert that the correct export is downloaded.
+ return $export->collection()->count() === $count;
+ }
+ );
+ }
+
+ public function testItShouldExportSelectedTransfers()
+ {
+ $count = 5;
+ $transfers = factory(Transfer::class, $count)->create();
+
+ \Excel::fake();
+
+ $this->loginAs()
+ ->post(
+ route('bulk-actions.action', ['group' => 'banking', 'type' => 'transfers']),
+ ['handle' => 'export', 'selected' => [$transfers->random()->id]]
+ )
+ ->assertStatus(200);
+
+ \Excel::assertDownloaded(
+ \Str::filename(trans_choice('general.transfers', 2)) . '.xlsx',
+ function (Export $export) {
+ return $export->collection()->count() === 1;
+ }
+ );
+ }
+
+ public function testItShouldImportTransfers()
+ {
+ \Excel::fake();
+
+ $this->loginAs()
+ ->post(
+ route('transfers.import'),
+ [
+ 'import' => UploadedFile::fake()->createWithContent(
+ 'transfers.xlsx',
+ File::get(public_path('files/import/transfers.xlsx'))
+ ),
+ ]
+ )
+ ->assertStatus(302);
+
+ \Excel::assertImported('transfers.xlsx');
+
+ $this->assertFlashLevel('success');
+ }
+
public function getRequest()
{
$from_account = factory(Account::class)->states('enabled', 'default_currency')->create();