1
0
mirror of https://github.com/invoiceninja/invoiceninja.git synced 2024-09-20 08:21:34 +02:00
invoiceninja/app/Models/Traits/GeneratesNumbers.php

374 lines
11 KiB
PHP
Raw Normal View History

2017-01-30 20:40:43 +01:00
<?php
2017-01-03 20:48:40 +01:00
2017-01-30 20:40:43 +01:00
namespace App\Models\Traits;
use App\Models\Client;
use App\Models\Invoice;
2017-01-03 20:48:40 +01:00
use Auth;
use Carbon;
/**
2017-01-30 20:40:43 +01:00
* Class GeneratesNumbers.
2017-01-03 20:48:40 +01:00
*/
trait GeneratesNumbers
{
/**
2017-01-04 09:11:32 +01:00
* @param $entity
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return mixed|string
*/
2017-01-04 09:11:32 +01:00
public function getNextNumber($entity = false)
2017-01-03 20:48:40 +01:00
{
2017-01-04 09:11:32 +01:00
$entity = $entity ?: new Client();
$entityType = $entity->getEntityType();
2017-01-03 20:48:40 +01:00
2017-01-04 09:11:32 +01:00
$counter = $this->getCounter($entityType);
$prefix = $this->getNumberPrefix($entityType);
$counterOffset = 0;
$check = false;
$lastNumber = false;
2017-01-04 09:11:32 +01:00
2017-01-08 22:41:39 +01:00
if ($entityType == ENTITY_CLIENT && ! $this->clientNumbersEnabled()) {
return '';
}
2017-01-04 09:11:32 +01:00
// confirm the invoice number isn't already taken
do {
if ($this->hasNumberPattern($entityType)) {
$number = $this->applyNumberPattern($entity, $counter);
} else {
2017-01-03 20:48:40 +01:00
$number = $prefix . str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
}
2017-01-04 09:11:32 +01:00
if ($entity->recurring_invoice_id) {
$number = $this->recurring_invoice_number_prefix . $number;
}
2017-01-04 09:11:32 +01:00
if ($entity->isEntityType(ENTITY_CLIENT)) {
$check = Client::scope(false, $this->id)->whereIdNumber($number)->withTrashed()->first();
} else {
$check = Invoice::scope(false, $this->id)->whereInvoiceNumber($number)->withTrashed()->first();
}
$counter++;
$counterOffset++;
// prevent getting stuck in a loop
if ($number == $lastNumber) {
return '';
}
$lastNumber = $number;
2017-01-04 09:11:32 +01:00
} while ($check);
// update the counter to be caught up
if ($counterOffset > 1) {
2018-04-26 11:01:15 +02:00
$this->syncOriginal();
2017-01-06 11:36:27 +01:00
if ($entity->isEntityType(ENTITY_CLIENT)) {
if ($this->clientNumbersEnabled()) {
$this->client_number_counter += $counterOffset - 1;
$this->save();
}
} elseif ($entity->isEntityType(ENTITY_CREDIT)) {
if ($this->creditNumbersEnabled()) {
$this->credit_number_counter += $counterOffset - 1;
$this->save();
}
2017-01-09 12:13:59 +01:00
} elseif ($entity->isType(INVOICE_TYPE_QUOTE)) {
2017-01-30 17:05:31 +01:00
if (! $this->share_counter) {
2017-01-09 12:13:59 +01:00
$this->quote_number_counter += $counterOffset - 1;
$this->save();
}
2017-01-04 09:11:32 +01:00
} else {
2017-01-06 11:36:27 +01:00
$this->invoice_number_counter += $counterOffset - 1;
$this->save();
2017-01-04 09:11:32 +01:00
}
2017-01-03 20:48:40 +01:00
}
return $number;
}
/**
2017-01-04 09:11:32 +01:00
* @param $entityType
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return string
*/
2017-01-04 09:11:32 +01:00
public function getNumberPrefix($entityType)
2017-01-03 20:48:40 +01:00
{
2017-01-30 17:05:31 +01:00
if (! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
2017-01-03 20:48:40 +01:00
return '';
}
2017-01-04 09:11:32 +01:00
$field = "{$entityType}_number_prefix";
2017-01-30 20:40:43 +01:00
2017-01-04 09:11:32 +01:00
return $this->$field ?: '';
2017-01-03 20:48:40 +01:00
}
/**
2017-01-04 09:11:32 +01:00
* @param $entityType
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return bool
*/
2017-01-04 09:11:32 +01:00
public function getNumberPattern($entityType)
2017-01-03 20:48:40 +01:00
{
2017-01-30 17:05:31 +01:00
if (! $this->hasFeature(FEATURE_INVOICE_SETTINGS)) {
2017-01-03 20:48:40 +01:00
return false;
}
2017-01-04 09:11:32 +01:00
$field = "{$entityType}_number_pattern";
2017-01-30 20:40:43 +01:00
2017-01-04 09:11:32 +01:00
return $this->$field;
2017-01-03 20:48:40 +01:00
}
/**
2017-01-04 09:11:32 +01:00
* @param $entityType
2017-01-30 20:40:43 +01:00
*
2017-01-04 09:11:32 +01:00
* @return bool
*/
public function hasNumberPattern($entityType)
{
return $this->getNumberPattern($entityType) ? true : false;
}
/**
* @param $entityType
2017-01-30 20:49:42 +01:00
* @param mixed $invoice
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return string
*/
public function hasClientNumberPattern($invoice)
{
2017-11-21 10:36:00 +01:00
if (! $this->isPro()) {
return false;
}
2017-01-03 20:48:40 +01:00
$pattern = $invoice->invoice_type_id == INVOICE_TYPE_QUOTE ? $this->quote_number_pattern : $this->invoice_number_pattern;
2017-03-29 10:46:52 +02:00
return strstr($pattern, '$client') !== false || strstr($pattern, '$idNumber') !== false;
2017-01-03 20:48:40 +01:00
}
/**
2017-01-04 09:11:32 +01:00
* @param $entity
2017-01-30 20:49:42 +01:00
* @param mixed $counter
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return bool|mixed
*/
2017-01-04 09:11:32 +01:00
public function applyNumberPattern($entity, $counter = 0)
2017-01-03 20:48:40 +01:00
{
2017-01-04 09:11:32 +01:00
$entityType = $entity->getEntityType();
$counter = $counter ?: $this->getCounter($entityType);
$pattern = $this->getNumberPattern($entityType);
2017-01-03 20:48:40 +01:00
2017-01-30 20:40:43 +01:00
if (! $pattern) {
2017-01-03 20:48:40 +01:00
return false;
}
$search = ['{$year}'];
$replace = [date('Y')];
$search[] = '{$counter}';
2017-01-04 09:11:32 +01:00
$replace[] = str_pad($counter, $this->invoice_number_padding, '0', STR_PAD_LEFT);
2017-01-03 20:48:40 +01:00
if (strstr($pattern, '{$userId}')) {
2017-01-04 11:52:32 +01:00
$userId = $entity->user ? $entity->user->public_id : (Auth::check() ? Auth::user()->public_id : 0);
2017-01-03 20:48:40 +01:00
$search[] = '{$userId}';
2017-01-04 11:52:32 +01:00
$replace[] = str_pad(($userId + 1), 2, '0', STR_PAD_LEFT);
2017-01-03 20:48:40 +01:00
}
$matches = false;
preg_match('/{\$date:(.*?)}/', $pattern, $matches);
if (count($matches) > 1) {
$format = $matches[1];
$search[] = $matches[0];
2017-03-31 10:55:26 +02:00
//$date = date_create()->format($format);
$date = Carbon::now(session(SESSION_TIMEZONE, DEFAULT_TIMEZONE))->format($format);
2017-01-03 20:48:40 +01:00
$replace[] = str_replace($format, $date, $matches[1]);
}
$pattern = str_replace($search, $replace, $pattern);
2017-03-29 10:46:52 +02:00
$pattern = $this->getClientInvoiceNumber($pattern, $entity);
2017-01-03 20:48:40 +01:00
return $pattern;
}
/**
* @param $pattern
* @param $invoice
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return mixed
*/
private function getClientInvoiceNumber($pattern, $invoice)
{
2017-03-29 10:46:52 +02:00
if (! $invoice->client_id) {
2017-01-03 20:48:40 +01:00
return $pattern;
}
$search = [
'{$custom1}',
'{$custom2}',
'{$idNumber}',
2017-03-29 10:46:52 +02:00
'{$clientCustom1}',
'{$clientCustom2}',
'{$clientIdNumber}',
2017-04-02 19:35:10 +02:00
'{$clientCounter}',
2017-01-03 20:48:40 +01:00
];
$client = $invoice->client;
2017-05-04 01:39:31 +02:00
$clientCounter = ($invoice->isQuote() && ! $this->share_counter) ? $client->quote_number_counter : $client->invoice_number_counter;
2017-01-03 20:48:40 +01:00
$replace = [
$client->custom_value1,
$client->custom_value2,
$client->id_number,
$client->custom_value1, // backwards compatibility
$client->custom_value2,
$client->id_number,
str_pad($clientCounter, $this->invoice_number_padding, '0', STR_PAD_LEFT),
2017-01-03 20:48:40 +01:00
];
return str_replace($search, $replace, $pattern);
}
/**
2017-01-04 09:11:32 +01:00
* @param $entityType
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return mixed
*/
2017-01-04 09:11:32 +01:00
public function getCounter($entityType)
2017-01-03 20:48:40 +01:00
{
2017-01-04 09:11:32 +01:00
if ($entityType == ENTITY_CLIENT) {
return $this->client_number_counter;
} elseif ($entityType == ENTITY_CREDIT) {
return $this->credit_number_counter;
2017-01-04 09:11:32 +01:00
} elseif ($entityType == ENTITY_QUOTE && ! $this->share_counter) {
return $this->quote_number_counter;
} else {
return $this->invoice_number_counter;
}
2017-01-03 20:48:40 +01:00
}
/**
* @param $entityType
2017-01-30 20:40:43 +01:00
*
2017-01-03 20:48:40 +01:00
* @return mixed|string
*/
public function previewNextInvoiceNumber($entityType = ENTITY_INVOICE)
{
2017-03-29 10:46:52 +02:00
$client = \App\Models\Client::scope()->first();
$invoice = $this->createInvoice($entityType, $client ? $client->id : 0);
2017-01-30 20:40:43 +01:00
2017-01-04 09:11:32 +01:00
return $this->getNextNumber($invoice);
2017-01-03 20:48:40 +01:00
}
/**
2017-01-04 09:11:32 +01:00
* @param $entity
2017-01-03 20:48:40 +01:00
*/
2017-01-04 09:11:32 +01:00
public function incrementCounter($entity)
2017-01-03 20:48:40 +01:00
{
2017-01-04 09:11:32 +01:00
if ($entity->isEntityType(ENTITY_CLIENT)) {
if ($this->client_number_counter > 0) {
2017-01-05 11:46:03 +01:00
$this->client_number_counter += 1;
}
2017-03-29 10:46:52 +02:00
$this->save();
return;
} elseif ($entity->isEntityType(ENTITY_CREDIT)) {
if ($this->credit_number_counter > 0) {
$this->credit_number_counter += 1;
}
$this->save();
return;
2017-03-29 10:46:52 +02:00
}
if ($this->usesClientInvoiceCounter()) {
2017-04-02 19:35:10 +02:00
if ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
$entity->client->quote_number_counter += 1;
} else {
$entity->client->invoice_number_counter += 1;
}
2017-03-29 10:46:52 +02:00
$entity->client->save();
}
if ($this->usesInvoiceCounter()) {
if ($entity->isType(INVOICE_TYPE_QUOTE) && ! $this->share_counter) {
$this->quote_number_counter += 1;
} else {
$this->invoice_number_counter += 1;
}
$this->save();
2017-01-03 20:48:40 +01:00
}
2017-03-29 10:46:52 +02:00
}
public function usesInvoiceCounter()
{
return ! $this->hasNumberPattern(ENTITY_INVOICE) || strpos($this->invoice_number_pattern, '{$counter}') !== false;
2017-03-29 10:46:52 +02:00
}
2017-01-03 20:48:40 +01:00
2017-03-29 10:46:52 +02:00
public function usesClientInvoiceCounter()
{
2017-04-02 19:35:10 +02:00
return strpos($this->invoice_number_pattern, '{$clientCounter}') !== false;
2017-01-03 20:48:40 +01:00
}
2017-01-04 09:11:32 +01:00
public function clientNumbersEnabled()
2017-01-03 20:48:40 +01:00
{
2017-02-28 19:24:30 +01:00
return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->client_number_counter > 0;
2017-01-03 20:48:40 +01:00
}
2017-03-23 10:39:44 +01:00
public function creditNumbersEnabled()
{
return $this->hasFeature(FEATURE_INVOICE_SETTINGS) && $this->credit_number_counter > 0;
}
2017-03-23 10:39:44 +01:00
public function checkCounterReset()
{
if (! $this->reset_counter_frequency_id || ! $this->reset_counter_date) {
return false;
}
$timezone = $this->getTimezone();
$resetDate = Carbon::parse($this->reset_counter_date, $timezone);
if (! $resetDate->isToday()) {
return false;
}
switch ($this->reset_counter_frequency_id) {
case FREQUENCY_WEEKLY:
$resetDate->addWeek();
break;
case FREQUENCY_TWO_WEEKS:
$resetDate->addWeeks(2);
break;
case FREQUENCY_FOUR_WEEKS:
$resetDate->addWeeks(4);
break;
case FREQUENCY_MONTHLY:
$resetDate->addMonth();
break;
case FREQUENCY_TWO_MONTHS:
$resetDate->addMonths(2);
break;
case FREQUENCY_THREE_MONTHS:
$resetDate->addMonths(3);
break;
2017-10-25 11:22:01 +02:00
case FREQUENCY_FOUR_MONTHS:
$resetDate->addMonths(4);
break;
2017-03-23 10:39:44 +01:00
case FREQUENCY_SIX_MONTHS:
$resetDate->addMonths(6);
break;
case FREQUENCY_ANNUALLY:
$resetDate->addYear();
break;
2017-12-31 08:55:00 +01:00
case FREQUENCY_TWO_YEARS:
$resetDate->addYears(2);
break;
2017-03-23 10:39:44 +01:00
}
$this->reset_counter_date = $resetDate->format('Y-m-d');
$this->invoice_number_counter = 1;
$this->quote_number_counter = 1;
$this->credit_number_counter = $this->credit_number_counter > 0 ? 1 : 0;
2017-03-23 10:39:44 +01:00
$this->save();
}
2017-01-03 20:48:40 +01:00
}