mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-10 13:12:50 +01:00
Merge branch 'v2' into v2-editable-invoice-amount
This commit is contained in:
commit
24685db429
@ -613,7 +613,6 @@ class CompanySettings extends BaseSettings
|
||||
'$total_taxes',
|
||||
'$line_taxes',
|
||||
'$subtotal',
|
||||
'$total',
|
||||
'$discount',
|
||||
'$custom_surcharge1',
|
||||
'$custom_surcharge2',
|
||||
@ -621,6 +620,7 @@ class CompanySettings extends BaseSettings
|
||||
'$custom_surcharge4',
|
||||
'$paid_to_date',
|
||||
'$client.balance',
|
||||
'$total',
|
||||
],
|
||||
];
|
||||
|
||||
|
@ -24,13 +24,10 @@ class VendorFactory
|
||||
$vendor->name = '';
|
||||
$vendor->website = '';
|
||||
$vendor->private_notes = '';
|
||||
$vendor->balance = 0;
|
||||
$vendor->paid_to_date = 0;
|
||||
$vendor->public_notes = '';
|
||||
$vendor->country_id = 4;
|
||||
$vendor->is_deleted = 0;
|
||||
|
||||
$vendor_contact = VendorContactFactory::create($company_id, $user_id);
|
||||
$vendor->contacts->add($vendor_contact);
|
||||
$vendor->vendor_hash = Str::random(40);
|
||||
|
||||
return $vendor;
|
||||
}
|
||||
|
@ -71,6 +71,7 @@ class BaseController extends Controller
|
||||
'company.clients.gateway_tokens',
|
||||
'company.clients.documents',
|
||||
'company.products',
|
||||
'company.products.documents',
|
||||
'company.recurring_invoices',
|
||||
'company.invoices.invitations.contact',
|
||||
'company.invoices.invitations.company',
|
||||
@ -92,6 +93,7 @@ class BaseController extends Controller
|
||||
'company.tasks',
|
||||
'company.projects',
|
||||
'company.designs',
|
||||
'company.documents',
|
||||
'company.webhooks',
|
||||
'company.tokens_hashed',
|
||||
];
|
||||
@ -202,7 +204,7 @@ class BaseController extends Controller
|
||||
$query->with(
|
||||
[
|
||||
'company' => function ($query) use ($updated_at) {
|
||||
$query->whereNotNull('updated_at');
|
||||
$query->whereNotNull('updated_at')->with('documents');
|
||||
},
|
||||
'company.clients' => function ($query) use ($updated_at) {
|
||||
$query->where('clients.updated_at', '>=', $updated_at)->with('contacts', 'gateway_tokens','documents');
|
||||
@ -217,7 +219,7 @@ class BaseController extends Controller
|
||||
$query->whereNotNull('updated_at');
|
||||
},
|
||||
'company.products' => function ($query) use ($updated_at) {
|
||||
$query->where('updated_at', '>=', $updated_at);
|
||||
$query->where('updated_at', '>=', $updated_at)->with('documents');
|
||||
},
|
||||
'company.recurring_invoices'=> function ($query) use ($updated_at) {
|
||||
$query->where('updated_at', '>=', $updated_at)->with('company');
|
||||
|
@ -11,7 +11,14 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\ExpenseFactory;
|
||||
use App\Filters\ExpenseFilters;
|
||||
use App\Http\Requests\Expense\CreateExpenseRequest;
|
||||
use App\Http\Requests\Expense\DestroyExpenseRequest;
|
||||
use App\Http\Requests\Expense\EditExpenseRequest;
|
||||
use App\Http\Requests\Expense\ShowExpenseRequest;
|
||||
use App\Http\Requests\Expense\StoreExpenseRequest;
|
||||
use App\Http\Requests\Expense\UpdateExpenseRequest;
|
||||
use App\Jobs\Entity\ActionEntity;
|
||||
use App\Jobs\Util\ProcessBulk;
|
||||
use App\Jobs\Util\UploadAvatar;
|
||||
@ -266,7 +273,7 @@ class ExpenseController extends BaseController
|
||||
return $request->disallowUpdate();
|
||||
}
|
||||
|
||||
$expense = $this->client_repo->save($request->all(), $expense);
|
||||
$expense = $this->expense_repo->save($request->all(), $expense);
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $expense->company, $expense);
|
||||
|
||||
@ -359,11 +366,7 @@ class ExpenseController extends BaseController
|
||||
*/
|
||||
public function store(StoreExpenseRequest $request)
|
||||
{
|
||||
$expense = $this->client_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
$expense->load('contacts', 'primary_contact');
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $expense->company, $expense);
|
||||
$expense = $this->expense_repo->save($request->all(), ExpenseFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
return $this->itemResponse($expense);
|
||||
}
|
||||
@ -485,7 +488,7 @@ class ExpenseController extends BaseController
|
||||
|
||||
$expenses->each(function ($expense, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $expense)) {
|
||||
$this->client_repo->{$action}($expense);
|
||||
$this->expense_repo->{$action}($expense);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -700,7 +700,7 @@ class InvoiceController extends BaseController
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
//check query paramater for email_type and set the template else use calculateTemplate
|
||||
//check query parameter for email_type and set the template else use calculateTemplate
|
||||
if (request()->has('email_type') && property_exists($invoice->company->settings, request()->input('email_type'))) {
|
||||
$this->reminder_template = $invoice->client->getSetting(request()->input('email_type'));
|
||||
} else {
|
||||
|
@ -11,7 +11,14 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Factory\VendorFactory;
|
||||
use App\Filters\VendorFilters;
|
||||
use App\Http\Requests\Vendor\CreateVendorRequest;
|
||||
use App\Http\Requests\Vendor\DestroyVendorRequest;
|
||||
use App\Http\Requests\Vendor\EditVendorRequest;
|
||||
use App\Http\Requests\Vendor\ShowVendorRequest;
|
||||
use App\Http\Requests\Vendor\StoreVendorRequest;
|
||||
use App\Http\Requests\Vendor\UpdateVendorRequest;
|
||||
use App\Jobs\Entity\ActionEntity;
|
||||
use App\Jobs\Util\ProcessBulk;
|
||||
use App\Jobs\Util\UploadAvatar;
|
||||
@ -31,7 +38,6 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* Class VendorController.
|
||||
* @covers App\Http\Controllers\VendorController
|
||||
*/
|
||||
class VendorController extends BaseController
|
||||
{
|
||||
@ -266,7 +272,7 @@ class VendorController extends BaseController
|
||||
return $request->disallowUpdate();
|
||||
}
|
||||
|
||||
$vendor = $this->client_repo->save($request->all(), $vendor);
|
||||
$vendor = $this->vendor_repo->save($request->all(), $vendor);
|
||||
|
||||
$this->uploadLogo($request->file('company_logo'), $vendor->company, $vendor);
|
||||
|
||||
@ -359,7 +365,7 @@ class VendorController extends BaseController
|
||||
*/
|
||||
public function store(StoreVendorRequest $request)
|
||||
{
|
||||
$vendor = $this->client_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
$vendor = $this->vendor_repo->save($request->all(), VendorFactory::create(auth()->user()->company()->id, auth()->user()->id));
|
||||
|
||||
$vendor->load('contacts', 'primary_contact');
|
||||
|
||||
@ -485,7 +491,7 @@ class VendorController extends BaseController
|
||||
|
||||
$vendors->each(function ($vendor, $key) use ($action) {
|
||||
if (auth()->user()->can('edit', $vendor)) {
|
||||
$this->client_repo->{$action}($vendor);
|
||||
$this->vendor_repo->{$action}($vendor);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -56,7 +56,7 @@ class Kernel extends HttpKernel
|
||||
'bindings',
|
||||
'query_logging',
|
||||
\App\Http\Middleware\StartupCheck::class,
|
||||
// \App\Http\Middleware\Cors::class,
|
||||
\App\Http\Middleware\Cors::class,
|
||||
],
|
||||
'contact' => [
|
||||
'throttle:60,1',
|
||||
|
@ -19,7 +19,7 @@ class RecurringInvoicesTable extends Component
|
||||
|
||||
$query = $query
|
||||
->where('client_id', auth('contact')->user()->client->id)
|
||||
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_COMPLETED])
|
||||
->whereIn('status_id', [RecurringInvoice::STATUS_PENDING, RecurringInvoice::STATUS_ACTIVE, RecurringInvoice::STATUS_PAUSED,RecurringInvoice::STATUS_COMPLETED])
|
||||
->orderBy('status_id', 'asc')
|
||||
->with('client')
|
||||
->orderBy($this->sort_field, $this->sort_asc ? 'asc' : 'desc')
|
||||
|
@ -16,7 +16,7 @@ class Cors
|
||||
// ALLOW OPTIONS METHOD
|
||||
$headers = [
|
||||
'Access-Control-Allow-Methods'=> 'POST, GET, OPTIONS, PUT, DELETE',
|
||||
'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range',
|
||||
'Access-Control-Allow-Headers'=> 'X-API-COMPANY-KEY,X-CLIENT-VERSION,X-API-SECRET,X-API-TOKEN,X-API-PASSWORD,DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range',
|
||||
];
|
||||
|
||||
return Response::make('OK', 200, $headers);
|
||||
|
47
app/Http/Requests/Expense/BulkExpenseRequest.php
Normal file
47
app/Http/Requests/Expense/BulkExpenseRequest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests\Expense;
|
||||
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\BulkOptions;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class BulkExpenseRequest extends FormRequest
|
||||
{
|
||||
use BulkOptions;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
if (! $this->has('action')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! in_array($this->action, $this->getBulkOptions(), true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return auth()->user()->can(auth()->user()->isAdmin(), Expense::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$rules = $this->getGlobalRules();
|
||||
|
||||
/* We don't require IDs on bulk storing. */
|
||||
if ($this->action !== self::$STORE_METHOD) {
|
||||
$rules['ids'] = ['required'];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
28
app/Http/Requests/Expense/CreateExpenseRequest.php
Normal file
28
app/Http/Requests/Expense/CreateExpenseRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Expense;
|
||||
|
||||
class CreateExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', Expense::class);
|
||||
}
|
||||
}
|
28
app/Http/Requests/Expense/DestroyExpenseRequest.php
Normal file
28
app/Http/Requests/Expense/DestroyExpenseRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Expense;
|
||||
|
||||
class DestroyExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense);
|
||||
}
|
||||
}
|
38
app/Http/Requests/Expense/EditExpenseRequest.php
Normal file
38
app/Http/Requests/Expense/EditExpenseRequest.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Expense;
|
||||
|
||||
class EditExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense);
|
||||
}
|
||||
|
||||
// public function prepareForValidation()
|
||||
// {
|
||||
// $input = $this->all();
|
||||
|
||||
// //$input['id'] = $this->encodePrimaryKey($input['id']);
|
||||
|
||||
// $this->replace($input);
|
||||
|
||||
// }
|
||||
}
|
28
app/Http/Requests/Expense/ShowExpenseRequest.php
Normal file
28
app/Http/Requests/Expense/ShowExpenseRequest.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Models\Expense;
|
||||
|
||||
class ShowExpenseRequest extends Request
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('view', $this->expense);
|
||||
}
|
||||
}
|
76
app/Http/Requests/Expense/StoreExpenseRequest.php
Normal file
76
app/Http/Requests/Expense/StoreExpenseRequest.php
Normal file
@ -0,0 +1,76 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\DataMapper\ExpenseSettings;
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\Expense\UniqueExpenseNumberRule;
|
||||
use App\Http\ValidationRules\ValidExpenseGroupSettingsRule;
|
||||
use App\Models\Expense;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class StoreExpenseRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('create', Expense::class);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
//$rules['name'] = 'required|min:1';
|
||||
$rules['id_number'] = 'unique:expenses,id_number,'.$this->id.',id,company_id,'.$this->company_id;
|
||||
//$rules['settings'] = new ValidExpenseGroupSettingsRule();
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
|
||||
$rules['number'] = new UniqueExpenseNumberRule($this->all());
|
||||
|
||||
// $contacts = request('contacts');
|
||||
|
||||
// if (is_array($contacts)) {
|
||||
// for ($i = 0; $i < count($contacts); $i++) {
|
||||
|
||||
// //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
|
||||
// }
|
||||
// }
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
// $input = $this->all();
|
||||
|
||||
|
||||
// $this->replace($input);
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
//'required' => trans('validation.required', ['attribute' => 'email']),
|
||||
'contacts.*.email.required' => ctrans('validation.email', ['attribute' => 'email']),
|
||||
];
|
||||
}
|
||||
}
|
77
app/Http/Requests/Expense/UpdateExpenseRequest.php
Normal file
77
app/Http/Requests/Expense/UpdateExpenseRequest.php
Normal file
@ -0,0 +1,77 @@
|
||||
<?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\Expense;
|
||||
|
||||
use App\Http\Requests\Request;
|
||||
use App\Http\ValidationRules\IsDeletedRule;
|
||||
use App\Http\ValidationRules\ValidExpenseGroupSettingsRule;
|
||||
use App\Utils\Traits\ChecksEntityStatus;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Validation\Rule;
|
||||
|
||||
class UpdateExpenseRequest extends Request
|
||||
{
|
||||
use MakesHash;
|
||||
use ChecksEntityStatus;
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function authorize() : bool
|
||||
{
|
||||
return auth()->user()->can('edit', $this->expense);
|
||||
}
|
||||
|
||||
public function rules()
|
||||
{
|
||||
/* Ensure we have a client name, and that all emails are unique*/
|
||||
|
||||
$rules['country_id'] = 'integer|nullable';
|
||||
//$rules['id_number'] = 'unique:clients,id_number,,id,company_id,' . auth()->user()->company()->id;
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
|
||||
if ($this->input('number')) {
|
||||
$rules['number'] = 'unique:expenses,number,'.$this->id.',id,company_id,'.$this->expense->company_id;
|
||||
}
|
||||
|
||||
$contacts = request('contacts');
|
||||
|
||||
if (is_array($contacts)) {
|
||||
// for ($i = 0; $i < count($contacts); $i++) {
|
||||
// // $rules['contacts.' . $i . '.email'] = 'nullable|email|unique:client_contacts,email,' . isset($contacts[$i]['id'].',company_id,'.$this->company_id);
|
||||
// //$rules['contacts.' . $i . '.email'] = 'nullable|email';
|
||||
// }
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return [
|
||||
'unique' => ctrans('validation.unique', ['attribute' => 'email']),
|
||||
'email' => ctrans('validation.email', ['attribute' => 'email']),
|
||||
'name.required' => ctrans('validation.required', ['attribute' => 'name']),
|
||||
'required' => ctrans('validation.required', ['attribute' => 'email']),
|
||||
];
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$this->replace($input);
|
||||
}
|
||||
}
|
19
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
19
app/Http/Requests/Vendor/StoreVendorRequest.php
vendored
@ -42,27 +42,24 @@ class StoreVendorRequest extends Request
|
||||
//$rules['settings'] = new ValidVendorGroupSettingsRule();
|
||||
$rules['contacts.*.email'] = 'nullable|distinct';
|
||||
|
||||
$contacts = request('contacts');
|
||||
// $contacts = request('contacts');
|
||||
|
||||
if (is_array($contacts)) {
|
||||
for ($i = 0; $i < count($contacts); $i++) {
|
||||
// if (is_array($contacts)) {
|
||||
// for ($i = 0; $i < count($contacts); $i++) {
|
||||
|
||||
//$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
|
||||
}
|
||||
}
|
||||
// //$rules['contacts.' . $i . '.email'] = 'nullable|email|distinct';
|
||||
// }
|
||||
// }
|
||||
|
||||
return $rules;
|
||||
}
|
||||
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
$input = $this->all();
|
||||
// $input = $this->all();
|
||||
|
||||
if (! isset($input['settings'])) {
|
||||
$input['settings'] = VendorSettings::defaults();
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
// $this->replace($input);
|
||||
}
|
||||
|
||||
public function messages()
|
||||
|
69
app/Http/ValidationRules/Expense/UniqueExpenseNumberRule.php
Normal file
69
app/Http/ValidationRules/Expense/UniqueExpenseNumberRule.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?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\ValidationRules\Expense;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Expense;
|
||||
use App\Models\User;
|
||||
use Illuminate\Contracts\Validation\Rule;
|
||||
|
||||
/**
|
||||
* Class UniqueExpenseNumberRule.
|
||||
*/
|
||||
class UniqueExpenseNumberRule implements Rule
|
||||
{
|
||||
public $input;
|
||||
|
||||
public function __construct($input)
|
||||
{
|
||||
$this->input = $input;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
public function passes($attribute, $value)
|
||||
{
|
||||
return $this->checkIfExpenseNumberUnique(); //if it exists, return false!
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function message()
|
||||
{
|
||||
return 'Expense number already taken';
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $email
|
||||
*
|
||||
* //off,when_sent,when_paid
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function checkIfExpenseNumberUnique() : bool
|
||||
{
|
||||
$expense = Expense::where('client_id', $this->input['client_id'])
|
||||
->where('number', $this->input['number'])
|
||||
->withTrashed()
|
||||
->exists();
|
||||
|
||||
if ($expense) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -92,6 +92,7 @@ class UserEmailChanged extends BaseMailerJob implements ShouldQueue
|
||||
'button' => ctrans('texts.account_login'),
|
||||
'signature' => $this->company->owner()->signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->settings,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -56,7 +56,7 @@ class SendFailedEmails implements ShouldQueue
|
||||
|
||||
private function processEmails()
|
||||
{
|
||||
\Log::error('processing emails');
|
||||
//\Log::error('processing emails');
|
||||
//info("process emails");
|
||||
//@todo check that the quota is available for the job
|
||||
|
||||
|
@ -84,6 +84,8 @@ class EntitySentObject
|
||||
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||
'signature' => $settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $settings,
|
||||
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ class EntityViewedObject
|
||||
'button' => ctrans("texts.view_{$this->entity_type}"),
|
||||
'signature' => $settings->email_signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $settings,
|
||||
|
||||
];
|
||||
|
||||
return $data;
|
||||
|
@ -76,6 +76,8 @@ class PaymentFailureObject
|
||||
),
|
||||
'signature' => $signature,
|
||||
'logo' => $this->company->present()->logo(),
|
||||
'settings' => $this->client->getMergedSettings(),
|
||||
|
||||
];
|
||||
|
||||
return $data;
|
||||
|
@ -36,11 +36,11 @@ class RecurringInvoice extends BaseModel
|
||||
/**
|
||||
* Invoice Statuses.
|
||||
*/
|
||||
const STATUS_DRAFT = 2;
|
||||
const STATUS_ACTIVE = 3;
|
||||
const STATUS_CANCELLED = 4;
|
||||
const STATUS_DRAFT = 1;
|
||||
const STATUS_ACTIVE = 2;
|
||||
const STATUS_PAUSED = 3;
|
||||
const STATUS_COMPLETED = 4;
|
||||
const STATUS_PENDING = -1;
|
||||
const STATUS_COMPLETED = -2;
|
||||
|
||||
/**
|
||||
* Recurring intervals //todo MAP WHEN WE MIGRATE.
|
||||
@ -102,6 +102,7 @@ class RecurringInvoice extends BaseModel
|
||||
'frequency_id',
|
||||
'next_send_date',
|
||||
'remaining_cycles',
|
||||
'auto_bill',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
@ -189,13 +190,10 @@ class RecurringInvoice extends BaseModel
|
||||
|
||||
public function getStatusAttribute()
|
||||
{
|
||||
if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) { //marked as active, but yet to fire first cycle
|
||||
if ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now())
|
||||
return self::STATUS_PENDING;
|
||||
} elseif ($this->status_id == self::STATUS_ACTIVE && $this->next_send_date > Carbon::now()) {
|
||||
return self::STATUS_COMPLETED;
|
||||
} else {
|
||||
else
|
||||
return $this->status_id;
|
||||
}
|
||||
}
|
||||
|
||||
public function nextSendDate() :?Carbon
|
||||
@ -288,16 +286,16 @@ class RecurringInvoice extends BaseModel
|
||||
return '<h4><span class="badge badge-light">'.ctrans('texts.draft').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_PENDING:
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.sent').'</span></h4>';
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.pending').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_ACTIVE:
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.partial').'</span></h4>';
|
||||
return '<h4><span class="badge badge-primary">'.ctrans('texts.active').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_COMPLETED:
|
||||
return '<h4><span class="badge badge-success">'.ctrans('texts.status_completed').'</span></h4>';
|
||||
break;
|
||||
case self::STATUS_CANCELLED:
|
||||
return '<h4><span class="badge badge-danger">'.ctrans('texts.overdue').'</span></h4>';
|
||||
case self::STATUS_PAUSED:
|
||||
return '<h4><span class="badge badge-danger">'.ctrans('texts.paused').'</span></h4>';
|
||||
break;
|
||||
default:
|
||||
// code...
|
||||
@ -368,7 +366,7 @@ class RecurringInvoice extends BaseModel
|
||||
/* Return early if nothing to send back! */
|
||||
if( $this->status_id == self::STATUS_COMPLETED ||
|
||||
$this->status_id == self::STATUS_DRAFT ||
|
||||
$this->status_id == self::STATUS_CANCELLED ||
|
||||
$this->status_id == self::STATUS_PAUSED ||
|
||||
$this->remaining_cycles == 0 ||
|
||||
!$this->next_send_date) {
|
||||
|
||||
|
@ -13,6 +13,7 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Filterable;
|
||||
use App\Models\VendorContact;
|
||||
use App\Utils\Traits\GeneratesCounter;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
@ -21,7 +22,8 @@ class Vendor extends BaseModel
|
||||
{
|
||||
use SoftDeletes;
|
||||
use Filterable;
|
||||
|
||||
use GeneratesCounter;
|
||||
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'id_number',
|
||||
@ -34,6 +36,7 @@ class Vendor extends BaseModel
|
||||
'postal_code',
|
||||
'country_id',
|
||||
'private_notes',
|
||||
'public_notes',
|
||||
'currency_id',
|
||||
'website',
|
||||
'transaction_name',
|
||||
@ -63,6 +66,12 @@ class Vendor extends BaseModel
|
||||
return self::class;
|
||||
}
|
||||
|
||||
public function primary_contact()
|
||||
{
|
||||
return $this->hasMany(VendorContact::class)->where('is_primary', true);
|
||||
}
|
||||
|
||||
|
||||
public function documents()
|
||||
{
|
||||
return $this->morphMany(Document::class, 'documentable');
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Admin;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
@ -114,6 +123,7 @@ class BaseNotification extends Notification implements ShouldQueue
|
||||
'view_text' => ctrans('texts.view_'.$this->entity_string),
|
||||
'logo' => $this->entity->company->present()->logo(),
|
||||
'signature' => $this->settings->email_signature,
|
||||
'settings' => $this->settings,
|
||||
|
||||
];
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Ninja;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications\Ninja;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Notifications;
|
||||
|
||||
|
@ -179,7 +179,7 @@ class CreditCard
|
||||
|
||||
$payment_type = PaymentType::parseCardType($payment_method_object['card']['brand']);
|
||||
|
||||
if ($state['save_card'] == true) {
|
||||
if ($state['save_card'] == true || $state['save_card'] == 'true') {
|
||||
$this->saveCard($state);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function boot()
|
||||
{
|
||||
Relation::morphMap([
|
||||
'invoices' => \App\Models\Invoice::class,
|
||||
'invoices' => \App\Models\Invoice::class,
|
||||
// 'credits' => \App\Models\Credit::class,
|
||||
'proposals' => \App\Models\Proposal::class,
|
||||
]);
|
||||
|
||||
|
@ -225,6 +225,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
],
|
||||
InvoiceWasPaid::class => [
|
||||
InvoicePaidActivity::class,
|
||||
CreateInvoicePdf::class,
|
||||
],
|
||||
InvoiceWasViewed::class => [
|
||||
InvoiceViewedActivity::class,
|
||||
@ -238,6 +239,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
],
|
||||
InvoiceWasDeleted::class => [
|
||||
InvoiceDeletedActivity::class,
|
||||
CreateInvoicePdf::class,
|
||||
],
|
||||
InvoiceWasArchived::class => [
|
||||
InvoiceArchivedActivity::class,
|
||||
@ -247,6 +249,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
],
|
||||
InvoiceWasReversed::class => [
|
||||
InvoiceReversedActivity::class,
|
||||
CreateInvoicePdf::class,
|
||||
],
|
||||
InvoiceWasCancelled::class => [
|
||||
InvoiceCancelledActivity::class,
|
||||
|
@ -13,6 +13,7 @@ namespace App\Repositories;
|
||||
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
@ -58,9 +59,17 @@ class VendorContactRepository extends BaseRepository
|
||||
|
||||
$update_contact->fill($contact);
|
||||
|
||||
if (array_key_exists('password', $contact) && strlen($contact['password']) > 1) {
|
||||
|
||||
$update_contact->password = Hash::make($contact['password']);
|
||||
|
||||
}
|
||||
|
||||
$update_contact->save();
|
||||
});
|
||||
|
||||
$vendor->load('contacts');
|
||||
|
||||
//always made sure we have one blank contact to maintain state
|
||||
if ($contacts->count() == 0) {
|
||||
$new_contact = new VendorContact;
|
||||
|
@ -18,6 +18,7 @@ use App\Factory\InvoiceItemFactory;
|
||||
use App\Factory\PaymentFactory;
|
||||
use App\Helpers\Invoice\InvoiceSum;
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Paymentable;
|
||||
@ -55,7 +56,7 @@ class HandleReversal extends AbstractService
|
||||
$total_paid = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
/*Adjust payment applied and the paymentables to the correct amount */
|
||||
$paymentables = Paymentable::wherePaymentableType(Invoice::class)
|
||||
$paymentables = Paymentable::wherePaymentableType('invoices')
|
||||
->wherePaymentableId($this->invoice->id)
|
||||
->get();
|
||||
|
||||
@ -95,6 +96,23 @@ class HandleReversal extends AbstractService
|
||||
$credit->service()->markSent()->save();
|
||||
}
|
||||
|
||||
/*If there is a payment linked, then the credit needs to be linked back to that payment in case of refund*/
|
||||
if($paymentables->count() > 0){
|
||||
|
||||
$payment = $paymentables->first()->payment;
|
||||
$payment->credits()->save($credit);
|
||||
|
||||
$paymentable_credit = $payment->credits()
|
||||
->wherePaymentableType(Credit::class)
|
||||
->wherePaymentableId($credit->id)
|
||||
->first();
|
||||
|
||||
//harvest the credit record and add in the amount for the credit.
|
||||
$paymentable_credit->pivot->amount = $total_paid;
|
||||
$paymentable_credit->pivot->save();
|
||||
|
||||
}
|
||||
|
||||
/* Set invoice balance to 0 */
|
||||
if ($this->invoice->balance != 0) {
|
||||
$this->invoice->ledger()->updateInvoiceBalance($balance_remaining * -1, $notes)->save();
|
||||
@ -119,37 +137,4 @@ class HandleReversal extends AbstractService
|
||||
//create a ledger row for this with the resulting Credit ( also include an explanation in the notes section )
|
||||
}
|
||||
|
||||
// public function run2()
|
||||
// {
|
||||
|
||||
// /* Check again!! */
|
||||
// if (!$this->invoice->invoiceReversable($this->invoice)) {
|
||||
// return $this->invoice;
|
||||
// }
|
||||
|
||||
// if($this->invoice->status_id == Invoice::STATUS_CANCELLED)
|
||||
// $this->invoice = $this->invoice->service()->reverseCancellation()->save();
|
||||
|
||||
// //$balance_remaining = $this->invoice->balance;
|
||||
|
||||
// //$total_paid = $this->invoice->amount - $this->invoice->balance;
|
||||
|
||||
// /*Adjust payment applied and the paymentables to the correct amount */
|
||||
// $paymentables = Paymentable::wherePaymentableType(Invoice::class)
|
||||
// ->wherePaymentableId($this->invoice->id)
|
||||
// ->get();
|
||||
|
||||
// $total_paid = 0;
|
||||
|
||||
// $paymentables->each(function ($paymentable) use ($total_paid) {
|
||||
|
||||
// $reversable_amount = $paymentable->amount - $paymentable->refunded;
|
||||
// $total_paid -= $reversable_amount;
|
||||
// $paymentable->amount = $paymentable->refunded;
|
||||
// $paymentable->save();
|
||||
// });
|
||||
|
||||
// //Unwinding any payments made to this invoice
|
||||
|
||||
// }
|
||||
}
|
||||
|
@ -206,6 +206,9 @@ class InvoiceService
|
||||
if($this->invoice->balance == 0)
|
||||
$this->setStatus(Invoice::STATUS_PAID);
|
||||
|
||||
if($this->invoice->balance > 0 && $this->invoice->balance < $this->invoice->amount)
|
||||
$this->setStatus(Invoice::STATUS_PARTIAL);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,8 @@ class UpdateInvoicePayment
|
||||
$pivot_invoice->pivot->amount = $paid_amount;
|
||||
$pivot_invoice->pivot->save();
|
||||
|
||||
$this->payment->applied += $paid_amount;
|
||||
|
||||
$invoice->service() //caution what if we amount paid was less than partial - we wipe it!
|
||||
->clearPartial()
|
||||
->updateBalance($paid_amount * -1)
|
||||
@ -82,6 +84,8 @@ class UpdateInvoicePayment
|
||||
event(new InvoiceWasUpdated($invoice, $invoice->company, Ninja::eventVars()));
|
||||
|
||||
});
|
||||
|
||||
$this->payment->save();
|
||||
|
||||
return $this->payment;
|
||||
}
|
||||
|
@ -76,6 +76,7 @@ class VendorTransformer extends EntityTransformer
|
||||
'name' => $vendor->name ?: '',
|
||||
'website' => $vendor->website ?: '',
|
||||
'private_notes' => $vendor->private_notes ?: '',
|
||||
'public_notes' => $vendor->public_notes ?: '',
|
||||
'last_login' => (int) $vendor->last_login,
|
||||
'address1' => $vendor->address1 ?: '',
|
||||
'address2' => $vendor->address2 ?: '',
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Utils\ClientPortal\CustomMessage;
|
||||
|
||||
|
@ -1,4 +1,13 @@
|
||||
<?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\Utils\ClientPortal\CustomMessage;
|
||||
|
||||
|
@ -13,11 +13,13 @@ namespace App\Utils\Traits;
|
||||
|
||||
use App\Models\Client;
|
||||
use App\Models\Credit;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Quote;
|
||||
use App\Models\RecurringInvoice;
|
||||
use App\Models\Timezone;
|
||||
use App\Models\Vendor;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
/**
|
||||
@ -238,6 +240,28 @@ trait GeneratesCounter
|
||||
return $client_number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the next client number.
|
||||
*
|
||||
* @param \App\Models\Vendor $vendor The vendor
|
||||
* @return string The next vendor number.
|
||||
*/
|
||||
public function getNextVendorNumber(Vendor $vendor) :string
|
||||
{
|
||||
$this->resetCompanyCounters($vendor->company);
|
||||
|
||||
$counter = $vendor->company->settings->vendor_number_counter;
|
||||
$setting_entity = $vendor->company->settings->vendor_number_counter;
|
||||
|
||||
$vendor_number = $this->checkEntityNumber(Vendor::class, $vendor, $counter, $vendor->company->settings->counter_padding, $vendor->company->settings->vendor_number_pattern);
|
||||
|
||||
$this->incrementCounter($vendor->company, 'vendor_number_counter');
|
||||
|
||||
return $vendor_number;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if it has shared counter.
|
||||
*
|
||||
@ -254,33 +278,29 @@ trait GeneratesCounter
|
||||
* Checks that the number has not already been used.
|
||||
*
|
||||
* @param Collection $entity The entity ie App\Models\Client, Invoice, Quote etc
|
||||
* @param int $counter The counter
|
||||
* @param int $padding The padding
|
||||
* @param int $counter The counter
|
||||
* @param int $padding The padding
|
||||
*
|
||||
* @return string The padded and prefixed invoice number
|
||||
* @return string The padded and prefixed entity number
|
||||
*/
|
||||
private function checkEntityNumber($class, $client, $counter, $padding, $pattern)
|
||||
private function checkEntityNumber($class, $entity, $counter, $padding, $pattern)
|
||||
{
|
||||
$check = false;
|
||||
|
||||
do {
|
||||
$number = $this->padCounter($counter, $padding);
|
||||
|
||||
$number = $this->applyNumberPattern($client, $number, $pattern);
|
||||
|
||||
if ($class == Invoice::class || $class == RecurringInvoice::class) {
|
||||
$check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
} elseif ($class == Client::class) {
|
||||
$check = $class::whereCompanyId($client->company_id)->whereIdNumber($number)->withTrashed()->first();
|
||||
} elseif ($class == Credit::class) {
|
||||
$check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
} elseif ($class == Quote::class) {
|
||||
$check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
} elseif ($class == Payment::class) {
|
||||
$check = $class::whereCompanyId($client->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
}
|
||||
$number = $this->applyNumberPattern($entity, $number, $pattern);
|
||||
|
||||
if ($class == Invoice::class || $class == RecurringInvoice::class)
|
||||
$check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
elseif ($class == Client::class || $class == Vendor::class)
|
||||
$check = $class::whereCompanyId($entity->company_id)->whereIdNumber($number)->withTrashed()->first();
|
||||
else
|
||||
$check = $class::whereCompanyId($entity->company_id)->whereNumber($number)->withTrashed()->first();
|
||||
|
||||
$counter++;
|
||||
|
||||
} while ($check);
|
||||
|
||||
return $number;
|
||||
@ -389,16 +409,74 @@ trait GeneratesCounter
|
||||
$client->company->save();
|
||||
}
|
||||
|
||||
private function resetCompanyCounters($company)
|
||||
{
|
||||
$timezone = Timezone::find($company->settings->timezone_id);
|
||||
|
||||
$reset_date = Carbon::parse($company->settings->reset_counter_date, $timezone->name);
|
||||
|
||||
if (! $reset_date->isToday() || ! $company->settings->reset_counter_date) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch ($company->reset_counter_frequency_id) {
|
||||
case RecurringInvoice::FREQUENCY_WEEKLY:
|
||||
$reset_date->addWeek();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_WEEKS:
|
||||
$reset_date->addWeeks(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_WEEKS:
|
||||
$reset_date->addWeeks(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_MONTHLY:
|
||||
$reset_date->addMonth();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_MONTHS:
|
||||
$reset_date->addMonths(2);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_THREE_MONTHS:
|
||||
$reset_date->addMonths(3);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_FOUR_MONTHS:
|
||||
$reset_date->addMonths(4);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_SIX_MONTHS:
|
||||
$reset_date->addMonths(6);
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_ANNUALLY:
|
||||
$reset_date->addYear();
|
||||
break;
|
||||
case RecurringInvoice::FREQUENCY_TWO_YEARS:
|
||||
$reset_date->addYears(2);
|
||||
break;
|
||||
}
|
||||
|
||||
$settings = $company->settings;
|
||||
$settings->reset_counter_date = $reset_date->format('Y-m-d');
|
||||
$settings->invoice_number_counter = 1;
|
||||
$settings->quote_number_counter = 1;
|
||||
$settings->credit_number_counter = 1;
|
||||
$settings->vendor_number_counter = 1;
|
||||
$settings->ticket_number_counter = 1;
|
||||
$settings->payment_number_counter = 1;
|
||||
$settings->task_number_counter = 1;
|
||||
$settings->expense_number_counter = 1;
|
||||
|
||||
$company->settings = $settings;
|
||||
$company->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* { function_description }.
|
||||
* Formats a entity number by pattern
|
||||
*
|
||||
* @param \App\Models\Client $client The client
|
||||
* @param string $counter The counter
|
||||
* @param null|string $pattern The pattern
|
||||
* @param \App\Models\BaseModel $entity The entity object
|
||||
* @param string $counter The counter
|
||||
* @param null|string $pattern The pattern
|
||||
*
|
||||
* @return string ( description_of_the_return_value )
|
||||
* @return string The formatted number pattern
|
||||
*/
|
||||
private function applyNumberPattern(Client $client, string $counter, $pattern) :string
|
||||
private function applyNumberPattern($entity, string $counter, $pattern) :string
|
||||
{
|
||||
if (! $pattern) {
|
||||
return $counter;
|
||||
@ -417,7 +495,7 @@ trait GeneratesCounter
|
||||
$replace[] = $counter;
|
||||
|
||||
if (strstr($pattern, '{$user_id}')) {
|
||||
$user_id = $client->user_id ? $client->user_id : 0;
|
||||
$user_id = $entity->user_id ? $entity->user_id : 0;
|
||||
$search[] = '{$user_id}';
|
||||
$replace[] = str_pad(($user_id), 2, '0', STR_PAD_LEFT);
|
||||
}
|
||||
@ -429,24 +507,24 @@ trait GeneratesCounter
|
||||
$search[] = $matches[0];
|
||||
|
||||
/* The following adjusts for the company timezone - may bork tests depending on the time of day the tests are run!!!!!!*/
|
||||
$date = Carbon::now($client->company->timezone()->name)->format($format);
|
||||
$date = Carbon::now($entity->company->timezone()->name)->format($format);
|
||||
$replace[] = str_replace($format, $date, $matches[1]);
|
||||
}
|
||||
|
||||
$search[] = '{$custom1}';
|
||||
$replace[] = $client->custom_value1;
|
||||
$replace[] = $entity->custom_value1;
|
||||
|
||||
$search[] = '{$custom2}';
|
||||
$replace[] = $client->custom_value2;
|
||||
$replace[] = $entity->custom_value2;
|
||||
|
||||
$search[] = '{$custom3}';
|
||||
$replace[] = $client->custom_value3;
|
||||
$replace[] = $entity->custom_value3;
|
||||
|
||||
$search[] = '{$custom4}';
|
||||
$replace[] = $client->custom_value4;
|
||||
$replace[] = $entity->custom_value4;
|
||||
|
||||
$search[] = '{$id_number}';
|
||||
$replace[] = $client->id_number;
|
||||
$replace[] = $entity->id_number;
|
||||
|
||||
return str_replace($search, $replace, $pattern);
|
||||
}
|
||||
|
@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class IdNumberFieldsForMissingEntities extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('expenses', function (Blueprint $table) {
|
||||
$table->string('number')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('tasks', function (Blueprint $table) {
|
||||
$table->string('number')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('vendors', function (Blueprint $table) {
|
||||
$table->text('vendor_hash')->nullable();
|
||||
$table->text('public_notes')->nullable();
|
||||
});
|
||||
|
||||
Schema::table('vendor_contacts', function (Blueprint $table) {
|
||||
$table->boolean('send_email')->default(0);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
2
public/js/clients/payments/checkout.com.js
vendored
2
public/js/clients/payments/checkout.com.js
vendored
@ -1,2 +1,2 @@
|
||||
/*! For license information please see checkout.com.js.LICENSE.txt */
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=9)}({9:function(e,t,n){e.exports=n("XYrq")},XYrq:function(e,t){window.CKOConfig={publicKey:document.querySelector('meta[name="public-key"]').content,customerEmail:document.querySelector('meta[name="customer-email"]').content,value:document.querySelector('meta[name="value"]').content,currency:document.querySelector('meta[name="currency"]').content,paymentMode:"cards",cardFormMode:"cardTokenisation",cardTokenised:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.data),document.querySelector('input[name="store_card"]').value=document.getElementById("store-card-checkbox").checked?1:0,document.getElementById("server-response").submit()}}}});
|
||||
!function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="/",n(n.s=9)}({9:function(e,t,n){e.exports=n("XYrq")},XYrq:function(e,t){window.CKOConfig={publicKey:document.querySelector('meta[name="public-key"]').content,customerEmail:document.querySelector('meta[name="customer-email"]').content,value:document.querySelector('meta[name="value"]').content,currency:document.querySelector('meta[name="currency"]').content,paymentMode:"cards",cardFormMode:"cardTokenisation",cardTokenised:function(e){document.querySelector('input[name="gateway_response"]').value=JSON.stringify(e.data),document.querySelector('input[name="store_card"]').value=document.querySelector("input[name=token-billing-checkbox]:checked").value,document.getElementById("server-response").submit()}}}});
|
@ -9,7 +9,7 @@
|
||||
"/js/clients/payments/alipay.js": "/js/clients/payments/alipay.js?id=766690d4207c0d8e75d1",
|
||||
"/js/clients/payments/authorize-credit-card-payment.js": "/js/clients/payments/authorize-credit-card-payment.js?id=914bbac0c52e458a55ea",
|
||||
"/js/clients/payments/card-js.min.js": "/js/clients/payments/card-js.min.js?id=7200ac43b87bddf1bedc",
|
||||
"/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=42d239882b80af83ad22",
|
||||
"/js/clients/payments/checkout.com.js": "/js/clients/payments/checkout.com.js?id=bee8407bd5dfae80dc6a",
|
||||
"/js/clients/payments/process.js": "/js/clients/payments/process.js?id=8f0a87e44b6e6824c010",
|
||||
"/js/clients/payments/sofort.js": "/js/clients/payments/sofort.js?id=ca99b358b094202d1e3a",
|
||||
"/js/clients/quotes/action-selectors.js": "/js/clients/quotes/action-selectors.js?id=1702ddb9f59521c7758f",
|
||||
|
@ -66,11 +66,6 @@ class AuthorizeAuthorizeCard {
|
||||
}
|
||||
|
||||
handle() {
|
||||
//this.handleFormValidation();
|
||||
|
||||
// At this point as an small API you can request this.form.valid to check if input elements are valid.
|
||||
// Note: this.form.valid will not handle empty fields.
|
||||
|
||||
this.cardButton.addEventListener("click", () => {
|
||||
this.cardButton.disabled = !this.cardButton.disabled;
|
||||
this.handleAuthorization();
|
||||
|
@ -23,7 +23,7 @@ window.CKOConfig = {
|
||||
|
||||
document.querySelector(
|
||||
'input[name="store_card"]'
|
||||
).value = document.getElementById('store-card-checkbox').checked ? 1 : 0;
|
||||
).value = document.querySelector('input[name=token-billing-checkbox]:checked').value;
|
||||
|
||||
document.getElementById('server-response').submit();
|
||||
},
|
||||
|
@ -3277,4 +3277,6 @@ return [
|
||||
|
||||
'under_payments_disabled' => 'Company doesn\'t support under payments.',
|
||||
'over_payments_disabled' => 'Company doesn\'t support over payments.',
|
||||
|
||||
'paused' => 'Paused',
|
||||
];
|
||||
|
@ -58,14 +58,32 @@
|
||||
</div>
|
||||
|
||||
@include('portal.ninja2020.gateways.authorize.credit_card')
|
||||
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
|
||||
<div class="{{ ($gateway->token_billing == 'optin' || $gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} bg-gray-50 px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.token_billing_checkbox') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<input type="checkbox" class="form-checkbox" name="store_card_checkbox"
|
||||
id="store_card_checkbox"/>
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="store_card_checkbox"
|
||||
id="proxy_is_default"
|
||||
value="true"
|
||||
{{ ($gateway->token_billing == 'always' || $gateway->token_billing == 'optout') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.yes') }}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="store_card_checkbox"
|
||||
id="proxy_is_default"
|
||||
value="false"
|
||||
{{ ($gateway->token_billing == 'off' || $gateway->token_billing == 'optin') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.no') }}</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
|
@ -84,12 +84,31 @@
|
||||
</button>
|
||||
</div>
|
||||
@else
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 flex items-center">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 mr-4">
|
||||
<div class="{{ ($gateway->company_gateway->token_billing == 'optin' || $gateway->company_gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} bg-gray-50 px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.token_billing_checkbox') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<input type="checkbox" id="store-card-checkbox" class="form-checkbox">
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="true"
|
||||
{{ ($gateway->company_gateway->token_billing == 'always' || $gateway->company_gateway->token_billing == 'optout') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.yes') }}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="false"
|
||||
{{ ($gateway->company_gateway->token_billing == 'off' || $gateway->company_gateway->token_billing == 'optin') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.no') }}</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
|
@ -22,7 +22,7 @@
|
||||
<h3 class="text-lg leading-6 font-medium text-gray-900">
|
||||
{{ ctrans('texts.add_credit_card') }}
|
||||
</h3>
|
||||
<p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500" translate>
|
||||
<p class="mt-1 max-w-2xl text-sm leading-5 text-gray-500">
|
||||
{{ ctrans('texts.authorize_for_future_use') }}
|
||||
</p>
|
||||
</div>
|
||||
@ -43,18 +43,30 @@
|
||||
<div id="card-element"></div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<div class="{{ ($gateway->token_billing == 'optin' || $gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} bg-gray-50 px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.save_as_default') }}
|
||||
{{ ctrans('texts.token_billing_checkbox') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<label for="yes" class="mr-4">
|
||||
<input type="radio" class="form-radio cursor-pointer" name="proxy_is_default" {{ $client->getSetting('auto_bill') == 'on' ? 'checked': '' }} id="proxy_is_default" value="true">
|
||||
<span class="ml-1">{{ ctrans('texts.yes') }}</span>
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="true"
|
||||
{{ ($gateway->token_billing == 'always' || $gateway->token_billing == 'optout') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.yes') }}</span>
|
||||
</label>
|
||||
<label for="no">
|
||||
<input type="radio" class="form-radio cursor-pointer" name="proxy_is_default" {{ $client->getSetting('auto_bill') == 'off' ? 'checked': '' }} id="proxy_is_default" value="false">
|
||||
<span class="ml-1">{{ ctrans('texts.no') }}</span>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="false"
|
||||
{{ ($gateway->token_billing == 'off' || $gateway->token_billing == 'optin') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.no') }}</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
|
@ -31,6 +31,28 @@
|
||||
</div>
|
||||
<div>
|
||||
<dl>
|
||||
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.subtotal') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['invoice_totals'], $client) }}
|
||||
</dd>
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.gateway_fees') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['fee_total'], $client) }}
|
||||
</dd>
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.total') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
{{ App\Utils\Number::formatMoney($total['amount_with_fee'], $client) }}
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
@if($token)
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6 flex items-center">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500 mr-4">
|
||||
@ -65,6 +87,7 @@
|
||||
placeholder="{{ ctrans('texts.name') }}">
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="bg-white px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.credit_card') }}
|
||||
@ -73,14 +96,35 @@
|
||||
<div id="card-element"></div>
|
||||
</dd>
|
||||
</div>
|
||||
<div class="bg-gray-50 px-4 py-5 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
|
||||
<div class="{{ ($gateway->company_gateway->token_billing == 'optin' || $gateway->company_gateway->token_billing == 'optout') ? 'sm:grid' : 'hidden' }} bg-gray-50 px-4 py-5 sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||
<dt class="text-sm leading-5 font-medium text-gray-500">
|
||||
{{ ctrans('texts.token_billing_checkbox') }}
|
||||
</dt>
|
||||
<dd class="mt-1 text-sm leading-5 text-gray-900 sm:mt-0 sm:col-span-2">
|
||||
<input type="checkbox" class="form-checkbox" name="token-billing-checkbox"/>
|
||||
<label class="mr-4">
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="true"
|
||||
{{ ($gateway->company_gateway->token_billing == 'always' || $gateway->company_gateway->token_billing == 'optout') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.yes') }}</span>
|
||||
</label>
|
||||
<label>
|
||||
<input
|
||||
type="radio"
|
||||
class="form-radio cursor-pointer"
|
||||
name="token-billing-checkbox"
|
||||
id="proxy_is_default"
|
||||
value="false"
|
||||
{{ ($gateway->company_gateway->token_billing == 'off' || $gateway->company_gateway->token_billing == 'optin') ? 'checked' : '' }} />
|
||||
<span class="ml-1 cursor-pointer">{{ ctrans('texts.no') }}</span>
|
||||
</label>
|
||||
</dd>
|
||||
</div>
|
||||
|
||||
<div class="bg-white px-4 py-5 flex justify-end">
|
||||
<button
|
||||
type="button"
|
||||
|
152
tests/Feature/ExpenseApiTest.php
Normal file
152
tests/Feature/ExpenseApiTest.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?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 Tests\Feature;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Account;
|
||||
use App\Models\Expense;
|
||||
use App\Models\ExpenseContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\ExpenseController
|
||||
*/
|
||||
class ExpenseApiTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
public function testExpensePost()
|
||||
{
|
||||
$data = [
|
||||
'public_notes' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expenses', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpensePut()
|
||||
{
|
||||
$data = [
|
||||
'public_notes' => $this->faker->firstName,
|
||||
'id_number' => 'Coolio',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->put('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id), $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpenseGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testExpenseNotArchived()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/expenses/'.$this->encodePrimaryKey($this->expense->id));
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data']['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseArchived()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expenses/bulk?action=archive', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseRestored()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expenses/bulk?action=restore', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testExpenseDeleted()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->expense->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/expenses/bulk?action=delete', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
}
|
152
tests/Feature/VendorApiTest.php
Normal file
152
tests/Feature/VendorApiTest.php
Normal file
@ -0,0 +1,152 @@
|
||||
<?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 Tests\Feature;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Models\Account;
|
||||
use App\Models\Vendor;
|
||||
use App\Models\VendorContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Http\Controllers\VendorController
|
||||
*/
|
||||
class VendorApiTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
public function testVendorPost()
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->faker->firstName,
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/vendors', $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testVendorPut()
|
||||
{
|
||||
$data = [
|
||||
'name' => $this->faker->firstName,
|
||||
'id_number' => 'Coolio',
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->put('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id), $data);
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testVendorGet()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id));
|
||||
|
||||
$response->assertStatus(200);
|
||||
}
|
||||
|
||||
public function testVendorNotArchived()
|
||||
{
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->get('/api/v1/vendors/'.$this->encodePrimaryKey($this->vendor->id));
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data']['archived_at']);
|
||||
}
|
||||
|
||||
public function testVendorArchived()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->vendor->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/vendors/bulk?action=archive', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertNotNull($arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testVendorRestored()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->vendor->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/vendors/bulk?action=restore', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertEquals(0, $arr['data'][0]['archived_at']);
|
||||
}
|
||||
|
||||
public function testVendorDeleted()
|
||||
{
|
||||
$data = [
|
||||
'ids' => [$this->encodePrimaryKey($this->vendor->id)],
|
||||
];
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token,
|
||||
])->post('/api/v1/vendors/bulk?action=delete', $data);
|
||||
|
||||
$arr = $response->json();
|
||||
|
||||
$this->assertTrue($arr['data'][0]['is_deleted']);
|
||||
}
|
||||
}
|
@ -65,6 +65,10 @@ trait MockAccountData
|
||||
|
||||
public $quote;
|
||||
|
||||
public $vendor;
|
||||
|
||||
public $expense;
|
||||
|
||||
public function makeTestData()
|
||||
{
|
||||
|
||||
@ -149,7 +153,7 @@ trait MockAccountData
|
||||
$this->client = factory(\App\Models\Client::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
]);
|
||||
|
||||
$contact = factory(\App\Models\ClientContact::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
@ -157,14 +161,39 @@ trait MockAccountData
|
||||
'company_id' => $this->company->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
]);
|
||||
]);
|
||||
|
||||
$contact2 = factory(\App\Models\ClientContact::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'client_id' => $this->client->id,
|
||||
'company_id' => $this->company->id,
|
||||
'send_email' => true,
|
||||
]);
|
||||
]);
|
||||
|
||||
$this->vendor = factory(\App\Models\Vendor::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
$vendor_contact = factory(\App\Models\VendorContact::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'company_id' => $this->company->id,
|
||||
'is_primary' => 1,
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
$vendor_contact2 = factory(\App\Models\VendorContact::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'vendor_id' => $this->vendor->id,
|
||||
'company_id' => $this->company->id,
|
||||
'send_email' => true,
|
||||
]);
|
||||
|
||||
$this->expense = factory(\App\Models\Expense::class)->create([
|
||||
'user_id' => $this->user->id,
|
||||
'company_id' => $this->company->id,
|
||||
]);
|
||||
|
||||
// $rels = collect($contact, $contact2);
|
||||
// $this->client->setRelation('contacts', $rels);
|
||||
|
@ -13,6 +13,7 @@ namespace Tests\Unit;
|
||||
use App\DataMapper\ClientSettings;
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Factory\ClientFactory;
|
||||
use App\Factory\VendorFactory;
|
||||
use App\Models\Client;
|
||||
use App\Models\Company;
|
||||
use App\Models\Credit;
|
||||
@ -293,6 +294,25 @@ class GeneratesCounterTest extends TestCase
|
||||
$this->assertEquals($client_number, date('Y').'-'.str_pad($this->client->user_id, 2, '0', STR_PAD_LEFT).'-0002');
|
||||
}
|
||||
|
||||
public function testVendorNumberPattern()
|
||||
{
|
||||
$settings = $this->company->settings;
|
||||
$settings->vendor_number_pattern = '{$year}-{$user_id}-{$counter}';
|
||||
$this->company->settings = $settings;
|
||||
$this->company->save();
|
||||
|
||||
$vendor = VendorFactory::create($this->company->id, $this->user->id);
|
||||
$vendor->save();
|
||||
|
||||
$vendor_number = $this->getNextVendorNumber($vendor);
|
||||
|
||||
$this->assertEquals($vendor_number, date('Y').'-'.str_pad($vendor->user_id, 2, '0', STR_PAD_LEFT).'-0001');
|
||||
|
||||
$vendor_number = $this->getNextVendorNumber($vendor);
|
||||
|
||||
$this->assertEquals($vendor_number, date('Y').'-'.str_pad($vendor->user_id, 2, '0', STR_PAD_LEFT).'-0002');
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
public function testClientNextNumber()
|
||||
|
Loading…
Reference in New Issue
Block a user