'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 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) { return null; // $this->next_send_date = now()->format('Y-m-d'); } switch ($this->frequency_id) { case self::FREQUENCY_DAILY: return Carbon::parse($this->next_send_date)->addDay(); case self::FREQUENCY_WEEKLY: return Carbon::parse($this->next_send_date)->addWeek(); case self::FREQUENCY_TWO_WEEKS: return Carbon::parse($this->next_send_date)->addWeeks(2); case self::FREQUENCY_FOUR_WEEKS: return Carbon::parse($this->next_send_date)->addWeeks(4); case self::FREQUENCY_MONTHLY: return Carbon::parse($this->next_send_date)->addMonthNoOverflow(); case self::FREQUENCY_TWO_MONTHS: return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(2); case self::FREQUENCY_THREE_MONTHS: return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(3); case self::FREQUENCY_FOUR_MONTHS: return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(4); case self::FREQUENCY_SIX_MONTHS: return Carbon::parse($this->next_send_date)->addMonthsNoOverflow(6); case self::FREQUENCY_ANNUALLY: return Carbon::parse($this->next_send_date)->addYear(); case self::FREQUENCY_TWO_YEARS: return Carbon::parse($this->next_send_date)->addYears(2); case self::FREQUENCY_THREE_YEARS: return Carbon::parse($this->next_send_date)->addYears(3); default: return null; } } public function nextDateByFrequency($date) { switch ($this->frequency_id) { case self::FREQUENCY_DAILY: return Carbon::parse($date)->addDay(); case self::FREQUENCY_WEEKLY: return Carbon::parse($date)->addWeek(); case self::FREQUENCY_TWO_WEEKS: return Carbon::parse($date)->addWeeks(2); case self::FREQUENCY_FOUR_WEEKS: return Carbon::parse($date)->addWeeks(4); case self::FREQUENCY_MONTHLY: return Carbon::parse($date)->addMonthNoOverflow(); case self::FREQUENCY_TWO_MONTHS: return Carbon::parse($date)->addMonthsNoOverflow(2); case self::FREQUENCY_THREE_MONTHS: return Carbon::parse($date)->addMonthsNoOverflow(3); case self::FREQUENCY_FOUR_MONTHS: return Carbon::parse($date)->addMonthsNoOverflow(4); case self::FREQUENCY_SIX_MONTHS: return Carbon::parse($date)->addMonthsNoOverflow(6); case self::FREQUENCY_ANNUALLY: return Carbon::parse($date)->addYear(); case self::FREQUENCY_TWO_YEARS: return Carbon::parse($date)->addYears(2); case self::FREQUENCY_THREE_YEARS: return Carbon::parse($date)->addYears(3); 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 '

'.ctrans('texts.draft').'

'; break; case self::STATUS_PENDING: return '

'.ctrans('texts.pending').'

'; break; case self::STATUS_ACTIVE: return '

'.ctrans('texts.active').'

'; break; case self::STATUS_COMPLETED: return '

'.ctrans('texts.status_completed').'

'; break; case self::STATUS_PAUSED: return '

'.ctrans('texts.paused').'

'; break; default: // code... break; } } public static function frequencyForKey(int $frequency_id) :string { switch ($frequency_id) { case self::FREQUENCY_DAILY: return ctrans('texts.freq_daily'); break; case self::FREQUENCY_WEEKLY: return ctrans('texts.freq_weekly'); break; case self::FREQUENCY_TWO_WEEKS: return ctrans('texts.freq_two_weeks'); break; case self::FREQUENCY_FOUR_WEEKS: return ctrans('texts.freq_four_weeks'); break; case self::FREQUENCY_MONTHLY: return ctrans('texts.freq_monthly'); break; case self::FREQUENCY_TWO_MONTHS: return ctrans('texts.freq_two_months'); break; case self::FREQUENCY_THREE_MONTHS: return ctrans('texts.freq_three_months'); break; case self::FREQUENCY_FOUR_MONTHS: return ctrans('texts.freq_four_months'); break; case self::FREQUENCY_SIX_MONTHS: return ctrans('texts.freq_six_months'); break; case self::FREQUENCY_ANNUALLY: return ctrans('texts.freq_annually'); break; case self::FREQUENCY_TWO_YEARS: return ctrans('texts.freq_two_years'); break; default: // code... break; } } public function calc() { $invoice_calc = null; if ($this->uses_inclusive_taxes) { $invoice_calc = new InvoiceSumInclusive($this); } else { $invoice_calc = new InvoiceSum($this); } return $invoice_calc->build(); } /* * Important to note when playing with carbon dates - in order * not to modify the original instance, always use a `->copy()` * */ public function recurringDates() { /* Return early if nothing to send back! */ if ($this->status_id == self::STATUS_COMPLETED || $this->remaining_cycles == 0 || !$this->next_send_date) { return []; } /* Endless - lets send 10 back*/ $iterations = $this->remaining_cycles; if ($this->remaining_cycles == -1) { $iterations = 10; } $data = []; if (!Carbon::parse($this->next_send_date)) { return $data; } $next_send_date = Carbon::parse($this->next_send_date)->copy(); for ($x=0; $x<$iterations; $x++) { // we don't add the days... we calc the day of the month!! $next_due_date = $this->calculateDueDate($next_send_date->copy()->format('Y-m-d')); $next_due_date_string = $next_due_date ? $next_due_date->format('Y-m-d') : ''; $next_send_date = Carbon::parse($next_send_date); $data[] = [ 'send_date' => $next_send_date->format('Y-m-d'), 'due_date' => $next_due_date_string ]; $next_send_date = $this->nextDateByFrequency($next_send_date->format('Y-m-d')); } /*If no due date is set - unset the due_date value */ // if(!$this->due_date_days || $this->due_date_days == 0){ // foreach($data as $key => $value) // $data[$key]['due_date'] = ''; // } return $data; } public function calculateDueDate($date) { switch ($this->due_date_days) { case 'terms': return $this->calculateDateFromTerms($date); break; default: return $this->setDayOfMonth($date, $this->due_date_days); break; } } /** * Calculates a date based on the client payment terms. * * @param Carbon $date A given date * @return NULL|Carbon The date */ public function calculateDateFromTerms($date) { $new_date = Carbon::parse($date); $client_payment_terms = $this->client->getSetting('payment_terms'); if ($client_payment_terms == '') {//no due date! return null; return null; } return $new_date->addDays($client_payment_terms); //add the number of days in the payment terms to the date } /** * Service entry points. */ public function service() :RecurringService { return new RecurringService($this); } public function subscription(): \Illuminate\Database\Eloquent\Relations\BelongsTo { return $this->belongsTo(Subscription::class); } }