File "RecurringInvoice.php"
Full Path: /home/pulsehostuk9/public_html/invoicer.pulsehost.co.uk/app/Models/RecurringInvoice.php
File size: 13.56 KB
MIME-type: text/x-php
Charset: utf-8
<?php
namespace App\Models;
use App\Http\Requests\RecurringInvoiceRequest;
use App\Services\SerialNumberFormatter;
use App\Traits\HasCustomFieldsTrait;
use Carbon\Carbon;
use Cron;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Vinkla\Hashids\Facades\Hashids;
class RecurringInvoice extends Model
{
use HasCustomFieldsTrait;
use HasFactory;
protected $guarded = [
'id',
];
protected $dates = [
'starts_at',
];
public const NONE = 'NONE';
public const COUNT = 'COUNT';
public const DATE = 'DATE';
public const COMPLETED = 'COMPLETED';
public const ON_HOLD = 'ON_HOLD';
public const ACTIVE = 'ACTIVE';
protected $appends = [
'formattedCreatedAt',
'formattedStartsAt',
'formattedNextInvoiceAt',
'formattedLimitDate',
];
protected function casts(): array
{
return [
'exchange_rate' => 'float',
'send_automatically' => 'boolean',
];
}
public function getFormattedStartsAtAttribute()
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->starts_at)->translatedFormat($dateFormat);
}
public function getFormattedNextInvoiceAtAttribute()
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->next_invoice_at)->translatedFormat($dateFormat);
}
public function getFormattedLimitDateAttribute()
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->limit_date)->format($dateFormat);
}
public function getFormattedCreatedAtAttribute()
{
$dateFormat = CompanySetting::getSetting('carbon_date_format', $this->company_id);
return Carbon::parse($this->created_at)->format($dateFormat);
}
public function invoices(): HasMany
{
return $this->hasMany(Invoice::class);
}
public function taxes(): HasMany
{
return $this->hasMany(Tax::class);
}
public function items(): HasMany
{
return $this->hasMany(InvoiceItem::class);
}
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
}
public function company(): BelongsTo
{
return $this->belongsTo(Company::class);
}
public function creator(): BelongsTo
{
return $this->belongsTo(User::class, 'creator_id');
}
public function currency(): BelongsTo
{
return $this->belongsTo(Currency::class);
}
public function scopeWhereCompany($query)
{
$query->where('recurring_invoices.company_id', request()->header('company'));
}
public function scopePaginateData($query, $limit)
{
if ($limit == 'all') {
return $query->get();
}
return $query->paginate($limit);
}
public function scopeWhereOrder($query, $orderByField, $orderBy)
{
$query->orderBy($orderByField, $orderBy);
}
public function scopeWhereStatus($query, $status)
{
return $query->where('recurring_invoices.status', $status);
}
public function scopeWhereCustomer($query, $customer_id)
{
$query->where('customer_id', $customer_id);
}
public function scopeRecurringInvoicesStartBetween($query, $start, $end)
{
return $query->whereBetween(
'starts_at',
[$start->format('Y-m-d'), $end->format('Y-m-d')]
);
}
public function scopeWhereSearch($query, $search)
{
foreach (explode(' ', $search) as $term) {
$query->whereHas('customer', function ($query) use ($term) {
$query->where('name', 'LIKE', '%'.$term.'%')
->orWhere('contact_name', 'LIKE', '%'.$term.'%')
->orWhere('company_name', 'LIKE', '%'.$term.'%');
});
}
}
public function scopeApplyFilters($query, array $filters)
{
$filters = collect($filters);
if ($filters->get('status') && $filters->get('status') !== 'ALL') {
$query->whereStatus($filters->get('status'));
}
if ($filters->get('search')) {
$query->whereSearch($filters->get('search'));
}
if ($filters->get('from_date') && $filters->get('to_date')) {
$start = Carbon::createFromFormat('Y-m-d', $filters->get('from_date'));
$end = Carbon::createFromFormat('Y-m-d', $filters->get('to_date'));
$query->recurringInvoicesStartBetween($start, $end);
}
if ($filters->get('customer_id')) {
$query->whereCustomer($filters->get('customer_id'));
}
if ($filters->get('orderByField') || $filters->get('orderBy')) {
$field = $filters->get('orderByField') ? $filters->get('orderByField') : 'created_at';
$orderBy = $filters->get('orderBy') ? $filters->get('orderBy') : 'asc';
$query->whereOrder($field, $orderBy);
}
}
public static function createFromRequest(RecurringInvoiceRequest $request)
{
$recurringInvoice = self::create($request->getRecurringInvoicePayload());
$company_currency = CompanySetting::getSetting('currency', $request->header('company'));
if ((string) $recurringInvoice['currency_id'] !== $company_currency) {
ExchangeRateLog::addExchangeRateLog($recurringInvoice);
}
self::createItems($recurringInvoice, $request->items);
if ($request->has('taxes') && (! empty($request->taxes))) {
self::createTaxes($recurringInvoice, $request->taxes);
}
if ($request->customFields) {
$recurringInvoice->addCustomFields($request->customFields);
}
return $recurringInvoice;
}
public function updateFromRequest(RecurringInvoiceRequest $request)
{
$data = $request->getRecurringInvoicePayload();
$this->update($data);
$company_currency = CompanySetting::getSetting('currency', $request->header('company'));
if ((string) $data['currency_id'] !== $company_currency) {
ExchangeRateLog::addExchangeRateLog($this);
}
$this->items()->delete();
self::createItems($this, $request->items);
$this->taxes()->delete();
if ($request->has('taxes') && (! empty($request->taxes))) {
self::createTaxes($this, $request->taxes);
}
if ($request->customFields) {
$this->updateCustomFields($request->customFields);
}
return $this;
}
public static function createItems($recurringInvoice, $invoiceItems)
{
foreach ($invoiceItems as $invoiceItem) {
$invoiceItem['company_id'] = $recurringInvoice->company_id;
$item = $recurringInvoice->items()->create($invoiceItem);
if (array_key_exists('taxes', $invoiceItem) && $invoiceItem['taxes']) {
foreach ($invoiceItem['taxes'] as $tax) {
$tax['company_id'] = $recurringInvoice->company_id;
if (gettype($tax['amount']) !== 'NULL') {
$item->taxes()->create($tax);
}
}
}
}
}
public static function createTaxes($recurringInvoice, $taxes)
{
foreach ($taxes as $tax) {
$tax['company_id'] = $recurringInvoice->company_id;
if (gettype($tax['amount']) !== 'NULL') {
$recurringInvoice->taxes()->create($tax);
}
}
}
public function generateInvoice()
{
if (Carbon::now()->lessThan($this->starts_at)) {
return;
}
if ($this->limit_by == 'DATE') {
$startDate = Carbon::today()->format('Y-m-d');
$endDate = $this->limit_date;
if ($endDate >= $startDate) {
$this->createInvoice();
$this->updateNextInvoiceDate();
} else {
$this->markStatusAsCompleted();
}
} elseif ($this->limit_by == 'COUNT') {
$invoiceCount = Invoice::where('recurring_invoice_id', $this->id)->count();
if ($invoiceCount < $this->limit_count) {
$this->createInvoice();
$this->updateNextInvoiceDate();
} else {
$this->markStatusAsCompleted();
}
} else {
$this->createInvoice();
$this->updateNextInvoiceDate();
}
}
public function createInvoice()
{
//get invoice_number
$serial = (new SerialNumberFormatter())
->setModel(new Invoice())
->setCompany($this->company_id)
->setCustomer($this->customer_id)
->setNextNumbers();
$days = intval(CompanySetting::getSetting('invoice_due_date_days', $this->company_id));
if (! $days || $days == 'null') {
$days = 7;
}
$newInvoice['creator_id'] = $this->creator_id;
$newInvoice['invoice_date'] = Carbon::today()->format('Y-m-d');
$newInvoice['due_date'] = Carbon::today()->addDays($days)->format('Y-m-d');
$newInvoice['status'] = Invoice::STATUS_DRAFT;
$newInvoice['company_id'] = $this->company_id;
$newInvoice['paid_status'] = Invoice::STATUS_UNPAID;
$newInvoice['sub_total'] = $this->sub_total;
$newInvoice['tax_per_item'] = $this->tax_per_item;
$newInvoice['discount_per_item'] = $this->discount_per_item;
$newInvoice['tax'] = $this->tax;
$newInvoice['total'] = $this->total;
$newInvoice['customer_id'] = $this->customer_id;
$newInvoice['currency_id'] = Customer::find($this->customer_id)->currency_id;
$newInvoice['template_name'] = $this->template_name;
$newInvoice['due_amount'] = $this->total;
$newInvoice['recurring_invoice_id'] = $this->id;
$newInvoice['discount_val'] = $this->discount_val;
$newInvoice['discount'] = $this->discount;
$newInvoice['discount_type'] = $this->discount_type;
$newInvoice['notes'] = $this->notes;
$newInvoice['exchange_rate'] = $this->exchange_rate;
$newInvoice['sales_tax_type'] = $this->sales_tax_type;
$newInvoice['sales_tax_address_type'] = $this->sales_tax_address_type;
$newInvoice['invoice_number'] = $serial->getNextNumber();
$newInvoice['sequence_number'] = $serial->nextSequenceNumber;
$newInvoice['customer_sequence_number'] = $serial->nextCustomerSequenceNumber;
$newInvoice['base_due_amount'] = $this->exchange_rate * $this->due_amount;
$newInvoice['base_discount_val'] = $this->exchange_rate * $this->discount_val;
$newInvoice['base_sub_total'] = $this->exchange_rate * $this->sub_total;
$newInvoice['base_tax'] = $this->exchange_rate * $this->tax;
$newInvoice['base_total'] = $this->exchange_rate * $this->total;
$invoice = Invoice::create($newInvoice);
$invoice->unique_hash = Hashids::connection(Invoice::class)->encode($invoice->id);
$invoice->save();
$this->load('items.taxes');
Invoice::createItems($invoice, $this->items->toArray());
if ($this->taxes()->exists()) {
Invoice::createTaxes($invoice, $this->taxes->toArray());
}
if ($this->fields()->exists()) {
$customField = [];
foreach ($this->fields as $data) {
$customField[] = [
'id' => $data->custom_field_id,
'value' => $data->defaultAnswer,
];
}
$invoice->addCustomFields($customField);
}
//send automatically
if ($this->send_automatically == true) {
$data = [
'body' => CompanySetting::getSetting('invoice_mail_body', $this->company_id),
'from' => config('mail.from.address'),
'to' => $this->customer->email,
'subject' => trans('invoices')['new_invoice'],
'invoice' => $invoice->toArray(),
'customer' => $invoice->customer->toArray(),
'company' => Company::find($invoice->company_id),
];
$invoice->send($data);
}
}
public function markStatusAsCompleted()
{
if ($this->status == $this->status) {
$this->status = self::COMPLETED;
$this->save();
}
}
public static function getNextInvoiceDate($frequency, $starts_at)
{
$cron = new Cron\CronExpression($frequency);
return $cron->getNextRunDate($starts_at)->format('Y-m-d H:i:s');
}
public function updateNextInvoiceDate()
{
$nextInvoiceAt = self::getNextInvoiceDate($this->frequency, $this->starts_at);
$this->next_invoice_at = $nextInvoiceAt;
$this->save();
}
public static function deleteRecurringInvoice($ids)
{
foreach ($ids as $id) {
$recurringInvoice = self::find($id);
if ($recurringInvoice->invoices()->exists()) {
$recurringInvoice->invoices()->update(['recurring_invoice_id' => null]);
}
if ($recurringInvoice->items()->exists()) {
$recurringInvoice->items()->delete();
}
if ($recurringInvoice->taxes()->exists()) {
$recurringInvoice->taxes()->delete();
}
$recurringInvoice->delete();
}
return true;
}
}