mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
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>
This commit is contained in:
parent
67da87b94f
commit
11cc40d23a
5
.gitignore
vendored
5
.gitignore
vendored
@ -21,4 +21,7 @@ yarn-error.log
|
||||
.env.dusk.local
|
||||
/public/vendors/*
|
||||
public/mix-manifest.json
|
||||
*.log
|
||||
*.log
|
||||
|
||||
# Ignore local migrations
|
||||
storage/migrations
|
||||
|
19
app/Exceptions/MigrationValidatorFailed.php
Normal file
19
app/Exceptions/MigrationValidatorFailed.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class MigrationValidatorFailed extends Exception
|
||||
{
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
// Send, an e-mail & notify users.
|
||||
}
|
||||
}
|
28
app/Exceptions/ProcessingMigrationArchiveFailed.php
Normal file
28
app/Exceptions/ProcessingMigrationArchiveFailed.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class ProcessingMigrationArchiveFailed extends Exception
|
||||
{
|
||||
/**
|
||||
* @var Throwable
|
||||
*/
|
||||
private $previous;
|
||||
|
||||
public function __construct($message = "", $code = 0, Throwable $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
$this->message = $message;
|
||||
$this->code = $code;
|
||||
$this->previous = $previous;
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
return 'Unable to open migration archive.';
|
||||
}
|
||||
|
||||
}
|
24
app/Exceptions/ResourceDependencyMissing.php
Normal file
24
app/Exceptions/ResourceDependencyMissing.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class ResourceDependencyMissing extends Exception
|
||||
{
|
||||
private $resource;
|
||||
private $dependency;
|
||||
|
||||
public function __construct($resource, $dependency)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->resource = $resource;
|
||||
$this->dependency = $dependency;
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
return "Resource '{$this->resource}' depends on '{$this->dependency}'.";
|
||||
}
|
||||
}
|
25
app/Exceptions/ResourceNotAvailableForMigration.php
Normal file
25
app/Exceptions/ResourceNotAvailableForMigration.php
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Throwable;
|
||||
|
||||
class ResourceNotAvailableForMigration extends Exception
|
||||
{
|
||||
private $resource;
|
||||
|
||||
public function __construct($resource)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->resource = $resource;
|
||||
}
|
||||
|
||||
public function report()
|
||||
{
|
||||
// TODO: Handle this nicely, throw response, etc.
|
||||
return "Resource {$this->resource} is not available for the migration.";
|
||||
}
|
||||
|
||||
}
|
@ -12,7 +12,9 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Account\CreateAccountRequest;
|
||||
use App\Http\Requests\Migration\UploadMigrationFileRequest;
|
||||
use App\Jobs\Account\CreateAccount;
|
||||
use App\Jobs\Util\StartMigration;
|
||||
use App\Models\Account;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyUser;
|
||||
@ -136,4 +138,16 @@ class MigrationController extends BaseController
|
||||
|
||||
return response()->json(['message'=>'Settings preserved'], 200);
|
||||
}
|
||||
|
||||
public function uploadMigrationFile(UploadMigrationFileRequest $request)
|
||||
{
|
||||
$file = $request->file('migration')->storeAs(
|
||||
'migrations', $request->file('migration')->getClientOriginalName()
|
||||
);
|
||||
|
||||
/** Not tested. */
|
||||
StartMigration::dispatchNow($file, auth()->user(), auth()->user()->company);
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
}
|
||||
|
30
app/Http/Requests/Migration/UploadMigrationFileRequest.php
Normal file
30
app/Http/Requests/Migration/UploadMigrationFileRequest.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Migration;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UploadMigrationFileRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'migration' => ['required', 'mimes:zip'],
|
||||
];
|
||||
}
|
||||
}
|
552
app/Jobs/Util/Import.php
Normal file
552
app/Jobs/Util/Import.php
Normal file
@ -0,0 +1,552 @@
|
||||
<?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']);
|
||||
}
|
||||
}
|
70
app/Jobs/Util/StartMigration.php
Normal file
70
app/Jobs/Util/StartMigration.php
Normal file
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Util;
|
||||
|
||||
use App\Exceptions\ProcessingMigrationArchiveFailed;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class StartMigration implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
private $filepath;
|
||||
|
||||
/**
|
||||
* @var User
|
||||
*/
|
||||
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* @var Company
|
||||
*/
|
||||
private $company;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param $filepath
|
||||
* @param User $user
|
||||
* @param Company $company
|
||||
*/
|
||||
public function __construct($filepath, User $user, Company $company)
|
||||
{
|
||||
$this->filepath = $filepath;
|
||||
$this->user = $user;
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$zip = new \ZipArchive();
|
||||
$archive = $zip->open($this->filepath);
|
||||
|
||||
$filename = pathinfo($this->filepath, PATHINFO_FILENAME);
|
||||
|
||||
try {
|
||||
if ($archive) {
|
||||
$zip->extractTo(storage_path("migrations/{$filename}"));
|
||||
$zip->close();
|
||||
} else {
|
||||
throw new ProcessingMigrationArchiveFailed();
|
||||
}
|
||||
} catch (ProcessingMigrationArchiveFailed $e) {
|
||||
// TODO: Break the code, stop the migration.. send an e-mail.
|
||||
}
|
||||
|
||||
// Rest of the migration..
|
||||
}
|
||||
}
|
@ -69,11 +69,13 @@ class PaymentRepository extends BaseRepository
|
||||
private function applyPayment(array $data, Payment $payment): ?Payment
|
||||
{
|
||||
|
||||
|
||||
$payment->fill($data);
|
||||
$payment->status_id = Payment::STATUS_COMPLETED;
|
||||
|
||||
$payment->save();
|
||||
|
||||
|
||||
if (!$payment->number)
|
||||
$payment->number = $payment->client->getNextPaymentNumber($payment->client);
|
||||
|
||||
@ -87,12 +89,12 @@ class PaymentRepository extends BaseRepository
|
||||
|
||||
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
|
||||
|
||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->company()->get();
|
||||
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
|
||||
|
||||
$payment->invoices()->saveMany($invoices);
|
||||
|
||||
foreach ($data['invoices'] as $paid_invoice) {
|
||||
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->company()->first();
|
||||
$invoice = Invoice::whereId($paid_invoice['invoice_id'])->first();
|
||||
|
||||
if ($invoice) {
|
||||
ApplyInvoicePayment::dispatchNow($invoice, $payment, $paid_invoice['amount'], $invoice->company);
|
||||
@ -107,12 +109,12 @@ class PaymentRepository extends BaseRepository
|
||||
|
||||
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->company()->get();
|
||||
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->get();
|
||||
|
||||
$payment->credits()->saveMany($credits);
|
||||
|
||||
foreach ($data['credits'] as $paid_credit) {
|
||||
$credit = Credit::whereId($paid_credit['credit_id'])->company()->first();
|
||||
$credit = Credit::whereId($paid_credit['credit_id'])->first();
|
||||
|
||||
if ($credit)
|
||||
ApplyCreditPayment::dispatchNow($paid_credit, $payment, $paid_credit['amount'], $credit->company);
|
||||
@ -145,7 +147,7 @@ class PaymentRepository extends BaseRepository
|
||||
|
||||
foreach ($data['invoices'] as $adjusted_invoice) {
|
||||
|
||||
$invoice = Invoice::whereId($adjusted_invoice['invoice_id'])->company()->first();
|
||||
$invoice = Invoice::whereId($adjusted_invoice['invoice_id'])->first();
|
||||
|
||||
$invoice_total_adjustment += $adjusted_invoice['amount'];
|
||||
|
||||
@ -154,7 +156,7 @@ class PaymentRepository extends BaseRepository
|
||||
//process and insert credit notes
|
||||
foreach ($adjusted_invoice['credits'] as $credit) {
|
||||
|
||||
$credit = $this->credit_repo->save($credit, CreditFactory::create(auth()->user()->company()->id, auth()->user()->id), $invoice);
|
||||
$credit = $this->credit_repo->save($credit, CreditFactory::create(auth()->user()->id, auth()->user()->id), $invoice);
|
||||
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,6 @@ class SystemHealth
|
||||
$system_health = false;
|
||||
}
|
||||
|
||||
|
||||
return [
|
||||
'system_health' => $system_health,
|
||||
'extensions' => self::extensions(),
|
||||
@ -91,7 +90,9 @@ class SystemHealth
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -81,11 +81,24 @@ trait GeneratesCounter
|
||||
$this->resetCounters($client);
|
||||
|
||||
//todo handle if we have specific client patterns in the future
|
||||
$pattern = $client->company->settings->credit_number_pattern;
|
||||
$prefix = $client->company->settings->credit_number_pattern;
|
||||
$padding = $client->company->settings->credit_number_pattern;
|
||||
$pattern = $client->getSetting('credit_number_pattern');
|
||||
//Determine if we are using client_counters
|
||||
if (strpos($pattern, 'clientCounter')) {
|
||||
$counter = $client->settings->credit_number_counter;
|
||||
$counter_entity = $client;
|
||||
} elseif (strpos($pattern, 'groupCounter')) {
|
||||
$counter = $client->group_settings->credit_number_counter;
|
||||
$counter_entity = $client->group_settings;
|
||||
} else {
|
||||
$counter = $client->company->settings->credit_number_counter;
|
||||
$counter_entity = $client->company;
|
||||
}
|
||||
|
||||
$credit_number = $this->checkEntityNumber(Credit::class, $client, $counter, $padding, $prefix, $pattern);
|
||||
//Return a valid counter
|
||||
$pattern = $client->getSetting('credit_number_pattern');
|
||||
$padding = $client->getSetting('counter_padding');
|
||||
|
||||
$credit_number = $this->checkEntityNumber(Credit::class, $client, $counter, $padding, $pattern);
|
||||
|
||||
$this->incrementCounter($client->company, 'credit_number_counter');
|
||||
|
||||
|
@ -60,7 +60,8 @@
|
||||
"laravel/dusk": "^5.0",
|
||||
"mockery/mockery": "^1.0",
|
||||
"nunomaduro/collision": "^2.0",
|
||||
"phpunit/phpunit": "^7.0"
|
||||
"phpunit/phpunit": "^7.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
|
105
routes/api.php
105
routes/api.php
@ -20,105 +20,108 @@ Route::middleware('auth:api')->get('/user', function (Request $request) {
|
||||
|
||||
Route::group(['middleware' => ['api_secret_check']], function () {
|
||||
|
||||
Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit');
|
||||
Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin');
|
||||
Route::post('api/v1/signup', 'AccountController@store')->name('signup.submit');
|
||||
Route::post('api/v1/oauth_login', 'Auth\LoginController@oauthApiLogin');
|
||||
|
||||
});
|
||||
|
||||
Route::group(['api_secret_check','email_db'], function () {
|
||||
Route::group(['api_secret_check', 'email_db'], function () {
|
||||
|
||||
Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit');
|
||||
Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset');
|
||||
Route::post('api/v1/login', 'Auth\LoginController@apiLogin')->name('login.submit');
|
||||
Route::post('api/v1/reset_password', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.reset');
|
||||
|
||||
});
|
||||
|
||||
Route::group(['middleware' => ['api_db','api_secret_check','token_auth','locale'], 'prefix' =>'api/v1', 'as' => 'api.'], function () {
|
||||
Route::group(['middleware' => ['api_db', 'api_secret_check', 'token_auth', 'locale'], 'prefix' => 'api/v1', 'as' => 'api.'], function () {
|
||||
|
||||
Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('activities', 'ActivityController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
Route::resource('clients', 'ClientController'); // name = (clients. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
Route::post('clients/bulk', 'ClientController@bulk')->name('clients.bulk');
|
||||
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('invoices', 'InvoiceController'); // name = (invoices. index / create / show / update / destroy / edit
|
||||
|
||||
Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action');
|
||||
Route::get('invoices/{invoice}/{action}', 'InvoiceController@action')->name('invoices.action');
|
||||
|
||||
Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk');
|
||||
Route::post('invoices/bulk', 'InvoiceController@bulk')->name('invoices.bulk');
|
||||
|
||||
Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit
|
||||
Route::resource('products', 'ProductController'); // name = (products. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk');
|
||||
Route::post('products/bulk', 'ProductController@bulk')->name('products.bulk');
|
||||
|
||||
Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit
|
||||
Route::resource('quotes', 'QuoteController'); // name = (quotes. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
|
||||
Route::post('quotes/bulk', 'QuoteController@bulk')->name('quotes.bulk');
|
||||
|
||||
Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('recurring_invoices', 'RecurringInvoiceController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk');
|
||||
Route::post('recurring_invoices/bulk', 'RecurringInvoiceController@bulk')->name('recurring_invoices.bulk');
|
||||
|
||||
Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
|
||||
Route::resource('recurring_quotes', 'RecurringQuoteController'); // name = (recurring_invoices. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk');
|
||||
Route::post('recurring_quotes/bulk', 'RecurringQuoteController@bulk')->name('recurring_quotes.bulk');
|
||||
|
||||
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit
|
||||
Route::resource('expenses', 'ExpenseController'); // name = (expenses. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
|
||||
Route::post('expenses/bulk', 'ExpenseController@bulk')->name('expenses.bulk');
|
||||
|
||||
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit
|
||||
Route::resource('vendors', 'VendorController'); // name = (vendors. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
|
||||
Route::post('vendors/bulk', 'VendorController@bulk')->name('vendors.bulk');
|
||||
|
||||
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit
|
||||
Route::resource('client_statement', 'ClientStatementController@statement'); // name = (client_statement. index / create / show / update / destroy / edit
|
||||
|
||||
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit
|
||||
Route::resource('payments', 'PaymentController'); // name = (payments. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund');
|
||||
Route::post('payments/refund', 'PaymentController@refund')->name('payments.refund');
|
||||
|
||||
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
|
||||
Route::post('payments/bulk', 'PaymentController@bulk')->name('payments.bulk');
|
||||
|
||||
Route::post('migrate', 'Migration\MigrateController@index')->name('migrate.start');
|
||||
|
||||
// Route::resource('users', 'UserController')->middleware('password_protected'); // name = (users. index / create / show / update / destroy / edit
|
||||
Route::get('users', 'UserController@index');
|
||||
Route::put('users/{user}', 'UserController@update')->middleware('password_protected');
|
||||
Route::post('users', 'UserController@store')->middleware('password_protected');
|
||||
Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected');
|
||||
Route::delete('users/{user}/detach_from_company','UserController@detach')->middleware('password_protected');
|
||||
Route::get('users', 'UserController@index');
|
||||
Route::put('users/{user}', 'UserController@update')->middleware('password_protected');
|
||||
Route::post('users', 'UserController@store')->middleware('password_protected');
|
||||
Route::post('users/{user}/attach_to_company', 'UserController@attach')->middleware('password_protected');
|
||||
Route::delete('users/{user}/detach_from_company', 'UserController@detach')->middleware('password_protected');
|
||||
|
||||
|
||||
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');
|
||||
Route::post('users/bulk', 'UserController@bulk')->name('users.bulk')->middleware('password_protected');
|
||||
|
||||
Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected');
|
||||
Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected');
|
||||
Route::post('migration/purge/{company}', 'MigrationController@purgeCompany')->middleware('password_protected');
|
||||
Route::post('migration/purge_save_settings/{company}', 'MigrationController@purgeCompanySaveSettings')->middleware('password_protected');
|
||||
Route::post('migration/upload_migration', 'MigrationController@uploadMigrationFile')->middleware('password_protected');
|
||||
|
||||
Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit
|
||||
Route::resource('companies', 'CompanyController'); // name = (companies. index / create / show / update / destroy / edit
|
||||
|
||||
Route::resource('company_gateways', 'CompanyGatewayController');
|
||||
Route::resource('company_gateways', 'CompanyGatewayController');
|
||||
|
||||
Route::resource('group_settings', 'GroupSettingController');
|
||||
Route::resource('group_settings', 'GroupSettingController');
|
||||
|
||||
Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('refresh', 'Auth\LoginController@refresh');
|
||||
Route::post('refresh', 'Auth\LoginController@refresh');
|
||||
|
||||
Route::post('templates', 'TemplateController@show')->name('templates.show');
|
||||
Route::post('templates', 'TemplateController@show')->name('templates.show');
|
||||
|
||||
/*
|
||||
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
/*
|
||||
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
|
||||
Route::post('tasks/bulk', 'TaskController@bulk')->name('tasks.bulk');
|
||||
|
||||
|
||||
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
|
||||
Route::resource('credits', 'CreditController'); // name = (credits. index / create / show / update / destroy / edit
|
||||
|
||||
Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
|
||||
Route::post('credits/bulk', 'CreditController@bulk')->name('credits.bulk');
|
||||
|
||||
|
||||
|
||||
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
*/
|
||||
Route::post('support/messages/send', 'Support\Messages\SendingController');
|
||||
Route::get('settings', 'SettingsController@index')->name('user.settings');
|
||||
*/
|
||||
Route::post('support/messages/send', 'Support\Messages\SendingController');
|
||||
});
|
||||
|
||||
Route::fallback('BaseController@notFound');
|
||||
Route::fallback('BaseController@notFound');
|
||||
|
@ -93,4 +93,22 @@ class MigrationTest extends TestCase
|
||||
$this->assertNotNull($this->company->settings->timezone_id);
|
||||
|
||||
}
|
||||
|
||||
public function testMigrationFileUpload()
|
||||
{
|
||||
$data = [];
|
||||
|
||||
$token = $this->company->tokens->first()->token;
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-TOKEN' => $token,
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
])->post('/api/v1/migration/upload_migration', $data);
|
||||
|
||||
dd($response->getContent()); // "{"message":"Access denied","errors":[]}"
|
||||
|
||||
$response->assertStatus(200);
|
||||
$this->assertTrue(file_exists(base_path('migrations/migration/migration.json')));
|
||||
}
|
||||
}
|
604
tests/Unit/Migration/ImportTest.php
Normal file
604
tests/Unit/Migration/ImportTest.php
Normal file
@ -0,0 +1,604 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Migration;
|
||||
|
||||
use App\Exceptions\MigrationValidatorFailed;
|
||||
use App\Exceptions\ResourceDependencyMissing;
|
||||
use App\Exceptions\ResourceNotAvailableForMigration;
|
||||
use App\Jobs\Util\Import;
|
||||
use App\Jobs\Util\StartMigration;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Product;
|
||||
use App\Models\Quote;
|
||||
use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Support\Str;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
class ImportTest extends TestCase
|
||||
{
|
||||
use MockAccountData;
|
||||
use DatabaseTransactions;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
public function testImportClassExists()
|
||||
{
|
||||
$status = class_exists('App\Jobs\Util\Import');
|
||||
|
||||
$this->assertTrue($status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure exception is thrown when resource
|
||||
* is not available for the migration.
|
||||
*/
|
||||
public function testExceptionOnUnavailableResource()
|
||||
{
|
||||
try {
|
||||
$data['panda_bears'] = [
|
||||
'name' => 'Awesome Panda Bear',
|
||||
];
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (ResourceNotAvailableForMigration $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testCompanyUpdating()
|
||||
{
|
||||
$original_company_key = $this->company->company_key;
|
||||
|
||||
$data['company'] = [
|
||||
'company_key' => 0,
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertNotEquals($original_company_key, $this->company->company_key);
|
||||
}
|
||||
|
||||
public function testTaxRatesInserting()
|
||||
{
|
||||
$total_tax_rates = TaxRate::count();
|
||||
|
||||
$data['tax_rates'] = [
|
||||
0 => [
|
||||
'name' => 'My awesome tax rate 1',
|
||||
'rate' => '1.000',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertNotEquals($total_tax_rates, TaxRate::count());
|
||||
}
|
||||
|
||||
public function testTaxRateUniqueValidation()
|
||||
{
|
||||
$original_number = TaxRate::count();
|
||||
|
||||
try {
|
||||
$data['tax_rates'] = [
|
||||
0 => [
|
||||
'name' => '',
|
||||
'rate' => '1.000',
|
||||
],
|
||||
1 => [
|
||||
'name' => 'My awesome tax rate 1',
|
||||
'rate' => '1.000',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (MigrationValidatorFailed $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
$this->assertEquals($original_number, TaxRate::count());
|
||||
}
|
||||
|
||||
public function testUsersImporting()
|
||||
{
|
||||
$original_number = User::count();
|
||||
|
||||
$data['users'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'first_name' => 'David',
|
||||
'last_name' => 'IN',
|
||||
'email' => 'my@awesomemail.com',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, User::count());
|
||||
}
|
||||
|
||||
public function testUserValidator()
|
||||
{
|
||||
$original_number = User::count();
|
||||
|
||||
try {
|
||||
$data['users'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'first_name' => 'David',
|
||||
'last_name' => 'IN',
|
||||
'email' => 'my@awesomemail.com',
|
||||
],
|
||||
1 => [
|
||||
'id' => 2,
|
||||
'first_name' => 'Someone',
|
||||
'last_name' => 'Else',
|
||||
'email' => 'my@awesomemail.com',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (MigrationValidatorFailed $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
$this->assertEquals($original_number, User::count());
|
||||
}
|
||||
|
||||
public function testClientImporting()
|
||||
{
|
||||
$original_number = Client::count();
|
||||
|
||||
$data['users'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'first_name' => 'David',
|
||||
'last_name' => 'IN',
|
||||
'email' => 'my@awesomemail.com',
|
||||
],
|
||||
1 => [
|
||||
'id' => 2,
|
||||
'first_name' => 'Someone',
|
||||
'last_name' => 'Else',
|
||||
'email' => 'my@awesomemail2.com',
|
||||
]
|
||||
];
|
||||
|
||||
$data['clients'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'name' => 'My awesome client',
|
||||
'balance' => '0.00',
|
||||
'user_id' => 1,
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Client::count());
|
||||
}
|
||||
|
||||
public function testProductsImporting()
|
||||
{
|
||||
$original_number = Product::count();
|
||||
|
||||
$data['products'] = [
|
||||
0 => [
|
||||
"company_id" => 1,
|
||||
"user_id" => 1,
|
||||
"custom_value1" => null,
|
||||
"custom_value2" => null,
|
||||
"product_key" => "et",
|
||||
"notes" => "Natus repudiandae occaecati odit est aliquam reiciendis. Nihil sit praesentium excepturi provident nostrum sint. In fugit a dicta voluptas neque quo vel ullam.",
|
||||
"cost" => "5.0000",
|
||||
"quantity" => "0.0000",
|
||||
"tax_name1" => null,
|
||||
"tax_name2" => null,
|
||||
"tax_rate1" => "0.000",
|
||||
"tax_rate2" => "0.000",
|
||||
"created_at" => "2020-01-22",
|
||||
"updated_at" => "2020-01-22",
|
||||
"deleted_at" => null
|
||||
],
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Product::count());
|
||||
}
|
||||
|
||||
public function testInvoicesFailsWithoutClient()
|
||||
{
|
||||
try {
|
||||
$data['invoices'] = [
|
||||
0 => [
|
||||
'client_id' => 1,
|
||||
'is_amount_discount' => false,
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (ResourceDependencyMissing $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testInvoicesImporting()
|
||||
{
|
||||
|
||||
$original_number = Invoice::count();
|
||||
|
||||
$data['clients'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'name' => 'My awesome client',
|
||||
'balance' => '0.00',
|
||||
'user_id' => 1,
|
||||
]
|
||||
];
|
||||
|
||||
$data['invoices'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'client_id' => 1,
|
||||
'discount' => '0.00',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Invoice::count());
|
||||
|
||||
Invoice::where('id', '>=', '0')->forceDelete();
|
||||
|
||||
$this->assertEquals(0, Invoice::count());
|
||||
|
||||
}
|
||||
|
||||
public function testQuotesFailsWithoutClient()
|
||||
{
|
||||
try {
|
||||
$data['quotes'] = [
|
||||
0 => [
|
||||
'client_id' => 1,
|
||||
'is_amount_discount' => false,
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (ResourceDependencyMissing $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testQuotesImporting()
|
||||
{
|
||||
$original_number = Quote::count();
|
||||
|
||||
$data['clients'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'name' => 'My awesome client',
|
||||
'balance' => '0.00',
|
||||
'user_id' => 1,
|
||||
]
|
||||
];
|
||||
|
||||
$data['quotes'] = [
|
||||
0 => [
|
||||
'client_id' => 1,
|
||||
'discount' => '0.00',
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Quote::count());
|
||||
}
|
||||
|
||||
|
||||
public function testImportFileExists()
|
||||
{
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$this->assertTrue(file_exists($migration_file));
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
$this->assertGreaterThan(1, count($migration_array));
|
||||
|
||||
}
|
||||
|
||||
public function testAllImport()
|
||||
{
|
||||
|
||||
//$this->makeTestData();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public function testClientAttributes()
|
||||
{
|
||||
$original_number = Client::count();
|
||||
|
||||
$random_balance = rand(0, 10);
|
||||
|
||||
$data['clients'] = [
|
||||
0 => [
|
||||
'id' => 1,
|
||||
'name' => 'My awesome unique client',
|
||||
'balance' => $random_balance,
|
||||
'user_id' => 1,
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$client = Client::where('name', 'My awesome unique client')
|
||||
->where('balance', $random_balance)
|
||||
->first();
|
||||
|
||||
// Originally was checked with ClientContact::whereEmail() but it throws 'array to string conversion' on insert.
|
||||
|
||||
$this->assertNotNull($client);
|
||||
$this->assertGreaterThan($original_number, Client::count());
|
||||
$this->assertGreaterThanOrEqual(0, $client->balance);
|
||||
}
|
||||
|
||||
public function testInvoiceImporting()
|
||||
{
|
||||
$original_number = Invoice::count();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Invoice::count());
|
||||
}
|
||||
|
||||
public function testInvoiceAttributes()
|
||||
{
|
||||
$original_number = Invoice::count();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Invoice::count());
|
||||
|
||||
$invoice_1 = Invoice::where('number', '0001')
|
||||
->where('discount', '0.00')
|
||||
->where('date', '2020-03-18')
|
||||
->first();
|
||||
|
||||
$invoice_2 = Invoice::where('number', '0018')
|
||||
->where('discount', '0.00')
|
||||
->where('date', '2019-10-15')
|
||||
->first();
|
||||
|
||||
$this->assertNotNull($invoice_1);
|
||||
$this->assertNotNull($invoice_2);
|
||||
|
||||
$this->assertEquals('43.7500', $invoice_1->amount);
|
||||
$this->assertEquals('55.2600', $invoice_2->amount);
|
||||
|
||||
$this->assertEquals('18.7700', $invoice_1->balance);
|
||||
$this->assertEquals('49.3700', $invoice_2->balance);
|
||||
}
|
||||
|
||||
public function testQuoteAttributes()
|
||||
{
|
||||
$original_number = Quote::count();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_number, Invoice::count());
|
||||
|
||||
$quote = Quote::where('number', '0002')
|
||||
->where('discount', '0.00')
|
||||
->where('date', '2020-04-26')
|
||||
->first();
|
||||
|
||||
$this->assertNotNull($quote);
|
||||
$this->assertEquals('0.0000', $quote->amount);
|
||||
$this->assertEquals('0.0000', $quote->balance);
|
||||
}
|
||||
|
||||
public function testPaymentsImport()
|
||||
{
|
||||
$original_count = Payment::count();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_count, Payment::count());
|
||||
}
|
||||
|
||||
public function testPaymentDependsOnClient()
|
||||
{
|
||||
try {
|
||||
$data['payments'] = [
|
||||
0 => [
|
||||
'client_id' => 1,
|
||||
'amount' => 1,
|
||||
]
|
||||
];
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
} catch (ResourceDependencyMissing $e) {
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
}
|
||||
|
||||
public function testQuotesImport()
|
||||
{
|
||||
$original_count = Credit::count();
|
||||
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$this->assertGreaterThan($original_count, Credit::count());
|
||||
}
|
||||
|
||||
public function testMigrationFileExists()
|
||||
{
|
||||
$migration_archive = base_path() . '/tests/Unit/Migration/migration.zip';
|
||||
|
||||
$this->assertTrue(file_exists($migration_archive));
|
||||
}
|
||||
|
||||
public function testMigrationFileBeingExtracted()
|
||||
{
|
||||
$migration_archive = base_path() . '/tests/Unit/Migration/migration.zip';
|
||||
|
||||
StartMigration::dispatchNow($migration_archive, $this->user, $this->company);
|
||||
|
||||
$extracted_archive = storage_path("migrations/migration");
|
||||
$migration_file = storage_path("migrations/migration/migration.json");
|
||||
|
||||
$this->assertTrue(file_exists($extracted_archive));
|
||||
$this->assertTrue(is_dir($extracted_archive));
|
||||
$this->assertTrue(file_exists($migration_file));
|
||||
}
|
||||
|
||||
public function testValidityOfImportedData()
|
||||
{
|
||||
$this->invoice->forceDelete();
|
||||
|
||||
$migration_file = base_path() . '/tests/Unit/Migration/migration.json';
|
||||
|
||||
$migration_array = json_decode(file_get_contents($migration_file), 1);
|
||||
|
||||
|
||||
Import::dispatchNow($migration_array, $this->company, $this->user);
|
||||
|
||||
$differences = [];
|
||||
|
||||
foreach ($migration_array['users'] as $key => $user) {
|
||||
$record = User::where('email', $user['email'])->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['users']['missing'][] = $user['email'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($migration_array['tax_rates'] as $key => $tax_rate) {
|
||||
$record = TaxRate::where('name', $tax_rate['name'])
|
||||
->where('rate', $tax_rate['rate'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['tax_rates']['missing'][] = $tax_rate['name'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($migration_array['clients'] as $key => $client) {
|
||||
$record = Client::where('name', $client['name'])
|
||||
->where('city', $client['city'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['clients']['missing'][] = $client['name'];
|
||||
}
|
||||
}
|
||||
|
||||
/* foreach ($migration_array['products'] as $key => $product) {
|
||||
$record = Product::where('product_key', $product['product_key'])
|
||||
->where('quantity', $product['quantity'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['products']['missing'][] = $product['notes'];
|
||||
}
|
||||
} */
|
||||
|
||||
foreach ($migration_array['invoices'] as $key => $invoices) {
|
||||
$record = Invoice::where('number', $invoices['number'])
|
||||
->where('is_amount_discount', $invoices['is_amount_discount'])
|
||||
->where('due_date', $invoices['due_date'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['invoices']['missing'][] = $invoices['id'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($migration_array['quotes'] as $key => $quote) {
|
||||
$record = Quote::where('number', $quote['number'])
|
||||
->where('is_amount_discount', $quote['is_amount_discount'])
|
||||
->where('due_date', $quote['due_date'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['quotes']['missing'][] = $quote['id'];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($migration_array['payments'] as $key => $payment) {
|
||||
$record = Payment::where('amount', $payment['amount'])
|
||||
->where('applied', $payment['applied'])
|
||||
->where('refunded', $payment['refunded'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['quotes']['missing'][] = $payment['id'];
|
||||
}
|
||||
}
|
||||
|
||||
/*foreach ($migration_array['credits'] as $key => $credit) {
|
||||
$record = Credit::where('number', $credit['number'])
|
||||
->where('date', $credit['date'])
|
||||
->first();
|
||||
|
||||
if (!$record) {
|
||||
$differences['credits']['missing'][] = $credit['id'];
|
||||
}
|
||||
}*/
|
||||
|
||||
$this->assertCount(0, $differences);
|
||||
}
|
||||
}
|
1
tests/Unit/Migration/migration.json
Normal file
1
tests/Unit/Migration/migration.json
Normal file
File diff suppressed because one or more lines are too long
BIN
tests/Unit/Migration/migration.zip
Normal file
BIN
tests/Unit/Migration/migration.zip
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user