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') +  {{ trans('general.add_new') }} + @endpermission +  {{ trans('import.import') }} +  {{ trans('general.export') }} +@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();