Merge remote-tracking branch 'upstream/master'

This commit is contained in:
rafael.sisweb 2015-11-17 10:54:44 -05:00
commit a8bc3f0663
14 changed files with 109 additions and 55 deletions

View File

@ -45,7 +45,7 @@ class SendReminders extends Command
foreach ($invoices as $invoice) { foreach ($invoices as $invoice) {
if ($reminder = $invoice->getReminder()) { if ($reminder = $invoice->getReminder()) {
$this->info('Send to' . $invoice->id); $this->info('Send to ' . $invoice->id);
$this->mailer->sendInvoice($invoice, $reminder); $this->mailer->sendInvoice($invoice, $reminder);
} }
} }

View File

@ -1,7 +1,6 @@
<?php namespace App\Http\Controllers; <?php namespace App\Http\Controllers;
use Auth; use Auth;
use Event;
use File; use File;
use Image; use Image;
use Input; use Input;
@ -227,6 +226,17 @@ class AccountController extends BaseController
private function showCompanyDetails() private function showCompanyDetails()
{ {
// check that logo is less than the max file size
$account = Auth::user()->account;
if ($account->hasLogo()) {
$filename = $account->getLogoPath();
$bytes = File::size($filename);
if ($bytes > MAX_LOGO_FILE_SIZE * 1000) {
$bytes /= 1000;
Session::flash('warning', trans('texts.logo_too_large', ['size' => round($bytes) . 'KB']));
}
}
$data = [ $data = [
'account' => Account::with('users')->findOrFail(Auth::user()->account_id), 'account' => Account::with('users')->findOrFail(Auth::user()->account_id),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
@ -842,7 +852,7 @@ class AccountController extends BaseController
{ {
$rules = array( $rules = array(
'name' => 'required', 'name' => 'required',
'logo' => 'sometimes|max:200|mimes:jpeg,gif,png', 'logo' => 'sometimes|max:' . MAX_LOGO_FILE_SIZE . '|mimes:jpeg,gif,png',
); );
$validator = Validator::make(Input::all(), $rules); $validator = Validator::make(Input::all(), $rules);
@ -905,7 +915,7 @@ class AccountController extends BaseController
} }
} }
Event::fire(new UserSettingsChanged()); event(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS); return Redirect::to('settings/' . ACCOUNT_COMPANY_DETAILS);
@ -940,7 +950,7 @@ class AccountController extends BaseController
$user->save(); $user->save();
Event::fire(new UserSettingsChanged()); event(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_USER_DETAILS); return Redirect::to('settings/' . ACCOUNT_USER_DETAILS);
} }
@ -957,7 +967,7 @@ class AccountController extends BaseController
$account->military_time = Input::get('military_time') ? true : false; $account->military_time = Input::get('military_time') ? true : false;
$account->save(); $account->save();
Event::fire(new UserSettingsChanged()); event(new UserSettingsChanged());
Session::flash('message', trans('texts.updated_settings')); Session::flash('message', trans('texts.updated_settings'));
return Redirect::to('settings/' . ACCOUNT_LOCALIZATION); return Redirect::to('settings/' . ACCOUNT_LOCALIZATION);

View File

@ -110,6 +110,7 @@ class DashboardController extends BaseController
->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id') ->leftJoin('invoices', 'invoices.id', '=', 'payments.invoice_id')
->where('payments.account_id', '=', Auth::user()->account_id) ->where('payments.account_id', '=', Auth::user()->account_id)
->where('payments.deleted_at', '=', null) ->where('payments.deleted_at', '=', null)
->where('invoices.deleted_at', '=', null)
->where('clients.deleted_at', '=', null) ->where('clients.deleted_at', '=', null)
->where('contacts.deleted_at', '=', null) ->where('contacts.deleted_at', '=', null)
->where('contacts.is_primary', '=', true) ->where('contacts.is_primary', '=', true)

View File

@ -4,6 +4,7 @@ use Auth;
use Utils; use Utils;
use Response; use Response;
use Input; use Input;
use Validator;
use App\Models\Invoice; use App\Models\Invoice;
use App\Models\Client; use App\Models\Client;
use App\Models\Contact; use App\Models\Contact;
@ -102,12 +103,18 @@ class InvoiceApiController extends Controller
} }
if (isset($data['email'])) { if (isset($data['email'])) {
$client = Client::scope()->whereHas('contacts', function($query) use ($data) { $email = $data['email'];
$query->where('email', '=', $data['email']); $client = Client::scope()->whereHas('contacts', function($query) use ($email) {
$query->where('email', '=', $email);
})->first(); })->first();
if (!$client) { if (!$client) {
$clientData = ['contact' => ['email' => $data['email']]]; $validator = Validator::make(['email'=>$email], ['email' => 'email']);
if ($validator->fails()) {
return $validator->message();
}
$clientData = ['contact' => ['email' => $email]];
foreach (['name', 'private_notes'] as $field) { foreach (['name', 'private_notes'] as $field) {
if (isset($data[$field])) { if (isset($data[$field])) {
$clientData[$field] = $data[$field]; $clientData[$field] = $data[$field];
@ -118,10 +125,8 @@ class InvoiceApiController extends Controller
$clientData[$field] = $data[$field]; $clientData[$field] = $data[$field];
} }
} }
$error = $this->clientRepo->getErrors($clientData);
if (!$error) { $client = $this->clientRepo->save($clientData);
$client = $this->clientRepo->save($clientData);
}
} }
} else if (isset($data['client_id'])) { } else if (isset($data['client_id'])) {
$client = Client::scope($data['client_id'])->first(); $client = Client::scope($data['client_id'])->first();

View File

@ -203,7 +203,10 @@ class InvoiceController extends BaseController
public function edit($publicId, $clone = false) public function edit($publicId, $clone = false)
{ {
$account = Auth::user()->account; $account = Auth::user()->account;
$invoice = Invoice::scope($publicId)->withTrashed()->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')->firstOrFail(); $invoice = Invoice::scope($publicId)
->with('invitations', 'account.country', 'client.contacts', 'client.country', 'invoice_items')
->withTrashed()
->firstOrFail();
$entityType = $invoice->getEntityType(); $entityType = $invoice->getEntityType();
$contactIds = DB::table('invitations') $contactIds = DB::table('invitations')
@ -270,6 +273,7 @@ class InvoiceController extends BaseController
$lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null; $lastSent = ($invoice->is_recurring && $invoice->last_sent_date) ? $invoice->recurring_invoices->last() : null;
$data = array( $data = array(
'clients' => Client::scope()->withTrashed()->with('contacts', 'country')->whereId($invoice->client_id)->get(),
'entityType' => $entityType, 'entityType' => $entityType,
'showBreadcrumbs' => $clone, 'showBreadcrumbs' => $clone,
'invoice' => $invoice, 'invoice' => $invoice,
@ -327,6 +331,7 @@ class InvoiceController extends BaseController
$invoice->public_id = 0; $invoice->public_id = 0;
$data = [ $data = [
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
'entityType' => $invoice->getEntityType(), 'entityType' => $invoice->getEntityType(),
'invoice' => $invoice, 'invoice' => $invoice,
'method' => 'POST', 'method' => 'POST',
@ -361,7 +366,6 @@ class InvoiceController extends BaseController
'account' => Auth::user()->account->load('country'), 'account' => Auth::user()->account->load('country'),
'products' => Product::scope()->with('default_tax_rate')->orderBy('id')->get(), 'products' => Product::scope()->with('default_tax_rate')->orderBy('id')->get(),
'countries' => Cache::get('countries'), 'countries' => Cache::get('countries'),
'clients' => Client::scope()->with('contacts', 'country')->orderBy('name')->get(),
'taxRates' => TaxRate::scope()->orderBy('name')->get(), 'taxRates' => TaxRate::scope()->orderBy('name')->get(),
'currencies' => Cache::get('currencies'), 'currencies' => Cache::get('currencies'),
'languages' => Cache::get('languages'), 'languages' => Cache::get('languages'),

View File

@ -1,6 +1,5 @@
<?php <?php
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
| Application Routes | Application Routes
@ -334,6 +333,7 @@ if (!defined('CONTACT_EMAIL')) {
define('MAX_NUM_USERS', 20); define('MAX_NUM_USERS', 20);
define('MAX_SUBDOMAIN_LENGTH', 30); define('MAX_SUBDOMAIN_LENGTH', 30);
define('MAX_IFRAME_URL_LENGTH', 250); define('MAX_IFRAME_URL_LENGTH', 250);
define('MAX_LOGO_FILE_SIZE', 200); // KB
define('DEFAULT_FONT_SIZE', 9); define('DEFAULT_FONT_SIZE', 9);
define('DEFAULT_SEND_RECURRING_HOUR', 8); define('DEFAULT_SEND_RECURRING_HOUR', 8);

View File

@ -207,12 +207,12 @@ class Utils
$data = [ $data = [
'context' => $context, 'context' => $context,
'user_id' => Auth::check() ? Auth::user()->id : 0, 'user_id' => Auth::check() ? Auth::user()->id : 0,
'account_id' => Auth::check() ? Auth::user()->account_id : 0,
'user_name' => Auth::check() ? Auth::user()->getDisplayName() : '', 'user_name' => Auth::check() ? Auth::user()->getDisplayName() : '',
'url' => Input::get('url', Request::url()), 'url' => Input::get('url', Request::url()),
'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '', 'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '',
'ip' => Request::getClientIp(), 'ip' => Request::getClientIp(),
'count' => Session::get('error_count', 0), 'count' => Session::get('error_count', 0),
//'input' => Input::all()
]; ];
Log::error($error."\n", $data); Log::error($error."\n", $data);

View File

@ -15,6 +15,7 @@ use App\Events\QuoteInvitationWasEmailed;
class Invoice extends EntityModel implements BalanceAffecting class Invoice extends EntityModel implements BalanceAffecting
{ {
use PresentableTrait; use PresentableTrait;
use OwnedByClientTrait;
use SoftDeletes { use SoftDeletes {
SoftDeletes::trashed as parentTrashed; SoftDeletes::trashed as parentTrashed;
} }

View File

@ -0,0 +1,13 @@
<?php namespace App\Models;
trait OwnedByClientTrait
{
public function isClientTrashed()
{
if (!$this->client) {
return false;
}
return $this->client->trashed();
}
}

View File

@ -23,8 +23,10 @@ class ContactMailer extends Mailer
$client = $invoice->client; $client = $invoice->client;
$account = $invoice->account; $account = $invoice->account;
if ($invoice->trashed() || $client->trashed()) { if ($client->trashed()) {
return false; return trans('texts.email_errors.inactive_client');
} elseif ($invoice->trashed()) {
return trans('texts.email_errors.inactive_invoice');
} }
$account->loadLocalizationSettings($client); $account->loadLocalizationSettings($client);
@ -38,7 +40,8 @@ class ContactMailer extends Mailer
} }
foreach ($invoice->invitations as $invitation) { foreach ($invoice->invitations as $invitation) {
if ($this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString)) { $response = $this->sendInvitation($invitation, $invoice, $emailTemplate, $emailSubject, $pdfString);
if ($response === true) {
$sent = true; $sent = true;
} }
} }
@ -53,7 +56,7 @@ class ContactMailer extends Mailer
} }
} }
return $sent ?: trans('texts.email_error'); return $response;
} }
private function sendInvitation($invitation, $invoice, $body, $subject, $pdfString) private function sendInvitation($invitation, $invoice, $body, $subject, $pdfString)
@ -70,12 +73,14 @@ class ContactMailer extends Mailer
} }
} }
if (!$user->email || !$user->confirmed) { if (!$user->email || !$user->registered) {
return false; return trans('texts.email_errors.user_unregistered');
} } elseif (!$user->confirmed) {
return trans('texts.email_errors.user_unconfirmed');
if (!$invitation->contact->email || $invitation->contact->trashed()) { } elseif (!$invitation->contact->email) {
return false; return trans('texts.email_errors.invalid_contact_email');
} elseif ($invitation->contact->trashed()) {
return trans('texts.email_errors.inactive_contact');
} }
$variables = [ $variables = [
@ -108,7 +113,7 @@ class ContactMailer extends Mailer
if ($response === true) { if ($response === true) {
return true; return true;
} else { } else {
return false; return $response;
} }
} }

View File

@ -54,7 +54,7 @@ class ClientRepository extends BaseRepository
$contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts']; $contacts = isset($data['contact']) ? [$data['contact']] : $data['contacts'];
$contactIds = []; $contactIds = [];
foreach ($data['contacts'] as $contact) { foreach ($contacts as $contact) {
$contact = $client->addContact($contact, $first); $contact = $client->addContact($contact, $first);
$contactIds[] = $contact->public_id; $contactIds[] = $contact->public_id;
$first = false; $first = false;

View File

@ -918,4 +918,14 @@ return array(
'country' => 'Country', 'country' => 'Country',
'include' => 'Include', 'include' => 'Include',
'logo_too_large' => 'Your logo is :size, for better performance we suggest uploading an image file less than 200KB',
'email_errors' => [
'inactive_client' => 'Emails can not be sent to inactive clients',
'inactive_contact' => 'Emails can not be sent to inactive contacts',
'inactive_invoice' => 'Emails can not be sent to inactive invoices',
'user_unregistered' => 'Please register your account to send emails',
'user_unconfirmed' => 'Please confirm your account to send emails',
'invalid_contact_email' => 'Invalid contact email',
]
); );

View File

@ -22,7 +22,7 @@
->addClass('warn-on-exit') ->addClass('warn-on-exit')
->autocomplete('on') ->autocomplete('on')
->rules([ ->rules([
'name' => 'required' 'name' => 'required'
]) !!} ]) !!}
{{ Former::populate($account) }} {{ Former::populate($account) }}
@ -38,23 +38,28 @@
</div> </div>
<div class="panel-body form-padding-right"> <div class="panel-body form-padding-right">
{!! Former::text('name') !!} {!! Former::text('name') !!}
{!! Former::text('id_number') !!} {!! Former::text('id_number') !!}
{!! Former::text('vat_number') !!} {!! Former::text('vat_number') !!}
{!! Former::text('work_email') !!} {!! Former::text('work_email') !!}
{!! Former::text('work_phone') !!} {!! Former::text('work_phone') !!}
{!! Former::file('logo')->max(2, 'MB')->accept('image')->inlineHelp(trans('texts.logo_help')) !!} {!! Former::file('logo')->max(2, 'MB')->accept('image')->inlineHelp(trans('texts.logo_help')) !!}
@if ($account->hasLogo()) @if ($account->hasLogo())
<center> <div class="form-group">
{!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!} &nbsp; <div class="col-lg-4 col-sm-4"></div>
<a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a> <div class="col-lg-8 col-sm-8">
</center><br/> <a href="/{{ $account->getLogoPath().'?no_cache='.time() }}" target="_blank">
@endif {!! HTML::image($account->getLogoPath().'?no_cache='.time(), 'Logo', ['width' => 200]) !!}
</a> &nbsp;
<a href="#" onclick="deleteLogo()">{{ trans('texts.remove_logo') }}</a>
</div>
</div>
@endif
{!! Former::select('size_id')->addOption('','')->fromQuery($sizes, 'name', 'id') !!} {!! Former::select('size_id')->addOption('','')->fromQuery($sizes, 'name', 'id') !!}
{!! Former::select('industry_id')->addOption('','')->fromQuery($industries, 'name', 'id') !!} {!! Former::select('industry_id')->addOption('','')->fromQuery($industries, 'name', 'id') !!}
</div> </div>
</div> </div>

View File

@ -392,19 +392,18 @@
{!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!} {!! Button::primary(trans('texts.download_pdf'))->withAttributes(array('onclick' => 'onDownloadClick()'))->appendIcon(Icon::create('download-alt')) !!}
@if (!$invoice->trashed()) @if ($invoice->isClientTrashed())
<!-- do nothing -->
@elseif ($invoice->trashed())
{!! Button::success(trans('texts.restore'))->withAttributes(['onclick' => 'submitBulkAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!}
@elseif (!$invoice->trashed())
{!! Button::success(trans("texts.save_{$entityType}"))->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))->appendIcon(Icon::create('floppy-disk')) !!} {!! Button::success(trans("texts.save_{$entityType}"))->withAttributes(array('id' => 'saveButton', 'onclick' => 'onSaveClick()'))->appendIcon(Icon::create('floppy-disk')) !!}
{!! Button::info(trans("texts.email_{$entityType}"))->withAttributes(array('id' => 'emailButton', 'onclick' => 'onEmailClick()'))->appendIcon(Icon::create('send')) !!} {!! Button::info(trans("texts.email_{$entityType}"))->withAttributes(array('id' => 'emailButton', 'onclick' => 'onEmailClick()'))->appendIcon(Icon::create('send')) !!}
@if ($invoice->id) @if ($invoice->id)
{!! DropdownButton::normal(trans('texts.more_actions')) {!! DropdownButton::normal(trans('texts.more_actions'))
->withContents($actions) ->withContents($actions)
->dropup() !!} ->dropup() !!}
@endif @endif
@elseif ($invoice->trashed())
{!! Button::success(trans('texts.restore'))->withAttributes(['onclick' => 'submitBulkAction("restore")'])->appendIcon(Icon::create('cloud-download')) !!}
@endif @endif
</div> </div>
@ -1007,16 +1006,17 @@
} }
function isEmailValid() { function isEmailValid() {
var isValid = false; var isValid = true;
var sendTo = false; var sendTo = false;
var client = model.invoice().client(); var client = model.invoice().client();
for (var i=0; i<client.contacts().length; i++) { for (var i=0; i<client.contacts().length; i++) {
var contact = client.contacts()[i]; var contact = client.contacts()[i];
if ( ! contact.send_invoice()) {
continue;
}
if (isValidEmailAddress(contact.email())) { if (isValidEmailAddress(contact.email())) {
isValid = true; isValid = true;
if (contact.send_invoice() || client.contacts().length == 1) { sendTo = true;
sendTo = true;
}
} else { } else {
isValid = false; isValid = false;
break; break;