'object', 'line_items' => 'object', 'backup' => 'object', 'updated_at' => 'timestamp', 'created_at' => 'timestamp', 'deleted_at' => 'timestamp', ]; protected $appends = [ 'hashed_id', 'status', ]; protected $touches = []; public function getEntityType() { return self::class; } public function getDateAttribute($value) { if (! empty($value)) { return (new Carbon($value))->format('Y-m-d'); } return $value; } public function getDueDateAttribute($value) { if (! empty($value)) { return (new Carbon($value))->format('Y-m-d'); } return $value; } public function getPartialDueDateAttribute($value) { if (! empty($value)) { return (new Carbon($value))->format('Y-m-d'); } return $value; } public function vendor() { return $this->belongsTo(Vendor::class); } public function activities() { return $this->hasMany(Activity::class)->orderBy('id', 'DESC')->take(50); } public function history() { return $this->hasManyThrough(Backup::class, Activity::class); } public function company() { return $this->belongsTo(Company::class); } public function client() { return $this->belongsTo(Client::class)->withTrashed(); } public function project() { return $this->belongsTo(Project::class)->withTrashed(); } public function user() { return $this->belongsTo(User::class)->withTrashed(); } public function assigned_user() { return $this->belongsTo(User::class, 'assigned_user_id', 'id')->withTrashed(); } public function invoices() { return $this->hasMany(Invoice::class, 'recurring_id', 'id')->withTrashed(); } public function invitations() { return $this->hasMany(RecurringInvoiceInvitation::class); } public function documents() { return $this->morphMany(Document::class, 'documentable'); } public function getStatusAttribute() { if ($this->status_id == self::STATUS_ACTIVE && Carbon::parse($this->next_send_date)->isFuture()) { return self::STATUS_PENDING; } else { return $this->status_id; } } public function nextSendDate() :?Carbon { if (!$this->next_send_date_client) { return null; } $offset = $this->client->timezone_offset(); /* If this setting is enabled, the recurring invoice may be set in the past */ if($this->company->stop_on_unpaid_recurring) { /* Lets set the next send date to now so we increment from today, rather than in the past*/ if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3))) $this->next_send_date_client = now()->format('Y-m-d'); } /* As we are firing at UTC+0 if our offset is negative it is technically firing the day before so we always need to add ON a day - a day = 86400 seconds */ // if($offset < 0) // $offset += 86400; switch ($this->frequency_id) { case self::FREQUENCY_DAILY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay()->addSeconds($offset); case self::FREQUENCY_WEEKLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek()->addSeconds($offset); case self::FREQUENCY_TWO_WEEKS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2)->addSeconds($offset); case self::FREQUENCY_FOUR_WEEKS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4)->addSeconds($offset); case self::FREQUENCY_MONTHLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow()->addSeconds($offset); case self::FREQUENCY_TWO_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset); case self::FREQUENCY_THREE_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset); case self::FREQUENCY_FOUR_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset); case self::FREQUENCY_SIX_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6)->addSeconds($offset); case self::FREQUENCY_ANNUALLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear()->addSeconds($offset); case self::FREQUENCY_TWO_YEARS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2)->addSeconds($offset); case self::FREQUENCY_THREE_YEARS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3)->addSeconds($offset); default: return null; } } public function nextSendDateClient() :?Carbon { if (!$this->next_send_date_client) { return null; } /* If this setting is enabled, the recurring invoice may be set in the past */ if($this->company->stop_on_unpaid_recurring) { /* Lets set the next send date to now so we increment from today, rather than in the past*/ if(Carbon::parse($this->next_send_date)->lt(now()->subDays(3))) $this->next_send_date_client = now()->format('Y-m-d'); } switch ($this->frequency_id) { case self::FREQUENCY_DAILY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addDay(); case self::FREQUENCY_WEEKLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeek(); case self::FREQUENCY_TWO_WEEKS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(2); case self::FREQUENCY_FOUR_WEEKS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addWeeks(4); case self::FREQUENCY_MONTHLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthNoOverflow(); case self::FREQUENCY_TWO_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(2); case self::FREQUENCY_THREE_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(3); case self::FREQUENCY_FOUR_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(4); case self::FREQUENCY_SIX_MONTHS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addMonthsNoOverflow(6); case self::FREQUENCY_ANNUALLY: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYear(); case self::FREQUENCY_TWO_YEARS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(2); case self::FREQUENCY_THREE_YEARS: return Carbon::parse($this->next_send_date_client)->startOfDay()->addYears(3); default: return null; } } public function nextDateByFrequency($date) { $offset = $this->client->timezone_offset(); switch ($this->frequency_id) { case self::FREQUENCY_DAILY: return Carbon::parse($date)->startOfDay()->addDay()->addSeconds($offset); case self::FREQUENCY_WEEKLY: return Carbon::parse($date)->startOfDay()->addWeek()->addSeconds($offset); case self::FREQUENCY_TWO_WEEKS: return Carbon::parse($date)->startOfDay()->addWeeks(2)->addSeconds($offset); case self::FREQUENCY_FOUR_WEEKS: return Carbon::parse($date)->startOfDay()->addWeeks(4)->addSeconds($offset); case self::FREQUENCY_MONTHLY: return Carbon::parse($date)->startOfDay()->addMonthNoOverflow()->addSeconds($offset); case self::FREQUENCY_TWO_MONTHS: return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(2)->addSeconds($offset); case self::FREQUENCY_THREE_MONTHS: return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(3)->addSeconds($offset); case self::FREQUENCY_FOUR_MONTHS: return Carbon::parse($date)->startOfDay()->addMonthsNoOverflow(4)->addSeconds($offset); case self::FREQUENCY_SIX_MONTHS: return Carbon::parse($date)->addMonthsNoOverflow(6)->addSeconds($offset); case self::FREQUENCY_ANNUALLY: return Carbon::parse($date)->startOfDay()->addYear()->addSeconds($offset); case self::FREQUENCY_TWO_YEARS: return Carbon::parse($date)->startOfDay()->addYears(2)->addSeconds($offset); case self::FREQUENCY_THREE_YEARS: return Carbon::parse($date)->startOfDay()->addYears(3)->addSeconds($offset); default: return null; } } public function remainingCycles() : int { if ($this->remaining_cycles == 0) { return 0; } elseif ($this->remaining_cycles == -1) { return -1; } else { return $this->remaining_cycles - 1; } } public function setCompleted() : void { $this->status_id = self::STATUS_COMPLETED; $this->next_send_date = null; $this->remaining_cycles = 0; $this->save(); } public static function badgeForStatus(int $status) { switch ($status) { case self::STATUS_DRAFT: return '