Merge pull request #4360 from turbo124/v5-develop

Fixes for migrations
This commit is contained in:
David Bomba 2020-11-25 20:24:06 +11:00 committed by GitHub
commit efbf8069b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 769 additions and 338 deletions

View File

@ -22,6 +22,7 @@ use App\Models\Credit;
use App\Models\Invitation;
use App\Models\Invoice;
use App\Models\InvoiceInvitation;
use App\Models\Payment;
use App\Utils\Ninja;
use Carbon;
use DB;
@ -299,6 +300,9 @@ class CheckData extends Command
foreach (Client::cursor() as $client) {
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$credit_balance = $client->credits->where('is_deleted', false)->sum('balance');
$invoice_balance -= $credit_balance;
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
@ -322,8 +326,11 @@ class CheckData extends Command
$total_invoice_payments = 0;
foreach ($client->invoices->where('is_deleted', false)->where('status_id', '>', 1) as $invoice) {
$total_amount = $invoice->payments->whereNull('deleted_at')->sum('pivot.amount');
$total_refund = $invoice->payments->whereNull('deleted_at')->sum('pivot.refunded');
// $total_amount = $invoice->payments->whereNull('deleted_at')->sum('pivot.amount');
// $total_refund = $invoice->payments->whereNull('deleted_at')->sum('pivot.refunded');
$total_amount = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
$total_refund = $invoice->payments->where('is_deleted', false)->whereIn('status_id', [Payment::STATUS_COMPLETED, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
$total_invoice_payments += ($total_amount - $total_refund);
}
@ -357,8 +364,8 @@ class CheckData extends Command
Client::cursor()->each(function ($client) use ($wrong_balances) {
$client->invoices->where('is_deleted', false)->whereIn('status_id', '!=', Invoice::STATUS_DRAFT)->each(function ($invoice) use ($wrong_balances, $client) {
$total_amount = $invoice->payments->sum('pivot.amount');
$total_refund = $invoice->payments->sum('pivot.refunded');
$total_amount = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.amount');
$total_refund = $invoice->payments->whereIn('status_id', [Payment::STATUS_PAID, Payment:: STATUS_PENDING, Payment::STATUS_PARTIALLY_REFUNDED])->sum('pivot.refunded');
$total_credit = $invoice->credits->sum('amount');
$total_paid = $total_amount - $total_refund;
@ -383,7 +390,11 @@ class CheckData extends Command
$wrong_paid_to_dates = 0;
foreach (Client::cursor() as $client) {
$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
//$invoice_balance = $client->invoices->where('is_deleted', false)->where('status_id', '>', 1)->sum('balance');
$invoice_balance = Invoice::where('client_id', $client->id)->where('is_deleted', false)->where('status_id', '>', 1)->withTrashed()->sum('balance');
$client_balance = Credit::where('client_id', $client->id)->where('is_deleted', false)->withTrashed()->sum('balance');
$invoice_balance -= $client_balance;
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
@ -395,7 +406,7 @@ class CheckData extends Command
}
}
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect paid_to_dates");
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect client balances");
}
private function checkLogoFiles()

View File

@ -125,6 +125,7 @@ class ImportMigrations extends Command
{
$company = Company::factory()->create([
'account_id' => $account->id,
'is_disabled' => true,
]);
if (! $account->default_company_id) {

View File

@ -51,7 +51,7 @@ class InvoiceItem
public $custom_value4 = '';
public $type_id = 1; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee
public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee
public static $casts = [
'type_id' => 'string',

View File

@ -160,6 +160,18 @@ class InvoiceSum
{
$this->total += $this->total_taxes;
if($this->invoice->custom_value1 > 0)
$this->total += $this->invoice->custom_value1;
if($this->invoice->custom_value2 > 0)
$this->total += $this->invoice->custom_value2;
if($this->invoice->custom_value3 > 0)
$this->total += $this->invoice->custom_value3;
if($this->invoice->custom_value4 > 0)
$this->total += $this->invoice->custom_value4;
return $this;
}

View File

@ -66,7 +66,7 @@ class InvoiceSumInclusive
->calculateCustomValues()
->calculateInvoiceTaxes()
->setTaxMap()
// ->calculateTotals()
->calculateTotals() //just don't add the taxes!!
->calculateBalance()
->calculatePartial();
@ -170,7 +170,19 @@ class InvoiceSumInclusive
private function calculateTotals()
{
$this->total += $this->total_taxes;
//$this->total += $this->total_taxes;
if($this->invoice->custom_value1 > 0)
$this->total += $this->invoice->custom_value1;
if($this->invoice->custom_value2 > 0)
$this->total += $this->invoice->custom_value2;
if($this->invoice->custom_value3 > 0)
$this->total += $this->invoice->custom_value3;
if($this->invoice->custom_value4 > 0)
$this->total += $this->invoice->custom_value4;
return $this;
}

View File

@ -98,6 +98,24 @@ class MigrationController extends BaseController
return response()->json(['message' => 'Company purged'], 200);
}
private function purgeCompanyWithForceFlag(Company $company)
{
$account = $company->account;
$company_id = $company->id;
$company->delete();
/*Update the new default company if necessary*/
if ($company_id == $account->default_company_id && $account->companies->count() >= 1) {
$new_default_company = $account->companies->first();
if ($new_default_company) {
$account->default_company_id = $new_default_company->id;
$account->save();
}
}
}
/**
* Purge Company but save settings.
*
@ -201,6 +219,7 @@ class MigrationController extends BaseController
*/
public function startMigration(Request $request)
{
$companies = json_decode($request->companies);
if (app()->environment() === 'local') {
@ -208,21 +227,21 @@ class MigrationController extends BaseController
}
foreach ($companies as $company) {
$is_valid = $request->file($company->company_key)->isValid();
$is_valid = $request->file($company->company_index)->isValid();
if (!$is_valid) {
// We might want to send user something's wrong with migration or nope?
continue;
}
$user = auth()->user();
// Look for possible existing company (based on company keys).
$existing_company = Company::where('company_key', $request->company_key)->first();
$existing_company = Company::whereRaw('BINARY `company_key` = ?', [$company->company_key])->first();
$checks = [
'existing_company' => (bool) $existing_company,
'existing_company' => $existing_company ? (bool)1 : false,
'force' => property_exists($company, 'force') ? (bool) $company->force : false,
];
@ -230,7 +249,7 @@ class MigrationController extends BaseController
if ($checks['existing_company'] == true && $checks['force'] == false) {
info('Migrating: Existing company without force. (CASE_01)');
MailRouter::dispatch(new ExistingMigration(), $company, $user);
MailRouter::dispatch(new ExistingMigration(), $existing_company, $user);
return response()->json([
'_id' => Str::uuid(),
@ -241,7 +260,8 @@ class MigrationController extends BaseController
// If there's existing company and force ** is provided ** - purge the company and migrate again.
if ($checks['existing_company'] == true && $checks['force'] == true) {
$this->purgeCompany($existing_company);
info("purging the existing company here");
$this->purgeCompanyWithForceFlag($existing_company);
$account = auth()->user()->account;
$fresh_company = (new ImportMigrations())->getCompany($account);
@ -300,10 +320,10 @@ class MigrationController extends BaseController
]);
}
$migration_file = $request->file($company->company_key)
$migration_file = $request->file($company->company_index)
->storeAs(
'migrations',
$request->file($company->company_key)->getClientOriginalName()
$request->file($company->company_index)->getClientOriginalName()
);
if (app()->environment() == 'testing') {
@ -311,7 +331,7 @@ class MigrationController extends BaseController
}
try {
StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $fresh_company)->delay(now()->addSeconds(60));
StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $fresh_company)->delay(now()->addSeconds(5));
} catch (\Exception $e) {
info($e->getMessage());
}

View File

@ -70,7 +70,7 @@ class UpdateOrCreateProduct implements ShouldQueue
continue;
}
$product = Product::firstOrNew(['product_key' => $item->product_key, 'company_id' => $this->invoice->company->id]);
$product = Product::withTrashed()->firstOrNew(['product_key' => $item->product_key, 'company_id' => $this->invoice->company->id]);
$product->product_key = $item->product_key;
$product->notes = isset($item->notes) ? $item->notes : '';
@ -94,4 +94,10 @@ class UpdateOrCreateProduct implements ShouldQueue
$product->save();
}
}
public function failed($exception = null)
{
info("update create failed with = ");
info(print_r($exception->getMessage(),1));
}
}

View File

@ -74,6 +74,7 @@ use App\Repositories\VendorRepository;
use App\Utils\Traits\CleanLineItems;
use App\Utils\Traits\CompanyGatewayFeesAndLimitsSaver;
use App\Utils\Traits\MakesHash;
use App\Utils\Traits\SavesDocuments;
use App\Utils\Traits\Uploadable;
use Exception;
use Illuminate\Bus\Queueable;
@ -93,10 +94,11 @@ class Import implements ShouldQueue
use MakesHash;
use CleanLineItems;
use Uploadable;
use SavesDocuments;
/**
* @var array
*/
private $data;
private $file_path; //the file path - using a different JSON parser here.
/**
* @var Company
@ -115,10 +117,10 @@ class Import implements ShouldQueue
'vendors',
'projects',
'products',
'credits',
'invoices',
'recurring_invoices',
'quotes',
'credits',
'payments',
'company_gateways',
'client_gateway_tokens',
@ -126,7 +128,7 @@ class Import implements ShouldQueue
'task_statuses',
'expenses',
'tasks',
// //'documents',
'documents',
];
/**
@ -150,9 +152,9 @@ class Import implements ShouldQueue
public $tries = 1;
public $timeout = 86400;
public $timeout = 0;
public $backoff = 86430;
// public $backoff = 86430;
// public $maxExceptions = 2;
/**
@ -163,9 +165,9 @@ class Import implements ShouldQueue
* @param User $user
* @param array $resources
*/
public function __construct(array $data, Company $company, User $user, array $resources = [])
public function __construct(string $file_path, Company $company, User $user, array $resources = [])
{
$this->data = $data;
$this->file_path = $file_path;
$this->company = $company;
$this->user = $user;
$this->resources = $resources;
@ -180,18 +182,23 @@ class Import implements ShouldQueue
{
set_time_limit(0);
foreach ($this->data as $key => $resource) {
if (! in_array($key, $this->available_imports)) {
// $jsonStream = \JsonMachine\JsonMachine::fromFile($this->file_path, "/data");
$array = json_decode(file_get_contents($this->file_path), 1);
$data = $array['data'];
foreach ($this->available_imports as $import) {
if (! array_key_exists($import, $data)) {
//throw new ResourceNotAvailableForMigration("Resource {$key} is not available for migration.");
info("Resource {$key} is not available for migration.");
info("Resource {$import} is not available for migration.");
continue;
}
$method = sprintf('process%s', Str::ucfirst(Str::camel($key)));
$method = sprintf('process%s', Str::ucfirst(Str::camel($import)));
info("Importing {$key}");
info("Importing {$import}");
$this->{$method}($resource);
$this->{$method}($data[$import]);
}
$this->setInitialCompanyLedgerBalances();
@ -754,18 +761,18 @@ class Import implements ShouldQueue
unset($modified['id']);
$invoice = $quote_repository->save(
$quote = $quote_repository->save(
$modified,
QuoteFactory::create($this->company->id, $modified['user_id'])
);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "invoices_{$resource['id']}";
$key = "quotes_{$resource['id']}";
$this->ids['quotes'][$key] = [
'old' => $resource['id'],
'new' => $invoice->id,
'new' => $quote->id,
];
}
@ -778,6 +785,7 @@ class Import implements ShouldQueue
private function processPayments(array $data): void
{
Payment::reguard();
$rules = [
@ -811,7 +819,7 @@ class Import implements ShouldQueue
if (isset($modified['invoices'])) {
foreach ($modified['invoices'] as $key => $invoice) {
if($modified['amount'] >= 0)
if($this->tryTransformingId('invoices', $invoice['invoice_id']))
$modified['invoices'][$key]['invoice_id'] = $this->transformId('invoices', $invoice['invoice_id']);
else{
$modified['credits'][$key]['credit_id'] = $this->transformId('credits', $invoice['invoice_id']);
@ -834,6 +842,15 @@ class Import implements ShouldQueue
'new' => $payment->id,
],
];
//depending on the status, we do a final action.
//s$payment = $this->updatePaymentForStatus($payment, $modified['status_id']);
// if($modified['is_deleted'])
// $payment->service()->deletePayment();
// if(isset($modified['deleted_at']))
// $payment->delete();
}
Payment::reguard();
@ -843,12 +860,52 @@ class Import implements ShouldQueue
$payment_repository = null;
}
private function updatePaymentForStatus($payment, $status_id) :Payment
{
// define('PAYMENT_STATUS_PENDING', 1);
// define('PAYMENT_STATUS_VOIDED', 2);
// define('PAYMENT_STATUS_FAILED', 3);
// define('PAYMENT_STATUS_COMPLETED', 4);
// define('PAYMENT_STATUS_PARTIALLY_REFUNDED', 5);
// define('PAYMENT_STATUS_REFUNDED', 6);
switch ($status_id) {
case 1:
return $payment;
break;
case 2:
return $payment->service()->deletePayment();
break;
case 3:
return $payment->service()->deletePayment();
break;
case 4:
return $payment;
break;
case 5:
$payment->status_id = Payment::STATUS_PARTIALLY_REFUNDED;
$payment->save();
return $payment;
break;
case 6:
$payment->status_id = Payment::STATUS_REFUNDED;
$payment->save();
return $payment;
break;
default:
return $payment;
break;
}
}
private function processDocuments(array $data): void
{
Document::unguard();
// Document::unguard();
/* No validators since data provided by database is already valid. */
foreach ($data as $resource) {
foreach($data as $resource)
{
$modified = $resource;
if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && ! array_key_exists('invoices', $this->ids)) {
@ -859,42 +916,67 @@ class Import implements ShouldQueue
throw new ResourceDependencyMissing('Processing documents failed, because of missing dependency - expenses.');
}
/* Remove because of polymorphic joins. */
unset($modified['invoice_id']);
unset($modified['expense_id']);
if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && array_key_exists('invoices', $this->ids)) {
$modified['documentable_id'] = $this->transformId('invoices', $resource['invoice_id']);
$modified['documentable_type'] = Invoice::class;
$invoice_id = $this->transformId('invoices', $resource['invoice_id']);
$entity = Invoice::where('id', $invoice_id)->withTrashed()->first();
}
if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) {
$modified['documentable_id'] = $this->transformId('expenses', $resource['expense_id']);
$modified['documentable_type'] = Expense::class;
$expense_id = $this->transformId('expenses', $resource['expense_id']);
$entity = Expense::where('id', $expense_id)->withTrashed()->first();
}
$modified['user_id'] = $this->processUserId($resource);
$modified['company_id'] = $this->company->id;
$this->saveDocument(file_get_contents($resource['url']), $entity, $is_public = true);
$document = Document::create($modified);
// $entity = $modified['documentable_type']::find($modified['documentable_id']);
// $entity->documents()->save($modified);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$this->ids['documents'] = [
"documents_{$old_user_key}" => [
'old' => $resource['id'],
'new' => $document->id,
],
];
}
Document::reguard();
// foreach ($data as $resource) {
// $modified = $resource;
/*Improve memory handling by setting everything to null when we have finished*/
$data = null;
// if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && ! array_key_exists('invoices', $this->ids)) {
// throw new ResourceDependencyMissing('Processing documents failed, because of missing dependency - invoices.');
// }
// if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && ! array_key_exists('expenses', $this->ids)) {
// throw new ResourceDependencyMissing('Processing documents failed, because of missing dependency - expenses.');
// }
// /* Remove because of polymorphic joins. */
// unset($modified['invoice_id']);
// unset($modified['expense_id']);
// if (array_key_exists('invoice_id', $resource) && $resource['invoice_id'] && array_key_exists('invoices', $this->ids)) {
// $modified['documentable_id'] = $this->transformId('invoices', $resource['invoice_id']);
// $modified['documentable_type'] = Invoice::class;
// }
// if (array_key_exists('expense_id', $resource) && $resource['expense_id'] && array_key_exists('expenses', $this->ids)) {
// $modified['documentable_id'] = $this->transformId('expenses', $resource['expense_id']);
// $modified['documentable_type'] = Expense::class;
// }
// $modified['user_id'] = $this->processUserId($resource);
// $modified['company_id'] = $this->company->id;
// $document = Document::create($modified);
// // $entity = $modified['documentable_type']::find($modified['documentable_id']);
// // $entity->documents()->save($modified);
// $old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
// $this->ids['documents'] = [
// "documents_{$old_user_key}" => [
// 'old' => $resource['id'],
// 'new' => $document->id,
// ],
// ];
// }
// Document::reguard();
// /*Improve memory handling by setting everything to null when we have finished*/
// $data = null;
}
private function processPaymentTerms(array $data) :void
@ -1113,14 +1195,13 @@ class Import implements ShouldQueue
$project = Project::Create($modified);
$old_user_key = array_key_exists('user_id', $resource) ?? $this->user->id;
$key = "projects_{$resource['id']}";
$this->ids['projects'] = [
"projects_{$old_user_key}" => [
'old' => $resource['id'],
'new' => $project->id,
],
$this->ids['projects'][$key] = [
'old' => $resource['id'],
'new' => $project->id,
];
}
Project::reguard();
@ -1208,6 +1289,7 @@ class Import implements ShouldQueue
public function transformId($resource, string $old): int
{
if (! array_key_exists($resource, $this->ids)) {
info(print_r($resource,1));
throw new Exception("Resource {$resource} not available.");
}
@ -1218,6 +1300,19 @@ class Import implements ShouldQueue
return $this->ids[$resource]["{$resource}_{$old}"]['new'];
}
private function tryTransformingId($resource, string $old): ?int
{
if (! array_key_exists($resource, $this->ids)) {
return false;
}
if (! array_key_exists("{$resource}_{$old}", $this->ids[$resource])) {
return false;
}
return $this->ids[$resource]["{$resource}_{$old}"]['new'];
}
/**
* Process & handle user_id.
*

View File

@ -53,7 +53,7 @@ class StartMigration implements ShouldQueue
*/
public $tries = 1;
public $timeout = 86400;
public $timeout = 0;
// public $maxExceptions = 2;
@ -107,9 +107,9 @@ class StartMigration implements ShouldQueue
throw new NonExistingMigrationFile('Migration file does not exist, or it is corrupted.');
}
$data = json_decode(file_get_contents($file), 1);
Import::dispatchNow($data['data'], $this->company, $this->user);
//$data = json_decode(file_get_contents($file), 1);
//Import::dispatchNow($data['data'], $this->company, $this->user);
Import::dispatchNow($file, $this->company, $this->user);
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
Mail::to($this->user)->send(new MigrationFailed($e, $e->getMessage()));

View File

@ -46,8 +46,6 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use Laracasts\Presenter\PresentableTrait;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
use Staudenmeir\EloquentHasManyDeep\HasTableAlias;
class Company extends BaseModel
{
@ -55,8 +53,6 @@ class Company extends BaseModel
use MakesHash;
use CompanySettingsSaver;
use ThrottlesEmail;
use HasRelationships;
use HasTableAlias;
const ENTITY_RECURRING_INVOICE = 'recurring_invoice';
const ENTITY_CREDIT = 'credit';

View File

@ -13,11 +13,10 @@ namespace App\Models;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Eloquent\SoftDeletes;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class CompanyUser extends Pivot
{
use HasRelationships;
use SoftDeletes;
// protected $guarded = ['id'];
@ -95,7 +94,6 @@ class CompanyUser extends Pivot
//return $this->hasMany(CompanyToken::class);
//return $this->hasOne(CompanyToken::class, 'user_id', 'user_id','company_id', 'company_id');
//return $this->hasOneDeep(CompanyToken::class, [CompanyUser::class], ['user_id','company_id'], ['company_id','company_id']);
//return $this->belongsTo(CompanyToken::class, 'user_id', 'user_id');

View File

@ -186,7 +186,7 @@ class Invoice extends BaseModel
public function payments()
{
return $this->morphToMany(Payment::class, 'paymentable')->withPivot('amount', 'refunded')->withTimestamps()->withTrashed();
return $this->morphToMany(Payment::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps();
}
public function company_ledger()

View File

@ -131,12 +131,12 @@ class Payment extends BaseModel
public function invoices()
{
return $this->morphedByMany(Invoice::class, 'paymentable')->withPivot('amount', 'refunded')->withTimestamps();
return $this->morphedByMany(Invoice::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps();
}
public function credits()
{
return $this->morphedByMany(Credit::class, 'paymentable')->withPivot('amount', 'refunded')->withTimestamps();
return $this->morphedByMany(Credit::class, 'paymentable')->withTrashed()->withPivot('amount', 'refunded')->withTimestamps();
}
public function company_ledger()

View File

@ -33,7 +33,6 @@ use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Laracasts\Presenter\PresentableTrait;
use Staudenmeir\EloquentHasManyDeep\HasRelationships;
class User extends Authenticatable implements MustVerifyEmail
{
@ -44,7 +43,6 @@ class User extends Authenticatable implements MustVerifyEmail
use UserSessionAttributes;
use UserSettings;
use Filterable;
use HasRelationships;
use HasFactory;
protected $guard = 'user';

View File

@ -25,7 +25,12 @@ class ClientObserver
*/
public function created(Client $client)
{
WebhookHandler::dispatch(Webhook::EVENT_CREATE_CLIENT, $client, $client->company);
$subscriptions = Webhook::where('company_id', $client->company->id)
->where('event_id', Webhook::EVENT_CREATE_CLIENT)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_CLIENT, $client, $client->company);
}
/**
@ -36,7 +41,12 @@ class ClientObserver
*/
public function updated(Client $client)
{
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_CLIENT, $client, $client->company);
$subscriptions = Webhook::where('company_id', $client->company->id)
->where('event_id', Webhook::EVENT_UPDATE_CLIENT)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_CLIENT, $client, $client->company);
}
/**
@ -47,7 +57,12 @@ class ClientObserver
*/
public function deleted(Client $client)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_CLIENT, $client, $client->company);
$subscriptions = Webhook::where('company_id', $client->company->id)
->where('event_id', Webhook::EVENT_DELETE_CLIENT)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_CLIENT, $client, $client->company);
}
/**

View File

@ -25,7 +25,12 @@ class ExpenseObserver
*/
public function created(Expense $expense)
{
WebhookHandler::dispatch(Webhook::EVENT_CREATE_EXPENSE, $expense, $expense->company);
$subscriptions = Webhook::where('company_id', $expense->company->id)
->where('event_id', Webhook::EVENT_CREATE_EXPENSE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_EXPENSE, $expense, $expense->company);
}
/**
@ -36,7 +41,12 @@ class ExpenseObserver
*/
public function updated(Expense $expense)
{
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_EXPENSE, $expense, $expense->company);
$subscriptions = Webhook::where('company_id', $expense->company->id)
->where('event_id', Webhook::EVENT_UPDATE_EXPENSE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_EXPENSE, $expense, $expense->company);
}
/**
@ -47,7 +57,12 @@ class ExpenseObserver
*/
public function deleted(Expense $expense)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_EXPENSE, $expense, $expense->company);
$subscriptions = Webhook::where('company_id', $expense->company->id)
->where('event_id', Webhook::EVENT_DELETE_EXPENSE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_EXPENSE, $expense, $expense->company);
}
/**

View File

@ -26,7 +26,13 @@ class InvoiceObserver
*/
public function created(Invoice $invoice)
{
WebhookHandler::dispatch(Webhook::EVENT_CREATE_INVOICE, $invoice, $invoice->company);
$subscriptions = Webhook::where('company_id', $invoice->company->id)
->where('event_id', Webhook::EVENT_CREATE_INVOICE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_INVOICE, $invoice, $invoice->company);
}
/**
@ -37,7 +43,12 @@ class InvoiceObserver
*/
public function updated(Invoice $invoice)
{
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
$subscriptions = Webhook::where('company_id', $invoice->company->id)
->where('event_id', Webhook::EVENT_UPDATE_INVOICE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_INVOICE, $invoice, $invoice->company);
}
/**
@ -48,7 +59,12 @@ class InvoiceObserver
*/
public function deleted(Invoice $invoice)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_INVOICE, $invoice, $invoice->company);
$subscriptions = Webhook::where('company_id', $invoice->company->id)
->where('event_id', Webhook::EVENT_DELETE_INVOICE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_INVOICE, $invoice, $invoice->company);
}
/**

View File

@ -26,7 +26,13 @@ class PaymentObserver
*/
public function created(Payment $payment)
{
WebhookHandler::dispatch(Webhook::EVENT_CREATE_PAYMENT, $payment, $payment->company);
$subscriptions = Webhook::where('company_id', $payment->company->id)
->where('event_id', Webhook::EVENT_CREATE_PAYMENT)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_PAYMENT, $payment, $payment->company);
}
/**
@ -47,7 +53,12 @@ class PaymentObserver
*/
public function deleted(Payment $payment)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_PAYMENT, $payment, $payment->company);
$subscriptions = Webhook::where('company_id', $payment->company->id)
->where('event_id', Webhook::EVENT_DELETE_PAYMENT)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_PAYMENT, $payment, $payment->company);
}
/**

View File

@ -25,6 +25,11 @@ class QuoteObserver
*/
public function created(Quote $quote)
{
$subscriptions = Webhook::where('company_id', $quote->company->id)
->where('event_id', Webhook::EVENT_CREATE_QUOTE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_QUOTE, $quote, $quote->company);
}
@ -36,7 +41,12 @@ class QuoteObserver
*/
public function updated(Quote $quote)
{
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_QUOTE, $quote, $quote->company);
$subscriptions = Webhook::where('company_id', $quote->company->id)
->where('event_id', Webhook::EVENT_UPDATE_QUOTE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_QUOTE, $quote, $quote->company);
}
/**
@ -47,7 +57,12 @@ class QuoteObserver
*/
public function deleted(Quote $quote)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_QUOTE, $quote, $quote->company);
$subscriptions = Webhook::where('company_id', $quote->company->id)
->where('event_id', Webhook::EVENT_DELETE_QUOTE)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_QUOTE, $quote, $quote->company);
}
/**

View File

@ -25,7 +25,13 @@ class TaskObserver
*/
public function created(Task $task)
{
WebhookHandler::dispatch(Webhook::EVENT_CREATE_TASK, $task, $task->company);
$subscriptions = Webhook::where('company_id', $task->company->id)
->where('event_id', Webhook::EVENT_CREATE_TASK)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_CREATE_TASK, $task, $task->company);
}
/**
@ -36,7 +42,13 @@ class TaskObserver
*/
public function updated(Task $task)
{
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_TASK, $task, $task->company);
$subscriptions = Webhook::where('company_id', $task->company->id)
->where('event_id', Webhook::EVENT_UPDATE_TASK)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_UPDATE_TASK, $task, $task->company);
}
/**
@ -47,7 +59,14 @@ class TaskObserver
*/
public function deleted(Task $task)
{
WebhookHandler::dispatch(Webhook::EVENT_DELETE_TASK, $task, $task->company);
$subscriptions = Webhook::where('company_id', $task->company->id)
->where('event_id', Webhook::EVENT_DELETE_TASK)
->exists();
if($subscriptions)
WebhookHandler::dispatch(Webhook::EVENT_DELETE_TASK, $task, $task->company);
}
/**

View File

@ -73,6 +73,9 @@ class ActivityRepository extends BaseRepository
*/
public function createBackup($entity, $activity)
{
if($entity->company->is_disabled)
return;
$backup = new Backup();
if (get_class($entity) == Invoice::class || get_class($entity) == Quote::class || get_class($entity) == Credit::class) {

View File

@ -162,9 +162,9 @@ class BaseRepository
$class = new ReflectionClass($model);
if (array_key_exists('client_id', $data)) {
$client = Client::find($data['client_id']);
$client = Client::where('id', $data['client_id'])->withTrashed()->first();
} else {
$client = Client::find($model->client_id);
$client = Client::where('id', $model->client_id)->withTrashed()->first();
}
$state = [];

View File

@ -46,9 +46,9 @@ class InvoiceMigrationRepository extends BaseRepository
$class = new ReflectionClass($model);
if (array_key_exists('client_id', $data)) {
$client = Client::find($data['client_id']);
$client = Client::where('id', $data['client_id'])->withTrashed()->first();
} else {
$client = Client::find($model->client_id);
$client = Client::where('id', $model->client_id)->withTrashed()->first();
}
$state = [];

View File

@ -87,7 +87,15 @@ class PaymentMigrationRepository extends BaseRepository
/*Fill the payment*/
$payment->fill($data);
$payment->status_id = Payment::STATUS_COMPLETED;
//$payment->status_id = Payment::STATUS_COMPLETED;
if(!array_key_exists('status_id', $data)){
info("payment with no status id?");
info(print_r($data,1));
}
$payment->status_id = $data['status_id'];
$payment->deleted_at = $data['deleted_at'] ?: NULL;
$payment->save();
/*Ensure payment number generated*/
@ -102,13 +110,15 @@ class PaymentMigrationRepository extends BaseRepository
/*Iterate through invoices and apply payments*/
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
$invoice_totals = array_sum(array_column($data['invoices'], 'amount'));
$refund_totals = array_sum(array_column($data['invoices'], 'refunded'));
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->get();
$invoices = Invoice::whereIn('id', array_column($data['invoices'], 'invoice_id'))->withTrashed()->get();
$payment->invoices()->saveMany($invoices);
$payment->invoices->each(function ($inv) use ($invoice_totals) {
$payment->invoices->each(function ($inv) use ($invoice_totals, $refund_totals) {
$inv->pivot->amount = $invoice_totals;
$inv->pivot->refunded = $refund_totals;
$inv->pivot->save();
});
}
@ -116,7 +126,7 @@ class PaymentMigrationRepository extends BaseRepository
if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) {
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->get();
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->withTrashed()->get();
$payment->credits()->saveMany($credits);
@ -164,7 +174,7 @@ class PaymentMigrationRepository extends BaseRepository
*/
private function processExchangeRates($data, $payment)
{
$client = Client::find($data['client_id']);
$client = Client::where('id', $data['client_id'])->withTrashed()->first();
$client_currency = $client->getSetting('currency_id');
$company_currency = $client->company->settings->currency_id;

View File

@ -12,6 +12,7 @@
namespace App\Repositories;
use App\Events\Payment\PaymentWasCreated;
use App\Events\Payment\PaymentWasDeleted;
use App\Factory\CreditFactory;
use App\Jobs\Credit\ApplyCreditPayment;
use App\Libraries\Currency\Conversion\CurrencyApi;
@ -72,7 +73,7 @@ class PaymentRepository extends BaseRepository
$this->processExchangeRates($data, $payment);
$is_existing_payment = false;
$client = Client::find($data['client_id']);
$client = Client::where('id', $data['client_id'])->withTrashed()->first();
/*We only update the paid to date ONCE per payment*/
if (array_key_exists('invoices', $data) && is_array($data['invoices']) && count($data['invoices']) > 0) {
@ -146,7 +147,7 @@ class PaymentRepository extends BaseRepository
//todo optimize into a single query
foreach ($data['credits'] as $paid_credit) {
$credit = Credit::find($this->decodePrimaryKey($paid_credit['credit_id']));
$credit = Credit::withTrashed()->find($this->decodePrimaryKey($paid_credit['credit_id']));
if ($credit) {
ApplyCreditPayment::dispatchNow($credit, $payment, $paid_credit['amount'], $credit->company);
@ -198,9 +199,12 @@ class PaymentRepository extends BaseRepository
return;
}
$payment->service()->deletePayment();
$payment = $payment->service()->deletePayment();
return parent::delete($payment);
event(new PaymentWasDeleted($payment, $payment->company, Ninja::eventVars()));
return $payment;
//return parent::delete($payment);
}
public function restore($payment)

View File

@ -36,11 +36,15 @@ class DeletePayment
public function run()
{
if($this->payment->is_deleted)
return $this->payment;
return $this->setStatus(Payment::STATUS_CANCELLED) //sets status of payment
->updateCreditables() //return the credits first
->adjustInvoices()
->updateClient()
->deletePaymentables()
->cleanupPayment()
->save();
}
@ -52,6 +56,15 @@ class DeletePayment
//set applied amount to 0
private function cleanupPayment()
{
$this->payment->is_deleted = true;
// $entity->save();
$this->payment->delete();
return $this;
}
private function deletePaymentables()
{
$this->payment->paymentables()->update(['deleted_at' => now()]);

View File

@ -68,6 +68,7 @@ class Phantom
$file_path = $path.$entity_obj->number.'.pdf';
$url = config('ninja.app_url').'phantom/'.$entity.'/'.$invitation->key.'?phantomjs_secret='.config('ninja.phantomjs_secret');
info($url);
$key = config('ninja.phantomjs_key');
$secret = config('ninja.phantomjs_key');

View File

@ -28,7 +28,9 @@ trait CleanLineItems
$cleaned_items = [];
foreach ($items as $item) {
$cleaned_items[] = $this->cleanLineItem($item);
}
return $cleaned_items;

View File

@ -577,10 +577,10 @@ trait GeneratesCounter
$search[] = '{$counter}';
$replace[] = $counter;
$search[] = '{$clientCounter}';
$search[] = '{$client_counter}';
$replace[] = $counter;
$search[] = '{$groupCounter}';
$search[] = '{$group_counter}';
$replace[] = $counter;
if (strstr($pattern, '{$user_id}')) {
@ -600,20 +600,39 @@ trait GeneratesCounter
$replace[] = str_replace($format, $date, $matches[1]);
}
$search[] = '{$custom1}';
$replace[] = $entity->custom_value1;
if($entity instanceof Client || $entity instanceof Vendor){
$search[] = '{$client_custom1}';
$replace[] = $entity->custom_value1;
$search[] = '{$custom2}';
$replace[] = $entity->custom_value2;
$search[] = '{$client_custom2}';
$replace[] = $entity->custom_value2;
$search[] = '{$custom3}';
$replace[] = $entity->custom_value3;
$search[] = '{$client_custom3}';
$replace[] = $entity->custom_value3;
$search[] = '{$custom4}';
$replace[] = $entity->custom_value4;
$search[] = '{$client_custom4}';
$replace[] = $entity->custom_value4;
$search[] = '{$id_number}';
$replace[] = $entity->id_number;
$search[] = '{$id_number}';
$replace[] = $entity->id_number;
}
else
{
$search[] = '{$client_custom1}';
$replace[] = $entity->client->custom_value1;
$search[] = '{$client_custom2}';
$replace[] = $entity->client->custom_value2;
$search[] = '{$client_custom3}';
$replace[] = $entity->client->custom_value3;
$search[] = '{$client_custom4}';
$replace[] = $entity->client->custom_value4;
$search[] = '{$id_number}';
$replace[] = $entity->client->id_number;
}
return str_replace($search, $replace, $pattern);
}

View File

@ -43,4 +43,30 @@ trait SavesDocuments
);
}
}
public function saveDocument($document, $entity, $is_public = true)
{
if ($entity instanceof Company) {
$account = $entity->account;
$company = $entity;
} else {
$account = $entity->company->account;
$company = $entity->company;
}
if (! $account->hasFeature(Account::FEATURE_DOCUMENTS)) {
return false;
}
$document = UploadFile::dispatchNow(
$document,
UploadFile::DOCUMENT,
$entity->user,
$entity->company,
$entity,
null,
$is_public
);
}
}

View File

@ -58,7 +58,6 @@
"predis/predis": "^1.1",
"sentry/sentry-laravel": "^2",
"spatie/browsershot": "^3.37",
"staudenmeir/eloquent-has-many-deep": "^1.11",
"stripe/stripe-php": "^7.50",
"turbo124/beacon": "^1",
"turbo124/laravel-gmail": "^5.0",

473
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -38,14 +38,14 @@ return [
'driver' => 'database',
'table' => 'jobs',
'queue' => 'default',
'retry_after' => 90,
'retry_after' => 90000000,
],
'beanstalkd' => [
'driver' => 'beanstalkd',
'host' => 'localhost',
'queue' => 'default',
'retry_after' => 90,
'retry_after' => 90000000,
'block_for' => 0,
],
@ -63,7 +63,7 @@ return [
'driver' => 'redis',
'connection' => 'default',
'queue' => env('REDIS_QUEUE', 'default'),
'retry_after' => 90,
'retry_after' => 90000000,
'block_for' => null,
],

View File

@ -95,6 +95,55 @@
document.addEventListener('DOMContentLoaded', function(event) {
document.getElementById('loader').style.display = 'none';
});
/*
function invokeServiceWorkerUpdateFlow() {
// you have a better UI here, reloading is not a great user experince here.
const confirmed = confirm('New version of the app is available. Refresh now');
if (confirmed) {
window.location.reload();
}
}
async function handleServiceWorker() {
if ('serviceWorker' in navigator) {
// get the ServiceWorkerRegistration instance
const registration = await navigator.serviceWorker.getRegistration();
// (it is also returned from navigator.serviceWorker.register() function)
if (registration) {
// detect Service Worker update available and wait for it to become installed
registration.addEventListener('updatefound', () => {
if (registration.installing) {
// wait until the new Service worker is actually installed (ready to take over)
registration.installing.addEventListener('statechange', () => {
if (registration.waiting) {
// if there's an existing controller (previous Service Worker), show the prompt
if (navigator.serviceWorker.controller) {
invokeServiceWorkerUpdateFlow(registration);
} else {
// otherwise it's the first install, nothing to do
console.log('Service Worker initialized for the first time');
}
}
});
}
});
let refreshing = false;
// detect controller change and refresh the page
navigator.serviceWorker.addEventListener('controllerchange', () => {
if (!refreshing) {
window.location.reload();
refreshing = true;
}
});
}
}
}
handleServiceWorker();
*/
</script>
<script defer src="main.dart.js?v={{ config('ninja.app_version') }}" type="application/javascript"></script>

View File

@ -167,21 +167,21 @@ class GeneratesCounterTest extends TestCase
public function testInvoiceClientNumberPattern()
{
$settings = $this->company->settings;
$settings->client_number_pattern = '{$year}-{$clientCounter}';
$settings->client_number_pattern = '{$year}-{$client_counter}';
$settings->client_number_counter = 10;
$this->company->settings = $settings;
$this->company->save();
$settings = $this->client->settings;
$settings->client_number_pattern = '{$year}-{$clientCounter}';
$settings->client_number_pattern = '{$year}-{$client_counter}';
$settings->client_number_counter = 10;
$this->client->settings = $settings;
$this->client->save();
$this->client->fresh();
$this->assertEquals($this->client->settings->client_number_counter, 10);
$this->assertEquals($this->client->getSetting('client_number_pattern'), '{$year}-{$clientCounter}');
$this->assertEquals($this->client->getSetting('client_number_pattern'), '{$year}-{$client_counter}');
$invoice_number = $this->getNextClientNumber($this->client);