2017-01-30 20:40:43 +01:00
|
|
|
<?php
|
2015-03-16 22:45:25 +01:00
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
namespace App\Models;
|
|
|
|
|
2017-04-07 12:46:00 +02:00
|
|
|
use App\Events\InvoiceWasCreated;
|
|
|
|
use App\Events\InvoiceWasUpdated;
|
|
|
|
use App\Events\QuoteWasCreated;
|
|
|
|
use App\Events\QuoteWasUpdated;
|
2017-01-30 20:40:43 +01:00
|
|
|
use App\Events\InvoiceInvitationWasEmailed;
|
2015-10-29 15:42:05 +01:00
|
|
|
use App\Events\QuoteInvitationWasEmailed;
|
2017-01-02 19:47:40 +01:00
|
|
|
use App\Libraries\CurlUtils;
|
2017-02-05 08:53:49 +01:00
|
|
|
use App\Models\Activity;
|
2017-03-15 16:09:23 +01:00
|
|
|
use App\Models\Traits\ChargesFees;
|
2017-01-30 20:40:43 +01:00
|
|
|
use DateTime;
|
|
|
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
|
|
|
use Laracasts\Presenter\PresentableTrait;
|
|
|
|
use Utils;
|
2017-03-13 13:54:15 +01:00
|
|
|
use Carbon;
|
2015-10-28 20:22:07 +01:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
2017-01-30 20:40:43 +01:00
|
|
|
* Class Invoice.
|
2016-07-03 18:11:58 +02:00
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
class Invoice extends EntityModel implements BalanceAffecting
|
2015-03-16 22:45:25 +01:00
|
|
|
{
|
2015-11-08 09:43:49 +01:00
|
|
|
use PresentableTrait;
|
2015-11-16 20:21:48 +01:00
|
|
|
use OwnedByClientTrait;
|
2017-03-15 16:09:23 +01:00
|
|
|
use ChargesFees;
|
2015-10-25 08:13:06 +01:00
|
|
|
use SoftDeletes {
|
|
|
|
SoftDeletes::trashed as parentTrashed;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @var string
|
|
|
|
*/
|
2015-11-08 09:43:49 +01:00
|
|
|
protected $presenter = 'App\Ninja\Presenters\InvoicePresenter';
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2015-03-31 11:38:24 +02:00
|
|
|
protected $dates = ['deleted_at'];
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2016-03-31 11:29:01 +02:00
|
|
|
protected $fillable = [
|
|
|
|
'tax_name1',
|
|
|
|
'tax_rate1',
|
|
|
|
'tax_name2',
|
2016-05-29 14:34:44 +02:00
|
|
|
'tax_rate2',
|
2016-03-31 11:29:01 +02:00
|
|
|
];
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2015-04-21 22:09:45 +02:00
|
|
|
protected $casts = [
|
|
|
|
'is_recurring' => 'boolean',
|
2015-06-10 10:34:20 +02:00
|
|
|
'has_tasks' => 'boolean',
|
2016-05-09 22:29:02 +02:00
|
|
|
'client_enable_auto_bill' => 'boolean',
|
2016-01-10 11:25:05 +01:00
|
|
|
'has_expenses' => 'boolean',
|
2015-04-21 22:09:45 +02:00
|
|
|
];
|
|
|
|
|
2015-11-24 20:45:38 +01:00
|
|
|
// used for custom invoice numbers
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
2015-10-22 20:48:12 +02:00
|
|
|
public static $patternFields = [
|
|
|
|
'counter',
|
2017-04-02 19:35:10 +02:00
|
|
|
'clientCounter',
|
2017-03-29 10:55:54 +02:00
|
|
|
'clientIdNumber',
|
2017-03-29 10:46:52 +02:00
|
|
|
'clientCustom1',
|
|
|
|
'clientCustom2',
|
2015-10-22 20:48:12 +02:00
|
|
|
'userId',
|
|
|
|
'year',
|
|
|
|
'date:',
|
|
|
|
];
|
2015-10-28 20:22:07 +01:00
|
|
|
|
2017-04-04 15:57:33 +02:00
|
|
|
/**
|
|
|
|
* @var array
|
|
|
|
*/
|
|
|
|
public static $requestFields = [
|
|
|
|
'invoice_number',
|
|
|
|
'invoice_date',
|
|
|
|
'due_date',
|
|
|
|
'po_number',
|
|
|
|
'discount',
|
|
|
|
'partial',
|
|
|
|
];
|
|
|
|
|
2016-12-26 14:38:53 +01:00
|
|
|
public static $statusClasses = [
|
|
|
|
INVOICE_STATUS_SENT => 'info',
|
|
|
|
INVOICE_STATUS_VIEWED => 'warning',
|
|
|
|
INVOICE_STATUS_APPROVED => 'success',
|
|
|
|
INVOICE_STATUS_PARTIAL => 'primary',
|
|
|
|
INVOICE_STATUS_PAID => 'success',
|
|
|
|
];
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2015-11-24 20:45:38 +01:00
|
|
|
public static function getImportColumns()
|
|
|
|
{
|
|
|
|
return [
|
2017-04-13 09:25:49 +02:00
|
|
|
'name',
|
|
|
|
'invoice_number',
|
|
|
|
'po_number',
|
|
|
|
'invoice_date',
|
|
|
|
'due_date',
|
|
|
|
'amount',
|
|
|
|
'paid',
|
|
|
|
'notes',
|
|
|
|
'terms',
|
|
|
|
'product',
|
|
|
|
'quantity',
|
2015-11-24 20:45:38 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2015-11-24 20:45:38 +01:00
|
|
|
public static function getImportMap()
|
|
|
|
{
|
|
|
|
return [
|
2015-11-25 10:35:24 +01:00
|
|
|
'number^po' => 'invoice_number',
|
|
|
|
'amount' => 'amount',
|
2017-01-15 19:38:36 +01:00
|
|
|
'client|organization' => 'name',
|
2015-11-25 10:35:24 +01:00
|
|
|
'paid^date' => 'paid',
|
2017-01-15 19:38:36 +01:00
|
|
|
'invoice date|create date' => 'invoice_date',
|
|
|
|
'po number' => 'po_number',
|
|
|
|
'due date' => 'due_date',
|
2015-11-24 20:45:38 +01:00
|
|
|
'terms' => 'terms',
|
|
|
|
'notes' => 'notes',
|
2017-04-13 09:25:49 +02:00
|
|
|
'product|item' => 'product',
|
|
|
|
'quantity|qty' => 'quantity',
|
2015-11-24 20:45:38 +01:00
|
|
|
];
|
|
|
|
}
|
2016-07-03 18:11:58 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
public function getRoute()
|
|
|
|
{
|
2016-08-23 22:20:03 +02:00
|
|
|
if ($this->is_recurring) {
|
|
|
|
$entityType = 'recurring_invoice';
|
|
|
|
} else {
|
|
|
|
$entityType = $this->getEntityType();
|
|
|
|
}
|
|
|
|
|
2015-10-28 20:22:07 +01:00
|
|
|
return "/{$entityType}s/{$this->public_id}/edit";
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
public function getDisplayName()
|
|
|
|
{
|
2016-07-21 14:35:23 +02:00
|
|
|
return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number;
|
2015-10-28 20:22:07 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-11-02 07:51:57 +01:00
|
|
|
public function affectsBalance()
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
return $this->isType(INVOICE_TYPE_STANDARD) && ! $this->is_recurring && $this->is_public;
|
2015-11-02 07:51:57 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return float|int
|
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
public function getAdjustment()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->affectsBalance()) {
|
2015-10-28 20:22:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->getRawAdjustment();
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return float
|
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
private function getRawAdjustment()
|
|
|
|
{
|
2016-12-04 22:26:38 +01:00
|
|
|
// if we've just made the invoice public then apply the full amount
|
|
|
|
if ($this->is_public && ! $this->getOriginal('is_public')) {
|
|
|
|
return $this->amount;
|
|
|
|
}
|
|
|
|
|
2015-10-28 20:22:07 +01:00
|
|
|
return floatval($this->amount) - floatval($this->getOriginal('amount'));
|
|
|
|
}
|
|
|
|
|
2017-01-06 11:36:46 +01:00
|
|
|
public function isChanged()
|
|
|
|
{
|
2017-01-08 11:23:02 +01:00
|
|
|
if (Utils::isNinja()) {
|
|
|
|
if ($this->getRawAdjustment() != 0) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-08 20:45:42 +01:00
|
|
|
|
2017-01-08 11:23:02 +01:00
|
|
|
foreach ([
|
|
|
|
'invoice_number',
|
|
|
|
'po_number',
|
|
|
|
'invoice_date',
|
|
|
|
'due_date',
|
|
|
|
'terms',
|
|
|
|
'public_notes',
|
|
|
|
'invoice_footer',
|
|
|
|
'partial',
|
|
|
|
] as $field) {
|
|
|
|
if ($this->$field != $this->getOriginal($field)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
$dirty = $this->getDirty();
|
2017-01-06 11:36:46 +01:00
|
|
|
|
2017-01-08 11:23:02 +01:00
|
|
|
unset($dirty['invoice_status_id']);
|
|
|
|
unset($dirty['client_enable_auto_bill']);
|
|
|
|
unset($dirty['quote_invoice_id']);
|
2017-01-06 11:36:46 +01:00
|
|
|
|
2017-01-08 11:23:02 +01:00
|
|
|
return count($dirty) > 0;
|
|
|
|
}
|
2017-01-06 11:36:46 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param bool $calculate
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return int|mixed
|
|
|
|
*/
|
2016-02-24 21:58:42 +01:00
|
|
|
public function getAmountPaid($calculate = false)
|
2015-10-28 20:22:07 +01:00
|
|
|
{
|
2016-05-26 16:56:54 +02:00
|
|
|
if ($this->isType(INVOICE_TYPE_QUOTE) || $this->is_recurring) {
|
2015-10-28 20:22:07 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-24 21:58:42 +01:00
|
|
|
if ($calculate) {
|
|
|
|
$amount = 0;
|
|
|
|
foreach ($this->payments as $payment) {
|
2016-08-28 12:20:16 +02:00
|
|
|
if ($payment->payment_status_id == PAYMENT_STATUS_VOIDED || $payment->payment_status_id == PAYMENT_STATUS_FAILED) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$amount += $payment->getCompletedAmount();
|
2016-02-24 21:58:42 +01:00
|
|
|
}
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2016-02-24 21:58:42 +01:00
|
|
|
return $amount;
|
|
|
|
} else {
|
2017-01-30 20:40:43 +01:00
|
|
|
return $this->amount - $this->balance;
|
2016-02-24 21:58:42 +01:00
|
|
|
}
|
2015-10-28 20:22:07 +01:00
|
|
|
}
|
2016-01-10 11:25:05 +01:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-10-25 08:13:06 +01:00
|
|
|
public function trashed()
|
2015-10-23 13:55:18 +02:00
|
|
|
{
|
2015-10-25 08:13:06 +01:00
|
|
|
if ($this->client && $this->client->trashed()) {
|
2015-10-23 13:55:18 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-10-25 08:13:06 +01:00
|
|
|
return self::parentTrashed();
|
2015-10-23 13:55:18 +02:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function account()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->belongsTo('App\Models\Account');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function user()
|
|
|
|
{
|
2015-09-25 11:57:40 +02:00
|
|
|
return $this->belongsTo('App\Models\User')->withTrashed();
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function client()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->belongsTo('App\Models\Client')->withTrashed();
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function invoice_items()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->hasMany('App\Models\InvoiceItem')->orderBy('id');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-03-23 03:23:45 +01:00
|
|
|
public function documents()
|
|
|
|
{
|
|
|
|
return $this->hasMany('App\Models\Document')->orderBy('id');
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function invoice_status()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->belongsTo('App\Models\InvoiceStatus');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function invoice_design()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->belongsTo('App\Models\InvoiceDesign');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function payments()
|
2016-01-05 13:51:27 +01:00
|
|
|
{
|
2016-02-07 12:01:39 +01:00
|
|
|
return $this->hasMany('App\Models\Payment', 'invoice_id', 'id');
|
2016-01-05 13:51:27 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
|
|
*/
|
2015-07-07 22:08:16 +02:00
|
|
|
public function recurring_invoice()
|
|
|
|
{
|
|
|
|
return $this->belongsTo('App\Models\Invoice');
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
|
|
|
*/
|
2015-08-10 17:48:41 +02:00
|
|
|
public function recurring_invoices()
|
|
|
|
{
|
|
|
|
return $this->hasMany('App\Models\Invoice', 'recurring_invoice_id');
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
|
|
|
*/
|
2016-02-25 10:25:07 +01:00
|
|
|
public function frequency()
|
|
|
|
{
|
|
|
|
return $this->belongsTo('App\Models\Frequency');
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function invitations()
|
|
|
|
{
|
2015-03-31 11:38:24 +02:00
|
|
|
return $this->hasMany('App\Models\Invitation')->orderBy('invitations.contact_id');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-02-17 09:10:33 +01:00
|
|
|
public function expenses()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
return $this->hasMany('App\Models\Expense', 'invoice_id', 'id')->withTrashed();
|
2016-02-17 09:10:33 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $query
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-05-05 18:25:26 +02:00
|
|
|
public function scopeInvoices($query)
|
|
|
|
{
|
2016-05-26 16:56:54 +02:00
|
|
|
return $query->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
2016-05-05 18:25:26 +02:00
|
|
|
->where('is_recurring', '=', false);
|
|
|
|
}
|
|
|
|
|
2016-11-27 10:46:32 +01:00
|
|
|
/**
|
|
|
|
* @param $query
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-11-27 10:46:32 +01:00
|
|
|
* @return mixed
|
|
|
|
*/
|
|
|
|
public function scopeRecurring($query)
|
|
|
|
{
|
|
|
|
return $query->where('invoice_type_id', '=', INVOICE_TYPE_STANDARD)
|
|
|
|
->where('is_recurring', '=', true);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $query
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-06-06 21:17:16 +02:00
|
|
|
public function scopeQuotes($query)
|
|
|
|
{
|
|
|
|
return $query->where('invoice_type_id', '=', INVOICE_TYPE_QUOTE)
|
|
|
|
->where('is_recurring', '=', false);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $query
|
|
|
|
* @param $typeId
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2016-05-26 16:56:54 +02:00
|
|
|
public function scopeInvoiceType($query, $typeId)
|
|
|
|
{
|
|
|
|
return $query->where('invoice_type_id', '=', $typeId);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $typeId
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function isType($typeId)
|
|
|
|
{
|
2016-05-26 16:56:54 +02:00
|
|
|
return $this->invoice_type_id == $typeId;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function isQuote()
|
|
|
|
{
|
2016-05-29 14:34:44 +02:00
|
|
|
return $this->isType(INVOICE_TYPE_QUOTE);
|
|
|
|
}
|
|
|
|
|
2016-08-28 19:52:25 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function isInvoice()
|
|
|
|
{
|
2016-08-28 19:52:25 +02:00
|
|
|
return $this->isType(INVOICE_TYPE_STANDARD) && ! $this->is_recurring;
|
|
|
|
}
|
|
|
|
|
2017-01-13 08:02:22 +01:00
|
|
|
public function markSentIfUnsent()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->isSent()) {
|
2017-01-13 08:02:22 +01:00
|
|
|
$this->markSent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public function markSent()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->isSent()) {
|
2017-01-13 08:02:22 +01:00
|
|
|
$this->invoice_status_id = INVOICE_STATUS_SENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->is_public = true;
|
|
|
|
$this->save();
|
|
|
|
|
|
|
|
$this->markInvitationsSent();
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
2017-01-30 20:49:42 +01:00
|
|
|
* @param bool $notify
|
|
|
|
* @param mixed $reminder
|
2016-07-03 18:11:58 +02:00
|
|
|
*/
|
2017-01-05 11:46:03 +01:00
|
|
|
public function markInvitationsSent($notify = false, $reminder = false)
|
2015-10-29 15:42:05 +01:00
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->relationLoaded('invitations')) {
|
2016-12-04 23:35:08 +01:00
|
|
|
$this->load('invitations');
|
|
|
|
}
|
2016-12-04 22:26:38 +01:00
|
|
|
|
2015-10-29 15:42:05 +01:00
|
|
|
foreach ($this->invitations as $invitation) {
|
2017-01-05 11:46:03 +01:00
|
|
|
$this->markInvitationSent($invitation, false, $notify, $reminder);
|
2015-10-29 15:42:05 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-04 23:35:08 +01:00
|
|
|
public function areInvitationsSent()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->relationLoaded('invitations')) {
|
2016-12-04 23:35:08 +01:00
|
|
|
$this->load('invitations');
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->invitations as $invitation) {
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $invitation->isSent()) {
|
2016-12-04 23:35:08 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $invitation
|
2017-01-30 20:49:42 +01:00
|
|
|
* @param bool $messageId
|
|
|
|
* @param bool $notify
|
|
|
|
* @param mixed $notes
|
2016-07-03 18:11:58 +02:00
|
|
|
*/
|
2017-01-05 11:46:03 +01:00
|
|
|
public function markInvitationSent($invitation, $messageId = false, $notify = true, $notes = false)
|
2015-10-28 20:22:07 +01:00
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->isSent()) {
|
2015-10-28 20:22:07 +01:00
|
|
|
$this->invoice_status_id = INVOICE_STATUS_SENT;
|
|
|
|
$this->save();
|
|
|
|
}
|
2015-10-29 15:42:05 +01:00
|
|
|
|
|
|
|
$invitation->markSent($messageId);
|
|
|
|
|
2016-01-10 11:25:05 +01:00
|
|
|
// if the user marks it as sent rather than acually sending it
|
2015-10-29 15:42:05 +01:00
|
|
|
// then we won't track it in the activity log
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $notify) {
|
2015-10-29 15:42:05 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-26 16:56:54 +02:00
|
|
|
if ($this->isType(INVOICE_TYPE_QUOTE)) {
|
2017-01-05 11:46:03 +01:00
|
|
|
event(new QuoteInvitationWasEmailed($invitation, $notes));
|
2015-10-29 15:42:05 +01:00
|
|
|
} else {
|
2017-01-05 11:46:03 +01:00
|
|
|
event(new InvoiceInvitationWasEmailed($invitation, $notes));
|
2015-10-29 15:42:05 +01:00
|
|
|
}
|
2015-10-28 20:22:07 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function markViewed()
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $this->isViewed()) {
|
2015-10-28 20:22:07 +01:00
|
|
|
$this->invoice_status_id = INVOICE_STATUS_VIEWED;
|
|
|
|
$this->save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param bool $save
|
|
|
|
*/
|
2015-11-18 15:40:50 +01:00
|
|
|
public function updatePaidStatus($save = true)
|
2015-10-28 20:22:07 +01:00
|
|
|
{
|
2015-11-18 23:02:01 +01:00
|
|
|
$statusId = false;
|
2017-03-26 20:31:13 +02:00
|
|
|
if ($this->amount != 0 && $this->balance == 0) {
|
2015-11-18 23:02:01 +01:00
|
|
|
$statusId = INVOICE_STATUS_PAID;
|
|
|
|
} elseif ($this->balance > 0 && $this->balance < $this->amount) {
|
|
|
|
$statusId = INVOICE_STATUS_PARTIAL;
|
|
|
|
} elseif ($this->isPartial() && $this->balance > 0) {
|
|
|
|
$statusId = ($this->balance == $this->amount ? INVOICE_STATUS_SENT : INVOICE_STATUS_PARTIAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($statusId && $statusId != $this->invoice_status_id) {
|
|
|
|
$this->invoice_status_id = $statusId;
|
2015-11-18 15:40:50 +01:00
|
|
|
if ($save) {
|
|
|
|
$this->save();
|
|
|
|
}
|
2015-10-28 20:22:07 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-20 09:41:57 +01:00
|
|
|
public function markApproved()
|
|
|
|
{
|
2016-05-26 16:56:54 +02:00
|
|
|
if ($this->isType(INVOICE_TYPE_QUOTE)) {
|
2016-01-20 09:41:57 +01:00
|
|
|
$this->invoice_status_id = INVOICE_STATUS_APPROVED;
|
|
|
|
$this->save();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $balanceAdjustment
|
|
|
|
* @param int $partial
|
|
|
|
*/
|
2015-10-28 20:22:07 +01:00
|
|
|
public function updateBalances($balanceAdjustment, $partial = 0)
|
|
|
|
{
|
2015-10-30 13:56:25 +01:00
|
|
|
if ($this->is_deleted) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-03-17 12:57:56 +01:00
|
|
|
$balanceAdjustment = floatval($balanceAdjustment);
|
|
|
|
$partial = floatval($partial);
|
|
|
|
|
|
|
|
if (! $balanceAdjustment && $this->partial == $partial) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-10-28 20:22:07 +01:00
|
|
|
$this->balance = $this->balance + $balanceAdjustment;
|
|
|
|
|
|
|
|
if ($this->partial > 0) {
|
|
|
|
$this->partial = $partial;
|
|
|
|
}
|
|
|
|
|
|
|
|
$this->save();
|
2017-03-17 12:57:56 +01:00
|
|
|
|
|
|
|
// mark fees as paid
|
2017-03-26 09:37:32 +02:00
|
|
|
if ($balanceAdjustment != 0 && $this->account->gateway_fee_enabled) {
|
2017-03-17 12:57:56 +01:00
|
|
|
if ($invoiceItem = $this->getGatewayFeeItem()) {
|
|
|
|
$invoiceItem->markFeePaid();
|
|
|
|
}
|
|
|
|
}
|
2015-10-28 20:22:07 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function getName()
|
|
|
|
{
|
2015-08-10 17:48:41 +02:00
|
|
|
return $this->is_recurring ? trans('texts.recurring') : $this->invoice_number;
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-04-22 23:40:21 +02:00
|
|
|
public function getFileName()
|
|
|
|
{
|
|
|
|
$entityType = $this->getEntityType();
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2015-04-22 23:40:21 +02:00
|
|
|
return trans("texts.$entityType") . '_' . $this->invoice_number . '.pdf';
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-05-09 20:25:16 +02:00
|
|
|
public function getPDFPath()
|
|
|
|
{
|
|
|
|
return storage_path() . '/pdfcache/cache-' . $this->id . '.pdf';
|
|
|
|
}
|
|
|
|
|
2016-10-30 10:04:08 +01:00
|
|
|
public function canBePaid()
|
|
|
|
{
|
2017-03-26 20:31:13 +02:00
|
|
|
return floatval($this->balance) != 0 && ! $this->is_deleted && $this->isInvoice();
|
2016-10-30 10:04:08 +01:00
|
|
|
}
|
|
|
|
|
2016-12-26 14:38:53 +01:00
|
|
|
public static function calcStatusLabel($status, $class, $entityType, $quoteInvoiceId)
|
|
|
|
{
|
|
|
|
if ($quoteInvoiceId) {
|
|
|
|
$label = 'converted';
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif ($class == 'danger') {
|
2016-12-26 14:38:53 +01:00
|
|
|
$label = $entityType == ENTITY_INVOICE ? 'overdue' : 'expired';
|
|
|
|
} else {
|
|
|
|
$label = 'status_' . strtolower($status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return trans("texts.{$label}");
|
|
|
|
}
|
|
|
|
|
2017-02-07 10:52:49 +01:00
|
|
|
public static function calcStatusClass($statusId, $balance, $dueDate, $isRecurring)
|
2016-12-26 14:38:53 +01:00
|
|
|
{
|
2017-02-07 10:52:49 +01:00
|
|
|
if ($statusId >= INVOICE_STATUS_SENT && ! $isRecurring && static::calcIsOverdue($balance, $dueDate)) {
|
2016-12-26 14:38:53 +01:00
|
|
|
return 'danger';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset(static::$statusClasses[$statusId])) {
|
|
|
|
return static::$statusClasses[$statusId];
|
|
|
|
}
|
|
|
|
|
|
|
|
return 'default';
|
|
|
|
}
|
|
|
|
|
|
|
|
public static function calcIsOverdue($balance, $dueDate)
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! Utils::parseFloat($balance) > 0) {
|
2016-12-26 14:38:53 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $dueDate || $dueDate == '0000-00-00') {
|
2016-12-26 14:38:53 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-01 19:11:38 +01:00
|
|
|
// it isn't considered overdue until the end of the day
|
2017-01-30 20:40:43 +01:00
|
|
|
return time() > (strtotime($dueDate) + (60 * 60 * 24));
|
2016-12-26 14:38:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function statusClass()
|
|
|
|
{
|
2017-02-07 10:52:49 +01:00
|
|
|
return static::calcStatusClass($this->invoice_status_id, $this->balance, $this->getOriginal('due_date'), $this->is_recurring);
|
2016-12-26 14:38:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function statusLabel()
|
|
|
|
{
|
|
|
|
return static::calcStatusLabel($this->invoice_status->name, $this->statusClass(), $this->getEntityType(), $this->quote_invoice_id);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $invoice
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return string
|
|
|
|
*/
|
2015-07-29 21:55:12 +02:00
|
|
|
public static function calcLink($invoice)
|
|
|
|
{
|
|
|
|
return link_to('invoices/' . $invoice->public_id, $invoice->invoice_number);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function getLink()
|
|
|
|
{
|
2015-07-29 21:55:12 +02:00
|
|
|
return self::calcLink($this);
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-08-13 21:19:37 +02:00
|
|
|
public function getInvitationLink($type = 'view', $forceOnsite = false)
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->relationLoaded('invitations')) {
|
2016-08-13 21:19:37 +02:00
|
|
|
$this->load('invitations');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->invitations[0]->getLink($type, $forceOnsite);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function getEntityType()
|
|
|
|
{
|
2016-05-26 16:56:54 +02:00
|
|
|
return $this->isType(INVOICE_TYPE_QUOTE) ? ENTITY_QUOTE : ENTITY_INVOICE;
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-08-31 21:10:41 +02:00
|
|
|
public function subEntityType()
|
|
|
|
{
|
|
|
|
if ($this->is_recurring) {
|
|
|
|
return ENTITY_RECURRING_INVOICE;
|
|
|
|
} else {
|
|
|
|
return $this->getEntityType();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function isSent()
|
|
|
|
{
|
2017-01-13 08:02:22 +01:00
|
|
|
return $this->invoice_status_id >= INVOICE_STATUS_SENT && $this->getOriginal('is_public');
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function isViewed()
|
|
|
|
{
|
|
|
|
return $this->invoice_status_id >= INVOICE_STATUS_VIEWED;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-11-18 23:02:01 +01:00
|
|
|
public function isPartial()
|
|
|
|
{
|
|
|
|
return $this->invoice_status_id >= INVOICE_STATUS_PARTIAL;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function isPaid()
|
|
|
|
{
|
|
|
|
return $this->invoice_status_id >= INVOICE_STATUS_PAID;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-12-16 12:49:26 +01:00
|
|
|
public function isOverdue()
|
|
|
|
{
|
2016-12-26 14:38:53 +01:00
|
|
|
return static::calcIsOverdue($this->balance, $this->due_date);
|
2015-12-16 12:49:26 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-04-16 21:57:12 +02:00
|
|
|
public function getRequestedAmount()
|
|
|
|
{
|
2017-03-17 13:55:15 +01:00
|
|
|
$fee = 0;
|
2017-03-26 09:37:32 +02:00
|
|
|
if ($this->account->gateway_fee_enabled) {
|
2017-03-17 13:55:15 +01:00
|
|
|
$fee = $this->getGatewayFee();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->partial > 0) {
|
|
|
|
return $this->partial + $fee;
|
|
|
|
} else {
|
|
|
|
return $this->balance;
|
|
|
|
}
|
2015-04-16 21:57:12 +02:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2016-01-14 22:28:17 +01:00
|
|
|
public function getCurrencyCode()
|
|
|
|
{
|
|
|
|
if ($this->client->currency) {
|
|
|
|
return $this->client->currency->code;
|
|
|
|
} elseif ($this->account->currency) {
|
|
|
|
return $this->account->currency->code;
|
|
|
|
} else {
|
|
|
|
return 'USD';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return $this
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function hidePrivateFields()
|
|
|
|
{
|
|
|
|
$this->setVisible([
|
|
|
|
'invoice_number',
|
|
|
|
'discount',
|
|
|
|
'is_amount_discount',
|
|
|
|
'po_number',
|
|
|
|
'invoice_date',
|
|
|
|
'due_date',
|
|
|
|
'terms',
|
|
|
|
'invoice_footer',
|
|
|
|
'public_notes',
|
|
|
|
'amount',
|
|
|
|
'balance',
|
|
|
|
'invoice_items',
|
2016-03-23 03:23:45 +01:00
|
|
|
'documents',
|
2016-03-24 23:15:52 +01:00
|
|
|
'expenses',
|
2015-03-16 22:45:25 +01:00
|
|
|
'client',
|
2016-03-31 11:29:01 +02:00
|
|
|
'tax_name1',
|
|
|
|
'tax_rate1',
|
|
|
|
'tax_name2',
|
|
|
|
'tax_rate2',
|
2015-03-16 22:45:25 +01:00
|
|
|
'account',
|
|
|
|
'invoice_design',
|
|
|
|
'invoice_design_id',
|
2016-01-07 08:08:30 +01:00
|
|
|
'invoice_fonts',
|
2016-04-19 04:35:18 +02:00
|
|
|
'features',
|
2016-05-26 16:56:54 +02:00
|
|
|
'invoice_type_id',
|
2015-03-16 22:45:25 +01:00
|
|
|
'custom_value1',
|
|
|
|
'custom_value2',
|
|
|
|
'custom_taxes1',
|
2015-03-15 13:01:13 +01:00
|
|
|
'custom_taxes2',
|
2015-04-16 19:12:56 +02:00
|
|
|
'partial',
|
2015-06-10 10:34:20 +02:00
|
|
|
'has_tasks',
|
2015-10-11 16:41:09 +02:00
|
|
|
'custom_text_value1',
|
|
|
|
'custom_text_value2',
|
2016-01-10 11:25:05 +01:00
|
|
|
'has_expenses',
|
2015-03-15 13:01:13 +01:00
|
|
|
]);
|
2015-03-16 22:45:25 +01:00
|
|
|
|
|
|
|
$this->client->setVisible([
|
|
|
|
'name',
|
|
|
|
'id_number',
|
|
|
|
'vat_number',
|
|
|
|
'address1',
|
|
|
|
'address2',
|
|
|
|
'city',
|
|
|
|
'state',
|
|
|
|
'postal_code',
|
|
|
|
'work_phone',
|
|
|
|
'payment_terms',
|
|
|
|
'contacts',
|
|
|
|
'country',
|
|
|
|
'currency_id',
|
2016-05-01 07:55:59 +02:00
|
|
|
'country_id',
|
2015-03-16 22:45:25 +01:00
|
|
|
'custom_value1',
|
2015-03-15 13:01:13 +01:00
|
|
|
'custom_value2',
|
|
|
|
]);
|
2015-03-16 22:45:25 +01:00
|
|
|
|
|
|
|
$this->account->setVisible([
|
|
|
|
'name',
|
2015-12-31 12:31:50 +01:00
|
|
|
'website',
|
2015-03-16 22:45:25 +01:00
|
|
|
'id_number',
|
|
|
|
'vat_number',
|
|
|
|
'address1',
|
|
|
|
'address2',
|
|
|
|
'city',
|
|
|
|
'state',
|
|
|
|
'postal_code',
|
|
|
|
'work_phone',
|
|
|
|
'work_email',
|
|
|
|
'country',
|
|
|
|
'currency_id',
|
|
|
|
'custom_label1',
|
|
|
|
'custom_value1',
|
|
|
|
'custom_label2',
|
|
|
|
'custom_value2',
|
|
|
|
'custom_client_label1',
|
|
|
|
'custom_client_label2',
|
|
|
|
'primary_color',
|
|
|
|
'secondary_color',
|
|
|
|
'hide_quantity',
|
|
|
|
'hide_paid_to_date',
|
2016-01-31 22:42:20 +01:00
|
|
|
'all_pages_header',
|
|
|
|
'all_pages_footer',
|
2015-03-16 22:45:25 +01:00
|
|
|
'custom_invoice_label1',
|
2015-03-15 13:01:13 +01:00
|
|
|
'custom_invoice_label2',
|
|
|
|
'pdf_email_attachment',
|
2015-09-07 11:07:55 +02:00
|
|
|
'show_item_taxes',
|
2015-10-11 16:41:09 +02:00
|
|
|
'custom_invoice_text_label1',
|
|
|
|
'custom_invoice_text_label2',
|
2016-02-28 12:59:52 +01:00
|
|
|
'custom_invoice_item_label1',
|
|
|
|
'custom_invoice_item_label2',
|
2016-04-19 09:51:57 +02:00
|
|
|
'invoice_embed_documents',
|
|
|
|
'page_size',
|
2016-08-15 17:08:06 +02:00
|
|
|
'include_item_taxes_inline',
|
2016-09-05 14:28:59 +02:00
|
|
|
'invoice_fields',
|
2017-04-02 19:50:49 +02:00
|
|
|
'show_currency_code',
|
2015-03-15 13:01:13 +01:00
|
|
|
]);
|
2015-03-16 22:45:25 +01:00
|
|
|
|
|
|
|
foreach ($this->invoice_items as $invoiceItem) {
|
|
|
|
$invoiceItem->setVisible([
|
|
|
|
'product_key',
|
|
|
|
'notes',
|
2016-02-28 12:59:52 +01:00
|
|
|
'custom_value1',
|
|
|
|
'custom_value2',
|
2015-03-16 22:45:25 +01:00
|
|
|
'cost',
|
|
|
|
'qty',
|
2016-03-31 11:29:01 +02:00
|
|
|
'tax_name1',
|
|
|
|
'tax_rate1',
|
|
|
|
'tax_name2',
|
|
|
|
'tax_rate2',
|
2015-03-15 13:01:13 +01:00
|
|
|
]);
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->client->contacts as $contact) {
|
|
|
|
$contact->setVisible([
|
|
|
|
'first_name',
|
|
|
|
'last_name',
|
|
|
|
'email',
|
2015-03-15 13:01:13 +01:00
|
|
|
'phone',
|
2017-04-16 13:31:14 +02:00
|
|
|
'custom_value1',
|
|
|
|
'custom_value2',
|
2015-03-15 13:01:13 +01:00
|
|
|
]);
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2016-03-24 23:15:52 +01:00
|
|
|
foreach ($this->documents as $document) {
|
|
|
|
$document->setVisible([
|
|
|
|
'public_id',
|
|
|
|
'name',
|
|
|
|
]);
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-03-24 23:15:52 +01:00
|
|
|
foreach ($this->expenses as $expense) {
|
|
|
|
$expense->setVisible([
|
|
|
|
'documents',
|
|
|
|
]);
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-03-24 23:15:52 +01:00
|
|
|
foreach ($expense->documents as $document) {
|
|
|
|
$document->setVisible([
|
|
|
|
'public_id',
|
|
|
|
'name',
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-16 22:45:25 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @throws \Recurr\Exception\MissingData
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
|
|
|
* @return bool|\Recurr\RecurrenceCollection
|
2016-07-03 18:11:58 +02:00
|
|
|
*/
|
2015-10-15 16:14:13 +02:00
|
|
|
public function getSchedule()
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $this->start_date || ! $this->is_recurring || ! $this->frequency_id) {
|
2015-10-15 16:14:13 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-20 10:23:38 +02:00
|
|
|
$startDate = $this->getOriginal('last_sent_date') ?: $this->getOriginal('start_date');
|
2015-10-22 20:48:12 +02:00
|
|
|
$startDate .= ' ' . $this->account->recurring_hour . ':00:00';
|
2015-10-15 21:37:01 +02:00
|
|
|
$startDate = $this->account->getDateTime($startDate);
|
2015-10-18 12:37:04 +02:00
|
|
|
$endDate = $this->end_date ? $this->account->getDateTime($this->getOriginal('end_date')) : null;
|
2015-10-15 21:37:01 +02:00
|
|
|
$timezone = $this->account->getTimezone();
|
2015-10-15 16:14:13 +02:00
|
|
|
|
|
|
|
$rule = $this->getRecurrenceRule();
|
|
|
|
$rule = new \Recurr\Rule("{$rule}", $startDate, $endDate, $timezone);
|
|
|
|
|
|
|
|
// Fix for months with less than 31 days
|
|
|
|
$transformerConfig = new \Recurr\Transformer\ArrayTransformerConfig();
|
|
|
|
$transformerConfig->enableLastDayOfMonthFix();
|
2016-01-10 11:25:05 +01:00
|
|
|
|
2015-10-15 16:14:13 +02:00
|
|
|
$transformer = new \Recurr\Transformer\ArrayTransformer();
|
|
|
|
$transformer->setConfig($transformerConfig);
|
|
|
|
$dates = $transformer->transform($rule);
|
|
|
|
|
|
|
|
if (count($dates) < 2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $dates;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return null
|
|
|
|
*/
|
2015-10-15 16:14:13 +02:00
|
|
|
public function getNextSendDate()
|
|
|
|
{
|
2017-02-14 14:09:57 +01:00
|
|
|
if (! $this->is_public) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
if ($this->start_date && ! $this->last_sent_date) {
|
2015-10-22 20:48:12 +02:00
|
|
|
$startDate = $this->getOriginal('start_date') . ' ' . $this->account->recurring_hour . ':00:00';
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2015-10-15 21:37:01 +02:00
|
|
|
return $this->account->getDateTime($startDate);
|
2015-10-15 16:14:13 +02:00
|
|
|
}
|
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $schedule = $this->getSchedule()) {
|
2015-10-15 16:14:13 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (count($schedule) < 2) {
|
|
|
|
return null;
|
|
|
|
}
|
2016-01-10 11:25:05 +01:00
|
|
|
|
2015-10-15 16:14:13 +02:00
|
|
|
return $schedule[1]->getStart();
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param null $invoice_date
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return mixed|null
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function getDueDate($invoice_date = null)
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $this->is_recurring) {
|
2016-01-01 05:59:02 +01:00
|
|
|
return $this->due_date ? $this->due_date : null;
|
2017-01-30 17:05:31 +01:00
|
|
|
} else {
|
2016-01-01 05:59:02 +01:00
|
|
|
$now = time();
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($invoice_date) {
|
2016-01-01 05:59:02 +01:00
|
|
|
// If $invoice_date is specified, all calculations are based on that date
|
2017-01-30 17:05:31 +01:00
|
|
|
if (is_numeric($invoice_date)) {
|
2016-01-01 05:59:02 +01:00
|
|
|
$now = $invoice_date;
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif (is_string($invoice_date)) {
|
2016-01-01 05:59:02 +01:00
|
|
|
$now = strtotime($invoice_date);
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif ($invoice_date instanceof \DateTime) {
|
2016-01-01 05:59:02 +01:00
|
|
|
$now = $invoice_date->getTimestamp();
|
|
|
|
}
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($this->due_date && $this->due_date != '0000-00-00') {
|
2016-01-01 05:59:02 +01:00
|
|
|
// This is a recurring invoice; we're using a custom format here.
|
|
|
|
// The year is always 1998; January is 1st, 2nd, last day of the month.
|
|
|
|
// February is 1st Sunday after, 1st Monday after, ..., through 4th Saturday after.
|
|
|
|
$dueDateVal = strtotime($this->due_date);
|
2017-01-30 20:40:43 +01:00
|
|
|
$monthVal = (int) date('n', $dueDateVal);
|
|
|
|
$dayVal = (int) date('j', $dueDateVal);
|
2016-01-13 10:09:00 +01:00
|
|
|
$dueDate = false;
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($monthVal == 1) {// January; day of month
|
2017-01-30 20:40:43 +01:00
|
|
|
$currentDay = (int) date('j', $now);
|
|
|
|
$lastDayOfMonth = (int) date('t', $now);
|
2016-01-01 05:59:02 +01:00
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
$dueYear = (int) date('Y', $now); // This year
|
|
|
|
$dueMonth = (int) date('n', $now); // This month
|
|
|
|
$dueDay = $dayVal; // The day specified for the invoice
|
2016-01-01 05:59:02 +01:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($dueDay > $lastDayOfMonth) {
|
2016-01-01 05:59:02 +01:00
|
|
|
// No later than the end of the month
|
|
|
|
$dueDay = $lastDayOfMonth;
|
|
|
|
}
|
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($currentDay >= $dueDay) {
|
2016-01-01 05:59:02 +01:00
|
|
|
// Wait until next month
|
|
|
|
// We don't need to handle the December->January wraparaound, since PHP handles month 13 as January of next year
|
|
|
|
$dueMonth++;
|
|
|
|
|
|
|
|
// Reset the due day
|
|
|
|
$dueDay = $dayVal;
|
2017-01-30 20:40:43 +01:00
|
|
|
$lastDayOfMonth = (int) date('t', mktime(0, 0, 0, $dueMonth, 1, $dueYear)); // The number of days in next month
|
2016-01-01 05:59:02 +01:00
|
|
|
|
|
|
|
// Check against the last day again
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($dueDay > $lastDayOfMonth) {
|
2016-01-01 05:59:02 +01:00
|
|
|
// No later than the end of the month
|
|
|
|
$dueDay = $lastDayOfMonth;
|
2016-05-29 14:34:44 +02:00
|
|
|
}
|
2016-01-01 05:59:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$dueDate = mktime(0, 0, 0, $dueMonth, $dueDay, $dueYear);
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif ($monthVal == 2) {// February; day of week
|
2016-07-03 18:11:58 +02:00
|
|
|
$ordinals = ['first', 'second', 'third', 'fourth'];
|
|
|
|
$daysOfWeek = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday'];
|
2016-01-01 05:59:02 +01:00
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
$ordinalIndex = ceil($dayVal / 7) - 1; // 1-7 are "first"; 8-14 are "second", etc.
|
|
|
|
$dayOfWeekIndex = ($dayVal - 1) % 7; // 1,8,15,22 are Sunday, 2,9,16,23 are Monday, etc.
|
|
|
|
$dayStr = $ordinals[$ordinalIndex] . ' ' . $daysOfWeek[$dayOfWeekIndex]; // "first sunday", "first monday", etc.
|
2016-01-01 05:59:02 +01:00
|
|
|
|
|
|
|
$dueDate = strtotime($dayStr, $now);
|
|
|
|
}
|
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($dueDate) {
|
2017-01-30 20:40:43 +01:00
|
|
|
return date('Y-m-d', $dueDate); // SQL format
|
2016-01-01 05:59:02 +01:00
|
|
|
}
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif ($this->client->payment_terms != 0) {
|
2016-01-01 05:59:02 +01:00
|
|
|
// No custom due date set for this invoice; use the client's payment terms
|
2017-01-30 13:35:04 +01:00
|
|
|
$days = $this->client->defaultDaysDue();
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2017-01-30 13:35:04 +01:00
|
|
|
return date('Y-m-d', strtotime('+'.$days.' day', $now));
|
2017-01-30 17:05:31 +01:00
|
|
|
} elseif ($this->account->payment_terms != 0) {
|
2017-01-30 13:35:04 +01:00
|
|
|
$days = $this->account->defaultDaysDue();
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2016-01-01 05:59:02 +01:00
|
|
|
return date('Y-m-d', strtotime('+'.$days.' day', $now));
|
|
|
|
}
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-01-01 05:59:02 +01:00
|
|
|
// Couldn't calculate one
|
|
|
|
return null;
|
|
|
|
}
|
2015-10-15 16:14:13 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param int $min
|
|
|
|
* @param int $max
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return null
|
|
|
|
*/
|
2015-10-15 16:14:13 +02:00
|
|
|
public function getPrettySchedule($min = 1, $max = 10)
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $schedule = $this->getSchedule($max)) {
|
2015-10-15 16:14:13 +02:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$dates = [];
|
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
for ($i = $min; $i < min($max, count($schedule)); $i++) {
|
2015-10-15 16:14:13 +02:00
|
|
|
$date = $schedule[$i];
|
2016-01-01 05:59:02 +01:00
|
|
|
$dateStart = $date->getStart();
|
|
|
|
$date = $this->account->formatDate($dateStart);
|
|
|
|
$dueDate = $this->getDueDate($dateStart);
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if ($dueDate) {
|
2016-01-01 05:59:02 +01:00
|
|
|
$date .= ' <small>(' . trans('texts.due') . ' ' . $this->account->formatDate($dueDate) . ')</small>';
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2015-10-15 16:14:13 +02:00
|
|
|
$dates[] = $date;
|
|
|
|
}
|
|
|
|
|
|
|
|
return implode('<br/>', $dates);
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return string
|
|
|
|
*/
|
2015-10-15 16:14:13 +02:00
|
|
|
private function getRecurrenceRule()
|
|
|
|
{
|
|
|
|
$rule = '';
|
|
|
|
|
|
|
|
switch ($this->frequency_id) {
|
|
|
|
case FREQUENCY_WEEKLY:
|
|
|
|
$rule = 'FREQ=WEEKLY;';
|
|
|
|
break;
|
|
|
|
case FREQUENCY_TWO_WEEKS:
|
|
|
|
$rule = 'FREQ=WEEKLY;INTERVAL=2;';
|
|
|
|
break;
|
|
|
|
case FREQUENCY_FOUR_WEEKS:
|
|
|
|
$rule = 'FREQ=WEEKLY;INTERVAL=4;';
|
|
|
|
break;
|
|
|
|
case FREQUENCY_MONTHLY:
|
|
|
|
$rule = 'FREQ=MONTHLY;';
|
|
|
|
break;
|
2017-02-27 10:05:42 +01:00
|
|
|
case FREQUENCY_TWO_MONTHS:
|
|
|
|
$rule = 'FREQ=MONTHLY;INTERVAL=2;';
|
|
|
|
break;
|
2015-10-15 16:14:13 +02:00
|
|
|
case FREQUENCY_THREE_MONTHS:
|
|
|
|
$rule = 'FREQ=MONTHLY;INTERVAL=3;';
|
|
|
|
break;
|
|
|
|
case FREQUENCY_SIX_MONTHS:
|
|
|
|
$rule = 'FREQ=MONTHLY;INTERVAL=6;';
|
|
|
|
break;
|
|
|
|
case FREQUENCY_ANNUALLY:
|
|
|
|
$rule = 'FREQ=YEARLY;';
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->end_date) {
|
2015-12-24 12:17:11 +01:00
|
|
|
$rule .= 'UNTIL=' . $this->getOriginal('end_date');
|
2015-10-15 16:14:13 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return $rule;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2015-10-20 10:23:38 +02:00
|
|
|
public function shouldSendToday()
|
|
|
|
{
|
|
|
|
if (!$nextSendDate = $this->getNextSendDate()) {
|
|
|
|
return false;
|
|
|
|
}
|
2016-01-10 11:25:05 +01:00
|
|
|
|
2015-10-20 10:23:38 +02:00
|
|
|
return $this->account->getDateTime() >= $nextSendDate;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2015-03-16 22:45:25 +01:00
|
|
|
public function shouldSendToday()
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $this->user->confirmed) {
|
2016-07-01 09:29:24 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-13 13:54:15 +01:00
|
|
|
$account = $this->account;
|
|
|
|
$timezone = $account->getTimezone();
|
|
|
|
|
|
|
|
if (! $this->start_date || Carbon::parse($this->start_date, $timezone)->isFuture()) {
|
2015-03-16 22:45:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-03-13 13:54:15 +01:00
|
|
|
if ($this->end_date && Carbon::parse($this->end_date, $timezone)->isPast()) {
|
2015-03-16 22:45:25 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $this->last_sent_date) {
|
2015-03-16 22:45:25 +01:00
|
|
|
return true;
|
|
|
|
} else {
|
2017-03-29 17:02:36 +02:00
|
|
|
$date1 = new DateTime($this->last_sent_date);
|
|
|
|
$date2 = new DateTime();
|
|
|
|
$diff = $date2->diff($date1);
|
|
|
|
$daysSinceLastSent = $diff->format('%a');
|
|
|
|
$monthsSinceLastSent = ($diff->format('%y') * 12) + $diff->format('%m');
|
|
|
|
|
2017-03-31 10:55:26 +02:00
|
|
|
// check we don't send a few hours early due to timezone difference
|
|
|
|
if (Carbon::now()->format('Y-m-d') != Carbon::now($timezone)->format('Y-m-d')) {
|
|
|
|
return false;
|
|
|
|
}
|
2015-03-16 22:45:25 +01:00
|
|
|
|
2017-03-31 10:55:26 +02:00
|
|
|
// check we never send twice on one day
|
2015-03-16 22:45:25 +01:00
|
|
|
if ($daysSinceLastSent == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($this->frequency_id) {
|
|
|
|
case FREQUENCY_WEEKLY:
|
|
|
|
return $daysSinceLastSent >= 7;
|
|
|
|
case FREQUENCY_TWO_WEEKS:
|
|
|
|
return $daysSinceLastSent >= 14;
|
|
|
|
case FREQUENCY_FOUR_WEEKS:
|
|
|
|
return $daysSinceLastSent >= 28;
|
|
|
|
case FREQUENCY_MONTHLY:
|
|
|
|
return $monthsSinceLastSent >= 1;
|
2017-02-27 10:05:42 +01:00
|
|
|
case FREQUENCY_TWO_MONTHS:
|
|
|
|
return $monthsSinceLastSent >= 2;
|
2015-03-16 22:45:25 +01:00
|
|
|
case FREQUENCY_THREE_MONTHS:
|
|
|
|
return $monthsSinceLastSent >= 3;
|
|
|
|
case FREQUENCY_SIX_MONTHS:
|
|
|
|
return $monthsSinceLastSent >= 6;
|
|
|
|
case FREQUENCY_ANNUALLY:
|
|
|
|
return $monthsSinceLastSent >= 12;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-17 21:01:06 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool|string
|
|
|
|
*/
|
2015-10-13 09:11:44 +02:00
|
|
|
public function getPDFString()
|
2015-09-17 21:01:06 +02:00
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! env('PHANTOMJS_CLOUD_KEY') && ! env('PHANTOMJS_BIN_PATH')) {
|
2015-10-13 09:11:44 +02:00
|
|
|
return false;
|
2015-09-29 12:21:57 +02:00
|
|
|
}
|
2015-10-13 09:11:44 +02:00
|
|
|
|
2017-03-10 15:36:47 +01:00
|
|
|
if (Utils::isTravis()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-10-13 09:11:44 +02:00
|
|
|
$invitation = $this->invitations[0];
|
2016-04-13 12:15:57 +02:00
|
|
|
$link = $invitation->getLink('view', true);
|
2017-01-25 13:25:07 +01:00
|
|
|
$pdfString = false;
|
|
|
|
|
|
|
|
try {
|
|
|
|
if (env('PHANTOMJS_BIN_PATH')) {
|
2017-02-15 21:58:27 +01:00
|
|
|
$pdfString = CurlUtils::phantom('GET', $link . '?phantomjs=true&phantomjs_secret=' . env('PHANTOMJS_SECRET'));
|
2017-03-21 16:28:22 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (! $pdfString && (Utils::isNinja() || ! env('PHANTOMJS_BIN_PATH'))) {
|
|
|
|
if ($key = env('PHANTOMJS_CLOUD_KEY')) {
|
|
|
|
if (Utils::isNinjaDev()) {
|
|
|
|
$link = env('TEST_LINK');
|
|
|
|
}
|
|
|
|
$url = "http://api.phantomjscloud.com/api/browser/v2/{$key}/?request=%7Burl:%22{$link}?phantomjs=true%22,renderType:%22html%22%7D";
|
|
|
|
$pdfString = CurlUtils::get($url);
|
2017-01-25 13:25:07 +01:00
|
|
|
}
|
2017-01-02 19:47:40 +01:00
|
|
|
}
|
2016-02-17 16:50:01 +01:00
|
|
|
|
2017-01-25 13:25:07 +01:00
|
|
|
$pdfString = strip_tags($pdfString);
|
2017-01-29 08:06:10 +01:00
|
|
|
} catch (\Exception $exception) {
|
2017-03-09 15:24:12 +01:00
|
|
|
Utils::logError("PhantomJS - Failed to load: {$exception->getMessage()}");
|
2017-01-25 13:25:07 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $pdfString || strlen($pdfString) < 200) {
|
2017-03-09 15:24:12 +01:00
|
|
|
Utils::logError("PhantomJS - Invalid response: {$pdfString}");
|
2016-02-17 16:50:01 +01:00
|
|
|
return false;
|
2016-01-14 22:28:17 +01:00
|
|
|
}
|
|
|
|
|
2017-03-09 15:24:12 +01:00
|
|
|
if ($pdf = Utils::decodePDF($pdfString)) {
|
|
|
|
return $pdf;
|
|
|
|
} else {
|
|
|
|
Utils::logError("PhantomJS - Unable to decode: {$pdfString}");
|
|
|
|
return false;
|
|
|
|
}
|
2015-09-17 21:01:06 +02:00
|
|
|
}
|
2016-02-23 22:32:39 +01:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $invoiceItem
|
|
|
|
* @param $invoiceTotal
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return float|int
|
|
|
|
*/
|
2016-02-23 22:32:39 +01:00
|
|
|
public function getItemTaxable($invoiceItem, $invoiceTotal)
|
|
|
|
{
|
|
|
|
$total = $invoiceItem->qty * $invoiceItem->cost;
|
|
|
|
|
|
|
|
if ($this->discount > 0) {
|
|
|
|
if ($this->is_amount_discount) {
|
2017-03-02 10:08:33 +01:00
|
|
|
$total -= $invoiceTotal ? ($total / ($invoiceTotal + $this->discount) * $this->discount) : 0;
|
2016-02-23 22:32:39 +01:00
|
|
|
} else {
|
|
|
|
$total *= (100 - $this->discount) / 100;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-11 21:23:31 +01:00
|
|
|
return round($total, 2);
|
2016-02-23 22:32:39 +01:00
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return float|int|mixed
|
|
|
|
*/
|
2016-02-23 22:32:39 +01:00
|
|
|
public function getTaxable()
|
|
|
|
{
|
|
|
|
$total = 0;
|
|
|
|
|
|
|
|
foreach ($this->invoice_items as $invoiceItem) {
|
|
|
|
$total += $invoiceItem->qty * $invoiceItem->cost;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->discount > 0) {
|
|
|
|
if ($this->is_amount_discount) {
|
|
|
|
$total -= $this->discount;
|
|
|
|
} else {
|
|
|
|
$total *= (100 - $this->discount) / 100;
|
|
|
|
$total = round($total, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->custom_value1 && $this->custom_taxes1) {
|
|
|
|
$total += $this->custom_value1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->custom_value2 && $this->custom_taxes2) {
|
|
|
|
$total += $this->custom_value2;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $total;
|
|
|
|
}
|
|
|
|
|
2016-05-29 14:34:44 +02:00
|
|
|
// if $calculatePaid is true we'll loop through each payment to
|
2016-03-31 11:29:01 +02:00
|
|
|
// determine the sum, otherwise we'll use the cached paid_to_date amount
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param bool $calculatePaid
|
2017-01-30 20:40:43 +01:00
|
|
|
*
|
2016-07-03 18:11:58 +02:00
|
|
|
* @return array
|
|
|
|
*/
|
2016-02-24 21:58:42 +01:00
|
|
|
public function getTaxes($calculatePaid = false)
|
2016-02-23 22:32:39 +01:00
|
|
|
{
|
|
|
|
$taxes = [];
|
|
|
|
$taxable = $this->getTaxable();
|
2016-03-31 11:29:01 +02:00
|
|
|
$paidAmount = $this->getAmountPaid($calculatePaid);
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-03-31 11:29:01 +02:00
|
|
|
if ($this->tax_name1) {
|
|
|
|
$invoiceTaxAmount = round($taxable * ($this->tax_rate1 / 100), 2);
|
2017-01-30 20:40:43 +01:00
|
|
|
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
2016-03-31 11:29:01 +02:00
|
|
|
$this->calculateTax($taxes, $this->tax_name1, $this->tax_rate1, $invoiceTaxAmount, $invoicePaidAmount);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->tax_name2) {
|
|
|
|
$invoiceTaxAmount = round($taxable * ($this->tax_rate2 / 100), 2);
|
2017-01-30 20:40:43 +01:00
|
|
|
$invoicePaidAmount = floatval($this->amount) && $invoiceTaxAmount ? ($paidAmount / $this->amount * $invoiceTaxAmount) : 0;
|
2016-03-31 11:29:01 +02:00
|
|
|
$this->calculateTax($taxes, $this->tax_name2, $this->tax_rate2, $invoiceTaxAmount, $invoicePaidAmount);
|
2016-02-23 22:32:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->invoice_items as $invoiceItem) {
|
2017-03-11 21:23:31 +01:00
|
|
|
$itemTaxable = $this->getItemTaxable($invoiceItem, $taxable);
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-03-31 11:29:01 +02:00
|
|
|
if ($invoiceItem->tax_name1) {
|
2017-03-11 21:23:31 +01:00
|
|
|
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate1 / 100), 2);
|
2017-01-30 20:40:43 +01:00
|
|
|
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
2016-03-31 11:29:01 +02:00
|
|
|
$this->calculateTax($taxes, $invoiceItem->tax_name1, $invoiceItem->tax_rate1, $itemTaxAmount, $itemPaidAmount);
|
2016-02-23 22:32:39 +01:00
|
|
|
}
|
|
|
|
|
2016-03-31 11:29:01 +02:00
|
|
|
if ($invoiceItem->tax_name2) {
|
2017-03-11 21:23:31 +01:00
|
|
|
$itemTaxAmount = round($itemTaxable * ($invoiceItem->tax_rate2 / 100), 2);
|
2017-01-30 20:40:43 +01:00
|
|
|
$itemPaidAmount = floatval($this->amount) && $itemTaxAmount ? ($paidAmount / $this->amount * $itemTaxAmount) : 0;
|
2016-03-31 11:29:01 +02:00
|
|
|
$this->calculateTax($taxes, $invoiceItem->tax_name2, $invoiceItem->tax_rate2, $itemTaxAmount, $itemPaidAmount);
|
2016-02-23 22:32:39 +01:00
|
|
|
}
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-02-23 22:32:39 +01:00
|
|
|
return $taxes;
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @param $taxes
|
|
|
|
* @param $name
|
|
|
|
* @param $rate
|
|
|
|
* @param $amount
|
|
|
|
* @param $paid
|
|
|
|
*/
|
2016-05-29 14:34:44 +02:00
|
|
|
private function calculateTax(&$taxes, $name, $rate, $amount, $paid)
|
|
|
|
{
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! $amount) {
|
2016-03-31 11:29:01 +02:00
|
|
|
return;
|
2016-05-29 14:34:44 +02:00
|
|
|
}
|
|
|
|
|
2016-03-31 11:29:01 +02:00
|
|
|
$amount = round($amount, 2);
|
|
|
|
$paid = round($paid, 2);
|
|
|
|
$key = $rate . ' ' . $name;
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-01-30 17:05:31 +01:00
|
|
|
if (! isset($taxes[$key])) {
|
2016-03-31 11:29:01 +02:00
|
|
|
$taxes[$key] = [
|
|
|
|
'name' => $name,
|
2017-01-30 20:40:43 +01:00
|
|
|
'rate' => $rate + 0,
|
2016-03-31 11:29:01 +02:00
|
|
|
'amount' => 0,
|
2017-01-30 20:40:43 +01:00
|
|
|
'paid' => 0,
|
2016-03-31 11:29:01 +02:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
$taxes[$key]['amount'] += $amount;
|
2016-05-29 14:34:44 +02:00
|
|
|
$taxes[$key]['paid'] += $paid;
|
2016-03-31 11:29:01 +02:00
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2017-04-28 15:42:19 +02:00
|
|
|
/**
|
|
|
|
* @return int
|
|
|
|
*/
|
|
|
|
public function countDocuments()
|
|
|
|
{
|
|
|
|
$count = count($this->documents);
|
|
|
|
|
|
|
|
foreach ($this->expenses as $expense) {
|
|
|
|
if ($expense->invoice_documents) {
|
|
|
|
$count += count($expense->documents);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $count;
|
|
|
|
}
|
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function hasDocuments()
|
|
|
|
{
|
|
|
|
if (count($this->documents)) {
|
|
|
|
return true;
|
|
|
|
}
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2016-03-24 23:15:52 +01:00
|
|
|
return $this->hasExpenseDocuments();
|
|
|
|
}
|
2016-05-29 14:34:44 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function hasExpenseDocuments()
|
|
|
|
{
|
|
|
|
foreach ($this->expenses as $expense) {
|
2017-04-28 15:42:19 +02:00
|
|
|
if ($expense->invoice_documents && count($expense->documents)) {
|
2017-01-30 17:05:31 +01:00
|
|
|
return true;
|
|
|
|
}
|
2016-03-24 23:15:52 +01:00
|
|
|
}
|
2017-01-30 20:40:43 +01:00
|
|
|
|
2016-03-24 23:15:52 +01:00
|
|
|
return false;
|
|
|
|
}
|
2016-05-25 04:49:06 +02:00
|
|
|
|
2016-07-03 18:11:58 +02:00
|
|
|
/**
|
|
|
|
* @return bool
|
|
|
|
*/
|
2017-01-30 17:05:31 +01:00
|
|
|
public function getAutoBillEnabled()
|
|
|
|
{
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $this->is_recurring) {
|
2016-05-25 04:49:06 +02:00
|
|
|
$recurInvoice = $this->recurring_invoice;
|
|
|
|
} else {
|
|
|
|
$recurInvoice = $this;
|
|
|
|
}
|
|
|
|
|
2017-01-30 20:40:43 +01:00
|
|
|
if (! $recurInvoice) {
|
2016-05-25 04:49:06 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $recurInvoice->auto_bill == AUTO_BILL_ALWAYS || ($recurInvoice->auto_bill != AUTO_BILL_OFF && $recurInvoice->client_enable_auto_bill);
|
|
|
|
}
|
2016-11-18 14:31:43 +01:00
|
|
|
|
|
|
|
public static function getStatuses($entityType = false)
|
|
|
|
{
|
2016-11-20 15:08:36 +01:00
|
|
|
$statuses = [];
|
2016-11-18 14:31:43 +01:00
|
|
|
|
|
|
|
if ($entityType == ENTITY_RECURRING_INVOICE) {
|
|
|
|
return $statuses;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach (\Cache::get('invoiceStatus') as $status) {
|
|
|
|
if ($entityType == ENTITY_QUOTE) {
|
|
|
|
if (in_array($status->id, [INVOICE_STATUS_PAID, INVOICE_STATUS_PARTIAL])) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-20 15:08:36 +01:00
|
|
|
} elseif ($entityType == ENTITY_INVOICE) {
|
|
|
|
if (in_array($status->id, [INVOICE_STATUS_APPROVED])) {
|
|
|
|
continue;
|
|
|
|
}
|
2016-11-18 14:31:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$statuses[$status->id] = trans('texts.status_' . strtolower($status->name));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($entityType == ENTITY_INVOICE) {
|
2017-02-07 15:26:23 +01:00
|
|
|
$statuses[INVOICE_STATUS_UNPAID] = trans('texts.unpaid');
|
2016-11-18 14:31:43 +01:00
|
|
|
$statuses[INVOICE_STATUS_OVERDUE] = trans('texts.overdue');
|
|
|
|
}
|
|
|
|
|
|
|
|
return $statuses;
|
|
|
|
}
|
2017-02-05 08:53:49 +01:00
|
|
|
|
|
|
|
public function emailHistory()
|
|
|
|
{
|
|
|
|
return Activity::scope()
|
|
|
|
->with(['contact'])
|
|
|
|
->whereInvoiceId($this->id)
|
|
|
|
->whereIn('activity_type_id', [ACTIVITY_TYPE_EMAIL_INVOICE, ACTIVITY_TYPE_EMAIL_QUOTE])
|
|
|
|
->orderBy('id', 'desc')
|
|
|
|
->get();
|
|
|
|
}
|
2015-03-16 22:45:25 +01:00
|
|
|
}
|
|
|
|
|
2015-07-12 21:43:45 +02:00
|
|
|
Invoice::creating(function ($invoice) {
|
2017-03-26 20:15:51 +02:00
|
|
|
if (! $invoice->is_recurring && $invoice->amount >= 0) {
|
2015-10-22 20:48:12 +02:00
|
|
|
$invoice->account->incrementCounter($invoice);
|
2015-08-10 17:48:41 +02:00
|
|
|
}
|
2015-07-12 21:43:45 +02:00
|
|
|
});
|
2017-04-07 12:46:00 +02:00
|
|
|
|
|
|
|
Invoice::created(function ($invoice) {
|
|
|
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
|
|
|
event(new QuoteWasCreated($invoice));
|
|
|
|
} else {
|
|
|
|
event(new InvoiceWasCreated($invoice));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Invoice::updating(function ($invoice) {
|
|
|
|
if ($invoice->isType(INVOICE_TYPE_QUOTE)) {
|
|
|
|
event(new QuoteWasUpdated($invoice));
|
|
|
|
} else {
|
|
|
|
event(new InvoiceWasUpdated($invoice));
|
|
|
|
}
|
|
|
|
});
|