mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Merge branch 'v5-develop' into preview
This commit is contained in:
commit
f0b518d156
@ -1 +1 @@
|
||||
5.5.14
|
||||
5.5.17
|
@ -114,6 +114,7 @@ class CheckData extends Command
|
||||
$this->checkEntityInvitations();
|
||||
$this->checkCompanyData();
|
||||
$this->checkBalanceVsPaidStatus();
|
||||
$this->checkDuplicateRecurringInvoices();
|
||||
|
||||
if(Ninja::isHosted())
|
||||
$this->checkAccountStatuses();
|
||||
@ -145,6 +146,23 @@ class CheckData extends Command
|
||||
$this->log .= $str."\n";
|
||||
}
|
||||
|
||||
private function checkDuplicateRecurringInvoices()
|
||||
{
|
||||
|
||||
if(Ninja::isHosted())
|
||||
{
|
||||
$c = Client::on('db-ninja-01')->where('company_id', config('ninja.ninja_default_company_id'))
|
||||
->with('recurring_invoices')
|
||||
->cursor()
|
||||
->each(function ($client){
|
||||
if($client->recurring_invoices()->where('is_deleted', 0)->where('deleted_at', null)->count() > 1)
|
||||
$this->logMessage("Duplicate Recurring Invoice => {$client->custom_value1}");
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private function checkOAuth()
|
||||
{
|
||||
// check for duplicate oauth ids
|
||||
|
@ -232,7 +232,12 @@ class BaseController extends Controller
|
||||
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts.company', 'gateway_tokens', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_client')) {
|
||||
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
// $query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.company_gateways' => function ($query) use ($user) {
|
||||
@ -246,7 +251,11 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_credit')) {
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
// $query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
},
|
||||
'company.designs'=> function ($query) use ($updated_at, $user) {
|
||||
@ -263,7 +272,11 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_expense')) {
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
// $query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
},
|
||||
'company.groups' => function ($query) use ($updated_at, $user) {
|
||||
@ -276,14 +289,25 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_invoice')) {
|
||||
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
// $query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.payments'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('paymentables', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_payment')) {
|
||||
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
// $query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($updated_at, $user) {
|
||||
@ -297,49 +321,88 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_product')) {
|
||||
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
// $query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
'company.projects'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_project')) {
|
||||
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
// $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.purchase_orders'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_purchase_order')) {
|
||||
$query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
// $query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.quotes'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_quote')) {
|
||||
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
// $query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('invitations', 'documents', 'client.gateway_tokens', 'client.group_settings', 'client.company');
|
||||
|
||||
if (! $user->hasPermission('view_recurring_invoice')) {
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
// $query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.recurring_expenses'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_recurring_expense')) {
|
||||
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
// $query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.tasks'=> function ($query) use ($updated_at, $user) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_task')) {
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
// $query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.tax_rates'=> function ($query) use ($updated_at, $user) {
|
||||
@ -349,7 +412,12 @@ class BaseController extends Controller
|
||||
$query->where('updated_at', '>=', $updated_at)->with('contacts', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_vendor')) {
|
||||
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
// $query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($updated_at, $user) {
|
||||
@ -480,7 +548,12 @@ class BaseController extends Controller
|
||||
$query->where('clients.created_at', '>=', $created_at)->with('contacts.company', 'gateway_tokens', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_client')) {
|
||||
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
// $query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('clients.user_id', $user->id)->orWhere('clients.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.company_gateways' => function ($query) use ($user) {
|
||||
@ -494,7 +567,11 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_credit')) {
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
// $query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('credits.user_id', $user->id)->orWhere('credits.assigned_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
},
|
||||
'company.documents'=> function ($query) use ($created_at, $user) {
|
||||
@ -504,7 +581,13 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_expense')) {
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
// $query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('expenses.user_id', $user->id)->orWhere('expenses.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
'company.groups' => function ($query) use ($created_at, $user) {
|
||||
@ -514,14 +597,24 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_invoice')) {
|
||||
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
// $query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('invoices.user_id', $user->id)->orWhere('invoices.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.payments'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('paymentables', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_payment')) {
|
||||
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
// $query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('payments.user_id', $user->id)->orWhere('payments.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.payment_terms'=> function ($query) use ($created_at, $user) {
|
||||
@ -531,42 +624,67 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_product')) {
|
||||
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
// $query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('products.user_id', $user->id)->orWhere('products.assigned_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
},
|
||||
'company.projects'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_project')) {
|
||||
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
// $query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('projects.user_id', $user->id)->orWhere('projects.assigned_user_id', $user->id);
|
||||
});
|
||||
}
|
||||
},
|
||||
'company.purchase_orders'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_purchase_order')) {
|
||||
$query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
// $query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('purchase_orders.user_id', $user->id)->orWhere('purchase_orders.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.quotes'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_quote')) {
|
||||
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
// $query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('quotes.user_id', $user->id)->orWhere('quotes.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('invitations', 'documents', 'client.gateway_tokens', 'client.group_settings', 'client.company');
|
||||
|
||||
if (! $user->hasPermission('view_recurring_invoice')) {
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
// $query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('recurring_invoices.user_id', $user->id)->orWhere('recurring_invoices.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.tasks'=> function ($query) use ($created_at, $user) {
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_task')) {
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
// $query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('tasks.user_id', $user->id)->orWhere('tasks.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.tax_rates' => function ($query) use ($created_at, $user) {
|
||||
@ -576,7 +694,12 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('contacts', 'documents');
|
||||
|
||||
if (! $user->hasPermission('view_vendor')) {
|
||||
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
// $query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('vendors.user_id', $user->id)->orWhere('vendors.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
'company.expense_categories'=> function ($query) use ($created_at, $user) {
|
||||
@ -610,7 +733,12 @@ class BaseController extends Controller
|
||||
$query->where('created_at', '>=', $created_at)->with('documents');
|
||||
|
||||
if (! $user->hasPermission('view_recurring_expense')) {
|
||||
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
// $query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
|
||||
$query->whereNested(function($query) use ($user) {
|
||||
$query->where('recurring_expenses.user_id', $user->id)->orWhere('recurring_expenses.assigned_user_id', $user->id);
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
]
|
||||
|
@ -35,6 +35,7 @@ class CreditController extends Controller
|
||||
$data = [
|
||||
'credit' => $credit,
|
||||
'key' => $invitation ? $invitation->key : false,
|
||||
'invitation' => $invitation
|
||||
];
|
||||
|
||||
if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {
|
||||
|
@ -69,6 +69,7 @@ class InvoiceController extends Controller
|
||||
|
||||
$data = [
|
||||
'invoice' => $invoice,
|
||||
'invitation' => $invitation,
|
||||
'key' => $invitation ? $invitation->key : false,
|
||||
];
|
||||
|
||||
|
@ -98,6 +98,27 @@ class NinjaPlanController extends Controller
|
||||
$stripe_response = json_decode($request->input('gateway_response'));
|
||||
$customer = $gateway_driver->findOrCreateCustomer();
|
||||
|
||||
//27-08-2022 Ensure customer is updated appropriately
|
||||
$update_client_object['name'] = $client->present()->name();
|
||||
$update_client_object['phone'] = substr($client->present()->phone(), 0, 20);
|
||||
|
||||
$update_client_object['address']['line1'] = $client->address1 ?: '';
|
||||
$update_client_object['address']['line2'] = $client->address2 ?: '';
|
||||
$update_client_object['address']['city'] = $client->city ?: '';
|
||||
$update_client_object['address']['postal_code'] = $client->postal_code ?: '';
|
||||
$update_client_object['address']['state'] = $client->state ?: '';
|
||||
$update_client_object['address']['country'] = $client->country ? $client->country->iso_3166_2 : '';
|
||||
|
||||
$update_client_object['shipping']['name'] = $client->present()->name();
|
||||
$update_client_object['shipping']['address']['line1'] = $client->shipping_address1 ?: '';
|
||||
$update_client_object['shipping']['address']['line2'] = $client->shipping_address2 ?: '';
|
||||
$update_client_object['shipping']['address']['city'] = $client->shipping_city ?: '';
|
||||
$update_client_object['shipping']['address']['postal_code'] = $client->shipping_postal_code ?: '';
|
||||
$update_client_object['shipping']['address']['state'] = $client->shipping_state ?: '';
|
||||
$update_client_object['shipping']['address']['country'] = $client->shipping_country ? $client->shipping_country->iso_3166_2 : '';
|
||||
|
||||
\Stripe\Customer::update($customer->id, $update_client_object, $gateway_driver->stripe_connect_auth);
|
||||
|
||||
$gateway_driver->attach($stripe_response->payment_method, $customer);
|
||||
$method = $gateway_driver->getStripePaymentMethod($stripe_response->payment_method);
|
||||
|
||||
|
@ -61,6 +61,7 @@ class QuoteController extends Controller
|
||||
$data = [
|
||||
'quote' => $quote,
|
||||
'key' => $invitation ? $invitation->key : false,
|
||||
'invitation' => $invitation
|
||||
];
|
||||
|
||||
if ($invitation && auth()->guard('contact') && ! request()->has('silent') && ! $invitation->viewed_date) {
|
||||
@ -180,7 +181,6 @@ class QuoteController extends Controller
|
||||
if ($process) {
|
||||
foreach ($quotes as $quote) {
|
||||
$quote->service()->approve(auth()->user())->save();
|
||||
// event(new QuoteWasApproved(auth()->guard('contact')->user(), $quote, $quote->company, Ninja::eventVars()));
|
||||
|
||||
if (request()->has('signature') && ! is_null(request()->signature) && ! empty(request()->signature)) {
|
||||
InjectSignature::dispatch($quote, request()->signature);
|
||||
|
@ -99,6 +99,7 @@ class PurchaseOrderController extends Controller
|
||||
'settings' => $purchase_order->company->settings,
|
||||
'sidebar' => $this->sidebarMenu(),
|
||||
'company' => $purchase_order->company,
|
||||
'invitation' => $invitation
|
||||
];
|
||||
|
||||
if ($request->query('mode') === 'fullscreen') {
|
||||
|
@ -28,17 +28,17 @@ class QuotesTable extends Component
|
||||
|
||||
public $company;
|
||||
|
||||
public $sort_field = 'status_id'; // Default sortBy. Feel free to change or pull from client/company settings.
|
||||
public $sort = 'status_id'; // Default sortBy. Feel free to change or pull from client/company settings.
|
||||
|
||||
public $sort_asc = true;
|
||||
|
||||
public function sortBy($field)
|
||||
{
|
||||
$this->sort_field === $field
|
||||
$this->sort === $field
|
||||
? $this->sort_asc = ! $this->sort_asc
|
||||
: $this->sort_asc = true;
|
||||
|
||||
$this->sort_field = $field;
|
||||
$this->sort = $field;
|
||||
}
|
||||
|
||||
public function mount()
|
||||
@ -51,7 +51,7 @@ class QuotesTable extends Component
|
||||
|
||||
$query = Quote::query()
|
||||
->with('client.gateway_tokens', 'company', 'client.contacts')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc');
|
||||
->orderBy($this->sort, $this->sort_asc ? 'asc' : 'desc');
|
||||
|
||||
if (count($this->status) > 0) {
|
||||
|
||||
|
@ -69,7 +69,7 @@ class StoreExpenseRequest extends Request
|
||||
|
||||
/* Ensure the project is related */
|
||||
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();
|
||||
|
||||
if($project){
|
||||
$input['client_id'] = $project->client_id;
|
||||
|
@ -64,7 +64,7 @@ class UpdateExpenseRequest extends Request
|
||||
|
||||
/* Ensure the project is related */
|
||||
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();
|
||||
|
||||
if($project){
|
||||
$input['client_id'] = $project->client_id;
|
||||
|
@ -68,15 +68,14 @@ class StorePaymentRequest extends Request
|
||||
if (isset($input['credits']) && is_array($input['credits']) !== false) {
|
||||
foreach ($input['credits'] as $key => $value) {
|
||||
if (array_key_exists('credit_id', $input['credits'][$key])) {
|
||||
$input['credits'][$key]['credit_id'] = $value['credit_id'];
|
||||
// $input['credits'][$key]['credit_id'] = $value['credit_id'];
|
||||
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
||||
|
||||
$credits_total += $value['amount'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if (array_key_exists('amount', $input))
|
||||
// $input['amount'] = 0;
|
||||
|
||||
if (isset($input['credits']) && is_array($input['credits']) === false) {
|
||||
$input['credits'] = null;
|
||||
}
|
||||
@ -97,14 +96,14 @@ class StorePaymentRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'amount' => ['numeric', 'bail', new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule()],
|
||||
'amount' => ['numeric', 'bail', new PaymentAmountsBalanceRule(), new ValidCreditsPresentRule($this->all())],
|
||||
'client_id' => 'bail|required|exists:clients,id',
|
||||
'invoices.*.invoice_id' => 'bail|required|distinct|exists:invoices,id',
|
||||
'invoices.*.amount' => 'bail|required',
|
||||
'invoices.*.invoice_id' => new ValidInvoicesRules($this->all()),
|
||||
'credits.*.credit_id' => 'bail|required|exists:credits,id',
|
||||
'credits.*.credit_id' => new ValidCreditsRules($this->all()),
|
||||
'credits.*.amount' => ['required', new CreditsSumRule($this->all())],
|
||||
'credits.*.amount' => ['bail','required', new CreditsSumRule($this->all())],
|
||||
'invoices' => new ValidPayableInvoicesRule(),
|
||||
'number' => ['nullable', 'bail', Rule::unique('payments')->where('company_id', auth()->user()->company()->id)],
|
||||
|
||||
|
@ -36,7 +36,7 @@ class UpdatePaymentRequest extends Request
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule],
|
||||
'invoices' => ['array', new PaymentAppliedValidAmount, new ValidCreditsPresentRule($this->all())],
|
||||
'invoices.*.invoice_id' => 'distinct',
|
||||
'documents' => 'mimes:png,ai,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx',
|
||||
];
|
||||
@ -79,6 +79,14 @@ class UpdatePaymentRequest extends Request
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($input['credits']) && is_array($input['credits']) !== false) {
|
||||
foreach ($input['credits'] as $key => $value) {
|
||||
if (array_key_exists('credits', $input['credits'][$key])) {
|
||||
$input['credits'][$key]['credit_id'] = $this->decodePrimaryKey($value['credit_id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
|
@ -70,8 +70,8 @@ class StoreTaskRequest extends Request
|
||||
|
||||
/* Ensure the project is related */
|
||||
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||
|
||||
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();
|
||||
;
|
||||
if($project){
|
||||
$input['client_id'] = $project->client_id;
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class UpdateTaskRequest extends Request
|
||||
|
||||
/* Ensure the project is related */
|
||||
if (array_key_exists('project_id', $input) && isset($input['project_id'])) {
|
||||
$project = Project::withTrashed()->find($input['project_id'])->company()->first();
|
||||
$project = Project::withTrashed()->where('id', $input['project_id'])->company()->first();
|
||||
|
||||
if($project){
|
||||
$input['client_id'] = $project->client_id;
|
||||
|
@ -51,10 +51,22 @@ class ValidCreditsRules implements Rule
|
||||
|
||||
$unique_array = [];
|
||||
|
||||
$total_credit_amount = array_sum(array_column($this->input['credits'], 'amount'));
|
||||
|
||||
if($total_credit_amount <= 0){
|
||||
|
||||
$this->error_msg = "Total of credits must be more than zero.";
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$cred_collection = Credit::withTrashed()->whereIn('id', array_column($this->input['credits'], 'credit_id'))->get();
|
||||
|
||||
foreach ($this->input['credits'] as $credit) {
|
||||
$unique_array[] = $credit['credit_id'];
|
||||
|
||||
$cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
|
||||
// $cred = Credit::find($this->decodePrimaryKey($credit['credit_id']));
|
||||
$cred = $cred_collection->firstWhere('id', $credit['credit_id']);
|
||||
|
||||
if (! $cred) {
|
||||
$this->error_msg = ctrans('texts.credit_not_found');
|
||||
|
@ -51,6 +51,9 @@ class ValidInvoicesRules implements Rule
|
||||
|
||||
$unique_array = [];
|
||||
|
||||
/////
|
||||
$inv_collection = Invoice::withTrashed()->whereIn('id', array_column($this->input['invoices'], 'invoice_id'))->get();
|
||||
|
||||
//todo optimize this into a single query
|
||||
foreach ($this->input['invoices'] as $invoice) {
|
||||
$unique_array[] = $invoice['invoice_id'];
|
||||
@ -61,7 +64,10 @@ class ValidInvoicesRules implements Rule
|
||||
return false;
|
||||
}
|
||||
|
||||
$inv = Invoice::withTrashed()->whereId($invoice['invoice_id'])->first();
|
||||
/////
|
||||
$inv = $inv_collection->firstWhere('id', $invoice['invoice_id']);
|
||||
|
||||
// $inv = Invoice::withTrashed()->whereId($invoice['invoice_id'])->first();
|
||||
|
||||
if (! $inv) {
|
||||
$this->error_msg = ctrans('texts.invoice_not_found');
|
||||
|
@ -22,6 +22,13 @@ class ValidCreditsPresentRule implements Rule
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
private $input;
|
||||
|
||||
public function __construct($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
@ -44,11 +51,10 @@ class ValidCreditsPresentRule implements Rule
|
||||
{
|
||||
//todo need to ensure the clients credits are here not random ones!
|
||||
|
||||
if (request()->input('credits') && is_array(request()->input('credits')) && count(request()->input('credits')) > 0) {
|
||||
$credit_collection = Credit::whereIn('id', $this->transformKeys(array_column(request()->input('credits'), 'credit_id')))
|
||||
->count();
|
||||
if (array_key_exists('credits', $this->input) && is_array($this->input['credits']) && count($this->input['credits']) > 0) {
|
||||
$credit_collection = Credit::whereIn('id', array_column($this->input['credits'], 'credit_id'))->count();
|
||||
|
||||
return $credit_collection == count(request()->input('credits'));
|
||||
return $credit_collection == count($this->input['credits']);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -85,6 +85,11 @@ class CreateAccount
|
||||
$sp794f3f->hosted_client_count = config('ninja.quotas.free.clients');
|
||||
$sp794f3f->hosted_company_count = config('ninja.quotas.free.max_companies');
|
||||
$sp794f3f->account_sms_verified = true;
|
||||
|
||||
if(in_array($this->getDomain($this->request['email']), ['gmail.com', 'hotmail.com', 'outlook.com', 'yahoo.com'])){
|
||||
$sp794f3f->account_sms_verified = false;
|
||||
}
|
||||
|
||||
// $sp794f3f->trial_started = now();
|
||||
// $sp794f3f->trial_plan = 'pro';
|
||||
}
|
||||
@ -155,4 +160,19 @@ class CreateAccount
|
||||
|
||||
return $sp794f3f;
|
||||
}
|
||||
|
||||
private function getDomain($email)
|
||||
{
|
||||
if( filter_var( $email, FILTER_VALIDATE_EMAIL ) ) {
|
||||
// split on @ and return last value of array (the domain)
|
||||
$domain = explode('@', $email);
|
||||
|
||||
$domain_name = end($domain);
|
||||
|
||||
return $domain_name;
|
||||
}
|
||||
|
||||
return 'gmail.com';
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -85,6 +85,13 @@ class ApplyCreditPayment implements ShouldQueue
|
||||
->save();
|
||||
}
|
||||
|
||||
//22-08-2022
|
||||
$this->credit
|
||||
->client
|
||||
->service()
|
||||
->adjustCreditBalance($this->amount * -1)
|
||||
->save();
|
||||
|
||||
/* Update Payment Applied Amount*/
|
||||
$this->payment->save();
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace App\Jobs\Ninja;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -54,6 +55,8 @@ class CompanySizeCheck implements ShouldQueue
|
||||
|
||||
private function check()
|
||||
{
|
||||
nlog("Checking all company sizes");
|
||||
|
||||
Company::where('is_large', false)->withCount(['invoices', 'clients', 'products'])->cursor()->each(function ($company) {
|
||||
if ($company->invoices_count > 500 || $company->products_count > 500 || $company->clients_count > 500) {
|
||||
nlog("Marking company {$company->id} as large");
|
||||
@ -61,5 +64,17 @@ class CompanySizeCheck implements ShouldQueue
|
||||
$company->account->companies()->update(['is_large' => true]);
|
||||
}
|
||||
});
|
||||
|
||||
nlog("updating all client credit balances");
|
||||
|
||||
Client::where('updated_at', '>', now()->subDay())
|
||||
->cursor()
|
||||
->each(function ($client){
|
||||
|
||||
$client->credit_balance = $client->service()->getCreditBalance();
|
||||
$client->save();
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -42,13 +42,16 @@ class MailSentListener implements ShouldQueue
|
||||
*/
|
||||
public function handle(MessageSent $event)
|
||||
{
|
||||
if(!Ninja::isHosted());
|
||||
if(!Ninja::isHosted())
|
||||
return;
|
||||
|
||||
$message_id = $event->sent->getMessageId();
|
||||
|
||||
$message = MessageConverter::toEmail($event->sent->getOriginalMessage());
|
||||
|
||||
if(!$message->getHeaders()->get('x-invitation'))
|
||||
return;
|
||||
|
||||
$invitation_key = $message->getHeaders()->get('x-invitation')->getValue();
|
||||
|
||||
if($message_id && $invitation_key)
|
||||
|
@ -120,6 +120,7 @@ class Company extends BaseModel
|
||||
'inventory_notification_threshold',
|
||||
'stock_notification',
|
||||
'enabled_expense_tax_rates',
|
||||
'invoice_task_project',
|
||||
];
|
||||
|
||||
protected $hidden = [
|
||||
|
@ -24,9 +24,6 @@ class UserObserver
|
||||
public function created(User $user)
|
||||
{
|
||||
|
||||
if(class_exists(\Modules\Admin\Jobs\Account\UserQuality::class))
|
||||
\Modules\Admin\Jobs\Account\UserQuality::dispatch($user, $user->account->key);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -38,9 +35,6 @@ class UserObserver
|
||||
public function updated(User $user)
|
||||
{
|
||||
|
||||
if(class_exists(\Modules\Admin\Jobs\Account\UserQuality::class))
|
||||
\Modules\Admin\Jobs\Account\UserQuality::dispatch($user, $user->account->key);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -339,6 +339,11 @@ class BaseRepository
|
||||
else
|
||||
event('eloquent.updated: App\Models\Credit', $model);
|
||||
|
||||
if (($state['finished_amount'] != $state['starting_amount']) && ($model->status_id != Credit::STATUS_DRAFT)) {
|
||||
|
||||
$model->client->service()->adjustCreditBalance(($state['finished_amount'] - $state['starting_amount']))->save();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ($model instanceof Quote) {
|
||||
|
@ -43,4 +43,31 @@ class CreditRepository extends BaseRepository
|
||||
{
|
||||
return CreditInvitation::where('key', $key)->first();
|
||||
}
|
||||
|
||||
public function delete($credit)
|
||||
{
|
||||
if ($credit->is_deleted) {
|
||||
return;
|
||||
}
|
||||
|
||||
$credit = $credit->service()->deleteCredit()->save();
|
||||
|
||||
return parent::delete($credit);
|
||||
|
||||
}
|
||||
|
||||
public function restore($credit)
|
||||
{
|
||||
//we cannot restore a deleted payment.
|
||||
if (! $credit->trashed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
parent::restore($credit);
|
||||
|
||||
$credit = $credit->service()->restoreCredit()->save();
|
||||
|
||||
return $credit;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -73,28 +73,38 @@ class PaymentRepository extends BaseRepository {
|
||||
unset($data['exchange_rate']);
|
||||
|
||||
$is_existing_payment = false;
|
||||
$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) {
|
||||
if ($data['amount'] == '') {
|
||||
$data['amount'] = array_sum(array_column($data['invoices'], 'amount'));
|
||||
\DB::connection(config('database.default'))->transaction(function () use ($data) {
|
||||
|
||||
$client = Client::where('id', $data['client_id'])->withTrashed()->lockForUpdate()->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) {
|
||||
if ($data['amount'] == '') {
|
||||
$data['amount'] = array_sum(array_column($data['invoices'], 'amount'));
|
||||
}
|
||||
|
||||
// $client->service()->updatePaidToDate($data['amount'])->save();
|
||||
$client->paid_to_date += $data['amount'];
|
||||
$client->save();
|
||||
}
|
||||
|
||||
$client->service()->updatePaidToDate($data['amount'])->save();
|
||||
}
|
||||
else{
|
||||
//this fixes an edge case with unapplied payments
|
||||
// $client->service()->updatePaidToDate($data['amount'])->save();
|
||||
$client->paid_to_date += $data['amount'];
|
||||
$client->save();
|
||||
}
|
||||
|
||||
else{
|
||||
//this fixes an edge case with unapplied payments
|
||||
$client->service()->updatePaidToDate($data['amount'])->save();
|
||||
}
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) {
|
||||
$_credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits']) && count($data['credits']) > 0) {
|
||||
$_credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
// $client->service()->updatePaidToDate($_credit_totals)->save();
|
||||
$client->paid_to_date += $_credit_totals;
|
||||
$client->save();
|
||||
}
|
||||
|
||||
$client->service()->updatePaidToDate($_credit_totals)->save();
|
||||
|
||||
}
|
||||
}, 1);
|
||||
|
||||
}
|
||||
|
||||
@ -139,7 +149,8 @@ class PaymentRepository extends BaseRepository {
|
||||
|
||||
//todo optimize this into a single query
|
||||
foreach ($data['invoices'] as $paid_invoice) {
|
||||
$invoice = Invoice::withTrashed()->whereId($paid_invoice['invoice_id'])->first();
|
||||
// $invoice = Invoice::withTrashed()->whereId($paid_invoice['invoice_id'])->first();
|
||||
$invoice = $invoices->firstWhere('id', $paid_invoice['invoice_id']);
|
||||
|
||||
if ($invoice) {
|
||||
$invoice = $invoice->service()
|
||||
@ -157,16 +168,20 @@ class PaymentRepository extends BaseRepository {
|
||||
if (array_key_exists('credits', $data) && is_array($data['credits'])) {
|
||||
$credit_totals = array_sum(array_column($data['credits'], 'amount'));
|
||||
|
||||
$credits = Credit::whereIn('id', $this->transformKeys(array_column($data['credits'], 'credit_id')))->get();
|
||||
// $credits = Credit::whereIn('id', $this->transformKeys(array_column($data['credits'], 'credit_id')))->get();
|
||||
|
||||
$credits = Credit::whereIn('id', array_column($data['credits'], 'credit_id'))->get();
|
||||
|
||||
$payment->credits()->saveMany($credits);
|
||||
|
||||
//todo optimize into a single query
|
||||
foreach ($data['credits'] as $paid_credit) {
|
||||
$credit = Credit::withTrashed()->find($this->decodePrimaryKey($paid_credit['credit_id']));
|
||||
// $credit = Credit::withTrashed()->find($paid_credit['credit_id']);
|
||||
$credit = $credits->firstWhere('id', $paid_credit['credit_id']);
|
||||
|
||||
if ($credit) {
|
||||
$credit = $credit->service()->markSent()->save();
|
||||
ApplyCreditPayment::dispatchNow($credit, $payment, $paid_credit['amount'], $credit->company);
|
||||
(new ApplyCreditPayment($credit, $payment, $paid_credit['amount'], $credit->company))->handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -245,7 +260,7 @@ class PaymentRepository extends BaseRepository {
|
||||
event(new PaymentWasDeleted($payment, $payment->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $payment;
|
||||
//return parent::delete($payment);
|
||||
|
||||
}
|
||||
|
||||
public function restore($payment)
|
||||
|
@ -48,6 +48,7 @@ class InstantPayment
|
||||
|
||||
public function run()
|
||||
{
|
||||
|
||||
$is_credit_payment = false;
|
||||
|
||||
$tokens = [];
|
||||
@ -69,7 +70,8 @@ class InstantPayment
|
||||
$invoices->each(function ($invoice) {
|
||||
$invoice->service()
|
||||
->markSent()
|
||||
->removeUnpaidGatewayFees();
|
||||
->removeUnpaidGatewayFees()
|
||||
->save();
|
||||
});
|
||||
|
||||
/* pop non payable invoice from the $payable_invoices array */
|
||||
@ -107,6 +109,7 @@ class InstantPayment
|
||||
$payable_amount = Number::roundValue(Number::parseFloat($payable_invoice['amount'], $client->currency()->precision));
|
||||
$invoice_balance = Number::roundValue(($invoice->partial > 0 ? $invoice->partial : $invoice->balance), $client->currency()->precision);
|
||||
|
||||
|
||||
/*If we don't allow under/over payments force the payable amount - prevents inspect element adjustments in JS*/
|
||||
|
||||
if ($settings->client_portal_allow_under_payment == false && $settings->client_portal_allow_over_payment == false) {
|
||||
|
@ -142,6 +142,7 @@ class CreditService
|
||||
$client = $this->credit->client->fresh();
|
||||
$client->service()
|
||||
->updatePaidToDate($adjustment)
|
||||
->adjustCreditBalance($adjustment * -1)
|
||||
->save();
|
||||
|
||||
event('eloquent.created: App\Models\Payment', $payment);
|
||||
@ -256,6 +257,29 @@ class CreditService
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function deleteCredit()
|
||||
{
|
||||
$this->credit
|
||||
->client
|
||||
->service()
|
||||
->adjustCreditBalance($this->credit->balance * -1)
|
||||
->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function restoreCredit()
|
||||
{
|
||||
$this->credit
|
||||
->client
|
||||
->service()
|
||||
->adjustCreditBalance($this->credit->balance)
|
||||
->save();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the credit.
|
||||
* @return Credit object
|
||||
|
@ -45,6 +45,11 @@ class MarkSent
|
||||
->touchPdf()
|
||||
->save();
|
||||
|
||||
$this->client
|
||||
->service()
|
||||
->adjustCreditBalance($this->credit->amount)
|
||||
->save();
|
||||
|
||||
event(new CreditWasMarkedSent($this->credit, $this->credit->company, Ninja::eventVars(auth()->user() ? auth()->user()->id : null)));
|
||||
|
||||
return $this->credit;
|
||||
|
@ -49,6 +49,6 @@ class SendEmail
|
||||
}
|
||||
});
|
||||
|
||||
$this->credit->service()->markSent();
|
||||
$this->credit->service()->markSent()->save();
|
||||
}
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ class ApplyPaymentAmount extends AbstractService
|
||||
public function run()
|
||||
{
|
||||
if ($this->invoice->status_id == Invoice::STATUS_DRAFT) {
|
||||
$this->invoice->service()->markSent();
|
||||
$this->invoice->service()->markSent()->save();
|
||||
}
|
||||
|
||||
/*Don't double pay*/
|
||||
|
@ -162,6 +162,7 @@ class DeletePayment
|
||||
$client
|
||||
->service()
|
||||
->updatePaidToDate(($paymentable_credit->pivot->amount) * -1)
|
||||
->adjustCreditBalance($paymentable_credit->pivot->amount)
|
||||
->save();
|
||||
});
|
||||
}
|
||||
|
@ -178,6 +178,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'track_inventory' => (bool) $company->track_inventory,
|
||||
'enable_applying_payments' => (bool) $company->enable_applying_payments,
|
||||
'enabled_expense_tax_rates' => (int) $company->enabled_expense_tax_rates,
|
||||
'invoice_task_project' => (bool) $company->invoice_task_project,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ namespace App\Transformers;
|
||||
|
||||
use App\Models\PurchaseOrder;
|
||||
use App\Models\PurchaseOrderInvitation;
|
||||
use App\Models\Vendor;
|
||||
use App\Transformers\DocumentTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
@ -27,7 +28,8 @@ class PurchaseOrderTransformer extends EntityTransformer
|
||||
];
|
||||
|
||||
protected $availableIncludes = [
|
||||
'expense'
|
||||
'expense',
|
||||
'vendor',
|
||||
];
|
||||
|
||||
public function includeInvitations(PurchaseOrder $purchase_order)
|
||||
@ -49,7 +51,22 @@ class PurchaseOrderTransformer extends EntityTransformer
|
||||
{
|
||||
$transformer = new ExpenseTransformer($this->serializer);
|
||||
|
||||
return $this->includeItem($purchase_order->expense, $transformer, Document::class);
|
||||
if (!$purchase_order->expense) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->includeItem($purchase_order->expense, $transformer, Expense::class);
|
||||
}
|
||||
|
||||
public function includeVendor(PurchaseOrder $purchase_order)
|
||||
{
|
||||
$transformer = new VendorTransformer($this->serializer);
|
||||
|
||||
if (!$purchase_order->vendor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->includeItem($purchase_order->vendor, $transformer, Vendor::class);
|
||||
}
|
||||
|
||||
public function transform(PurchaseOrder $purchase_order)
|
||||
|
@ -90,7 +90,7 @@ class Number
|
||||
return (float) $s;
|
||||
}
|
||||
|
||||
// remove all seperators from first part and keep the end
|
||||
// remove all separators from first part and keep the end
|
||||
$s = str_replace('.', '', substr($s, 0, -3)).substr($s, -3);
|
||||
|
||||
// return float
|
||||
|
@ -95,12 +95,6 @@ return [
|
||||
'strict' => env('DB_STRICT', false),
|
||||
'engine' => 'InnoDB ROW_FORMAT=DYNAMIC',
|
||||
'options' => [],
|
||||
// 'options' => [
|
||||
// PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT => false,
|
||||
// PDO::MYSQL_ATTR_SSL_KEY => env("DB_CLIENT_KEY", ''),
|
||||
// PDO::MYSQL_ATTR_SSL_CERT => env("DB_CLIENT_CERT", ''),
|
||||
// PDO::MYSQL_ATTR_SSL_CA => env("DB_CA_CERT", ''),
|
||||
// ],
|
||||
],
|
||||
|
||||
'db-ninja-01a' => [
|
||||
|
@ -14,8 +14,8 @@ return [
|
||||
'require_https' => env('REQUIRE_HTTPS', true),
|
||||
'app_url' => rtrim(env('APP_URL', ''), '/'),
|
||||
'app_domain' => env('APP_DOMAIN', 'invoicing.co'),
|
||||
'app_version' => '5.5.14',
|
||||
'app_tag' => '5.5.14',
|
||||
'app_version' => '5.5.17',
|
||||
'app_tag' => '5.5.17',
|
||||
'minimum_client_version' => '5.0.16',
|
||||
'terms_version' => '1.0.1',
|
||||
'api_secret' => env('API_SECRET', ''),
|
||||
|
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
//
|
||||
Schema::table('companies', function (Blueprint $table) {
|
||||
$table->boolean('invoice_task_project')->default(0);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
};
|
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
|
||||
Schema::table('purchase_order_invitations', function (Blueprint $table) {
|
||||
$table->enum('email_status', ['delivered', 'bounced', 'spam'])->nullable();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
|
||||
}
|
||||
};
|
@ -800,7 +800,7 @@ $LANG = array(
|
||||
'activity_51' => ':user deleted user :user',
|
||||
'activity_52' => ':user restored user :user',
|
||||
'activity_53' => ':user marked sent :invoice',
|
||||
'activity_54' => ':user reopened ticket :ticket',
|
||||
'activity_54' => ':user paid invoice :invoice',
|
||||
'activity_55' => ':contact replied ticket :ticket',
|
||||
'activity_56' => ':user viewed ticket :ticket',
|
||||
|
||||
@ -4767,6 +4767,9 @@ $LANG = array(
|
||||
'bulk_email_invoices' => 'Email Invoices',
|
||||
'bulk_email_quotes' => 'Email Quotes',
|
||||
'bulk_email_credits' => 'Email Credits',
|
||||
'archive_purchase_order' => 'Archive Purchase Order',
|
||||
'restore_purchase_order' => 'Restore Purchase Order',
|
||||
'delete_purchase_order' => 'Delete Purchase Order',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
6
public/flutter_service_worker.js
vendored
6
public/flutter_service_worker.js
vendored
@ -7,7 +7,7 @@ const RESOURCES = {
|
||||
"canvaskit/profiling/canvaskit.js": "ae2949af4efc61d28a4a80fffa1db900",
|
||||
"canvaskit/profiling/canvaskit.wasm": "95e736ab31147d1b2c7b25f11d4c32cd",
|
||||
"canvaskit/canvaskit.wasm": "4b83d89d9fecbea8ca46f2f760c5a9ba",
|
||||
"main.dart.js": "809c193905ea4c80a7c0b2b0e484e1db",
|
||||
"main.dart.js": "3234746bf3c8a3ccec3cf901969c352c",
|
||||
"favicon.ico": "51636d3a390451561744c42188ccd628",
|
||||
"assets/AssetManifest.json": "759f9ef9973f7e26c2a51450b55bb9fa",
|
||||
"assets/assets/google_fonts/Roboto-Regular.ttf": "8a36205bd9b83e03af0591a004bc97f4",
|
||||
@ -295,9 +295,9 @@ const RESOURCES = {
|
||||
"assets/FontManifest.json": "087fb858dc3cbfbf6baf6a30004922f1",
|
||||
"assets/NOTICES": "254a5bf1eeb00601955e148b31cb925c",
|
||||
"flutter.js": "eb2682e33f25cd8f1fc59011497c35f8",
|
||||
"/": "f1ab1648b6acf56aebbd6ae07968a461",
|
||||
"/": "48872e415511ff066e9403545e4ba572",
|
||||
"favicon.png": "dca91c54388f52eded692718d5a98b8b",
|
||||
"version.json": "a10748384e57f928f4d2871ac7563faf",
|
||||
"version.json": "9eca00898047311eda7456072e79d77d",
|
||||
"manifest.json": "ef43d90e57aa7682d7e2cfba2f484a40",
|
||||
"icons/Icon-192.png": "bb1cf5f6982006952211c7c8404ffbed",
|
||||
"icons/Icon-512.png": "0f9aff01367f0a0c69773d25ca16ef35"
|
||||
|
78643
public/main.dart.js
vendored
78643
public/main.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
78401
public/main.foss.dart.js
vendored
78401
public/main.foss.dart.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
608
public/main.profile.dart.js
vendored
608
public/main.profile.dart.js
vendored
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.91","build_number":"91","package_name":"invoiceninja_flutter"}
|
||||
{"app_name":"invoiceninja_flutter","version":"5.0.92","build_number":"92","package_name":"invoiceninja_flutter"}
|
@ -125,7 +125,7 @@
|
||||
<img src="{{ $logo ?? '' }}" alt="" width="155" border="0" align="middle" style="display:block;" />
|
||||
<div style="mso-hide:all;">
|
||||
<![endif]-->
|
||||
<img class="logo-light" src="{{ $logo ?? '' }}" alt="" style="margin-top: 10px; max-width: 570px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||
<img class="logo-light" src="{{ $logo ?? '' }}" alt="" width="400" style="margin-top: 10px; max-width: 570px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||
<!--[if gte mso 9]>
|
||||
</div>
|
||||
<![endif]-->
|
||||
|
@ -81,6 +81,15 @@
|
||||
text-align: left !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!--[if gte mso 9]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
</head>
|
||||
|
||||
<body
|
||||
@ -102,7 +111,7 @@
|
||||
<img src="{{ $logo ?? '' }}" alt="" width="400" border="0" align="middle" style="display:block;" />
|
||||
<div style="mso-hide:all;">
|
||||
<![endif]-->
|
||||
<img src="{{ $logo ?? '' }}" alt="" style="margin-top: 40px; max-width: 155px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||
<img src="{{ $logo ?? '' }}" alt="" width="400" style="margin-top: 40px; max-width: 155px; display: block; margin-left: auto; margin-right: auto;"/>
|
||||
<!--[if gte mso 9]>
|
||||
</div>
|
||||
<![endif]-->
|
||||
|
@ -21,7 +21,7 @@
|
||||
</script>
|
||||
<script>
|
||||
window.flutterConfiguration = {
|
||||
canvasKitBaseUrl: "{{config('ninja.app_url')}}/canvaskit/"
|
||||
canvasKitBaseUrl: "/canvaskit/"
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
|
@ -3,7 +3,7 @@
|
||||
@endphp
|
||||
|
||||
@push('head')
|
||||
<meta name="pdf-url" content="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}?cache_buster={{time()}}">
|
||||
<meta name="pdf-url" content="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}">
|
||||
<script src="{{ asset('js/vendor/pdf.js/pdf.min.js') }}"></script>
|
||||
@endpush
|
||||
|
||||
@ -86,7 +86,7 @@
|
||||
<canvas id="pdf-placeholder" class="shadow rounded-lg bg-white"></canvas>
|
||||
</div>
|
||||
@else
|
||||
<iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path(null, 'url', true) }}?cache_buster={{time()}}" class="h-screen w-full border-0 mt-4"></iframe>
|
||||
<iframe id="pdf-iframe" src="{{ $url ?? $entity->pdf_file_path($invitation, 'url', true) }}?cache_buster={{time()}}" class="h-screen w-full border-0 mt-4"></iframe>
|
||||
@endif
|
||||
|
||||
|
||||
|
@ -33,7 +33,7 @@
|
||||
|
||||
@include('portal.ninja2020.components.entity-documents', ['entity' => $credit])
|
||||
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $credit])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $credit, 'invitation' => $invitation])
|
||||
|
||||
|
||||
@endsection
|
||||
|
@ -1,2 +1,2 @@
|
||||
<iframe src="{{ $invoice->pdf_file_path(null, 'url', true) }}"
|
||||
<iframe src="{{ $invoice->pdf_file_path($invitation, 'url', true) }}"
|
||||
style="position:fixed; top:0; left:0; bottom:0; right:0; width:100%; height:100%; border:none; margin:0; padding:0; overflow:hidden; z-index:999999;"></iframe>
|
||||
|
@ -96,7 +96,7 @@
|
||||
@endif
|
||||
|
||||
@include('portal.ninja2020.components.entity-documents', ['entity' => $invoice])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $invoice])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $invoice, 'invitation' => $invitation])
|
||||
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$invoice], 'entity_type' => ctrans('texts.invoice')])
|
||||
@include('portal.ninja2020.invoices.includes.signature')
|
||||
@endsection
|
||||
|
@ -46,7 +46,7 @@
|
||||
@endif
|
||||
|
||||
@include('portal.ninja2020.components.entity-documents', ['entity' => $purchase_order])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $purchase_order])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $purchase_order, 'invitation' => $invitation])
|
||||
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$purchase_order], 'entity_type' => ctrans('texts.purchase_order')])
|
||||
@include('portal.ninja2020.invoices.includes.signature')
|
||||
@endsection
|
||||
|
@ -105,7 +105,7 @@
|
||||
@endif
|
||||
|
||||
@include('portal.ninja2020.components.entity-documents', ['entity' => $quote])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $quote])
|
||||
@include('portal.ninja2020.components.pdf-viewer', ['entity' => $quote, 'invitation' => $invitation])
|
||||
@include('portal.ninja2020.invoices.includes.terms', ['entities' => [$quote], 'entity_type' => ctrans('texts.quote')])
|
||||
@include('portal.ninja2020.invoices.includes.signature')
|
||||
@endsection
|
||||
|
@ -13,11 +13,13 @@ namespace Tests\Feature;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Factory\InvoiceItemFactory;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyToken;
|
||||
use App\Models\Credit;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
@ -61,6 +63,97 @@ class ClientTest extends TestCase
|
||||
$this->makeTestData();
|
||||
}
|
||||
|
||||
private function buildLineItems($number = 2)
|
||||
{
|
||||
$line_items = [];
|
||||
|
||||
for($x=0; $x<$number; $x++)
|
||||
{
|
||||
$item = InvoiceItemFactory::create();
|
||||
$item->quantity = 1;
|
||||
$item->cost = 10;
|
||||
|
||||
$line_items[] = $item;
|
||||
}
|
||||
|
||||
return $line_items;
|
||||
}
|
||||
|
||||
public function testCreditBalance()
|
||||
{
|
||||
$this->client->credit_balance = 0;
|
||||
$this->client->save();
|
||||
|
||||
$this->assertEquals(0, $this->client->credit_balance);
|
||||
|
||||
$credit = [
|
||||
'status_id' => 1,
|
||||
'number' => 'dfdfd',
|
||||
'discount' => 0,
|
||||
'is_amount_discount' => 1,
|
||||
'number' => '34343xx43',
|
||||
'public_notes' => 'notes',
|
||||
'is_deleted' => 0,
|
||||
'custom_value1' => 0,
|
||||
'custom_value2' => 0,
|
||||
'custom_value3' => 0,
|
||||
'custom_value4' => 0,
|
||||
'status' => 1,
|
||||
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||
'line_items' => $this->buildLineItems()
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/credits/', $credit)
|
||||
->assertStatus(200);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$credit_id = $arr['data']['id'];
|
||||
|
||||
$credit = Credit::find($this->decodePrimaryKey($credit_id));
|
||||
|
||||
$this->assertNotNull($credit);
|
||||
|
||||
$this->assertEquals(0, $credit->balance);
|
||||
|
||||
$credit->service()->markSent()->save();
|
||||
|
||||
$this->assertEquals(20, $credit->balance);
|
||||
$this->assertEquals(20, $credit->client->fresh()->credit_balance);
|
||||
|
||||
//lets now update the credit and increase its balance, this should also increase the credit balance
|
||||
|
||||
$data = [
|
||||
'number' => 'dfdfd',
|
||||
'discount' => 0,
|
||||
'is_amount_discount' => 1,
|
||||
'number' => '34343xx43',
|
||||
'public_notes' => 'notes',
|
||||
'is_deleted' => 0,
|
||||
'custom_value1' => 0,
|
||||
'custom_value2' => 0,
|
||||
'custom_value3' => 0,
|
||||
'custom_value4' => 0,
|
||||
'status' => 1,
|
||||
'client_id' => $this->encodePrimaryKey($this->client->id),
|
||||
'line_items' => $this->buildLineItems(3)
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->put('/api/v1/credits/'.$credit->hashed_id, $data)
|
||||
->assertStatus(200);
|
||||
|
||||
$credit = $credit->fresh();
|
||||
|
||||
$this->assertEquals(30, $credit->balance);
|
||||
$this->assertEquals(30, $credit->client->fresh()->credit_balance);
|
||||
}
|
||||
|
||||
public function testStoreClientUsingCountryCode()
|
||||
{
|
||||
$data = [
|
||||
|
@ -78,7 +78,7 @@ class ImportCompanyTest extends TestCase
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->artisan('db:seed');
|
||||
// $this->artisan('db:seed');
|
||||
|
||||
$this->withoutMiddleware(
|
||||
ThrottleRequests::class
|
||||
|
@ -70,4 +70,54 @@ class CreditBalanceTest extends TestCase
|
||||
|
||||
$this->assertEquals($this->client->service()->getCreditBalance(), 0);
|
||||
}
|
||||
|
||||
public function testCreditDeleteCheckClientBalance()
|
||||
{
|
||||
$credit = Credit::factory()->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
'client_id' => $this->client->id,
|
||||
'balance' => 10,
|
||||
'number' => 'testing-number-01',
|
||||
'status_id' => Credit::STATUS_SENT,
|
||||
]);
|
||||
|
||||
$credit->client->credit_balance = 10;
|
||||
$credit->push();
|
||||
|
||||
|
||||
//delete invoice
|
||||
$data = [
|
||||
'ids' => [$credit->hashed_id],
|
||||
];
|
||||
|
||||
//restore invoice
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/credits/bulk?action=delete', $data)->assertStatus(200);
|
||||
|
||||
$client = $credit->client->fresh();
|
||||
|
||||
$this->assertEquals(0, $client->credit_balance);
|
||||
|
||||
//restore invoice
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/credits/bulk?action=restore', $data)->assertStatus(200);
|
||||
|
||||
$client = $credit->client->fresh();
|
||||
|
||||
$this->assertEquals(10, $client->credit_balance);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/credits/bulk?action=archive', $data)->assertStatus(200);
|
||||
|
||||
$client = $credit->client->fresh();
|
||||
|
||||
$this->assertEquals(10, $client->credit_balance);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user