1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-08 20:22:42 +01:00

Merge pull request #4289 from beganovich/v4-migration-fixes

(v4) (wip) (no-merge) Migration fixes & improvements
This commit is contained in:
David Bomba 2020-11-14 17:03:07 +11:00 committed by GitHub
commit 1254ba0ee0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 167 deletions

View File

@ -2,22 +2,9 @@
namespace App\Http\Controllers\Migration;
use App\Models\User;
use App\Models\Credit;
use App\Models\Contact;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Product;
use App\Models\TaxRate;
use App\Libraries\Utils;
use App\Models\Document;
use App\Models\PaymentMethod;
use App\Models\AccountGateway;
use App\Models\AccountGatewayToken;
use App\Traits\GenerateMigrationResources;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Crypt;
use App\Models\AccountGatewaySettings;
use App\Services\Migration\AuthService;
use App\Http\Controllers\BaseController;
use App\Services\Migration\CompanyService;
@ -26,6 +13,7 @@ use App\Http\Requests\MigrationTypeRequest;
use App\Services\Migration\CompleteService;
use App\Http\Requests\MigrationEndpointRequest;
use App\Http\Requests\MigrationCompaniesRequest;
use App\Models\Account;
class StepsController extends BaseController
{
@ -159,10 +147,9 @@ class StepsController extends BaseController
);
}
$companyService = (new CompanyService(session('MIGRATION_ACCOUNT_TOKEN')))
->endpoint(session('MIGRATION_ENDPOINT'))
$companyService = (new CompanyService())
->start();
if ($companyService->isSuccessful()) {
return view('migration.companies', ['companies' => $companyService->getCompanies()]);
}
@ -180,15 +167,12 @@ class StepsController extends BaseController
);
}
foreach ($request->companies as $company) {
(new CompleteService(session('MIGRATION_ACCOUNT_TOKEN')))
->file($this->getMigrationFile())
->force(array_key_exists('force', $company))
->company($company['id'])
->endpoint(session('MIGRATION_ENDPOINT'))
->companyKey($request->account_key)
->start();
}
$migrationData = $this->generateMigrationData($request->all());
$completeService = (new CompleteService(session('MIGRATION_ACCOUNT_TOKEN')))
->data($migrationData)
->endpoint(session('MIGRATION_ENDPOINT'))
->start();
return view('migration.completed');
}
@ -224,51 +208,63 @@ class StepsController extends BaseController
*
* @return string
*/
public function getMigrationFile()
public function generateMigrationData(array $data): array
{
$this->account = Auth::user()->account;
$migrationData = [];
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
foreach ($data['companies'] as $company) {
$account = Account::where('account_key', $company['id'])->firstOrFail();
$output = fopen('php://output', 'w') or Utils::fatalError();
$this->account = $account;
$fileName = "{$accountKey}-{$date}-invoiceninja";
$date = date('Y-m-d');
$accountKey = $this->account->account_key;
$data = [
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'payment_terms' => $this->getPaymentTerms(),
'clients' => $this->getClients(),
'vendors' => $this->getVendors(),
'projects' => $this->getProjects(),
'products' => $this->getProducts(),
'invoices' => $this->getInvoices(),
'recurring_invoices' => $this->getRecurringInvoices(),
'quotes' => $this->getQuotes(),
'credits' => $this->getCreditsNotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'documents' => $this->getDocuments(),
'company_gateways' => $this->getCompanyGateways(),
'client_gateway_tokens' => $this->getClientGatewayTokens(),
'expense_categories' => $this->getExpenseCategories(),
'task_statuses' => $this->getTaskStatuses(),
'expenses' => $this->getExpenses(),
'tasks' => $this->getTasks(),
];
$output = fopen('php://output', 'w') or Utils::fatalError();
$file = storage_path("migrations/{$fileName}.zip");
$fileName = "{$accountKey}-{$date}-invoiceninja";
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($data, JSON_PRETTY_PRINT));
$zip->close();
$localMigrationData['data'] = [
'company' => $this->getCompany(),
'users' => $this->getUsers(),
'tax_rates' => $this->getTaxRates(),
'payment_terms' => $this->getPaymentTerms(),
'clients' => $this->getClients(),
'vendors' => $this->getVendors(),
'projects' => $this->getProjects(),
'products' => $this->getProducts(),
'invoices' => $this->getInvoices(),
'recurring_invoices' => $this->getRecurringInvoices(),
'quotes' => $this->getQuotes(),
'credits' => $this->getCreditsNotes(),
'payments' => array_merge($this->getPayments(), $this->getCredits()),
'documents' => $this->getDocuments(),
'company_gateways' => $this->getCompanyGateways(),
'client_gateway_tokens' => $this->getClientGatewayTokens(),
'expense_categories' => $this->getExpenseCategories(),
'task_statuses' => $this->getTaskStatuses(),
'expenses' => $this->getExpenses(),
'tasks' => $this->getTasks(),
];
$localMigrationData['force'] = array_key_exists('force', $company) ? true : false;
$file = storage_path("migrations/{$fileName}.zip");
$zip = new \ZipArchive();
$zip->open($file, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
$zip->addFromString('migration.json', json_encode($localMigrationData, JSON_PRETTY_PRINT));
$zip->close();
$localMigrationData['file'] = $file;
$migrationData[] = $localMigrationData;
}
return $migrationData;
// header('Content-Type: application/zip');
// header('Content-Length: ' . filesize($file));
// header("Content-Disposition: attachment; filename={$fileName}.zip");
return $file;
}
}

View File

@ -38,24 +38,21 @@ class AuthService
$body = Body::json($data);
$response = Request::post($this->getUrl(), $this->getHeaders(), $body);
try {
$response = Request::post($this->getUrl(), $this->getHeaders(), $body);
if ($response->code == 200) {
try {
$this->isSuccessful = true;
$this->token = $response->body->data[0]->token->token;
} catch (\Exception $e) {
info($e->getMessage());
$this->isSuccessful = true;
$this->token = $response->body->data[0]->token->token;
if (in_array($response->code, [401, 422, 500])) {
$this->isSuccessful = false;
$this->errors = [trans('texts.migration_went_wrong')];
$this->processErrors($response->body);
}
}
} catch (\Exception $e) {
info($e->getMessage());
if (in_array($response->code, [401, 422, 500])) {
$this->isSuccessful = false;
$this->processErrors($response->body);
$this->errors = [trans('texts.migration_went_wrong')];
}
return $this;

View File

@ -2,46 +2,34 @@
namespace App\Services\Migration;
use App\Models\Account;
use Unirest\Request;
use Unirest\Request\Body;
class CompanyService
{
protected $token;
protected $endpoint = 'https://app.invoiceninja.com';
protected $uri = '/api/v1/companies';
protected $errors = [];
protected $isSuccessful;
protected $companies = [];
public function __construct(string $token)
{
$this->token = $token;
}
public function endpoint(string $endpoint)
{
$this->endpoint = $endpoint;
return $this;
}
public function start()
{
$response = Request::get($this->getUrl(), $this->getHeaders());
try {
foreach (session(SESSION_USER_ACCOUNTS) as $company) {
$account = Account::find($company->account_id);
if ($response->code == 200) {
$this->isSuccessful = true;
foreach($response->body->data as $company) {
$this->companies[] = $company;
if ($account) {
$this->companies[] = [
'id' => $company->account_id,
'name' => $account->name,
'company_key' => $account->account_key,
];
}
}
}
if (in_array($response->code, [401, 422, 500])) {
$this->isSuccessful = true;
} catch (\Exception $th) {
$this->isSuccessful = false;
$this->processErrors($response->body);
$this->errors = [];
}
return $this;
@ -61,29 +49,8 @@ class CompanyService
return [];
}
public function getErrors()
{
return $this->errors;
}
private function getHeaders()
{
return [
'X-Requested-With' => 'XMLHttpRequest',
'X-Api-Token' => $this->token,
];
}
private function getUrl()
{
return $this->endpoint . $this->uri;
}
private function processErrors($errors)
{
$array = (array) $errors;
$this->errors = $array;
}
}

View File

@ -4,42 +4,29 @@ namespace App\Services\Migration;
use Illuminate\Support\Facades\Storage;
use Unirest\Request;
use Unirest\Request\Body;
class CompleteService
{
protected $token;
protected $company;
protected $file;
protected $endpoint = 'https://app.invoiceninja.com';
protected $uri = '/api/v1/migration/start/';
protected $uri = 'api/v1/migration/start';
protected $errors = [];
protected $isSuccessful;
protected $force = false;
protected $companyKey;
protected $data;
public function __construct(string $token)
{
$this->token = $token;
}
public function file($file)
public function data(array $data)
{
$this->file = $file;
return $this;
}
public function force($option)
{
$this->force = $option;
return $this;
}
public function company($company)
{
$this->company = $company;
$this->data = $data;
return $this;
}
@ -51,26 +38,29 @@ class CompleteService
return $this;
}
public function companyKey(string $key)
{
$this->companyKey = $key;
return $this;
}
public function start()
{
$body = [
'migration' => \Unirest\Request\Body::file($this->file, 'application/zip'),
'force' => $this->force,
'company_key' => $this->companyKey,
];
$files = [];
$response = Request::post($this->getUrl(), $this->getHeaders(), $body);
foreach ($this->data as $companyKey => $companyData) {
$data[] = [
'company_key' => $companyKey,
'force' => $companyData['force'],
];
$files[$companyKey] = $companyData['file'];
}
$body = \Unirest\Request\Body::multipart(['companies' => json_encode($data)], $files);
try {
$response = Request::post($this->getUrl(), $this->getHeaders(), $body);
} catch (\Exception $e) {
info($e->getMessage());
}
if ($response->code == 200) {
$this->isSuccessful = true;
$this->deleteFile();
}
if (in_array($response->code, [401, 422, 500])) {
@ -88,7 +78,6 @@ class CompleteService
return $this->isSuccessful;
}
public function getErrors()
{
return $this->errors;
@ -105,11 +94,11 @@ class CompleteService
private function getUrl()
{
return $this->endpoint . $this->uri . $this->company;
return "{$this->endpoint}/{$this->uri}";
}
public function deleteFile()
public function deleteFile(string $path)
{
Storage::delete($this->file);
Storage::delete($path);
}
}

View File

@ -3790,6 +3790,8 @@ $LANG = array(
'tax_name1' => 'Tax Name 1',
'tax_name2' => 'Tax Name 2',
'transaction_id' => 'Transaction ID',
'migration_select_company_label' => 'Awesome! Please select the companies you would like to migrate.',
'force_migration' => 'Force migration',
'invoice_late' => 'Invoice Late',
'quote_expired' => 'Quote Expired',
'recurring_invoice_total' => 'Invoice Total',

View File

@ -11,22 +11,23 @@
<h3 class="panel-title">{!! trans('texts.welcome_to_the_new_version') !!}</h3>
</div>
<div class="panel-body">
<h4>Awesome! Please select the company you would like to apply migration.</h4>
<h4>{!! trans('texts.migration_select_company_label') !!}</h4>
<form action="{{ url('/migration/companies') }}" method="post" id="auth-form">
{{ csrf_field() }}
<input type="hidden" name="account_key" value="{{ auth()->user()->account->account_key }}">
@foreach($companies as $company)
<div class="form-check">
<input class="form-check-input" id="company_{{ $company->id }}" type="checkbox" name="companies[{{ $company->id }}][id]" id="company1" value="{{ $company->id }}" checked>
<label class="form-check-label" for="company_{{ $company->id }}">
Name: {{ $company->settings->name }} ID: {{ $company->id }}
<input class="form-check-input" id="{{ $company['company_key'] }}" type="checkbox" name="companies[{{ $company['company_key'] }}][id]" value="{{ $company['company_key'] }}">
<label class="form-check-label" for="{{ $company['company_key'] }}">
{{ $company['name'] }}
</label>
</div>
<div class="form-group">
<input type="checkbox" name="companies[{{ $company->id }}][force]">
<label for="force">Force migration</label>
<small>* All current company data will be wiped.</small>
<label for="companies[{{ $company['company_key'] }}][force]">
<input type="checkbox" id="companies[{{ $company['company_key'] }}][force]" name="companies[{{ $company['company_key'] }}][force]">
<small>{!! trans('texts.force_migration') !!}</small>
</label>
</div>
@endforeach
</form>