1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-11-11 05:32:39 +01:00
invoiceninja/app/Jobs/Util/Import.php
Benjamin Beganović 11cc40d23a Migrate commits from 2-migration-with-json into v2 (#3241)
* Scaffold test case

* Import.php tests:
- Basic test scaffold
- Test if exception is thrown when unknown resource
- Company update test

* Migration importer & exception classes

* Company migration test
- Added 3rd parameter for accepting custom resources
- Wip tax_rates migration

* Tax rate migration

* Tax rate update
- Added company_id & user_id property modifiers

* Users migration

* Save IDs for users importing

* Add 'transformIds' method

* Importing clients
- An exception for resource not migration
- Dependency logic
- Removing id on insert

* Exception for unresolved dependency

* Import clients

* Method for inspecting user_id

* Importing invoices

* Importing quotes

* Fix tests & wrap with try-catch

* Fix tax_rates user_id transform

* Working on migration

* Tests for migration

* fixes for test

* Tests for Import.php
- Added ext-json to composer.json

* Tests for Import.php
- Added ext-json to composer.json

* Change migration exceptions to MigrationValidatorFailed

* Fixes for tests and counters

* Unzipping the migration archive
- Changed .gitignore to ignore all local migrations

* Comparing local data with inserted

* Ignore verification - wip

* Fix formatting for api.php

* Uploading file test (wip)

* Fix typo

Co-authored-by: David Bomba <turbo124@gmail.com>
2020-01-24 07:35:00 +11:00

553 lines
15 KiB
PHP

<?php
namespace App\Jobs\Util;
use App\Exceptions\MigrationValidatorFailed;
use App\Exceptions\ResourceDependencyMissing;
use App\Exceptions\ResourceNotAvailableForMigration;
use App\Factory\ClientFactory;
use App\Factory\CreditFactory;
use App\Factory\InvoiceFactory;
use App\Factory\PaymentFactory;
use App\Factory\ProductFactory;
use App\Factory\QuoteFactory;
use App\Factory\TaxRateFactory;
use App\Factory\UserFactory;
use App\Http\Requests\Company\UpdateCompanyRequest;
use App\Http\ValidationRules\ValidUserForCompany;
use App\Jobs\Company\CreateCompanyToken;
use App\Libraries\MultiDB;
use App\Models\Client;
use App\Models\Company;
use App\Models\Credit;
use App\Models\Invoice;
use App\Models\Payment;
use App\Models\Product;
use App\Models\Quote;
use App\Models\TaxRate;
use App\Models\User;
use App\Repositories\ClientContactRepository;
use App\Repositories\ClientRepository;
use App\Repositories\CompanyRepository;
use App\Repositories\CreditRepository;
use App\Repositories\InvoiceRepository;
use App\Repositories\PaymentRepository;
use App\Repositories\ProductRepository;
use App\Repositories\QuoteRepository;
use App\Repositories\UserRepository;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
class Import implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* @var array
*/
private $data;
/**
* @var Company
*/
private $company;
/**
* @var array
*/
private $available_imports = [
'company', 'users', 'tax_rates', 'clients', 'products', 'invoices', 'quotes', 'payments', 'credits',
];
/**
* @var User
*/
private $user;
/**
* Custom list of resources to be imported.
*
* @var array
*/
private $resources;
/**
* Local state manager for ids.
*
* @var array
*/
private $ids = [];
/**
* Create a new job instance.
*
* @param array $data
* @param Company $company
* @param User $user
* @param array $resources
*/
public function __construct(array $data, Company $company, User $user, array $resources = [])
{
$this->data = $data;
$this->company = $company;
$this->user = $user;
$this->resources = $resources;
}
/**
* Execute the job.
*
* @return void
* @throws \Exception
*/
public function handle()
{
foreach ($this->data as $key => $resource) {
if (!in_array($key, $this->available_imports)) {
throw new ResourceNotAvailableForMigration($key);
}
$method = sprintf("process%s", Str::ucfirst(Str::camel($key)));
$this->{$method}($resource);
}
}
/**
* @param array $data
* @throws \Exception
*/
private function processCompany(array $data): void
{
Company::unguard();
$rules = (new UpdateCompanyRequest())->rules();
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
if(isset($data['account_id']))
unset($data['account_id']);
$company_repository = new CompanyRepository();
$company_repository->save($data, $this->company);
}
/**
* @param array $data
* @throws \Exception
*/
private function processTaxRates(array $data): void
{
TaxRate::unguard();
$rules = [
'*.name' => 'required',
//'*.name' => 'required|distinct|unique:tax_rates,name,null,null,company_id,' . $this->company->id,
'*.rate' => 'required|numeric',
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
foreach ($data as $resource) {
$modified = $resource;
$company_id = $this->company->id;
$user_id = $this->processUserId($resource);
if(isset($resource['user_id']))
unset($resource['user_id']);
if(isset($resource['company_id']))
unset($resource['company_id']);
$tax_rate = TaxRateFactory::create($this->company->id, $user_id);
$tax_rate->fill($resource);
$tax_rate->save();
}
}
/**
* @param array $data
* @throws \Exception
*/
private function processUsers(array $data): void
{
User::unguard();
$rules = [
'*.first_name' => ['string'],
'*.last_name' => ['string'],
'*.email' => ['distinct'],
];
// if (config('ninja.db.multi_db_enabled')) {
// array_push($rules['*.email'], new ValidUserForCompany());
// }
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$user_repository = new UserRepository();
foreach ($data as $resource) {
$modified = $resource;
unset($modified['id']);
$user = $user_repository->save($modified, $this->fetchUser($resource['email']));
$user_agent = array_key_exists('token_name', $resource) ?: request()->server('HTTP_USER_AGENT');
CreateCompanyToken::dispatchNow($this->company, $user, $user_agent);
$key = "users_{$resource['id']}";
$this->ids['users'][$key] = [
'old' => $resource['id'],
'new' => $user->id,
];
}
}
/**
* @param array $data
* @throws ResourceDependencyMissing
* @throws \Exception
*/
private function processClients(array $data): void
{
Client::unguard();
$client_repository = new ClientRepository(new ClientContactRepository());
foreach ($data as $key => $resource) {
$modified = $resource;
$modified['company_id'] = $this->company->id;
$modified['user_id'] = $this->processUserId($resource);
unset($modified['id']);
$client = $client_repository->save($modified, ClientFactory::create(
$this->company->id, $modified['user_id'])
);
$key = "clients_{$resource['id']}";
$this->ids['clients'][$key] = [
'old' => $resource['id'],
'new' => $client->id,
];
}
}
private function processProducts(array $data): void
{
Product::unguard();
$rules = [
//'*.product_key' => 'required|distinct|unique:products,product_key,null,null,company_id,' . $this->company->id,
'*.cost' => 'numeric',
'*.price' => 'numeric',
'*.quantity' => 'numeric',
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$product_repository = new ProductRepository();
foreach ($data as $resource) {
$modified = $resource;
$modified['company_id'] = $this->company->id;
$modified['user_id'] = $this->processUserId($resource);
unset($modified['id']);
$product_repository->save($modified, ProductFactory::create(
$this->company->id, $modified['user_id'])
);
}
}
private function processInvoices(array $data): void
{
Invoice::unguard();
$rules = [
'*.client_id' => ['required'],
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$invoice_repository = new InvoiceRepository();
foreach ($data as $resource) {
$modified = $resource;
if (array_key_exists('client_id', $resource) && !array_key_exists('clients', $this->ids)) {
throw new ResourceDependencyMissing(array_key_first($data), 'clients');
}
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
$modified['user_id'] = $this->processUserId($resource);
$modified['company_id'] = $this->company->id;
unset($modified['id']);
$invoice = $invoice_repository->save(
$modified, InvoiceFactory::create($this->company->id, $modified['user_id'])
);
$key = "invoices_{$resource['id']}";
$this->ids['invoices'][$key] = [
'old' => $resource['id'],
'new' => $invoice->id,
];
}
}
private function processCredits(array $data): void
{
Credit::unguard();
$rules = [
'*.client_id' => ['required'],
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$credit_repository = new CreditRepository();
foreach ($data as $resource) {
$modified = $resource;
if (array_key_exists('client_id', $resource) && !array_key_exists('clients', $this->ids)) {
throw new ResourceDependencyMissing(array_key_first($data), 'clients');
}
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
$modified['user_id'] = $this->processUserId($resource);
$modified['company_id'] = $this->company->id;
unset($modified['id']);
$invoice = $credit_repository->save(
$modified, CreditFactory::create($this->company->id, $modified['user_id'])
);
$key = "credits_{$resource['id']}";
$this->ids['credits'][$key] = [
'old' => $resource['id'],
'new' => $invoice->id,
];
}
}
private function processQuotes(array $data): void
{
Quote::unguard();
$rules = [
'*.client_id' => ['required'],
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$quote_repository = new QuoteRepository();
foreach ($data as $resource) {
$modified = $resource;
if (array_key_exists('client_id', $resource) && !array_key_exists('clients', $this->ids)) {
throw new ResourceDependencyMissing(array_key_first($data), 'clients');
}
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
$modified['user_id'] = $this->processUserId($resource);
$modified['company_id'] = $this->company->id;
unset($modified['id']);
$invoice = $quote_repository->save(
$modified, QuoteFactory::create($this->company->id, $modified['user_id'])
);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['quotes'] = [
"quotes_{$old_user_key}" => [
'old' => $old_user_key,
'new' => $invoice->id,
]
];
}
}
private function processPayments(array $data): void
{
Payment::reguard();
$rules = [
'*.amount' => ['required'],
'*.client_id' => ['required'],
];
$validator = Validator::make($data, $rules);
if ($validator->fails()) {
throw new MigrationValidatorFailed($validator->errors());
}
$payment_repository = new PaymentRepository(new CreditRepository());
foreach ($data as $resource) {
$modified = $resource;
if (array_key_exists('client_id', $resource) && !array_key_exists('clients', $this->ids)) {
throw new ResourceDependencyMissing(array_key_first($data), 'clients');
}
$modified['client_id'] = $this->transformId('clients', $resource['client_id']);
$modified['user_id'] = $this->processUserId($resource);
//$modified['invoice_id'] = $this->transformId('invoices', $resource['invoice_id']);
$modified['company_id'] = $this->company->id;
//unset($modified['invoices']);
unset($modified['invoice_id']);
if(isset($modified['invoices']))
{
foreach($modified['invoices'] as $invoice)
$invoice['invoice_id'] = $this->transformId('invoices', $invoice['invoice_id']);
}
$payment = $payment_repository->save(
$modified, PaymentFactory::create($this->company->id, $modified['user_id'])
);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['payments'] = [
"payments_{$old_user_key}" => [
'old' => $old_user_key,
'new' => $payment->id,
]
];
}
}
/**
* |--------------------------------------------------------------------------
* | Additional migration methods.
* |--------------------------------------------------------------------------
* |
* | These methods aren't initialized automatically, so they don't depend on
* | the migration data.
*/
/**
* Cloned from App\Http\Requests\User\StoreUserRequest.
*
* @param string $data
* @return User
*/
public function fetchUser(string $data): User
{
$user = MultiDB::hasUser(['email' => $data]);
if (!$user) {
$user = UserFactory::create();
}
return $user;
}
/**
* @param string $resource
* @param string $old
* @return int
* @throws \Exception
*/
public function transformId(string $resource, string $old): int
{
if (!array_key_exists($resource, $this->ids)) {
throw new \Exception("Resource {$resource} not available.");
}
if (!array_key_exists("{$resource}_{$old}", $this->ids[$resource])) {
throw new \Exception("Missing resource key: {$resource}_{$old}");
}
return $this->ids[$resource]["{$resource}_{$old}"]['new'];
}
/**
* Process & handle user_id
*
* @param array $resource
* @return int|mixed
* @throws \Exception
*/
public function processUserId(array $resource)
{
if (!array_key_exists('user_id', $resource)) {
return $this->user->id;
}
if (array_key_exists('user_id', $resource) && !array_key_exists('users', $this->ids)) {
return $this->user->id;
}
return $this->transformId('users', $resource['user_id']);
}
}