Merge pull request #4173 from turbo124/v5-develop

Validation rules for projects / clients.
This commit is contained in:
David Bomba 2020-10-16 20:17:30 +11:00 committed by GitHub
commit 2e233a6437
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 199 additions and 13 deletions

View File

@ -154,10 +154,12 @@ class CompanySettings extends BaseSettings
public $email_style_custom = ''; //the template itself
public $email_subject_invoice = '';
public $email_subject_quote = '';
public $email_subject_credit = '';
public $email_subject_payment = '';
public $email_subject_payment_partial = '';
public $email_subject_statement = '';
public $email_template_invoice = '';
public $email_template_credit = '';
public $email_template_quote = '';
public $email_template_payment = '';
public $email_template_payment_partial = '';
@ -350,10 +352,12 @@ class CompanySettings extends BaseSettings
'email_signature' => 'string',
'email_subject_invoice' => 'string',
'email_subject_quote' => 'string',
'email_subject_credit' => 'string',
'email_subject_payment' => 'string',
'email_subject_payment_partial' => 'string',
'email_template_invoice' => 'string',
'email_template_quote' => 'string',
'email_template_credit' => 'string',
'email_template_payment' => 'string',
'email_template_payment_partial' => 'string',
'email_subject_reminder1' => 'string',

View File

@ -30,6 +30,9 @@ class EmailTemplateDefaults
case 'email_template_quote':
return self::emailQuoteTemplate();
break;
case 'email_template_credit':
return self::emailCreditTemplate();
break;
case 'email_template_payment':
return self::emailPaymentTemplate();
break;
@ -69,6 +72,9 @@ class EmailTemplateDefaults
case 'email_subject_quote':
return self::emailQuoteSubject();
break;
case 'email_subject_credit':
return self::emailCreditSubject();
break;
case 'email_subject_payment':
return self::emailPaymentSubject();
break;
@ -109,7 +115,11 @@ class EmailTemplateDefaults
public static function emailInvoiceSubject()
{
return ctrans('texts.invoice_subject', ['number'=>'$number', 'account'=>'$company.name']);
//return Parsedown::instance()->line(self::transformText('invoice_subject'));
}
public static function emailCreditSubject()
{
return ctrans('texts.credit_subject', ['number'=>'$number', 'account'=>'$company.name']);
}
public static function emailInvoiceTemplate()
@ -122,14 +132,11 @@ class EmailTemplateDefaults
$invoice_message = '<p>'.self::transformText('invoice_message').'</p><br><br><p>$view_link</p>';
return $invoice_message;
//return $converter->convertToHtml($invoice_message);
}
public static function emailQuoteSubject()
{
return ctrans('texts.quote_subject', ['number'=>'$number', 'account'=>'$company.name']);
//return Parsedown::instance()->line(self::transformText('quote_subject'));
}
public static function emailQuoteTemplate()
@ -158,6 +165,17 @@ class EmailTemplateDefaults
}
public static function emailCreditTemplate()
{
$converter = new CommonMarkConverter([
'html_input' => 'strip',
'allow_unsafe_links' => false,
]);
return $converter->convertToHtml(self::transformText('credit_message'));
}
public static function emailPaymentPartialTemplate()
{
$converter = new CommonMarkConverter([

View File

@ -17,6 +17,7 @@ use App\Events\Misc\InvitationWasViewed;
use App\Events\Quote\QuoteWasViewed;
use App\Http\Controllers\Controller;
use App\Models\InvoiceInvitation;
use App\Models\RecurringInvoiceInvitation;
use App\Utils\Ninja;
use App\Utils\Traits\MakesDates;
use App\Utils\Traits\MakesHash;

View File

@ -13,6 +13,7 @@ namespace App\Http\Requests\Invoice;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Invoice\UniqueInvoiceNumberRule;
use App\Http\ValidationRules\Project\ValidProjectForClient;
use App\Models\ClientContact;
use App\Models\Invoice;
use App\Utils\Traits\CleanLineItems;
@ -47,12 +48,14 @@ class StoreInvoiceRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
$rules['client_id'] = 'bail|required|exists:clients,id,company_id,'.auth()->user()->company()->id;
$rules['invitations.*.client_contact_id'] = 'distinct';
$rules['number'] = new UniqueInvoiceNumberRule($this->all());
$rules['project_id'] = ['bail', 'sometimes', new ValidProjectForClient($this->all())];
return $rules;
}
@ -68,6 +71,10 @@ class StoreInvoiceRequest extends Request
$input['client_id'] = $this->decodePrimaryKey($input['client_id']);
}
if (array_key_exists('project_id', $input) && is_string($input['project_id'])) {
$input['project_id'] = $this->decodePrimaryKey($input['project_id']);
}
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}

View File

@ -33,7 +33,8 @@ class StoreProjectRequest extends Request
{
$rules = [];
$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId();
//$rules['name'] ='required|unique:projects,name,null,null,company_id,'.auth()->user()->companyId();
$rules['name'] = 'required';
$rules['client_id'] = 'required|exists:clients,id,company_id,'.auth()->user()->company()->id;
return $rules;

View File

@ -12,6 +12,7 @@
namespace App\Http\Requests\RecurringInvoice;
use App\Http\Requests\Request;
use App\Http\ValidationRules\Recurring\UniqueRecurringInvoiceNumberRule;
use App\Models\Client;
use App\Models\RecurringInvoice;
use App\Utils\Traits\CleanLineItems;
@ -52,6 +53,8 @@ class StoreRecurringInvoiceRequest extends Request
$rules['frequency_id'] = 'required|integer';
$rules['number'] = new UniqueRecurringInvoiceNumberRule($this->all());
return $rules;
}

View File

@ -48,6 +48,10 @@ class UpdateRecurringInvoiceRequest extends Request
$rules['documents'] = 'file|mimes:png,ai,svg,jpeg,tiff,pdf,gif,psd,txt,doc,xls,ppt,xlsx,docx,pptx|max:20000';
}
if ($this->input('number')) {
$rules['number'] = 'unique:recurring_invoices,number,'.$this->id.',id,company_id,'.$this->recurring_invoice->company_id;
}
return $rules;
}

View File

@ -48,10 +48,13 @@ class StoreVendorRequest extends Request
protected function prepareForValidation()
{
// $input = $this->all();
$input = $this->all();
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
// $this->replace($input);
$this->replace($input);
}
public function messages()

View File

@ -69,6 +69,10 @@ class UpdateVendorRequest extends Request
{
$input = $this->all();
if (array_key_exists('assigned_user_id', $input) && is_string($input['assigned_user_id'])) {
$input['assigned_user_id'] = $this->decodePrimaryKey($input['assigned_user_id']);
}
$this->replace($input);
}
}

View File

@ -0,0 +1,55 @@
<?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\Project;
use App\Models\Project;
use App\Utils\Traits\MakesHash;
use Illuminate\Contracts\Validation\Rule;
/**
* Class ValidProjectForClient.
*/
class ValidProjectForClient implements Rule
{
use MakesHash;
public $input;
public function __construct($input)
{
$this->input = $input;
}
/**
* @param string $attribute
* @param mixed $value
* @return bool
*/
public function passes($attribute, $value)
{
if(is_string($this->input['project_id']))
$this->input['project_id'] = $this->decodePrimaryKey($this->input['project_id']);
$project = Project::findOrFail($this->input['project_id']);
return $project->client_id == $this->input['client_id'];
}
/**
* @return string
*/
public function message()
{
return "Project client does not match entity client";
}
}

View File

@ -0,0 +1,73 @@
<?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\Recurring;
use App\Libraries\MultiDB;
use App\Models\Invoice;
use App\Models\RecurringInvoice;
use App\Models\User;
use Illuminate\Contracts\Validation\Rule;
/**
* Class UniqueRecurringInvoiceNumberRule.
*/
class UniqueRecurringInvoiceNumberRule 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->checkIfInvoiceNumberUnique(); //if it exists, return false!
}
/**
* @return string
*/
public function message()
{
return "Recurring Invoice number {$this->input['number']} already taken";
}
/**
* @param $email
*
* //off,when_sent,when_paid
*
* @return bool
*/
private function checkIfInvoiceNumberUnique() : bool
{
if(empty($this->input['number']))
return true;
$invoice = RecurringInvoice::where('client_id', $this->input['client_id'])
->where('number', $this->input['number'])
->withTrashed()
->exists();
if ($invoice) {
return false;
}
return true;
}
}

View File

@ -50,7 +50,7 @@ class RecurringInvoiceInvitation extends BaseModel
*/
public function contact()
{
return $this->belongsTo(ClientContact::class)->withTrashed();
return $this->belongsTo(ClientContact::class, 'client_contact_id', 'id')->withTrashed();
}
/**

View File

@ -57,6 +57,7 @@ class VendorRepository extends BaseRepository
*/
public function save(array $data, Vendor $vendor) : ?Vendor
{
$vendor->fill($data);
$vendor->save();

View File

@ -28,7 +28,7 @@ class CompanyLedgerTransformer extends EntityTransformer
*/
public function transform(CompanyLedger $company_ledger)
{
$entity_name = lcfirst(class_basename($company_ledger->company_ledgerable_type)).'_id';
$entity_name = lcfirst(rtrim(class_basename($company_ledger->company_ledgerable_type), 's')).'_id';
return [
$entity_name => (string) $this->encodePrimaryKey($company_ledger->company_ledgerable_id),

View File

@ -104,6 +104,10 @@ trait AppSetup
'subject' => EmailTemplateDefaults::emailStatementSubject(),
'body' => EmailTemplateDefaults::emailStatementTemplate(),
],
'credit' => [
'subject' => EmailTemplateDefaults::emailCreditSubject(),
'body' => EmailTemplateDefaults::emailCreditTemplate(),
],
];
Cache::forever($name, $data);

View File

@ -11,6 +11,8 @@
namespace App\Utils\Traits;
use Illuminate\Support\Str;
/**
* Class Inviteable.
*/
@ -42,7 +44,9 @@ trait Inviteable
public function getLink() :string
{
$entity_type = strtolower(class_basename($this->entityType()));
//$entity_type = strtolower(class_basename($this->entityType()));
$entity_type = Str::snake(class_basename($this->entityType()));
//$this->with('company','contact',$this->entity_type);
//$this->with('company');

View File

@ -3283,5 +3283,7 @@ return [
'saved_at' => 'Saved at :time',
'credit_payment' => 'Credit applied to Invoice :invoice_number',
'credit_subject' => 'New credit :number from :account',
'credit_message' => 'To view your credit for :amount, click the link below.',
];

View File

@ -31,7 +31,7 @@ Route::group(['middleware' => ['auth:contact', 'locale'], 'prefix' => 'client',
Route::get('invoices/{invoice_invitation}', 'ClientPortal\InvoiceController@show')->name('invoice.show_invitation');
Route::get('recurring_invoices', 'ClientPortal\RecurringInvoiceController@index')->name('recurring_invoices.index')->middleware('portal_enabled');
Route::get('recurring_invoices/{recurring_invoice}', 'ClientPortal\RecurringInvoiceController@show')->name('recurring_invoices.show');
Route::get('recurring_invoices/{recurring_invoice}', 'ClientPortal\RecurringInvoiceController@show')->name('recurring_invoice.show');
Route::get('recurring_invoices/{recurring_invoice}/request_cancellation', 'ClientPortal\RecurringInvoiceController@requestCancellation')->name('recurring_invoices.request_cancellation');
Route::post('payments/process', 'ClientPortal\PaymentController@process')->name('payments.process');

View File

@ -99,6 +99,8 @@ class ShopInvoiceTest extends TestCase
$this->company->enable_shop_api = true;
$this->company->save();
Product::truncate();
$product = Product::factory()->create([
'user_id' => $this->user->id,
'company_id' => $this->company->id,