mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Merge branch 'v2'
This commit is contained in:
commit
7d1eeafebe
2
.github/workflows/phpunit.yml
vendored
2
.github/workflows/phpunit.yml
vendored
@ -82,7 +82,7 @@ jobs:
|
||||
|
||||
- name: Migrate Database
|
||||
run: |
|
||||
php artisan migrate:fresh --seed --force && php artisan db:seed --class=RandomDataSeeder --force
|
||||
php artisan migrate:fresh --seed --force && php artisan db:seed --force
|
||||
|
||||
- name: Prepare JS/CSS assets
|
||||
run: |
|
||||
|
5
.prettierrc
Normal file
5
.prettierrc
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5"
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
<img src="https://raw.githubusercontent.com/hillelcoren/invoice-ninja/master/public/images/round_logo.png" alt="Sublime's custom image"/>
|
||||
</p>
|
||||
|
||||

|
||||
[](https://travis-ci.org/invoiceninja/invoiceninja)
|
||||
[](https://codecov.io/gh/invoiceninja/invoiceninja)
|
||||
[](https://www.codacy.com/app/turbo124/invoiceninja?utm_source=github.com&utm_medium=referral&utm_content=invoiceninja/invoiceninja&utm_campaign=Badge_Grade)
|
||||
|
@ -5,7 +5,9 @@ namespace App\Console\Commands;
|
||||
use App;
|
||||
use App\Libraries\CurlUtils;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\CompanyLedger;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Invoice;
|
||||
@ -287,56 +289,59 @@ class CheckData extends Command
|
||||
|
||||
private function checkInvoiceBalances()
|
||||
{
|
||||
// $invoices = DB::table('invoices')
|
||||
// ->leftJoin('payments', function($join) {
|
||||
// $join->on('payments.invoice_id', '=', 'invoices.id')
|
||||
// ->where('payments.payment_status_id', '!=', 2)
|
||||
// ->where('payments.payment_status_id', '!=', 3)
|
||||
// ->where('payments.is_deleted', '=', 0);
|
||||
// })
|
||||
// ->where('invoices.updated_at', '>', '2017-10-01')
|
||||
// ->groupBy('invoices.id')
|
||||
// ->havingRaw('(invoices.amount - invoices.balance) != coalesce(sum(payments.amount - payments.refunded), 0)')
|
||||
// ->get(['invoices.id', 'invoices.amount', 'invoices.balance', DB::raw('coalesce(sum(payments.amount - payments.refunded), 0)')]);
|
||||
|
||||
// $this->logMessage($invoices->count() . ' invoices with incorrect balances');
|
||||
$wrong_balances = 0;
|
||||
$wrong_paid_to_dates = 0;
|
||||
|
||||
foreach(Client::cursor() as $client)
|
||||
{
|
||||
$invoice_balance = $client->invoices->where('is_deleted', false)->sum('balance');
|
||||
|
||||
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
|
||||
|
||||
if($ledger && $invoice_balance != $client->balance)
|
||||
{
|
||||
$wrong_balances++;
|
||||
$this->logMessage($client->present()->name . " - " . $client->id . " - balances do not match {$invoice_balance} - {$client->balance} - {$ledger->balance}");
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->logMessage("{$wrong_balances} clients with incorrect balances");
|
||||
|
||||
|
||||
// if ($invoices->count() > 0) {
|
||||
// $this->isValid = false;
|
||||
// }
|
||||
}
|
||||
|
||||
private function checkClientBalances()
|
||||
{
|
||||
// find all clients where the balance doesn't equal the sum of the outstanding invoices
|
||||
// $clients = DB::table('clients')
|
||||
// ->join('invoices', 'invoices.client_id', '=', 'clients.id')
|
||||
// ->join('accounts', 'accounts.id', '=', 'clients.company_id')
|
||||
// ->where('accounts.id', '!=', 20432)
|
||||
// ->where('clients.is_deleted', '=', 0)
|
||||
// ->where('invoices.is_deleted', '=', 0)
|
||||
// ->where('invoices.is_public', '=', 1)
|
||||
// ->where('invoices.invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
||||
// ->where('invoices.is_recurring', '=', 0)
|
||||
// ->havingRaw('abs(clients.balance - sum(invoices.balance)) > .01 and clients.balance != 999999999.9999');
|
||||
|
||||
// if ($this->option('client_id')) {
|
||||
// $clients->where('clients.id', '=', $this->option('client_id'));
|
||||
// }
|
||||
$wrong_balances = 0;
|
||||
$wrong_paid_to_dates = 0;
|
||||
|
||||
// $clients = $clients->groupBy('clients.id', 'clients.balance')
|
||||
// ->orderBy('accounts.company_id', 'DESC')
|
||||
// ->get(['accounts.company_id', 'clients.company_id', 'clients.id', 'clients.balance', 'clients.paid_to_date', DB::raw('sum(invoices.balance) actual_balance')]);
|
||||
// $this->logMessage($clients->count() . ' clients with incorrect balance/activities');
|
||||
foreach(Client::cursor() as $client)
|
||||
{
|
||||
$invoice_balance = $client->invoices->where('is_deleted', false)->sum('balance');
|
||||
$invoice_amounts = $client->invoices->where('is_deleted', false)->sum('amount') - $invoice_balance;
|
||||
|
||||
// if ($clients->count() > 0) {
|
||||
// $this->isValid = false;
|
||||
// }
|
||||
$ledger = CompanyLedger::where('client_id', $client->id)->orderBy('id', 'DESC')->first();
|
||||
|
||||
// foreach ($clients as $client) {
|
||||
// $this->logMessage("=== Company: {$client->company_id} Account:{$client->company_id} Client:{$client->id} Balance:{$client->balance} Actual Balance:{$client->actual_balance} ===");
|
||||
if($ledger && (string)$invoice_amounts != rtrim($client->paid_to_date, "0"))
|
||||
{
|
||||
|
||||
$wrong_paid_to_dates++;
|
||||
$this->logMessage($client->present()->name . " - " . $client->id . " - client paid to dates do not match {$invoice_amounts} - " .rtrim($client->paid_to_date, "0"));
|
||||
|
||||
$this->isValid = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$this->logMessage("{$wrong_paid_to_dates} clients with incorrect paid_to_dates");
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
private function checkLogoFiles()
|
||||
|
@ -135,28 +135,44 @@ class CreateTestData extends Command
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
foreach ($company->clients as $client) {
|
||||
for($x=0; $x<$this->count; $x++)
|
||||
{
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
$this->createProject($client);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function createMediumAccount()
|
||||
@ -217,49 +233,42 @@ class CreateTestData extends Command
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
foreach ($company->clients as $client) {
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
for($x=0; $x<$this->count; $x++)
|
||||
{
|
||||
$client = $company->clients->random();
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createInvoice($client);
|
||||
}
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createCredit($client);
|
||||
}
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createQuote($client);
|
||||
}
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createExpense($client);
|
||||
}
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createVendor($client);
|
||||
}
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createTask($client);
|
||||
}
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
|
||||
for ($i=0; $i<$this->count; $i++) {
|
||||
$this->createProject($client);
|
||||
}
|
||||
$this->createProject($client);
|
||||
}
|
||||
}
|
||||
|
||||
@ -322,25 +331,40 @@ class CreateTestData extends Command
|
||||
$this->createClient($company, $user);
|
||||
}
|
||||
|
||||
foreach ($company->clients as $client) {
|
||||
for($x=0; $x<$this->count; $x++)
|
||||
{
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating invoice for client #'.$client->id);
|
||||
$this->createInvoice($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating credit for client #'.$client->id);
|
||||
$this->createCredit($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating quote for client #'.$client->id);
|
||||
$this->createQuote($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating expense for client #'.$client->id);
|
||||
$this->createExpense($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating vendor for client #'.$client->id);
|
||||
$this->createVendor($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating task for client #'.$client->id);
|
||||
$this->createTask($client);
|
||||
|
||||
$client = $company->clients->random();
|
||||
|
||||
$this->info('creating project for client #'.$client->id);
|
||||
$this->createProject($client);
|
||||
}
|
||||
|
@ -38,6 +38,8 @@ class PostUpdate extends Command
|
||||
{
|
||||
set_time_limit(0);
|
||||
|
||||
info("running post update");
|
||||
|
||||
try {
|
||||
Artisan::call('migrate');
|
||||
} catch (Exception $e) {
|
||||
@ -85,12 +87,14 @@ class PostUpdate extends Command
|
||||
$output = $factory->createOutput();
|
||||
|
||||
$input = new \Symfony\Component\Console\Input\ArrayInput(array(
|
||||
'command' => 'update',
|
||||
'command' => 'install',
|
||||
));
|
||||
$input->setInteractive(false);
|
||||
echo "<pre>";
|
||||
$cmdret = $app->doRun($input,$output);
|
||||
echo "end!";
|
||||
|
||||
\Log::error(print_r($cmdret,1));
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $enable_client_portal_tasks = false;
|
||||
public $enable_client_portal_password = false;
|
||||
public $enable_client_portal = true;//implemented
|
||||
public $enable_client_portal_dashboard = true;//implemented
|
||||
public $enable_client_portal = true; //implemented
|
||||
public $enable_client_portal_dashboard = true; //implemented
|
||||
public $signature_on_pdf = false;
|
||||
public $document_email_attachment = false;
|
||||
public $send_portal_password = false;
|
||||
@ -55,7 +55,7 @@ class CompanySettings extends BaseSettings
|
||||
|
||||
public $default_task_rate = 0;
|
||||
|
||||
public $payment_terms = -1;
|
||||
public $payment_terms = "";
|
||||
public $send_reminders = false;
|
||||
|
||||
public $custom_message_dashboard = '';
|
||||
@ -231,7 +231,10 @@ class CompanySettings extends BaseSettings
|
||||
public $portal_custom_footer = '';
|
||||
public $portal_custom_js = '';
|
||||
|
||||
public $client_can_register = false;
|
||||
|
||||
public static $casts = [
|
||||
'client_can_register' => 'bool',
|
||||
'portal_design_id' => 'string',
|
||||
'late_fee_endless_percent' => 'float',
|
||||
'late_fee_endless_amount' => 'float',
|
||||
@ -348,7 +351,7 @@ class CompanySettings extends BaseSettings
|
||||
'credit_footer' => 'string',
|
||||
'credit_terms' => 'string',
|
||||
'name' => 'string',
|
||||
'payment_terms' => 'integer',
|
||||
'payment_terms' => 'string',
|
||||
'payment_type_id' => 'string',
|
||||
'phone' => 'string',
|
||||
'postal_code' => 'string',
|
||||
@ -411,7 +414,7 @@ class CompanySettings extends BaseSettings
|
||||
'custom_value4' => 'string',
|
||||
'inclusive_taxes' => 'bool',
|
||||
'name' => 'string',
|
||||
'payment_terms' => 'integer',
|
||||
'payment_terms' => 'string',
|
||||
'payment_type_id' => 'string',
|
||||
'phone' => 'string',
|
||||
'postal_code' => 'string',
|
||||
@ -479,7 +482,7 @@ class CompanySettings extends BaseSettings
|
||||
$data->timezone_id = (string) config('ninja.i18n.timezone_id');
|
||||
$data->currency_id = (string) config('ninja.i18n.currency_id');
|
||||
$data->language_id = (string) config('ninja.i18n.language_id');
|
||||
$data->payment_terms = (int) config('ninja.i18n.payment_terms');
|
||||
$data->payment_terms = (string) config('ninja.i18n.payment_terms');
|
||||
$data->military_time = (bool) config('ninja.i18n.military_time');
|
||||
$data->date_format_id = (string) config('ninja.i18n.date_format_id');
|
||||
$data->country_id = (string) config('ninja.i18n.country_id');
|
||||
|
@ -15,15 +15,16 @@ use Exception;
|
||||
use Illuminate\Auth\Access\AuthorizationException;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Database\Eloquent\ModelNotFoundException as ModelNotFoundException;
|
||||
use Illuminate\Database\Eloquent\RelationNotFoundException;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
use Illuminate\Http\Exceptions\ThrottleRequestsException;
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Sentry\State\Scope;
|
||||
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
use Illuminate\Database\Eloquent\RelationNotFoundException;
|
||||
use Sentry\State\Scope;
|
||||
use function Sentry\configureScope;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
@ -55,9 +56,12 @@ class Handler extends ExceptionHandler
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
if(!Schema::hasTable('accounts'))
|
||||
return;
|
||||
|
||||
if (app()->bound('sentry') && $this->shouldReport($exception)) {
|
||||
app('sentry')->configureScope(function (Scope $scope): void {
|
||||
if (auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
|
||||
if (auth()->guard('contact') && auth()->guard('contact')->user() && auth()->guard('contact')->user()->company->account->report_errors) {
|
||||
$scope->setUser([
|
||||
'id' => auth()->guard('contact')->user()->company->account->key,
|
||||
'email' => "anonymous@example.com",
|
||||
|
@ -26,15 +26,16 @@ class CloneQuoteToInvoiceFactory
|
||||
unset($quote_array['client']);
|
||||
unset($quote_array['company']);
|
||||
unset($quote_array['hashed_id']);
|
||||
unset($quote_array['invoice_id']);
|
||||
unset($quote_array['id']);
|
||||
|
||||
|
||||
foreach($quote_array as $key => $value)
|
||||
$invoice->{$key} = $value;
|
||||
|
||||
$invoice->status_id = Invoice::STATUS_DRAFT;
|
||||
$invoice->due_date = null;
|
||||
$invoice->partial_due_date = null;
|
||||
$invoice->number = null;
|
||||
$invoice->status_id = null;
|
||||
|
||||
return $invoice;
|
||||
|
||||
|
26
app/Factory/PaymentTermFactory.php
Normal file
26
app/Factory/PaymentTermFactory.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Factory;
|
||||
|
||||
use App\Models\PaymentTerm;
|
||||
|
||||
class PaymentTermFactory
|
||||
{
|
||||
public static function create(int $company_id, int $user_id) :PaymentTerm
|
||||
{
|
||||
$payment_term = new PaymentTerm;
|
||||
$payment_term->user_id = $user_id;
|
||||
$payment_term->company_id = $company_id;
|
||||
|
||||
return $payment_term;
|
||||
}
|
||||
}
|
@ -82,6 +82,11 @@ class QuoteFilters extends QueryFilters
|
||||
});
|
||||
}
|
||||
|
||||
public function number($number = '')
|
||||
{
|
||||
return $this->builder->where('number', 'like', '%'.$number.'%');
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the list based on $sort
|
||||
*
|
||||
|
@ -62,8 +62,14 @@ class GmailTransport extends Transport
|
||||
$this->gmail->cc($message->getCc());
|
||||
$this->gmail->bcc($message->getBcc());
|
||||
|
||||
\Log::error(print_r($message->getChildren(),1));
|
||||
|
||||
foreach($message->getChildren() as $child)
|
||||
$this->gmail->attach($child); //todo this should 'just work'
|
||||
|
||||
$this->gmail->send();
|
||||
|
||||
|
||||
$this->sendPerformed($message);
|
||||
|
||||
return $this->numberOfRecipients($message);
|
||||
|
@ -23,6 +23,7 @@ use App\Utils\Traits\AppSetup;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Request as Input;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use League\Fractal\Manager;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection;
|
||||
@ -265,6 +266,7 @@ class BaseController extends Controller
|
||||
'company.quotes.invitations.contact',
|
||||
'company.quotes.invitations.company',
|
||||
'company.credits',
|
||||
'company.payment_terms',
|
||||
//'company.credits.invitations.contact',
|
||||
//'company.credits.invitations.company',
|
||||
'company.vendors.contacts',
|
||||
@ -282,6 +284,7 @@ class BaseController extends Controller
|
||||
'company.users.company_user',
|
||||
'company.tax_rates',
|
||||
'company.groups',
|
||||
'company.payment_terms',
|
||||
];
|
||||
|
||||
/**
|
||||
@ -311,10 +314,16 @@ class BaseController extends Controller
|
||||
|
||||
public function flutterRoute()
|
||||
{
|
||||
if ((bool)$this->checkAppSetup() !== false) {
|
||||
|
||||
// // Ensure all request are over HTTPS in production
|
||||
// if (! request()->secure()) {
|
||||
// return redirect()->secure(request()->path());
|
||||
// }
|
||||
|
||||
if ((bool)$this->checkAppSetup() !== false && Schema::hasTable('accounts') && $account = Account::all()->first()) {
|
||||
$data = [];
|
||||
|
||||
if (Ninja::isSelfHost() && $account = Account::all()->first()) {
|
||||
if (Ninja::isSelfHost()) {
|
||||
$data['report_errors'] = $account->report_errors;
|
||||
} else {
|
||||
$data['report_errors'] = true;
|
||||
|
@ -41,7 +41,7 @@ class InvitationController extends Controller
|
||||
if ((bool)$invitation->contact->client->getSetting('enable_client_portal_password') !== false) {
|
||||
$this->middleware('auth:contact');
|
||||
} else {
|
||||
auth()->guard('contact')->login($invitation->contact, false);
|
||||
auth()->guard('contact')->login($invitation->contact, true);
|
||||
}
|
||||
|
||||
if (!request()->has('silent')) {
|
||||
|
@ -23,13 +23,13 @@ class SwitchCompanyController extends Controller
|
||||
|
||||
public function __invoke(string $contact)
|
||||
{
|
||||
$client_contact = ClientContact::query()
|
||||
->where('user_id', auth()->user()->id)
|
||||
|
||||
$client_contact = ClientContact::where('email', auth()->user()->email)
|
||||
->where('id', $this->transformKeys($contact))
|
||||
->first();
|
||||
|
||||
Auth::guard('contact')->login($client_contact, true);
|
||||
|
||||
return back();
|
||||
return redirect('/client/dashboard');
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ use App\Http\Requests\Company\StoreCompanyRequest;
|
||||
use App\Http\Requests\Company\UpdateCompanyRequest;
|
||||
use App\Http\Requests\SignupRequest;
|
||||
use App\Jobs\Company\CreateCompany;
|
||||
use App\Jobs\Company\CreateCompanyPaymentTerms;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use App\Jobs\Ninja\RefundCancelledAccount;
|
||||
use App\Jobs\RegisterNewAccount;
|
||||
@ -204,6 +205,8 @@ class CompanyController extends BaseController
|
||||
|
||||
$company = CreateCompany::dispatchNow($request->all(), auth()->user()->company()->account);
|
||||
|
||||
CreateCompanyPaymentTerms::dispatchNow($company, auth()->user());
|
||||
|
||||
$company = $this->company_repo->save($request->all(), $company);
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $company, $company);
|
||||
|
@ -14,7 +14,7 @@ namespace App\Http\Controllers;
|
||||
use App\Helpers\Email\InvoiceEmail;
|
||||
use App\Http\Requests\Email\SendEmailRequest;
|
||||
use App\Jobs\Invoice\EmailInvoice;
|
||||
use App\Jobs\Mail\EntitySentEmail;
|
||||
use App\Jobs\Mail\EntitySentMailer;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Quote;
|
||||
@ -115,14 +115,17 @@ class EmailController extends BaseController
|
||||
$entity_string = strtolower(class_basename($entity_obj));
|
||||
|
||||
$entity_obj->invitations->each(function ($invitation) use ($subject, $body, $entity_string, $entity_obj) {
|
||||
|
||||
if ($invitation->contact->send_email && $invitation->contact->email) {
|
||||
|
||||
$when = now()->addSeconds(1);
|
||||
|
||||
$invitation->contact->notify((new SendGenericNotification($invitation, $entity_string, $subject, $body))->delay($when));
|
||||
|
||||
EntitySentEmail::dispatch($invitation, $entity_string, $entity_obj->user, $invitation->company);
|
||||
EntitySentMailer::dispatch($invitation, $entity_string, $entity_obj->user, $invitation->company);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
if ($this instanceof Invoice) {
|
||||
|
@ -670,6 +670,9 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
//need to make sure the invoice is cancelled first!!
|
||||
$invoice->service()->handleCancellation()->save();
|
||||
|
||||
$this->invoice_repo->delete($invoice);
|
||||
|
||||
if (!$bulk) {
|
||||
|
@ -366,7 +366,7 @@ class MigrationController extends BaseController
|
||||
return;
|
||||
}
|
||||
|
||||
StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $company);
|
||||
StartMigration::dispatch(base_path("storage/app/public/$migration_file"), $user, $company)->delay(now()->addSeconds(60));
|
||||
|
||||
return response()->json([
|
||||
'_id' => Str::uuid(),
|
||||
|
12
app/Http/Controllers/OpenAPI/PaymentTermSchema.php
Normal file
12
app/Http/Controllers/OpenAPI/PaymentTermSchema.php
Normal file
@ -0,0 +1,12 @@
|
||||
<?php
|
||||
/**
|
||||
* @OA\Schema(
|
||||
* schema="PaymentTerm",
|
||||
* type="object",
|
||||
* @OA\Property(property="num_days", type="integer", example="1", description="The payment term length in days"),
|
||||
* @OA\Property(property="name", type="string", example="NET 1", description="The payment term length in string format"),
|
||||
* @OA\Property(property="created_at", type="number", format="integer", example="134341234234", description="Timestamp"),
|
||||
* @OA\Property(property="updated_at", type="number", format="integer", example="134341234234", description="Timestamp"),
|
||||
* @OA\Property(property="archived_at", type="number", format="integer", example="134341234234", description="Timestamp"),
|
||||
* )
|
||||
*/
|
477
app/Http/Controllers/PaymentTermController.php
Normal file
477
app/Http/Controllers/PaymentTermController.php
Normal file
@ -0,0 +1,477 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\PaymentTermFactory;
|
||||
use App\Http\Requests\PaymentTerm\CreatePaymentTermRequest;
|
||||
use App\Http\Requests\PaymentTerm\DestroyPaymentTermRequest;
|
||||
use App\Http\Requests\PaymentTerm\ShowPaymentTermRequest;
|
||||
use App\Http\Requests\PaymentTerm\StorePaymentTermRequest;
|
||||
use App\Http\Requests\PaymentTerm\UpdatePaymentTermRequest;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Repositories\PaymentTermRepository;
|
||||
use App\Transformers\PaymentTermTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PaymentTermController extends BaseController
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
protected $entity_type = PaymentTerm::class;
|
||||
|
||||
protected $entity_transformer = PaymentTermTransformer::class;
|
||||
|
||||
/**
|
||||
* @var PaymentRepository
|
||||
*/
|
||||
protected $payment_term_repo;
|
||||
|
||||
|
||||
/**
|
||||
* PaymentTermController constructor.
|
||||
*
|
||||
* @param \App\Repositories\PaymentTermRepository $payment_term_repo The payment term repo
|
||||
*/
|
||||
public function __construct(PaymentTermRepository $payment_term_repo)
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$this->payment_term_repo = $payment_term_repo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/payment_terms",
|
||||
* operationId="getPaymentTerms",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Gets a list of payment terms",
|
||||
* description="Lists payment terms",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="A list of payment terms",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$payment_terms = PaymentTerm::whereCompanyId(auth()->user()->company()->id)->orWhere('company_id', null);
|
||||
|
||||
return $this->listResponse($payment_terms);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @param \App\Http\Requests\Payment\CreatePaymentTermRequest $request The request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Get(
|
||||
* path="/api/v1/payment_terms/create",
|
||||
* operationId="getPaymentTermsCreate",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Gets a new blank PaymentTerm object",
|
||||
* description="Returns a blank object with default values",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="A blank PaymentTerm object",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/Payment"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function create(CreatePaymentTermRequest $request)
|
||||
{
|
||||
$payment_term = PaymentTermFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
|
||||
return $this->itemResponse($payment_term);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\Payment\StorePaymentRequest $request The request
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/payment_terms",
|
||||
* operationId="storePaymentTerm",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Adds a Payment",
|
||||
* description="Adds a Payment Term to the system",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\RequestBody(
|
||||
* description="The payment_terms request",
|
||||
* required=true,
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the saved Payment object",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function store(StorePaymentTermRequest $request)
|
||||
{
|
||||
$payment_term = PaymentTermFactory::create(auth()->user()->company()->id, auth()->user()->id);
|
||||
$payment_term->fill($request->all());
|
||||
$payment_term->save();
|
||||
|
||||
return $this->itemResponse($payment_term->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/payment_terms/{id}",
|
||||
* operationId="showPaymentTerm",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Shows a Payment Term",
|
||||
* description="Displays an Payment Term by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Payment Term Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the Payment Term object",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function show(ShowPaymentTermRequest $request, PaymentTerm $payment_term)
|
||||
{
|
||||
return $this->itemResponse($payment_term);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @OA\Get(
|
||||
* path="/api/v1/payment_terms/{id}/edit",
|
||||
* operationId="editPaymentTerms",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Shows an Payment Term for editting",
|
||||
* description="Displays an Payment Term by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Payment Term Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the Payment object",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function edit(EditPaymentRequest $request, Payment $payment)
|
||||
{
|
||||
return $this->itemResponse($payment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \App\Http\Requests\PaymentTerm\UpdatePaymentTermRequest $request The request
|
||||
* @param \App\Models\PaymentTerm $payment_term The payment term
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Put(
|
||||
* path="/api/v1/payment_terms/{id}",
|
||||
* operationId="updatePaymentTerm",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Updates a Payment Term",
|
||||
* description="Handles the updating of an Payment Termby id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Payment Term Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns the Payment Term object",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function update(UpdatePaymentTermRequest $request, PaymentTerm $payment_term)
|
||||
{
|
||||
$payment_term->fill($request->all());
|
||||
$payment_term->save();
|
||||
|
||||
return $this->itemResponse($payment_term->fresh());
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param \App\Http\Requests\PaymentTerm\DestroyPaymentTermRequest $request
|
||||
* @param \App\Models\PaymentTerm $payment_term
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*
|
||||
*
|
||||
* @OA\Delete(
|
||||
* path="/api/v1/payment_terms/{id}",
|
||||
* operationId="deletePaymentTerm",
|
||||
* tags={"payment_termss"},
|
||||
* summary="Deletes a Payment Term",
|
||||
* description="Handles the deletion of an PaymentTerm by id",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/include"),
|
||||
* @OA\Parameter(
|
||||
* name="id",
|
||||
* in="path",
|
||||
* description="The Payment Term Hashed ID",
|
||||
* example="D2J234DFA",
|
||||
* required=true,
|
||||
* @OA\Schema(
|
||||
* type="string",
|
||||
* format="string",
|
||||
* ),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="Returns a HTTP status",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
*
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function destroy(DestroyPaymentTermRequest $request, PaymentTerm $payment_term)
|
||||
{
|
||||
|
||||
$payment_term->delete();
|
||||
|
||||
return response()->json([], 200);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Perform bulk actions on the list view
|
||||
*
|
||||
* @return Collection
|
||||
*
|
||||
*
|
||||
* @OA\Post(
|
||||
* path="/api/v1/payment_terms/bulk",
|
||||
* operationId="bulkPaymentTerms",
|
||||
* tags={"payment_terms"},
|
||||
* summary="Performs bulk actions on an array of payment terms",
|
||||
* description="",
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Api-Token"),
|
||||
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
|
||||
* @OA\Parameter(ref="#/components/parameters/index"),
|
||||
* @OA\RequestBody(
|
||||
* description="Payment Ter,s",
|
||||
* required=true,
|
||||
* @OA\MediaType(
|
||||
* mediaType="application/json",
|
||||
* @OA\Schema(
|
||||
* type="array",
|
||||
* @OA\Items(
|
||||
* type="integer",
|
||||
* description="Array of hashed IDs to be bulk 'actioned",
|
||||
* example="[0,1,2,3]",
|
||||
* ),
|
||||
* )
|
||||
* )
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=200,
|
||||
* description="The Payment Terms response",
|
||||
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
|
||||
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
|
||||
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
|
||||
* @OA\JsonContent(ref="#/components/schemas/PaymentTerm"),
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response=422,
|
||||
* description="Validation error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
|
||||
|
||||
* ),
|
||||
* @OA\Response(
|
||||
* response="default",
|
||||
* description="Unexpected Error",
|
||||
* @OA\JsonContent(ref="#/components/schemas/Error"),
|
||||
* ),
|
||||
* )
|
||||
*
|
||||
*/
|
||||
public function bulk()
|
||||
{
|
||||
$action = request()->input('action');
|
||||
|
||||
$ids = request()->input('ids');
|
||||
|
||||
$payment_terms = PaymentTerm::withTrashed()->company()->find($this->transformKeys($ids));
|
||||
|
||||
$payment_terms->each(function ($payment_term, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $payment_term)) {
|
||||
$this->payment_term_repo->{$action}($payment_term);
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(PaymentTerm::withTrashed()->whereIn('id', $this->transformKeys($ids)));
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -528,6 +528,20 @@ class QuoteController extends BaseController
|
||||
return response()->json(['message' => 'Email Sent!'], 200);
|
||||
}
|
||||
|
||||
if($action == 'convert') {
|
||||
|
||||
$this->entity_type = Quote::class;
|
||||
$this->entity_transformer = QuoteTransformer::class;
|
||||
|
||||
$quotes->each(function ($quote, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $quote) && $quote->service()->isConvertable()) {
|
||||
$quote->service()->convertToInvoice();
|
||||
}
|
||||
});
|
||||
|
||||
return $this->listResponse(Quote::withTrashed()->whereIn('id', $this->transformKeys($ids))->company());
|
||||
}
|
||||
|
||||
/*
|
||||
* Send the other actions to the switch
|
||||
*/
|
||||
@ -642,14 +656,6 @@ class QuoteController extends BaseController
|
||||
}
|
||||
|
||||
return $this->itemResponse($quote->service()->approve()->save());
|
||||
break;
|
||||
case 'convert':
|
||||
|
||||
$this->entity_type = Invoice::class;
|
||||
$this->entity_transformer = InvoiceTransformer::class;
|
||||
|
||||
return $this->itemResponse($quote->service()->convertToInvoice());
|
||||
|
||||
break;
|
||||
case 'history':
|
||||
# code...
|
||||
|
@ -16,6 +16,7 @@ use Codedge\Updater\UpdaterManager;
|
||||
use Composer\Factory;
|
||||
use Composer\IO\NullIO;
|
||||
use Composer\Installer;
|
||||
use Cz\Git\GitRepository;
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
@ -66,22 +67,16 @@ class SelfUpdateController extends BaseController
|
||||
return response()->json(['message' => 'Self update not available on this system.'], 403);
|
||||
}
|
||||
|
||||
info("is new version available = ". $updater->source()->isNewVersionAvailable());
|
||||
/* .git MUST be owned/writable by the webserver user */
|
||||
$repo = new GitRepository(base_path());
|
||||
|
||||
// Get the new version available
|
||||
$versionAvailable = $updater->source()->getVersionAvailable();
|
||||
info("Are there changes to pull? " . $repo->hasChanges());
|
||||
|
||||
info($versionAvailable);
|
||||
$res = $repo->pull();
|
||||
|
||||
info("Are there any changes to pull? " . $repo->hasChanges());
|
||||
|
||||
// Create a release
|
||||
$release = $updater->source()->fetch($versionAvailable);
|
||||
|
||||
info(print_r($release,1));
|
||||
|
||||
// Run the update process
|
||||
$res = $updater->source()->update($release);
|
||||
|
||||
info(print_r($res,1));
|
||||
Artisan::call('ninja:post-update');
|
||||
|
||||
return response()->json(['message'=>$res], 200);
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
@ -34,9 +35,15 @@ trait VerifiesUserEmail
|
||||
$user->confirmation_code = null;
|
||||
$user->save();
|
||||
|
||||
return response()->json(['message' => ctrans('texts.security_confirmation')]);
|
||||
return $this->render('auth.confirmed', [
|
||||
'root' => 'themes',
|
||||
'message' => ctrans('texts.security_confirmation'),
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json(['message' => ctrans('texts.wrong_confirmation')]);
|
||||
return $this->render('auth.confirmed', [
|
||||
'root' => 'themes',
|
||||
'message' => ctrans('texts.wrong_confirmation'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,8 @@ class StartupCheck
|
||||
Session::flash('message', 'Cache cleared');
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Make sure our cache is built */
|
||||
$cached_tables = config('ninja.cached_tables');
|
||||
|
||||
|
@ -36,8 +36,8 @@ class CreateAccountRequest extends Request
|
||||
{
|
||||
return [
|
||||
//'email' => 'required|string|email|max:100',
|
||||
'first_name' => 'required|string|max:100',
|
||||
'last_name' => 'required|string:max:100',
|
||||
'first_name' => 'string|max:100',
|
||||
'last_name' => 'string:max:100',
|
||||
'password' => 'required|string|min:6',
|
||||
'email' => 'bail|required|email',
|
||||
'email' => new NewUniqueUserRule(),
|
||||
|
@ -49,6 +49,9 @@ class StoreInvoiceRequest extends Request
|
||||
|
||||
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
|
||||
|
||||
$rules['invitations.*.client_contact_id'] = 'distinct';
|
||||
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
29
app/Http/Requests/PaymentTerm/ActionPaymentTermRequest.php
Normal file
29
app/Http/Requests/PaymentTerm/ActionPaymentTermRequest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Payment;
|
||||
|
||||
class ActionPaymentTermRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
}
|
29
app/Http/Requests/PaymentTerm/CreatePaymentTermRequest.php
Normal file
29
app/Http/Requests/PaymentTerm/CreatePaymentTermRequest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PaymentTerm;
|
||||
|
||||
class CreatePaymentTermRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
}
|
29
app/Http/Requests/PaymentTerm/DestroyPaymentTermRequest.php
Normal file
29
app/Http/Requests/PaymentTerm/DestroyPaymentTermRequest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PaymentTerm;
|
||||
|
||||
class DestroyPaymentTermRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
}
|
46
app/Http/Requests/PaymentTerm/EditPaymentTermRequest.php
Normal file
46
app/Http/Requests/PaymentTerm/EditPaymentTermRequest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PaymentTerm;
|
||||
|
||||
class EditPaymentTermRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize()
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
//$input['id'] = $this->encodePrimaryKey($input['id']);
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
29
app/Http/Requests/PaymentTerm/ShowPaymentTermRequest.php
Normal file
29
app/Http/Requests/PaymentTerm/ShowPaymentTermRequest.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PaymentTerm;
|
||||
|
||||
class ShowPaymentTermRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
}
|
48
app/Http/Requests/PaymentTerm/StorePaymentTermRequest.php
Normal file
48
app/Http/Requests/PaymentTerm/StorePaymentTermRequest.php
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class StorePaymentTermRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
$rules = [
|
||||
|
||||
];
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
46
app/Http/Requests/PaymentTerm/UpdatePaymentTermRequest.php
Normal file
46
app/Http/Requests/PaymentTerm/UpdatePaymentTermRequest.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Http\Requests\PaymentTerm;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdatePaymentTermRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->isAdmin();
|
||||
}
|
||||
|
||||
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'num_days' => 'required',
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
@ -49,7 +49,8 @@ class PortalComposer
|
||||
$data['company'] = auth()->user()->company;
|
||||
$data['client'] = auth()->user()->client;
|
||||
$data['settings'] = auth()->user()->client->getMergedSettings();
|
||||
$data['multiple_contacts'] = ClientContact::where('email', auth('contact')->user()->email)->get();
|
||||
|
||||
$data['multiple_contacts'] = ClientContact::where('email', auth('contact')->user()->email)->whereNotNull('email')->distinct('company_id')->get();
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ namespace App\Jobs\Account;
|
||||
|
||||
use App\Events\Account\AccountCreated;
|
||||
use App\Jobs\Company\CreateCompany;
|
||||
use App\Jobs\Company\CreateCompanyPaymentTerms;
|
||||
use App\Jobs\Company\CreateCompanyToken;
|
||||
use App\Jobs\User\CreateUser;
|
||||
use App\Models\Account;
|
||||
@ -60,6 +61,8 @@ class CreateAccount
|
||||
|
||||
$spaa9f78 = CreateUser::dispatchNow($this->request, $sp794f3f, $sp035a66, true);
|
||||
|
||||
CreateCompanyPaymentTerms::dispatchNow($sp035a66, $spaa9f78);
|
||||
|
||||
if ($spaa9f78) {
|
||||
auth()->login($spaa9f78, false);
|
||||
}
|
||||
|
@ -57,10 +57,10 @@ class CreateCompany
|
||||
$company->ip = request()->ip();
|
||||
$company->settings = $settings;
|
||||
$company->db = config('database.default');
|
||||
$company->enabled_modules = config('ninja.enabled_modules');
|
||||
$company->subdomain = isset($this->request['subdomain']) ? $this->request['subdomain'] : '';
|
||||
$company->save();
|
||||
|
||||
|
||||
return $company;
|
||||
}
|
||||
}
|
||||
|
66
app/Jobs/Company/CreateCompanyPaymentTerms.php
Normal file
66
app/Jobs/Company/CreateCompanyPaymentTerms.php
Normal file
@ -0,0 +1,66 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Company;
|
||||
|
||||
use App\DataMapper\CompanySettings;
|
||||
use App\Events\UserSignedUp;
|
||||
use App\Models\Company;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class CreateCompanyPaymentTerms
|
||||
{
|
||||
use MakesHash;
|
||||
use Dispatchable;
|
||||
|
||||
protected $company;
|
||||
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
|
||||
public function __construct($company, $user)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
$paymentTerms = [
|
||||
['num_days' => 0, 'name' => 'Net 0', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 7, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 10, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 14, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 15, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 30, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 60, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
['num_days' => 90, 'name' => '', 'company_id' => $this->company->id, 'user_id' => $this->user->id],
|
||||
];
|
||||
|
||||
PaymentTerm::insert($paymentTerms);
|
||||
|
||||
}
|
||||
}
|
93
app/Jobs/Mail/EntityPaidMailer.php
Normal file
93
app/Jobs/Mail/EntityPaidMailer.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Mail;
|
||||
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\Google\Google;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntityNotificationMailer;
|
||||
use App\Mail\Admin\EntityPaidObject;
|
||||
use App\Mail\Admin\EntitySentObject;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
use App\Providers\MailServiceProvider;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class EntityPaidMailer extends BaseMailerJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $company;
|
||||
|
||||
public $user;
|
||||
|
||||
public $payment;
|
||||
|
||||
public $entity_type;
|
||||
|
||||
public $entity;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($payment, $user, $company)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->payment = $payment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
info("entity paid mailer");
|
||||
//Set DB
|
||||
//
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
if($this->company->company_users->first()->is_migrating)
|
||||
return true;
|
||||
|
||||
//if we need to set an email driver do it now
|
||||
$this->setMailDriver($this->payment->client->getSetting('email_sending_method'));
|
||||
|
||||
$mail_obj = (new EntityPaidObject($this->payment))->build();
|
||||
$mail_obj->from = [$this->payment->user->email, $this->payment->user->present()->name()];
|
||||
|
||||
//send email
|
||||
Mail::to($this->user->email)
|
||||
->send(new EntityNotificationMailer($mail_obj));
|
||||
|
||||
//catch errors
|
||||
if (count(Mail::failures()) > 0) {
|
||||
$this->logMailError(Mail::failures());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function logMailError($errors)
|
||||
{
|
||||
SystemLogger::dispatch(
|
||||
$errors,
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_SEND,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
$this->payment->client
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -5,7 +5,7 @@ namespace App\Jobs\Mail;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\Google\Google;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntitySent;
|
||||
use App\Mail\Admin\EntityNotificationMailer;
|
||||
use App\Mail\Admin\EntitySentObject;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
@ -18,7 +18,7 @@ use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class EntitySentEmail extends BaseMailerJob implements ShouldQueue
|
||||
class EntitySentMailer extends BaseMailerJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
@ -56,19 +56,19 @@ class EntitySentEmail extends BaseMailerJob implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
//set DB
|
||||
info("entity sent mailer");
|
||||
//Set DB
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
//if we need to set an email driver do it now
|
||||
$this->setMailDriver($this->entity->client->getSetting('email_sending_method'));
|
||||
|
||||
$mail_obj = (new EntitySentObject($this->invitation, $this->entity_type))->build();
|
||||
$mail_obj->from = $this->entity->user->present()->name();
|
||||
|
||||
$mail_obj->from = [$this->entity->user->email, $this->entity->user->present()->name()];
|
||||
|
||||
//send email
|
||||
// Mail::to($this->user->email)
|
||||
Mail::to('turbo124@gmail.com') //@todo
|
||||
->send(new EntitySent($mail_obj));
|
||||
Mail::to($this->user->email)
|
||||
->send(new EntityNotificationMailer($mail_obj));
|
||||
|
||||
//catch errors
|
||||
if (count(Mail::failures()) > 0) {
|
||||
@ -84,7 +84,7 @@ class EntitySentEmail extends BaseMailerJob implements ShouldQueue
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_SEND,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
$this->invoice->client
|
||||
$this->entity->client
|
||||
);
|
||||
}
|
||||
|
94
app/Jobs/Mail/EntityViewedMailer.php
Normal file
94
app/Jobs/Mail/EntityViewedMailer.php
Normal file
@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Mail;
|
||||
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\Google\Google;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntityNotificationMailer;
|
||||
use App\Mail\Admin\EntityViewedObject;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
use App\Providers\MailServiceProvider;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class EntityViewedMailer extends BaseMailerJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $company;
|
||||
|
||||
public $user;
|
||||
|
||||
public $invitation;
|
||||
|
||||
public $entity_type;
|
||||
|
||||
public $entity;
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($invitation, $entity_type, $user, $company)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
$this->user = $user;
|
||||
|
||||
$this->invitation = $invitation;
|
||||
|
||||
$this->entity = $invitation->{$entity_type};
|
||||
|
||||
$this->entity_type = $entity_type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
info("entity viewed mailer");
|
||||
|
||||
//Set DB
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
//if we need to set an email driver do it now
|
||||
$this->setMailDriver($this->entity->client->getSetting('email_sending_method'));
|
||||
|
||||
$mail_obj = (new EntityViewedObject($this->invitation, $this->entity_type))->build();
|
||||
$mail_obj->from = [$this->entity->user->email, $this->entity->user->present()->name()];
|
||||
|
||||
//send email
|
||||
Mail::to($this->user->email)
|
||||
->send(new EntityNotificationMailer($mail_obj));
|
||||
|
||||
//catch errors
|
||||
if (count(Mail::failures()) > 0) {
|
||||
$this->logMailError(Mail::failures());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private function logMailError($errors)
|
||||
{
|
||||
SystemLogger::dispatch(
|
||||
$errors,
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_SEND,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
$this->invoice->client
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
106
app/Jobs/Mail/PaymentFailureMailer.php
Normal file
106
app/Jobs/Mail/PaymentFailureMailer.php
Normal file
@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs\Mail;
|
||||
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Libraries\Google\Google;
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Mail\Admin\EntityNotificationMailer;
|
||||
use App\Mail\Admin\EntityPaidObject;
|
||||
use App\Mail\Admin\EntitySentObject;
|
||||
use App\Mail\Admin\PaymentFailureObject;
|
||||
use App\Models\SystemLog;
|
||||
use App\Models\User;
|
||||
use App\Providers\MailServiceProvider;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Config;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class PaymentFailureMailer extends BaseMailerJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels, UserNotifies;
|
||||
|
||||
public $client;
|
||||
|
||||
public $message;
|
||||
|
||||
public $company;
|
||||
|
||||
public $amount;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($client, $message, $company, $amount)
|
||||
{
|
||||
$this->company = $company;
|
||||
|
||||
$this->message = $message;
|
||||
|
||||
$this->client = $client;
|
||||
|
||||
$this->amount = $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
info("entity payment failure mailer");
|
||||
//Set DB
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
//if we need to set an email driver do it now
|
||||
$this->setMailDriver($this->client->getSetting('email_sending_method'));
|
||||
|
||||
//iterate through company_users
|
||||
$this->company->company_users->each(function ($company_user){
|
||||
|
||||
//determine if this user has the right permissions
|
||||
$methods = $this->findCompanyUserNotificationType($company_user, ['payment_failure']);
|
||||
|
||||
//if mail is a method type -fire mail!!
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
$mail_obj = (new PaymentFailureObject($this->client, $this->message, $this->amount, $this->company))->build();
|
||||
$mail_obj->from = [$this->company->owner()->email, $this->company->owner()->present()->name()];
|
||||
|
||||
//send email
|
||||
Mail::to($company_user->user->email)
|
||||
->send(new EntityNotificationMailer($mail_obj));
|
||||
|
||||
//catch errors
|
||||
if (count(Mail::failures()) > 0) {
|
||||
$this->logMailError(Mail::failures());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private function logMailError($errors)
|
||||
{
|
||||
SystemLogger::dispatch(
|
||||
$errors,
|
||||
SystemLog::CATEGORY_MAIL,
|
||||
SystemLog::EVENT_MAIL_SEND,
|
||||
SystemLog::TYPE_FAILURE,
|
||||
$this->client
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -31,6 +31,7 @@ use App\Libraries\MultiDB;
|
||||
use App\Mail\MigrationCompleted;
|
||||
use App\Mail\MigrationFailed;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\Company;
|
||||
use App\Models\CompanyGateway;
|
||||
@ -38,6 +39,7 @@ use App\Models\Credit;
|
||||
use App\Models\Document;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Quote;
|
||||
use App\Models\TaxRate;
|
||||
@ -86,6 +88,7 @@ class Import implements ShouldQueue
|
||||
private $available_imports = [
|
||||
'company',
|
||||
'users',
|
||||
'payment_terms',
|
||||
'tax_rates',
|
||||
'clients',
|
||||
'products',
|
||||
@ -145,7 +148,7 @@ class Import implements ShouldQueue
|
||||
* @return void
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function handle()
|
||||
public function handle() :bool
|
||||
{
|
||||
|
||||
set_time_limit(0);
|
||||
@ -154,7 +157,6 @@ class Import implements ShouldQueue
|
||||
if (! in_array($key, $this->available_imports)) {
|
||||
//throw new ResourceNotAvailableForMigration("Resource {$key} is not available for migration.");
|
||||
info("Resource {$key} is not available for migration.");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -168,6 +170,8 @@ class Import implements ShouldQueue
|
||||
Mail::to($this->user)->send(new MigrationCompleted());
|
||||
|
||||
info('Completed🚀🚀🚀🚀🚀 at '.now());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -366,7 +370,9 @@ class Import implements ShouldQueue
|
||||
unset($modified_contacts[$key]['id']);
|
||||
}
|
||||
|
||||
$contact_repository->save($modified_contacts, $client);
|
||||
$saveable_contacts['contacts'] = $modified_contacts;
|
||||
|
||||
$contact_repository->save($saveable_contacts, $client);
|
||||
}
|
||||
|
||||
$key = "clients_{$resource['id']}";
|
||||
@ -695,6 +701,28 @@ class Import implements ShouldQueue
|
||||
$data = null;
|
||||
}
|
||||
|
||||
private function processPaymentTerms(array $data) :void
|
||||
{
|
||||
|
||||
PaymentTerm::unguard();
|
||||
|
||||
$modified = collect($data)->map(function ($item){
|
||||
|
||||
$item['user_id'] = $this->user->id;
|
||||
$item['company_id'] = $this->company->id;
|
||||
|
||||
return $item;
|
||||
|
||||
})->toArray();
|
||||
|
||||
PaymentTerm::insert($modified);
|
||||
|
||||
PaymentTerm::reguard();
|
||||
|
||||
/*Improve memory handling by setting everything to null when we have finished*/
|
||||
$data = null;
|
||||
}
|
||||
|
||||
private function processCompanyGateways(array $data) :void
|
||||
{
|
||||
CompanyGateway::unguard();
|
||||
|
@ -74,6 +74,9 @@ class StartMigration implements ShouldQueue
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
set_time_limit(0);
|
||||
|
||||
MultiDB::setDb($this->company->db);
|
||||
|
||||
auth()->login($this->user, false);
|
||||
@ -96,7 +99,7 @@ class StartMigration implements ShouldQueue
|
||||
$zip->close();
|
||||
|
||||
if (app()->environment() == 'testing') {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->company->setMigration(true);
|
||||
@ -110,6 +113,9 @@ class StartMigration implements ShouldQueue
|
||||
$data = json_decode(file_get_contents($file), 1);
|
||||
|
||||
Import::dispatchNow($data, $this->company, $this->user);
|
||||
|
||||
$this->company->setMigration(false);
|
||||
|
||||
} catch (NonExistingMigrationFile | ProcessingMigrationArchiveFailed | ResourceNotAvailableForMigration | MigrationValidatorFailed | ResourceDependencyMissing $e) {
|
||||
$this->company->setMigration(false);
|
||||
|
||||
@ -119,6 +125,11 @@ class StartMigration implements ShouldQueue
|
||||
info($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
//always make sure we unset the migration as running
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function failed($exception = null)
|
||||
|
@ -37,18 +37,27 @@ class SubscriptionHandler implements ShouldQueue
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
public function handle() :bool
|
||||
{
|
||||
|
||||
if(!$this->entity->company || $this->entity->company->company_users->first()->is_migrating)
|
||||
return true;
|
||||
|
||||
info("i got past the check");
|
||||
|
||||
$subscriptions = Subscription::where('company_id', $this->entity->company_id)
|
||||
->where('event_id', $this->event_id)
|
||||
->get();
|
||||
|
||||
if(!$subscriptions || $subscriptions->count() == 0)
|
||||
return;
|
||||
return true;
|
||||
|
||||
$subscriptions->each(function($subscription) {
|
||||
$this->process($subscription);
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
private function process($subscription)
|
||||
|
@ -13,6 +13,7 @@ namespace App\Listeners\Credit;
|
||||
|
||||
use App\Factory\CreditInvitationFactory;
|
||||
use App\Factory\InvoiceInvitationFactory;
|
||||
use App\Models\CreditInvitation;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -38,7 +39,7 @@ class CreateCreditInvitation implements ShouldQueue
|
||||
$contacts = $credit->client->contacts;
|
||||
|
||||
$contacts->each(function ($contact) use ($credit) {
|
||||
$invitation = InvoiceInvitation::whereCompanyId($credit->company_id)
|
||||
$invitation = CreditInvitation::whereCompanyId($credit->company_id)
|
||||
->whereClientContactId($contact->id)
|
||||
->whereCreditId($credit->id)
|
||||
->first();
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Listeners\Invoice;
|
||||
|
||||
use App\Jobs\Mail\EntitySentMailer;
|
||||
use App\Models\Activity;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\InvoiceInvitation;
|
||||
@ -57,7 +58,7 @@ class InvoiceEmailedNotification implements ShouldQueue
|
||||
//This allows us better control of how we
|
||||
//handle the mailer
|
||||
|
||||
EntitySentEmail::dispatch($invitation, 'invoice', $user, $invitation->company);
|
||||
EntitySentMailer::dispatch($invitation, 'invoice', $user, $invitation->company);
|
||||
}
|
||||
|
||||
$notification->method = $methods;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Listeners\Misc;
|
||||
|
||||
use App\Jobs\Mail\EntityViewedMailer;
|
||||
use App\Notifications\Admin\EntityViewedNotification;
|
||||
use App\Utils\Traits\Notifications\UserNotifies;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
@ -44,11 +45,22 @@ class InvitationViewedListener implements ShouldQueue
|
||||
$notification = new EntityViewedNotification($invitation, $entity_name);
|
||||
|
||||
foreach ($invitation->company->company_users as $company_user) {
|
||||
|
||||
$entity_viewed = "{$entity_name}_viewed";
|
||||
|
||||
$notification->method = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]);
|
||||
$methods = $this->findUserNotificationTypes($invitation, $company_user, $entity_name, ['all_notifications', $entity_viewed]);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
EntityViewedMailer::dispatch($invitation, $entity_name, $company_user->user, $invitation->company);
|
||||
|
||||
}
|
||||
|
||||
$notification->method = $methods;
|
||||
|
||||
$company_user->user->notify($notification);
|
||||
|
||||
}
|
||||
|
||||
if (isset($invitation->company->slack_webhook_url)) {
|
||||
@ -57,14 +69,9 @@ class InvitationViewedListener implements ShouldQueue
|
||||
Notification::route('slack', $invitation->company->slack_webhook_url)
|
||||
->notify($notification);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private function userNotificationArray($notifications)
|
||||
{
|
||||
$via_array = [];
|
||||
|
||||
if (stripos($this->company_user->permissions, ) !== false);
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace App\Listeners\Payment;
|
||||
|
||||
use App\Jobs\Mail\EntityPaidMailer;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
@ -46,10 +47,26 @@ class PaymentNotification implements ShouldQueue
|
||||
|
||||
/*User notifications*/
|
||||
foreach ($payment->company->company_users as $company_user) {
|
||||
|
||||
if($company_user->is_migrating)
|
||||
return true;
|
||||
|
||||
$user = $company_user->user;
|
||||
|
||||
$methods = $this->findUserEntityNotificationType($payment, $company_user, ['all_notifications']);
|
||||
|
||||
if (($key = array_search('mail', $methods)) !== false) {
|
||||
unset($methods[$key]);
|
||||
|
||||
//Fire mail notification here!!!
|
||||
//This allows us better control of how we
|
||||
//handle the mailer
|
||||
|
||||
EntityPaidMailer::dispatch($payment, $user, $payment->company);
|
||||
}
|
||||
|
||||
$notification = new NewPaymentNotification($payment, $payment->company);
|
||||
$notification->method = $this->findUserEntityNotificationType($payment, $company_user, ['all_notifications']);
|
||||
$notification->method = $methods;
|
||||
|
||||
if ($user) {
|
||||
$user->notify($notification);
|
||||
|
58
app/Listeners/Quote/CreateQuoteInvitation.php
Normal file
58
app/Listeners/Quote/CreateQuoteInvitation.php
Normal file
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://creditninja.com)
|
||||
*
|
||||
* @link https://github.com/creditninja/creditninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://creditninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Listeners\Quote;
|
||||
|
||||
use App\Factory\CreditInvitationFactory;
|
||||
use App\Factory\InvoiceInvitationFactory;
|
||||
use App\Factory\QuoteInvitationFactory;
|
||||
use App\Models\InvoiceInvitation;
|
||||
use App\Models\QuoteInvitation;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Support\Facades\Blade;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Spatie\Browsershot\Browsershot;
|
||||
use Symfony\Component\Debug\Exception\FatalThrowableError;
|
||||
|
||||
class CreateQuoteInvitation implements ShouldQueue
|
||||
{
|
||||
/**
|
||||
* Handle the event.
|
||||
*
|
||||
* @param object $event
|
||||
* @return void
|
||||
*/
|
||||
public function handle($event)
|
||||
{
|
||||
$quote = $event->credit;
|
||||
|
||||
$contacts = $quote->client->contacts;
|
||||
|
||||
$contacts->each(function ($contact) use ($quote) {
|
||||
$invitation = QuoteInvitation::whereCompanyId($quote->company_id)
|
||||
->whereClientContactId($contact->id)
|
||||
->whereQuoteId($quote->id)
|
||||
->first();
|
||||
|
||||
if (!$invitation && $contact->send_credit) {
|
||||
$ii = QuoteInvitationFactory::create($quote->company_id, $quote->user_id);
|
||||
$ii->quote_id = $quote->id;
|
||||
$ii->client_contact_id = $contact->id;
|
||||
$ii->save();
|
||||
} elseif ($invitation && !$contact->send_credit) {
|
||||
$invitation->delete();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -14,10 +14,11 @@ namespace App\Mail\Admin;
|
||||
use App\Models\User;
|
||||
use Illuminate\Mail\Mailable;
|
||||
|
||||
class EntitySent extends Mailable
|
||||
class EntityNotificationMailer extends Mailable
|
||||
{
|
||||
|
||||
public $mail_obj;
|
||||
|
||||
/**
|
||||
* Create a new message instance.
|
||||
*
|
||||
@ -35,9 +36,9 @@ class EntitySent extends Mailable
|
||||
*/
|
||||
public function build()
|
||||
{
|
||||
return $this->from($this->mail_obj->from) //todo
|
||||
return $this->from($this->mail_obj->from[0], $this->mail_obj->from[1]) //todo
|
||||
->subject($this->mail_obj->subject)
|
||||
->markdown($this->mail_obj->markdown, ['data' => $this->mail_obj->data])
|
||||
->markdown($this->mail_obj->markdown, $this->mail_obj->data)
|
||||
->withSwiftMessage(function ($message) {
|
||||
$message->getHeaders()->addTextHeader('Tag', $this->mail_obj->tag);
|
||||
});
|
96
app/Mail/Admin/EntityPaidObject.php
Normal file
96
app/Mail/Admin/EntityPaidObject.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Mail\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Utils\Number;
|
||||
|
||||
class EntityPaidObject
|
||||
{
|
||||
public $invitation;
|
||||
|
||||
public $entity;
|
||||
|
||||
public $contact;
|
||||
|
||||
public $company;
|
||||
|
||||
public $settings;
|
||||
|
||||
public function __construct($payment)
|
||||
{
|
||||
$this->payment = $payment;
|
||||
$this->company = $payment->company;
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
$mail_obj = new \stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
private function getAmount()
|
||||
{
|
||||
return Number::formatMoney($this->payment->amount, $this->payment->client);
|
||||
}
|
||||
|
||||
private function getSubject()
|
||||
{
|
||||
return
|
||||
ctrans(
|
||||
'texts.notification_payment_paid_subject',
|
||||
['client' => $this->payment->client->present()->name()]
|
||||
);
|
||||
}
|
||||
|
||||
private function getData()
|
||||
{
|
||||
|
||||
$settings = $this->payment->client->getMergedSettings();
|
||||
|
||||
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
|
||||
|
||||
$invoice_texts = ctrans('texts.invoice_number_short');
|
||||
|
||||
foreach ($this->payment->invoices as $invoice) {
|
||||
$invoice_texts .= $invoice->number . ',';
|
||||
}
|
||||
|
||||
$invoice_texts = substr($invoice_texts, 0, -1);
|
||||
|
||||
$data = [
|
||||
'title' => ctrans(
|
||||
'texts.notification_payment_paid_subject',
|
||||
['client' => $this->payment->client->present()->name()]
|
||||
),
|
||||
'message' => ctrans(
|
||||
'texts.notification_payment_paid',
|
||||
['amount' => $amount,
|
||||
'client' => $this->payment->client->present()->name(),
|
||||
'invoice' => $invoice_texts,
|
||||
]
|
||||
),
|
||||
'url' => config('ninja.app_url') . '/payments/' . $this->payment->hashed_id,
|
||||
'button' => ctrans('texts.view_payment'),
|
||||
'signature' => $settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -71,13 +71,12 @@ class EntitySentObject
|
||||
|
||||
$settings = $this->entity->client->getMergedSettings();
|
||||
|
||||
$data = [
|
||||
return [
|
||||
'title' => $this->getSubject(),
|
||||
'message' => ctrans(
|
||||
"texts.notification_{$this->entity_type}_sent",
|
||||
[
|
||||
'amount' => $this->getAmount(),
|
||||
|
||||
'client' => $this->contact->present()->name(),
|
||||
'invoice' => $this->entity->number,
|
||||
]
|
||||
@ -88,6 +87,5 @@ class EntitySentObject
|
||||
'logo' => $this->company->present()->logo(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
92
app/Mail/Admin/EntityViewedObject.php
Normal file
92
app/Mail/Admin/EntityViewedObject.php
Normal file
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Mail\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Utils\Number;
|
||||
|
||||
class EntityViewedObject
|
||||
{
|
||||
public $invitation;
|
||||
|
||||
public $entity_type;
|
||||
|
||||
public $entity;
|
||||
|
||||
public $contact;
|
||||
|
||||
public $company;
|
||||
|
||||
public $settings;
|
||||
|
||||
public function __construct($invitation, $entity_type)
|
||||
{
|
||||
$this->invitation = $invitation;
|
||||
$this->entity_type = $entity_type;
|
||||
$this->entity = $invitation->{$entity_type};
|
||||
$this->contact = $invitation->contact;
|
||||
$this->company = $invitation->company;
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
$mail_obj = new \stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
private function getAmount()
|
||||
{
|
||||
return Number::formatMoney($this->entity->amount, $this->entity->client);
|
||||
}
|
||||
|
||||
private function getSubject()
|
||||
{
|
||||
return
|
||||
ctrans(
|
||||
"texts.notification_{$this->entity_type}_viewed_subject",
|
||||
[
|
||||
'client' => $this->contact->present()->name(),
|
||||
'invoice' => $this->entity->number,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
private function getData()
|
||||
{
|
||||
|
||||
$settings = $this->entity->client->getMergedSettings();
|
||||
|
||||
$data = [
|
||||
'title' => $this->getSubject(),
|
||||
'message' => ctrans(
|
||||
"texts.notification_{$this->entity_type}_viewed",
|
||||
[
|
||||
'amount' => $this->getAmount(),
|
||||
'client' => $this->contact->present()->name(),
|
||||
'invoice' => $this->entity->number,
|
||||
]
|
||||
),
|
||||
'url' => $this->invitation->getAdminLink(),
|
||||
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||
'signature' => $settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
83
app/Mail/Admin/PaymentFailureObject.php
Normal file
83
app/Mail/Admin/PaymentFailureObject.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Mail\Admin;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Utils\Number;
|
||||
|
||||
class PaymentFailureObject
|
||||
{
|
||||
public $client;
|
||||
|
||||
public $message;
|
||||
|
||||
public $company;
|
||||
|
||||
public $amount;
|
||||
|
||||
public function __construct($client, $message, $amount, $company)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->message = $message;
|
||||
$this->amount = $amount;
|
||||
$this->company = $company;
|
||||
}
|
||||
|
||||
public function build()
|
||||
{
|
||||
$mail_obj = new \stdClass;
|
||||
$mail_obj->amount = $this->getAmount();
|
||||
$mail_obj->subject = $this->getSubject();
|
||||
$mail_obj->data = $this->getData();
|
||||
$mail_obj->markdown = 'email.admin.generic';
|
||||
$mail_obj->tag = $this->company->company_key;
|
||||
|
||||
return $mail_obj;
|
||||
}
|
||||
|
||||
private function getAmount()
|
||||
{
|
||||
return Number::formatMoney($this->amount, $this->client);
|
||||
}
|
||||
|
||||
private function getSubject()
|
||||
{
|
||||
return
|
||||
ctrans(
|
||||
'texts.payment_failed_subject',
|
||||
['client' => $this->payment->client->present()->name()]
|
||||
);
|
||||
}
|
||||
|
||||
private function getData()
|
||||
{
|
||||
$signature = $this->client->getSetting('email_signature');
|
||||
|
||||
$data = [
|
||||
'title' => ctrans(
|
||||
'texts.payment_failed_subject',
|
||||
['client' => $this->client->present()->name()]
|
||||
),
|
||||
'message' => ctrans(
|
||||
'texts.notification_payment_paid',
|
||||
['amount' => $this->getAmount(),
|
||||
'client' => $this->client->present()->name(),
|
||||
'message' => $this->message,
|
||||
]
|
||||
),
|
||||
'signature' => $signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
@ -182,6 +182,11 @@ class Client extends BaseModel implements HasLocalePreference
|
||||
return $this->belongsTo(Country::class);
|
||||
}
|
||||
|
||||
public function invoices()
|
||||
{
|
||||
return $this->hasMany(Invoice::class);
|
||||
}
|
||||
|
||||
public function shipping_country()
|
||||
{
|
||||
return $this->belongsTo(Country::class, 'shipping_country_id', 'id');
|
||||
|
@ -284,6 +284,11 @@ class Company extends BaseModel
|
||||
return $this->hasMany(Design::class)->whereCompanyId($this->id)->orWhere('company_id', null);
|
||||
}
|
||||
|
||||
public function payment_terms()
|
||||
{
|
||||
return $this->hasMany(PaymentTerm::class)->whereCompanyId($this->id)->orWhere('company_id', null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
*/
|
||||
@ -401,9 +406,13 @@ class Company extends BaseModel
|
||||
|
||||
public function setMigration($status)
|
||||
{
|
||||
$this->company_users->each(function ($cu) use ($status) {
|
||||
$company_users = CompanyUser::where('company_id', $this->id)->get();
|
||||
|
||||
foreach($company_users as $cu)
|
||||
{
|
||||
$cu->is_migrating=$status;
|
||||
$cu->save();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,11 @@ class Payment extends BaseModel
|
||||
return $this->belongsTo(Company::class);
|
||||
}
|
||||
|
||||
public function contact()
|
||||
{
|
||||
return $this->belongsTo(ClientContact::class);
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class)->withTrashed();
|
||||
|
@ -30,6 +30,8 @@ class PaymentTerm extends BaseModel
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
protected $fillable = ['num_days'];
|
||||
|
||||
public function getNumDays()
|
||||
{
|
||||
return $this->num_days == -1 ? 0 : $this->num_days;
|
||||
@ -39,7 +41,7 @@ class PaymentTerm extends BaseModel
|
||||
{
|
||||
$default_terms = collect(config('ninja.payment_terms'));
|
||||
|
||||
$terms = self::scope()->get();
|
||||
$terms = self::whereCompanyId(auth()->user()->company()->id)->orWhere('company_id', null)->get();
|
||||
|
||||
$terms->map(function ($term) {
|
||||
return $term['num_days'];
|
||||
|
@ -21,28 +21,28 @@ class PaymentType extends StaticModel
|
||||
public $timestamps = false;
|
||||
|
||||
const CREDIT = 1;
|
||||
const ACH = 5;
|
||||
const VISA = 6;
|
||||
const MASTERCARD = 7;
|
||||
const AMERICAN_EXPRESS = 8;
|
||||
const DISCOVER = 9;
|
||||
const DINERS = 10;
|
||||
const EUROCARD = 11;
|
||||
const NOVA = 12;
|
||||
const CREDIT_CARD_OTHER = 13;
|
||||
const PAYPAL = 14;
|
||||
const CARTE_BLANCHE = 17;
|
||||
const UNIONPAY = 18;
|
||||
const JCB = 19;
|
||||
const LASER = 20;
|
||||
const MAESTRO = 21;
|
||||
const SOLO = 22;
|
||||
const SWITCH = 23;
|
||||
const ALIPAY = 28;
|
||||
const SOFORT = 29;
|
||||
const SEPA = 30;
|
||||
const GOCARDLESS = 31;
|
||||
const CRYPTO = 32;
|
||||
const ACH = 4;
|
||||
const VISA = 5;
|
||||
const MASTERCARD = 6;
|
||||
const AMERICAN_EXPRESS = 7;
|
||||
const DISCOVER = 8;
|
||||
const DINERS = 9;
|
||||
const EUROCARD = 10;
|
||||
const NOVA = 11;
|
||||
const CREDIT_CARD_OTHER = 12;
|
||||
const PAYPAL = 13;
|
||||
const CARTE_BLANCHE = 16;
|
||||
const UNIONPAY = 17;
|
||||
const JCB = 18;
|
||||
const LASER = 19;
|
||||
const MAESTRO = 20;
|
||||
const SOLO = 21;
|
||||
const SWITCH = 22;
|
||||
const ALIPAY = 27;
|
||||
const SOFORT = 28;
|
||||
const SEPA = 29;
|
||||
const GOCARDLESS = 30;
|
||||
const CRYPTO = 31;
|
||||
|
||||
public static function parseCardType($cardName)
|
||||
{
|
||||
|
@ -69,6 +69,7 @@ class EntitySentNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
$amount = Number::formatMoney($this->entity->amount, $this->entity->client);
|
||||
$subject = ctrans(
|
||||
"texts.notification_{$this->entity_name}_sent_subject",
|
||||
|
@ -68,6 +68,8 @@ class EntityViewedNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
|
||||
$data = $this->buildDataArray();
|
||||
$subject = $this->buildSubject();
|
||||
|
||||
|
@ -63,6 +63,9 @@ class InvoiceSentNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
|
||||
|
||||
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
|
||||
$subject = ctrans(
|
||||
'texts.notification_invoice_sent_subject',
|
||||
|
@ -62,6 +62,10 @@ class InvoiceViewedNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
|
||||
|
||||
$amount = Number::formatMoney($this->invoice->amount, $this->invoice->client);
|
||||
$subject = ctrans(
|
||||
'texts.notification_invoice_viewed_subject',
|
||||
|
@ -58,6 +58,9 @@ class NewPartialPaymentNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
|
||||
|
||||
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
|
||||
|
||||
$invoice_texts = ctrans('texts.invoice_number_short');
|
||||
|
@ -61,6 +61,9 @@ class NewPaymentNotification extends Notification implements ShouldQueue
|
||||
*/
|
||||
public function toMail($notifiable)
|
||||
{
|
||||
//@TODO THESE ARE @DEPRECATED NOW we are now using app/Mail/Admin/*
|
||||
|
||||
|
||||
$amount = Number::formatMoney($this->payment->amount, $this->payment->client);
|
||||
|
||||
$invoice_texts = ctrans('texts.invoice_number_short');
|
||||
|
@ -13,7 +13,7 @@
|
||||
namespace App\PaymentDrivers;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
//use App\Jobs\Invoice\UpdateInvoicePayment;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
@ -140,6 +140,9 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
|
||||
$this->client
|
||||
);
|
||||
} elseif (!$response->isSuccessful()) {
|
||||
|
||||
PaymentFailureMailer::dispatch($this->client, $response->getMessage, $this->client->company, $response['PAYMENTINFO_0_AMT']);
|
||||
|
||||
SystemLogger::dispatch(
|
||||
[
|
||||
'data' => $request->all(),
|
||||
@ -271,12 +274,12 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
|
||||
return $payment;
|
||||
}
|
||||
|
||||
public function refund(Payment $payment, $amount = null)
|
||||
public function refund(Payment $payment, $amount)
|
||||
{
|
||||
$this->gateway();
|
||||
|
||||
$response = $this->gateway
|
||||
->refund(['transactionReference' => $payment->transaction_reference, 'amount' => $amount ?? $payment->amount])
|
||||
->refund(['transactionReference' => $payment->transaction_reference, 'amount' => $amount])
|
||||
->send();
|
||||
|
||||
if ($response->isSuccessful()) {
|
||||
@ -305,6 +308,7 @@ class PayPalExpressPaymentDriver extends BasePaymentDriver
|
||||
$this->client
|
||||
);
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ namespace App\PaymentDrivers;
|
||||
|
||||
use App\Events\Payment\PaymentWasCreated;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Jobs\Mail\PaymentFailureMailer;
|
||||
use App\Jobs\Util\SystemLogger;
|
||||
use App\Models\ClientGatewayToken;
|
||||
use App\Models\GatewayType;
|
||||
@ -364,6 +365,9 @@ class StripePaymentDriver extends BasePaymentDriver
|
||||
|
||||
return redirect()->route('client.payments.show', ['payment' => $this->encodePrimaryKey($payment->id)]);
|
||||
} else {
|
||||
|
||||
PaymentFailureMailer::dispatch($this->client, $server_response->cancellation_reason, $this->client->company, $server_response->amount);
|
||||
|
||||
/*Fail and log*/
|
||||
SystemLogger::dispatch(
|
||||
[
|
||||
|
33
app/Policies/PaymentTermPolicy.php
Normal file
33
app/Policies/PaymentTermPolicy.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\Payment;
|
||||
use App\Models\User;
|
||||
|
||||
/**
|
||||
* Class PaymentTermPolicy
|
||||
* @package App\Policies
|
||||
*/
|
||||
class PaymentTermPolicy extends EntityPolicy
|
||||
{
|
||||
/**
|
||||
* Checks if the user has create permissions
|
||||
*
|
||||
* @param User $user
|
||||
* @return bool
|
||||
*/
|
||||
public function create(User $user) : bool
|
||||
{
|
||||
return $user->isAdmin() || $user->hasPermission('create_all');
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ use App\Models\Expense;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
@ -41,6 +42,7 @@ use App\Policies\ExpensePolicy;
|
||||
use App\Policies\GroupSettingPolicy;
|
||||
use App\Policies\InvoicePolicy;
|
||||
use App\Policies\PaymentPolicy;
|
||||
use App\Policies\PaymentTermPolicy;
|
||||
use App\Policies\ProductPolicy;
|
||||
use App\Policies\QuotePolicy;
|
||||
use App\Policies\RecurringInvoicePolicy;
|
||||
@ -72,6 +74,7 @@ class AuthServiceProvider extends ServiceProvider
|
||||
GroupSetting::class => GroupSettingPolicy::class,
|
||||
Invoice::class => InvoicePolicy::class,
|
||||
Payment::class => PaymentPolicy::class,
|
||||
PaymentTerm::class => PaymentTermPolicy::class,
|
||||
Product::class => ProductPolicy::class,
|
||||
Quote::class => QuotePolicy::class,
|
||||
RecurringInvoice::class => RecurringInvoicePolicy::class,
|
||||
|
@ -24,6 +24,7 @@ class ClientContactRepository extends BaseRepository
|
||||
{
|
||||
public function save(array $data, Client $client) : void
|
||||
{
|
||||
|
||||
if (isset($data['contacts'])) {
|
||||
$contacts = collect($data['contacts']);
|
||||
} else {
|
||||
@ -66,17 +67,20 @@ class ClientContactRepository extends BaseRepository
|
||||
}
|
||||
|
||||
$update_contact->save();
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
//always made sure we have one blank contact to maintain state
|
||||
if ($contacts->count() == 0) {
|
||||
if ($client->contacts->count() == 0) {
|
||||
|
||||
info("no contacts found");
|
||||
|
||||
$new_contact = ClientContactFactory::create($client->company_id, $client->user_id);
|
||||
$new_contact->client_id = $client->id;
|
||||
$new_contact->contact_key = Str::random(40);
|
||||
$new_contact->is_primary = true;
|
||||
$new_contact->save();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
20
app/Repositories/PaymentTermRepository.php
Normal file
20
app/Repositories/PaymentTermRepository.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Repositories;
|
||||
|
||||
/**
|
||||
* Class for payment term repository.
|
||||
*/
|
||||
class PaymentTermRepository extends BaseRepository
|
||||
{
|
||||
|
||||
}
|
@ -29,7 +29,11 @@ class MarkSent
|
||||
|
||||
event(new CreditWasMarkedSent($this->credit, $this->credit->company));
|
||||
|
||||
$this->credit->service()->setStatus(Credit::STATUS_SENT)->applyNumber()->save();
|
||||
$this->credit
|
||||
->service()
|
||||
->setStatus(Credit::STATUS_SENT)
|
||||
->applyNumber()
|
||||
->save();
|
||||
|
||||
return $this->credit;
|
||||
}
|
||||
|
@ -62,6 +62,8 @@ class ApplyPayment extends AbstractService
|
||||
$this->invoice->service()->clearPartial()->setStatus(Invoice::STATUS_PARTIAL)->updateBalance($this->payment_amount*-1);
|
||||
}
|
||||
|
||||
$this->invoice->service()->applyNumber()->save();
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
}
|
||||
|
@ -139,8 +139,13 @@ class InvoiceService
|
||||
/* One liners */
|
||||
public function setDueDate()
|
||||
{
|
||||
$this->invoice->due_date = Carbon::now()->addDays($this->invoice->client->getSetting('payment_terms'));
|
||||
if($this->invoice->due_date != '')
|
||||
return $this;
|
||||
|
||||
//$this->invoice->due_date = Carbon::now()->addDays($this->invoice->client->getSetting('payment_terms'));
|
||||
|
||||
$this->invoice->due_date = Carbon::parse($this->invoice->date)->addDays($this->invoice->client->getSetting('payment_terms'));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,7 @@ class MarkPaid extends AbstractService
|
||||
$this->invoice->service()
|
||||
->updateBalance($payment->amount*-1)
|
||||
->setStatus(Invoice::STATUS_PAID)
|
||||
->applyNumber()
|
||||
->save();
|
||||
|
||||
/* Update Invoice balance */
|
||||
|
@ -45,6 +45,7 @@ class MarkSent extends AbstractService
|
||||
->service()
|
||||
->setStatus(Invoice::STATUS_SENT)
|
||||
->applyNumber()
|
||||
->setDueDate()
|
||||
->save();
|
||||
|
||||
$this->client->service()->updateBalance($this->invoice->balance)->save();
|
||||
|
@ -10,10 +10,10 @@ class ConvertQuote
|
||||
private $client;
|
||||
private $invoice_repo;
|
||||
|
||||
public function __construct($client, InvoiceRepository $invoice_repo)
|
||||
public function __construct($client)
|
||||
{
|
||||
$this->client = $client;
|
||||
$this->invoice_repo = $invoice_repo;
|
||||
$this->invoice_repo = new InvoiceRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -23,9 +23,19 @@ class ConvertQuote
|
||||
public function run($quote)
|
||||
{
|
||||
$invoice = CloneQuoteToInvoiceFactory::create($quote, $quote->user_id, $quote->company_id);
|
||||
$this->invoice_repo->save([], $invoice);
|
||||
$invoice = $this->invoice_repo->save([], $invoice);
|
||||
|
||||
$invoice->fresh();
|
||||
|
||||
$invoice->service()
|
||||
->markSent()
|
||||
->createInvitations()
|
||||
->save();
|
||||
|
||||
$quote->invoice_id = $invoice->id;
|
||||
$quote->save();
|
||||
|
||||
// maybe should return invoice here
|
||||
return $quote;
|
||||
return $invoice;
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,11 @@ class MarkSent
|
||||
|
||||
event(new QuoteWasMarkedSent($this->quote, $this->quote->company));
|
||||
|
||||
$this->quote->service()->setStatus(Quote::STATUS_SENT)->applyNumber()->save();
|
||||
$this->quote
|
||||
->service()
|
||||
->setStatus(Quote::STATUS_SENT)
|
||||
->applyNumber()
|
||||
->save();
|
||||
|
||||
return $this->quote;
|
||||
}
|
||||
|
@ -21,6 +21,8 @@ class QuoteService
|
||||
{
|
||||
protected $quote;
|
||||
|
||||
public $invoice;
|
||||
|
||||
public function __construct($quote)
|
||||
{
|
||||
$this->quote = $quote;
|
||||
@ -38,17 +40,29 @@ class QuoteService
|
||||
public function markApproved()
|
||||
{
|
||||
$mark_approved = new MarkApproved($this->quote->client);
|
||||
|
||||
$this->quote = $mark_approved->run($this->quote);
|
||||
|
||||
if ($this->quote->client->getSetting('auto_convert_quote') === true) {
|
||||
$convert_quote = new ConvertQuote($this->quote->client);
|
||||
$this->quote = $convert_quote->run($this->quote);
|
||||
$this->convert();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function convert() :QuoteService
|
||||
{
|
||||
if($this->quote->invoice_id)
|
||||
return $this;
|
||||
|
||||
$convert_quote = new ConvertQuote($this->quote->client);
|
||||
|
||||
$this->invoice = $convert_quote->run($this->quote);
|
||||
|
||||
$this->quote->fresh();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getQuotePdf($contact = null)
|
||||
{
|
||||
$get_invoice_pdf = new GetQuotePdf();
|
||||
@ -101,8 +115,7 @@ class QuoteService
|
||||
$invoice = null;
|
||||
|
||||
if ($this->quote->client->getSetting('auto_convert_quote')) {
|
||||
$invoice = $this->convertToInvoice();
|
||||
$this->linkInvoiceToQuote($invoice)->save();
|
||||
$this->convert();
|
||||
}
|
||||
|
||||
if ($this->quote->client->getSetting('auto_archive_quote')) {
|
||||
@ -113,33 +126,27 @@ class QuoteService
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where we convert a quote to an invoice we link the two entities via the invoice_id parameter on the quote table
|
||||
* @param object $invoice The Invoice object
|
||||
* @return object QuoteService
|
||||
*/
|
||||
public function linkInvoiceToQuote($invoice) :QuoteService
|
||||
public function convertToInvoice()
|
||||
{
|
||||
$this->quote->invoice_id = $invoice->id;
|
||||
|
||||
return $this;
|
||||
//to prevent circular references we need to explicit call this here.
|
||||
$mark_approved = new MarkApproved($this->quote->client);
|
||||
$this->quote = $mark_approved->run($this->quote);
|
||||
|
||||
$this->convert();
|
||||
|
||||
return $this->invoice;
|
||||
}
|
||||
|
||||
public function convertToInvoice() :Invoice
|
||||
public function isConvertable() :bool
|
||||
{
|
||||
if($this->quote->invoice_id)
|
||||
return false;
|
||||
|
||||
$invoice = CloneQuoteToInvoiceFactory::create($this->quote, $this->quote->user_id);
|
||||
$invoice->status_id = Invoice::STATUS_SENT;
|
||||
$invoice->due_date = null;
|
||||
$invoice->number = null;
|
||||
$invoice->save();
|
||||
if($this->quote->status_id == Quote::STATUS_EXPIRED)
|
||||
return false;
|
||||
|
||||
$invoice->service()
|
||||
->markSent()
|
||||
->createInvitations()
|
||||
->save();
|
||||
|
||||
return $invoice;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -149,6 +156,7 @@ class QuoteService
|
||||
public function save() : ?Quote
|
||||
{
|
||||
$this->quote->save();
|
||||
|
||||
return $this->quote;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ use App\Models\Design;
|
||||
use App\Models\Expense;
|
||||
use App\Models\GroupSetting;
|
||||
use App\Models\Payment;
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Models\Product;
|
||||
use App\Models\Project;
|
||||
use App\Models\Quote;
|
||||
@ -31,6 +32,7 @@ use App\Models\TaxRate;
|
||||
use App\Models\User;
|
||||
use App\Transformers\CompanyLedgerTransformer;
|
||||
use App\Transformers\CreditTransformer;
|
||||
use App\Transformers\PaymentTermTransformer;
|
||||
use App\Transformers\TaskTransformer;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
@ -65,6 +67,7 @@ class CompanyTransformer extends EntityTransformer
|
||||
'expenses',
|
||||
'vendors',
|
||||
'payments',
|
||||
'payment_terms',
|
||||
'company_user',
|
||||
'groups',
|
||||
'company_gateways',
|
||||
@ -253,4 +256,11 @@ class CompanyTransformer extends EntityTransformer
|
||||
|
||||
return $this->includeCollection($company->ledger, $transformer, CompanyLedger::class);
|
||||
}
|
||||
|
||||
public function includePaymentTerms(Company $company)
|
||||
{
|
||||
$transformer = new PaymentTermTransformer($this->serializer);
|
||||
|
||||
return $this->includeCollection($company->payment_terms()->get(), $transformer, PaymentTerm::class);
|
||||
}
|
||||
}
|
||||
|
33
app/Transformers/PaymentTermTransformer.php
Normal file
33
app/Transformers/PaymentTermTransformer.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
namespace App\Transformers;
|
||||
|
||||
use App\Models\PaymentTerm;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
|
||||
class PaymentTermTransformer extends EntityTransformer
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
public function transform(PaymentTerm $payment_term)
|
||||
{
|
||||
return [
|
||||
'id' => (string) $this->encodePrimaryKey($payment_term->id),
|
||||
'num_days' => (int) $payment_term->num_days,
|
||||
'name' => (string) ctrans('texts.payment_terms_net') . ' ' . $payment_term->getNumDays(),
|
||||
'is_deleted' => (bool) $payment_term->is_deleted,
|
||||
'created_at' => (int) $payment_term->created_at,
|
||||
'updated_at' => (int) $payment_term->updated_at,
|
||||
'archived_at' => (int) $payment_term->deleted_at,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
@ -81,7 +81,7 @@ class QuoteTransformer extends EntityTransformer
|
||||
'client_id' => (string) $this->encodePrimaryKey($quote->client_id),
|
||||
'status_id' => (string)$quote->status_id,
|
||||
'design_id' => (string) $this->encodePrimaryKey($quote->design_id),
|
||||
'invoice_id' => (string)$quote->invoice_id,
|
||||
'invoice_id' => (string)$this->encodePrimaryKey($quote->invoice_id),
|
||||
'updated_at' => (int)$quote->updated_at,
|
||||
'archived_at' => (int)$quote->deleted_at,
|
||||
'created_at' => (int)$quote->created_at,
|
||||
|
@ -64,6 +64,7 @@ class UserTransformer extends EntityTransformer
|
||||
'custom_value2' => $user->custom_value2 ?: '',
|
||||
'custom_value3' => $user->custom_value3 ?: '',
|
||||
'custom_value4' => $user->custom_value4 ?: '',
|
||||
'oauth_provider_id' => (string)$user->oauth_provider_id,
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,7 @@ trait ActionsInvoice
|
||||
{
|
||||
public function invoiceDeletable($invoice) :bool
|
||||
{
|
||||
if ($invoice->status_id <= Invoice::STATUS_SENT && $invoice->is_deleted == false && $invoice->deleted_at == null) {
|
||||
if ($invoice->status_id <= Invoice::STATUS_SENT && $invoice->is_deleted == false && $invoice->deleted_at == null && $invoice->balance == 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ trait UserNotifies
|
||||
array_push($required_permissions, "all_user_notifications");
|
||||
}
|
||||
|
||||
if (count(array_intersect($required_permissions, $notifications->email)) >=1) {
|
||||
if (count(array_intersect($required_permissions, $notifications->email)) >=1 || count(array_intersect($required_permissions, "all_user_notifications")) >=1 || count(array_intersect($required_permissions, "all_notifications")) >=1) {
|
||||
array_push($notifiable_methods, 'mail');
|
||||
}
|
||||
|
||||
@ -54,13 +54,30 @@ trait UserNotifies
|
||||
array_push($required_permissions, "all_user_notifications");
|
||||
}
|
||||
|
||||
if (count(array_intersect($required_permissions, $notifications->email)) >=1) {
|
||||
if (count(array_intersect($required_permissions, $notifications->email)) >=1 || count(array_intersect($required_permissions, "all_user_notifications")) >=1 || count(array_intersect($required_permissions, "all_notifications")) >=1) {
|
||||
array_push($notifiable_methods, 'mail');
|
||||
}
|
||||
|
||||
return $notifiable_methods;
|
||||
}
|
||||
|
||||
public function findCompanyUserNotificationType($company_user, $required_permissions) :array
|
||||
{
|
||||
if ($this->migrationRunning($company_user)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$notifiable_methods = [];
|
||||
$notifications = $company_user->notifications;
|
||||
|
||||
if (count(array_intersect($required_permissions, $notifications->email)) >=1 || count(array_intersect($required_permissions, "all_user_notifications")) >=1 || count(array_intersect($required_permissions, "all_notifications")) >=1) {
|
||||
array_push($notifiable_methods, 'mail');
|
||||
}
|
||||
|
||||
return $notifiable_methods;
|
||||
|
||||
}
|
||||
|
||||
private function migrationRunning($company_user)
|
||||
{
|
||||
return $company_user->is_migrating;
|
||||
|
@ -23,10 +23,11 @@
|
||||
"asgrim/ofxparser": "^1.2",
|
||||
"beganovich/omnipay-checkout": "dev-master",
|
||||
"cleverit/ubl_invoice": "^1.3",
|
||||
"codedge/laravel-selfupdater": "~3.0",
|
||||
"composer/composer": "^1.10",
|
||||
"czproject/git-php": "^3.17",
|
||||
"dacastro4/laravel-gmail": "^3.2",
|
||||
"doctrine/dbal": "^2.10",
|
||||
"fedeisas/laravel-mail-css-inliner": "2.3",
|
||||
"fideloper/proxy": "^4.0",
|
||||
"fzaninotto/faker": "^1.4",
|
||||
"google/apiclient": "^2.0",
|
||||
|
@ -178,7 +178,6 @@ return [
|
||||
App\Providers\EventServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
App\Providers\ComposerServiceProvider::class,
|
||||
Codedge\Updater\UpdaterServiceProvider::class,
|
||||
App\Providers\MultiDBProvider::class,
|
||||
App\Providers\ClientPortalServiceProvider::class,
|
||||
],
|
||||
|
20
config/css-inliner.php
Normal file
20
config/css-inliner.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Css Files
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Css file of your style for your emails
|
||||
| The content of these files will be added directly into the inliner
|
||||
| Use absolute paths, ie. public_path('css/main.css')
|
||||
|
|
||||
*/
|
||||
|
||||
'css-files' => [
|
||||
public_path('css/app.css'),
|
||||
],
|
||||
|
||||
];
|
@ -25,6 +25,7 @@ return [
|
||||
'company_id' => 0,
|
||||
'hash_salt' => env('HASH_SALT', ''),
|
||||
'currency_converter_api_key' => env('OPENEXCHANGE_APP_ID',''),
|
||||
'enabled_modules' => 4095,
|
||||
|
||||
'environment' => env('NINJA_ENVIRONMENT', 'selfhost'), // 'hosted', 'development', 'selfhost', 'reseller'
|
||||
|
||||
@ -54,7 +55,7 @@ return [
|
||||
'datetime_format_id' => env('DEFAULT_DATETIME_FORMAT_ID', '1'),
|
||||
'locale' => env('DEFAULT_LOCALE', 'en'),
|
||||
'map_zoom' => env('DEFAULT_MAP_ZOOM', 10),
|
||||
'payment_terms' => env('DEFAULT_PAYMENT_TERMS', -1),
|
||||
'payment_terms' => env('DEFAULT_PAYMENT_TERMS', "-1"),
|
||||
'military_time' => env('MILITARY_TIME', 0),
|
||||
'first_day_of_week' => env('FIRST_DATE_OF_WEEK', 0),
|
||||
'first_month_of_year' => env('FIRST_MONTH_OF_YEAR', '2000-01-01')
|
||||
@ -97,41 +98,6 @@ return [
|
||||
'slack' => env('SLACK_WEBHOOK_URL', ''),
|
||||
'mail' => env('HOSTED_EMAIL', ''),
|
||||
],
|
||||
'payment_terms' => [
|
||||
[
|
||||
'num_days' => 0,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 7,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 10,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 14,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 15,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 30,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 60,
|
||||
'name' => '',
|
||||
],
|
||||
[
|
||||
'num_days' => 90,
|
||||
'name' => '',
|
||||
]
|
||||
],
|
||||
|
||||
'themes' => [
|
||||
'global' => 'ninja2020',
|
||||
'portal' => 'ninja2020',
|
||||
|
@ -1,152 +0,0 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Default source repository type
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The default source repository type you want to pull your updates from.
|
||||
|
|
||||
*/
|
||||
|
||||
'default' => env('SELF_UPDATER_SOURCE', 'github'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Version installed
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Set this to the version of your software installed on your system.
|
||||
|
|
||||
*/
|
||||
|
||||
'version_installed' => env('SELF_UPDATER_VERSION_INSTALLED', ''),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Repository types
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| A repository can be of different types, which can be specified here.
|
||||
| Current options:
|
||||
| - github
|
||||
| - http
|
||||
|
|
||||
*/
|
||||
|
||||
'repository_types' => [
|
||||
'github' => [
|
||||
'type' => 'github',
|
||||
'repository_vendor' => env('SELF_UPDATER_REPO_VENDOR', ''),
|
||||
'repository_name' => env('SELF_UPDATER_REPO_NAME', ''),
|
||||
'repository_url' => '',
|
||||
'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
|
||||
'private_access_token' => env('SELF_UPDATER_GITHUB_PRIVATE_ACCESS_TOKEN', ''),
|
||||
'use_branch' => env('SELF_UPDATER_BRANCH_NAME', 'v2'),
|
||||
|
||||
],
|
||||
'http' => [
|
||||
'type' => 'http',
|
||||
'repository_url' => env('SELF_UPDATER_REPO_URL', ''),
|
||||
'pkg_filename_format' => env('SELF_UPDATER_PKG_FILENAME_FORMAT', 'v_VERSION_'),
|
||||
'download_path' => env('SELF_UPDATER_DOWNLOAD_PATH', '/tmp'),
|
||||
'private_access_token' => env('SELF_UPDATER_HTTP_PRIVATE_ACCESS_TOKEN', ''),
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Exclude folders from update
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Specifiy folders which should not be updated and will be skipped during the
|
||||
| update process.
|
||||
|
|
||||
| Here's already a list of good examples to skip. You may want to keep those.
|
||||
|
|
||||
*/
|
||||
|
||||
'exclude_folders' => [
|
||||
'node_modules',
|
||||
'bootstrap/cache',
|
||||
'bower',
|
||||
'storage/app',
|
||||
'storage/framework',
|
||||
'storage/logs',
|
||||
'storage/self-update',
|
||||
'vendor',
|
||||
],
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Event Logging
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure if fired events should be logged
|
||||
|
|
||||
*/
|
||||
|
||||
'log_events' => env('SELF_UPDATER_LOG_EVENTS', true),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Mail To Settings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Configure if fired events should be logged
|
||||
|
|
||||
*/
|
||||
'notifications' => [
|
||||
'notifications' => [
|
||||
\Codedge\Updater\Notifications\Notifications\UpdateSucceeded::class => ['mail'],
|
||||
\Codedge\Updater\Notifications\Notifications\UpdateFailed::class => ['mail'],
|
||||
\Codedge\Updater\Notifications\Notifications\UpdateAvailable::class => ['mail'],
|
||||
],
|
||||
|
||||
/*
|
||||
* Here you can specify the notifiable to which the notifications should be sent. The default
|
||||
* notifiable will use the variables specified in this config file.
|
||||
*/
|
||||
'notifiable' => \Codedge\Updater\Notifications\Notifiable::class,
|
||||
|
||||
'mail' => [
|
||||
'to' => [
|
||||
'address' => env('SELF_UPDATER_MAILTO_ADDRESS', 'notifications@example.com'),
|
||||
'name' => env('SELF_UPDATER_MAILTO_NAME', ''),
|
||||
],
|
||||
|
||||
'from' => [
|
||||
'address' => env('SELF_UPDATER_MAIL_FROM_ADDRESS', 'updater@example.com'),
|
||||
'name' => env('SELF_UPDATER_MAIL_FROM_NAME', 'Update'),
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
/*
|
||||
|---------------------------------------------------------------------------
|
||||
| Register custom artisan commands
|
||||
|---------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
'artisan_commands' => [
|
||||
'pre_update' => [
|
||||
//'command:signature' => [
|
||||
// 'class' => Command class
|
||||
// 'params' => []
|
||||
//]
|
||||
],
|
||||
'post_update' => [
|
||||
'ninja:post-update' => [
|
||||
'class' => \App\Console\Commands\PostUpdate::class,
|
||||
'params' => [
|
||||
'log' => 1,
|
||||
'reset' => false,
|
||||
// etc.
|
||||
]
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
];
|
4
cypress.json
Normal file
4
cypress.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"video": false,
|
||||
"baseUrl": "http://ninja.test:8000/"
|
||||
}
|
5
cypress/fixtures/example.json
Normal file
5
cypress/fixtures/example.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
37
cypress/integration/client_portal/credits.spec.js
vendored
Normal file
37
cypress/integration/client_portal/credits.spec.js
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
describe('Credits', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
it('should show credits page', () => {
|
||||
cy.visit('/client/credits');
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/credits');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show credits text', () => {
|
||||
cy.visit('/client/credits');
|
||||
|
||||
cy.get('body')
|
||||
.find('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Credits');
|
||||
});
|
||||
|
||||
it('should have required table elements', () => {
|
||||
cy.visit('/client/credits');
|
||||
|
||||
cy.get('body')
|
||||
.find('table.credits-table > tbody > tr')
|
||||
.first()
|
||||
.find('a')
|
||||
.first()
|
||||
.should('contain.text', 'View')
|
||||
.click()
|
||||
.location()
|
||||
.should(location => {
|
||||
expect(location.pathname).to.eq('/client/credits/VolejRejNm');
|
||||
});
|
||||
});
|
||||
});
|
73
cypress/integration/client_portal/invoices.spec.js
vendored
Normal file
73
cypress/integration/client_portal/invoices.spec.js
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
context('Invoices', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
it('should show invoices page', () => {
|
||||
cy.visit('/client/invoices');
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/invoices');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show invoices text', () => {
|
||||
cy.visit('/client/invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Invoices');
|
||||
});
|
||||
|
||||
it('should show download and pay now buttons', () => {
|
||||
cy.visit('/client/invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('button[value="download"]')
|
||||
.first()
|
||||
.should('contain.text', 'Download');
|
||||
|
||||
cy.get('body')
|
||||
.find('button[value="payment"]')
|
||||
.first()
|
||||
.should('contain.text', 'Pay Now');
|
||||
});
|
||||
|
||||
it('should have per page options dropdown', () => {
|
||||
cy.visit('/client/invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('select')
|
||||
.first()
|
||||
.should('have.value', '10');
|
||||
});
|
||||
|
||||
it('should have required table elements', () => {
|
||||
cy.visit('/client/invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('table.invoices-table > tbody > tr')
|
||||
.first()
|
||||
.find('.button-link')
|
||||
.first()
|
||||
.should('contain.text', 'View')
|
||||
.click()
|
||||
.location()
|
||||
.should(location => {
|
||||
expect(location.pathname).to.eq('/client/invoices/VolejRejNm');
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter table content', () => {
|
||||
cy.visit('/client/invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('#paid-checkbox')
|
||||
.check();
|
||||
|
||||
cy.get('body')
|
||||
.find('table.invoices-table > tbody > tr')
|
||||
.first()
|
||||
.should('not.contain', 'Overdue');
|
||||
});
|
||||
});
|
48
cypress/integration/client_portal/login.spec.js
vendored
Normal file
48
cypress/integration/client_portal/login.spec.js
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
context('Login', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/client/login');
|
||||
});
|
||||
|
||||
it('should type into login form elements', () => {
|
||||
cy.get('#test_email')
|
||||
.invoke('val')
|
||||
.then(emailValue => {
|
||||
cy.get('#email')
|
||||
.type(emailValue)
|
||||
.should('have.value', emailValue);
|
||||
});
|
||||
|
||||
cy.get('#test_password')
|
||||
.invoke('val')
|
||||
.then(passwordValue => {
|
||||
cy.get('#password')
|
||||
.type(passwordValue)
|
||||
.should('have.value', passwordValue);
|
||||
});
|
||||
});
|
||||
|
||||
it('should login into client portal', () => {
|
||||
cy.get('#test_email')
|
||||
.invoke('val')
|
||||
.then(emailValue => {
|
||||
cy.get('#test_password')
|
||||
.invoke('val')
|
||||
.then(passwordValue => {
|
||||
cy.get('#email')
|
||||
.type(emailValue)
|
||||
.should('have.value', emailValue);
|
||||
cy.get('#password')
|
||||
.type(passwordValue)
|
||||
.should('have.value', passwordValue);
|
||||
cy.get('#loginBtn')
|
||||
.contains('Login')
|
||||
.click();
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq(
|
||||
'/client/dashboard'
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
39
cypress/integration/client_portal/payment_methods.spec.js
vendored
Normal file
39
cypress/integration/client_portal/payment_methods.spec.js
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
context('Payment methods', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
it('should show payment methods page', () => {
|
||||
cy.visit('/client/payment_methods');
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/payment_methods');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show payment methods text', () => {
|
||||
cy.visit('/client/payment_methods');
|
||||
|
||||
cy.get('body')
|
||||
.find('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Payment Method');
|
||||
});
|
||||
|
||||
it('should show add payment method button', () => {
|
||||
cy.visit('/client/payment_methods');
|
||||
|
||||
cy.get('body')
|
||||
.find('a.button.button-primary')
|
||||
.first()
|
||||
.should('contain.text', 'Add Payment Method');
|
||||
});
|
||||
|
||||
it('should have per page options dropdown', () => {
|
||||
cy.visit('/client/payment_methods');
|
||||
|
||||
cy.get('body')
|
||||
.find('select')
|
||||
.first()
|
||||
.should('have.value', '10');
|
||||
});
|
||||
});
|
46
cypress/integration/client_portal/payments.spec.js
vendored
Normal file
46
cypress/integration/client_portal/payments.spec.js
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
context('Payments', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
it('should show payments page', () => {
|
||||
cy.visit('/client/payments');
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/payments');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show payments text', () => {
|
||||
cy.visit('/client/payments');
|
||||
|
||||
cy.get('body')
|
||||
.find('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Payments');
|
||||
});
|
||||
|
||||
it('should have per page options dropdown', () => {
|
||||
cy.visit('/client/payments');
|
||||
|
||||
cy.get('body')
|
||||
.find('select')
|
||||
.first()
|
||||
.should('have.value', '10');
|
||||
});
|
||||
|
||||
it('should have required table elements', () => {
|
||||
cy.visit('/client/payments');
|
||||
|
||||
cy.get('body')
|
||||
.find('table.payments-table > tbody > tr')
|
||||
.first()
|
||||
.find('a')
|
||||
.first()
|
||||
.should('contain.text', 'View')
|
||||
.click()
|
||||
.location()
|
||||
.should(location => {
|
||||
expect(location.pathname).to.eq('/client/payments/VolejRejNm');
|
||||
});
|
||||
});
|
||||
})
|
73
cypress/integration/client_portal/quotes.spec.js
vendored
Normal file
73
cypress/integration/client_portal/quotes.spec.js
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
describe('Quotes', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
it('should show quotes page', () => {
|
||||
cy.visit('/client/quotes');
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/quotes');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show quotes text', () => {
|
||||
cy.visit('/client/quotes');
|
||||
|
||||
cy.get('body')
|
||||
.find('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Quotes');
|
||||
});
|
||||
|
||||
it('should show download and approve buttons', () => {
|
||||
cy.visit('/client/quotes');
|
||||
|
||||
cy.get('body')
|
||||
.find('button[value="download"]')
|
||||
.first()
|
||||
.should('contain.text', 'Download');
|
||||
|
||||
cy.get('body')
|
||||
.find('button[value="approve"]')
|
||||
.first()
|
||||
.should('contain.text', 'Approve');
|
||||
});
|
||||
|
||||
it('should have per page options dropdown', () => {
|
||||
cy.visit('/client/quotes');
|
||||
|
||||
cy.get('body')
|
||||
.find('select')
|
||||
.first()
|
||||
.should('have.value', '10');
|
||||
});
|
||||
|
||||
it('should have required table elements', () => {
|
||||
cy.visit('/client/quotes');
|
||||
|
||||
cy.get('body')
|
||||
.find('table.quotes-table > tbody > tr')
|
||||
.first()
|
||||
.find('.button-link')
|
||||
.first()
|
||||
.should('contain.text', 'View')
|
||||
.click()
|
||||
.location()
|
||||
.should(location => {
|
||||
expect(location.pathname).to.eq('/client/quotes/VolejRejNm');
|
||||
});
|
||||
});
|
||||
|
||||
it('should filter table content', () => {
|
||||
cy.visit('/client/quotes');
|
||||
|
||||
cy.get('body')
|
||||
.find('#draft-checkbox')
|
||||
.check();
|
||||
|
||||
cy.get('body')
|
||||
.find('table.quotes-table > tbody > tr')
|
||||
.first()
|
||||
.should('not.contain', 'Sent');
|
||||
});
|
||||
});
|
48
cypress/integration/client_portal/recurring_invoices.spec.js
vendored
Normal file
48
cypress/integration/client_portal/recurring_invoices.spec.js
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
context('Recurring invoices', () => {
|
||||
beforeEach(() => {
|
||||
cy.clientLogin();
|
||||
});
|
||||
|
||||
// test url
|
||||
|
||||
it('should show recurring invoices page', () => {
|
||||
cy.visit('/client/recurring_invoices');
|
||||
|
||||
cy.location().should(location => {
|
||||
expect(location.pathname).to.eq('/client/recurring_invoices');
|
||||
});
|
||||
});
|
||||
|
||||
it('should show reucrring invoices text', () => {
|
||||
cy.visit('/client/recurring_invoices');
|
||||
|
||||
cy.get('h3')
|
||||
.first()
|
||||
.should('contain.text', 'Recurring Invoices');
|
||||
});
|
||||
|
||||
it('should have per page options dropdown', () => {
|
||||
cy.visit('/client/recurring_invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('select')
|
||||
.first()
|
||||
.should('have.value', '10');
|
||||
});
|
||||
|
||||
it('should have required table elements', () => {
|
||||
cy.visit('/client/recurring_invoices');
|
||||
|
||||
cy.get('body')
|
||||
.find('table.recurring-invoices-table > tbody > tr')
|
||||
.first()
|
||||
.find('a')
|
||||
.first()
|
||||
.should('contain.text', 'View')
|
||||
.click()
|
||||
.location()
|
||||
.should(location => {
|
||||
expect(location.pathname).to.eq('/client/recurring_invoices/VolejRejNm');
|
||||
});
|
||||
});
|
||||
});
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user